|  | /* | 
|  | * Copyright (C) 2000-2009, Parallels, Inc. All rights reserved. | 
|  | * | 
|  | * This code is public domain. | 
|  | * | 
|  | * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | 
|  | * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <ctype.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <netdb.h> | 
|  | #include <sys/socket.h> | 
|  | #include <netinet/in.h> | 
|  |  | 
|  | #include <linux/if_ether.h> | 
|  | #include <linux/if_arp.h> | 
|  | #include <linux/if_packet.h> | 
|  | #include <linux/if.h> | 
|  |  | 
|  | #include <netinet/in.h> | 
|  |  | 
|  | #include <sys/ioctl.h> | 
|  | #include <arpa/inet.h> | 
|  |  | 
|  | #include <getopt.h> | 
|  | #include <unistd.h> | 
|  | #include <signal.h> | 
|  |  | 
|  | #define EXC_OK 0 | 
|  | #define EXC_USAGE	1 | 
|  | #define EXC_SYS		2 | 
|  | #define EXC_RECV	3 | 
|  | #define EXC_NORECV	4 | 
|  |  | 
|  | /* log facility */ | 
|  | #define LOG_ERROR	0x01 | 
|  | #define LOG_WARNING	0x02 | 
|  | #define LOG_INFO	0x03 | 
|  | #define LOG_DEBUG	0x04 | 
|  |  | 
|  | #define LOGGER_NAME "arpsend: " | 
|  | int debug_level = LOG_INFO; | 
|  |  | 
|  | #define logger(level, fmt, args...) \ | 
|  | (debug_level < level ? (void)0 : \ | 
|  | fprintf(stderr, LOGGER_NAME fmt "\n", ##args)) | 
|  |  | 
|  | #define IP_ADDR_LEN 4 | 
|  |  | 
|  | #define REQUEST ARPOP_REQUEST | 
|  | #define REPLY ARPOP_REPLY | 
|  |  | 
|  | struct arp_packet { | 
|  | u_char targ_hw_addr[ETH_ALEN];	/* ethernet destination MAC address */ | 
|  | u_char src_hw_addr[ETH_ALEN];	/* ethernet source MAC address */ | 
|  | u_short frame_type;		/* ethernet packet type */ | 
|  |  | 
|  | /* ARP packet */ | 
|  | u_short hw_type;		/* hardware address type (ethernet) */ | 
|  | u_short prot_type;		/* resolve protocol address type (ip) */ | 
|  | u_char hw_addr_size; | 
|  | u_char prot_addr_size; | 
|  | u_short op;			/* subtype */ | 
|  | u_char sndr_hw_addr[ETH_ALEN]; | 
|  | u_char sndr_ip_addr[IP_ADDR_LEN]; | 
|  | u_char rcpt_hw_addr[ETH_ALEN]; | 
|  | u_char rcpt_ip_addr[IP_ADDR_LEN]; | 
|  |  | 
|  | /* ethernet padding */ | 
|  | u_char padding[18]; | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | AR_NOTHING = 0, | 
|  | AR_UPDATE, | 
|  | AR_DETECT, | 
|  | AR_REQUEST, | 
|  | AR_REPLY | 
|  | } cmd = AR_NOTHING; | 
|  |  | 
|  | char* iface = NULL; | 
|  |  | 
|  | int timeout = 1; | 
|  | int count = -1; | 
|  |  | 
|  | char *ip6_addr; | 
|  |  | 
|  | /* source MAC address in Ethernet header */ | 
|  | unsigned char src_hwaddr[ETH_ALEN]; | 
|  | int src_hwaddr_flag = 0; | 
|  | /* target MAC address in Ethernet header */ | 
|  | unsigned char trg_hwaddr[ETH_ALEN]; | 
|  | int trg_hwaddr_flag = 0; | 
|  | /* source MAC address in ARP header */ | 
|  | unsigned char src_arp_hwaddr[ETH_ALEN]; | 
|  | int src_arp_hwaddr_flag = 0; | 
|  | /* target MAC address in ARP header */ | 
|  | unsigned char trg_arp_hwaddr[ETH_ALEN]; | 
|  | int trg_arp_hwaddr_flag = 0; | 
|  |  | 
|  | struct in_addr src_ipaddr; | 
|  | int src_ipaddr_flag = 0; | 
|  |  | 
|  | int at_once = 0; | 
|  |  | 
|  | #define MAX_IPADDR_NUMBER	1024 | 
|  | struct in_addr trg_ipaddr[MAX_IPADDR_NUMBER]; | 
|  | int trg_ipaddr_count = 0; | 
|  | int trg_ipaddr_flag = 0; | 
|  |  | 
|  | const unsigned char broadcast[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | 
|  | struct in_addr zero = {0x00000000}; | 
|  |  | 
|  | struct sockaddr_ll iaddr; | 
|  |  | 
|  | static char* print_hw_addr(const u_char* addr); | 
|  | static char* print_ip_addr(const u_char* addr); | 
|  | static char* print_arp_packet(struct arp_packet* pkt); | 
|  | static int read_hw_addr(u_char* buf, const char* str); | 
|  | static int read_ip_addr(struct in_addr* in_addr, const char* str); | 
|  |  | 
|  | char* program_name = NULL; | 
|  |  | 
|  | static void usage() | 
|  | { | 
|  | fprintf(stderr, "Usage: %s <-U -i <src_ip_addr> | -D -e <trg_ip_addr> " | 
|  | "[-e <trg_ip_addr>] ...> [-c <count>] [-w <timeout>] " | 
|  | "interface_name\n", program_name); | 
|  | exit(EXC_USAGE); | 
|  | } | 
|  |  | 
|  | static void parse_options (int argc, char **argv) | 
|  | { | 
|  | #define read_hw(addr)						\ | 
|  | ({							\ | 
|  | if (!strcmp(optarg, "broadcast"))		\ | 
|  | memcpy(addr, broadcast, ETH_ALEN);	\ | 
|  | else if (read_hw_addr(addr, optarg) < 0)	\ | 
|  | usage();				\ | 
|  | addr##_flag = 1;				\ | 
|  | }) | 
|  | #define read_ip(addr)					\ | 
|  | ({						\ | 
|  | if (read_ip_addr(&addr, optarg) < 0)	\ | 
|  | usage();			\ | 
|  | addr##_flag = 1;			\ | 
|  | }) | 
|  |  | 
|  | int c; | 
|  | static char short_options[] = "UDQPc:w:s:t:S:T:i:e:ov"; | 
|  | static struct option long_options[] = | 
|  | { | 
|  | {"update", 0, NULL, 'U'}, | 
|  | {"detect", 0, NULL, 'D'}, | 
|  | {"request", 0, NULL, 'Q'}, | 
|  | {"reply", 0, NULL, 'P'}, | 
|  | {"count", 1, NULL, 'c'}, | 
|  | {"timeout", 1, NULL, 'w'}, | 
|  | {"src-hw", 1, NULL, 's'}, | 
|  | {"trg-hw", 1, NULL, 't'}, | 
|  | {"src-arp", 1, NULL, 'S'}, | 
|  | {"trg-arp", 1, NULL, 'T'}, | 
|  | {"src-ip", 1, NULL, 'i'}, | 
|  | {"trg-ip", 1, NULL, 'e'}, | 
|  | {"at-once", 0, NULL, 'o'}, | 
|  | {NULL, 0, NULL, 0} | 
|  | }; | 
|  |  | 
|  | while ((c = getopt_long(argc, argv, | 
|  | short_options, long_options, NULL)) != -1) | 
|  | { | 
|  | switch (c) | 
|  | { | 
|  | case 'U': | 
|  | if (cmd) | 
|  | usage(); | 
|  | cmd = AR_UPDATE; | 
|  | break; | 
|  | case 'D': | 
|  | if (cmd) | 
|  | usage(); | 
|  | cmd = AR_DETECT; | 
|  | break; | 
|  | case 'Q': | 
|  | if (cmd) | 
|  | usage(); | 
|  | cmd = AR_REQUEST; | 
|  | break; | 
|  | case 'P': | 
|  | if (cmd) | 
|  | usage(); | 
|  | cmd = AR_REPLY; | 
|  | break; | 
|  | case 'c': | 
|  | count = atoi(optarg); | 
|  | if (!count) | 
|  | usage(); | 
|  | break; | 
|  | case 'w': | 
|  | timeout = atoi(optarg); | 
|  | break; | 
|  | case 's': | 
|  | read_hw(src_hwaddr); | 
|  | break; | 
|  | case 't': | 
|  | read_hw(trg_hwaddr); | 
|  | break; | 
|  | case 'S': | 
|  | read_hw(src_arp_hwaddr); | 
|  | break; | 
|  | case 'T': | 
|  | read_hw(trg_arp_hwaddr); | 
|  | break; | 
|  | case 'i': | 
|  | if (strchr(optarg, ':')) { | 
|  | ip6_addr = optarg; | 
|  | src_ipaddr_flag = 1; | 
|  | break; | 
|  | } | 
|  | read_ip(src_ipaddr); | 
|  | break; | 
|  | case 'e': | 
|  | if (strchr(optarg, ':')) { | 
|  | trg_ipaddr_flag = 1; | 
|  | ip6_addr = optarg; | 
|  | break; | 
|  | } | 
|  | if (trg_ipaddr_count >= MAX_IPADDR_NUMBER) | 
|  | usage(); | 
|  | if (read_ip_addr(&trg_ipaddr[trg_ipaddr_count++], | 
|  | optarg) < 0) | 
|  | usage(); | 
|  | trg_ipaddr_flag = 1; | 
|  | break; | 
|  | case 'o': | 
|  | at_once = 1; | 
|  | break; | 
|  | case 'v': | 
|  | debug_level ++ ; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | } | 
|  | } | 
|  |  | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (cmd == AR_NOTHING || argc != 1 | 
|  | || (cmd == AR_DETECT && !trg_ipaddr_flag) | 
|  | || (cmd == AR_UPDATE && !src_ipaddr_flag)) | 
|  | usage(); | 
|  |  | 
|  | iface = argv[0]; | 
|  |  | 
|  | /* user have to choose something */ | 
|  | if (!cmd) | 
|  | usage(); | 
|  |  | 
|  | if (cmd == AR_DETECT | 
|  | && trg_ipaddr_count <= 1) { | 
|  | /* for detection with no more then one target | 
|  | we exit on first reply */ | 
|  | at_once = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | u_char real_hwaddr[ETH_ALEN]; | 
|  | struct in_addr real_ipaddr; | 
|  |  | 
|  | static int init_device_addresses(int sock, const char* device) | 
|  | { | 
|  | struct ifreq ifr; | 
|  | int ifindex; | 
|  | short int ifflags; | 
|  |  | 
|  | memset(&ifr, 0, sizeof(ifr)); | 
|  | strncpy(ifr.ifr_name, device, IFNAMSIZ-1); | 
|  |  | 
|  | if (ioctl(sock, SIOCGIFINDEX, &ifr) != 0) { | 
|  | logger(LOG_ERROR, "unknown iface %s : %m", device); | 
|  | return -1; | 
|  | } | 
|  | ifindex = ifr.ifr_ifindex; | 
|  |  | 
|  | if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { | 
|  | logger(LOG_ERROR, "ioctl(SIOCGIFFLAGS) : %m"); | 
|  | return -1; | 
|  | } | 
|  | ifflags = ifr.ifr_flags; | 
|  |  | 
|  | if (!(ifflags & IFF_UP)) { | 
|  | logger(LOG_ERROR, "iface '%s' is down", device); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (ifflags & (IFF_NOARP|IFF_LOOPBACK)) { | 
|  | logger(LOG_ERROR, "iface '%s' is not ARPable", device); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (ifflags & IFF_SLAVE) { | 
|  | logger(LOG_ERROR, "iface '%s' is slave", device); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* get interface HW address */ | 
|  | if (ioctl(sock, SIOCGIFHWADDR, &ifr) != 0) { | 
|  | logger(LOG_ERROR, "can't get iface '%s' HW address : %m", device); | 
|  | return -1; | 
|  | } | 
|  | memcpy(real_hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); | 
|  |  | 
|  | if (ioctl(sock, SIOCGIFADDR, &ifr)) { | 
|  | logger(LOG_ERROR, "can't get iface '%s' address : %m", device); | 
|  | return -1; | 
|  | } | 
|  | memcpy(&real_ipaddr, ifr.ifr_addr.sa_data + 2, IP_ADDR_LEN); | 
|  |  | 
|  | logger(LOG_DEBUG, "got addresses hw='%s', ip='%s'", | 
|  | print_hw_addr(real_hwaddr), | 
|  | print_ip_addr((u_char*) &real_ipaddr)); | 
|  |  | 
|  | /* fill interface description struct */ | 
|  | iaddr.sll_ifindex = ifindex; | 
|  | iaddr.sll_family = AF_PACKET; | 
|  | iaddr.sll_protocol = htons(ETH_P_ARP); | 
|  | memcpy(iaddr.sll_addr, real_hwaddr, iaddr.sll_halen = ETH_ALEN); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int sock; | 
|  | /* sent packet */ | 
|  | struct arp_packet pkt; | 
|  |  | 
|  | static void create_arp_packet(struct arp_packet* pkt) | 
|  | { | 
|  | #define set_ip(to, from) (memcpy(to, from, IP_ADDR_LEN)) | 
|  | #define set_hw(to, from) (memcpy(to, from, ETH_ALEN)) | 
|  | #define check(check, two) (check##_flag ? check : two) | 
|  |  | 
|  | pkt->frame_type = htons(ETH_P_ARP); | 
|  | pkt->hw_type = htons(ARPHRD_ETHER); | 
|  | pkt->prot_type = htons(ETH_P_IP); | 
|  | pkt->hw_addr_size = ETH_ALEN; | 
|  | pkt->prot_addr_size = IP_ADDR_LEN; | 
|  | pkt->op = htons(cmd == AR_REPLY ? REPLY : REQUEST); | 
|  |  | 
|  | set_ip(pkt->sndr_ip_addr, | 
|  | (src_ipaddr_flag ? &src_ipaddr : &real_ipaddr)); | 
|  |  | 
|  | set_hw(pkt->targ_hw_addr, check(trg_hwaddr, broadcast)); | 
|  | set_hw(pkt->src_hw_addr, check(src_hwaddr, real_hwaddr)); | 
|  |  | 
|  | set_hw(pkt->rcpt_hw_addr, check(trg_arp_hwaddr, pkt->targ_hw_addr)); | 
|  | set_hw(pkt->sndr_hw_addr, check(src_arp_hwaddr, pkt->src_hw_addr)); | 
|  |  | 
|  | /* Special case. Because we support multiple recipient IP addresses | 
|  | we set up 'pkt.rcpt_ip_addr' separately for each of the specified | 
|  | (using -e option) 'trg_ipaddr' | 
|  | */ | 
|  | if (trg_ipaddr_flag) { | 
|  | /* at least one trg_ipaddr is specified */ | 
|  | set_ip(pkt->rcpt_ip_addr, &trg_ipaddr[0]); | 
|  | } else { | 
|  | set_ip(pkt->rcpt_ip_addr, &real_ipaddr); | 
|  | /* set 'trg_ipaddr' for checking incoming packets */ | 
|  | set_ip(&trg_ipaddr[0], &real_ipaddr); | 
|  | trg_ipaddr_count = 1; | 
|  | } | 
|  | #undef set_ip | 
|  | } | 
|  |  | 
|  | static void set_trg_ipaddr(struct arp_packet* pkt, const struct in_addr ipaddr) | 
|  | { | 
|  | memcpy(pkt->rcpt_ip_addr, &ipaddr, IP_ADDR_LEN); | 
|  | } | 
|  |  | 
|  | int recv_response = 0; | 
|  |  | 
|  | static void finish() | 
|  | { | 
|  | switch (cmd) | 
|  | { | 
|  | case AR_DETECT: | 
|  | case AR_UPDATE: | 
|  | exit((recv_response) ? EXC_RECV : EXC_OK); | 
|  | case AR_REQUEST: | 
|  | exit((recv_response) ? EXC_OK : EXC_NORECV); | 
|  | case AR_REPLY: | 
|  | exit(EXC_OK); | 
|  | default: | 
|  | exit(EXC_SYS); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int recv_pack(void *buf, int len, struct sockaddr_ll *from) | 
|  | { | 
|  | int rc = -1; | 
|  | int i; | 
|  | struct arp_packet * recv_pkt = (struct arp_packet*) buf; | 
|  |  | 
|  | if (recv_pkt->frame_type != htons(ETH_P_ARP)) { | 
|  | logger(LOG_ERROR, "unknown eth frame type : %#x", | 
|  | recv_pkt->frame_type); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (recv_pkt->op != htons(REQUEST) | 
|  | && recv_pkt->op != htons(REPLY)) { | 
|  | logger(LOG_ERROR, "unknown arp packet type : %#x", | 
|  | recv_pkt->op); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < trg_ipaddr_count; i ++) { | 
|  | // check for all sent packets addresses | 
|  | if (memcmp(&trg_ipaddr[i], recv_pkt->sndr_ip_addr, | 
|  | IP_ADDR_LEN)) | 
|  | continue; | 
|  | logger(LOG_DEBUG, "recv packet %s", | 
|  | print_arp_packet(recv_pkt)); | 
|  | logger(LOG_INFO, "%s is detected on another computer : %s", | 
|  | print_ip_addr((u_char*) &trg_ipaddr[i]), | 
|  | print_hw_addr(recv_pkt->sndr_hw_addr)); | 
|  | recv_response++; | 
|  | /* finish on first response */ | 
|  | if (at_once) | 
|  | finish(); | 
|  | } | 
|  |  | 
|  | /* unknown packet */ | 
|  | logger(LOG_DEBUG, "recv unknown packet %s", | 
|  | print_arp_packet(recv_pkt)); | 
|  | rc = 0; | 
|  | out: | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | static void sender(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (count-- == 0) | 
|  | finish(); | 
|  |  | 
|  | /* send multiple packets */ | 
|  | for (i = 0; i < trg_ipaddr_count; i ++) { | 
|  | set_trg_ipaddr(&pkt, trg_ipaddr[i]); | 
|  | logger(LOG_DEBUG, "send packet: %s", print_arp_packet(&pkt)); | 
|  | if (sendto(sock, &pkt, sizeof(pkt), 0, | 
|  | (struct sockaddr*) &iaddr, sizeof(iaddr)) < 0) { | 
|  | logger(LOG_ERROR, "sendto : %m"); | 
|  | exit(EXC_SYS); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (count == 0 | 
|  | && cmd != AR_DETECT | 
|  | && cmd != AR_REQUEST) | 
|  | finish(); | 
|  |  | 
|  | alarm(timeout); | 
|  | } | 
|  |  | 
|  | static void set_signal(int signo, void (*handler)(void)) | 
|  | { | 
|  | struct sigaction sa; | 
|  |  | 
|  | memset(&sa, 0, sizeof(sa)); | 
|  | sa.sa_handler = (void (*)(int))handler; | 
|  | sa.sa_flags = SA_RESTART; | 
|  | sigaction(signo, &sa, NULL); | 
|  | } | 
|  |  | 
|  | int main(int argc, char** argv) | 
|  | { | 
|  | sigset_t block_alarm; | 
|  | program_name = argv[0]; | 
|  | parse_options (argc, argv); | 
|  |  | 
|  | if (ip6_addr) { | 
|  | if (cmd == AR_UPDATE) | 
|  | execlp(SBINDIR "/ndsend", "ndsend", | 
|  | ip6_addr, iface, NULL); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); | 
|  | if (sock < 0) | 
|  | { | 
|  | logger(LOG_ERROR, "socket : %m"); | 
|  | exit(EXC_SYS); | 
|  | } | 
|  |  | 
|  | if (init_device_addresses(sock, iface) < 0) | 
|  | exit(EXC_SYS); | 
|  |  | 
|  | create_arp_packet(&pkt); | 
|  | set_signal(SIGALRM, sender); | 
|  | sigemptyset(&block_alarm); | 
|  | sigaddset(&block_alarm, SIGALRM); | 
|  | sender(); | 
|  |  | 
|  | while(1) | 
|  | { | 
|  | u_char packet[4096]; | 
|  | struct sockaddr_ll from; | 
|  | socklen_t alen = sizeof(from); | 
|  | int cc; | 
|  |  | 
|  | cc = recvfrom(sock, packet, sizeof(packet), 0, | 
|  | (struct sockaddr *)&from, &alen); | 
|  | if (cc < 0) | 
|  | { | 
|  | logger(LOG_ERROR, "recvfrom : %m"); | 
|  | continue; | 
|  | } | 
|  | sigprocmask (SIG_BLOCK, &block_alarm, NULL); | 
|  | recv_pack(packet, cc, &from); | 
|  | sigprocmask (SIG_UNBLOCK, &block_alarm, NULL); | 
|  | } | 
|  |  | 
|  | exit(EXC_OK); | 
|  | } | 
|  |  | 
|  | static char* get_buf() | 
|  | { | 
|  | static int num = 0; | 
|  | static char buf[10][1024]; | 
|  | return buf[num = (num+1) % 10]; | 
|  | } | 
|  |  | 
|  | static char* print_arp_packet(struct arp_packet* pkt) | 
|  | { | 
|  | char* point = get_buf(); | 
|  | sprintf(point, "eth '%s' -> eth '%s'; " | 
|  | "arp sndr '%s' '%s'; %s; arp recipient '%s' '%s'", | 
|  | print_hw_addr(pkt->src_hw_addr), | 
|  | print_hw_addr(pkt->targ_hw_addr), | 
|  | print_hw_addr(pkt->sndr_hw_addr), | 
|  | print_ip_addr(pkt->sndr_ip_addr), | 
|  | (pkt->op == htons(REQUEST)) ? "request" : | 
|  | (pkt->op == htons(REPLY) ? "reply" : "unknown"), | 
|  | print_hw_addr(pkt->rcpt_hw_addr), | 
|  | print_ip_addr(pkt->rcpt_ip_addr)); | 
|  | return point; | 
|  | } | 
|  |  | 
|  | /* get MAC address in user form */ | 
|  | static char* print_hw_addr(const u_char* addr) | 
|  | { | 
|  | int i; | 
|  | char* point = get_buf(); | 
|  | char* current = point; | 
|  |  | 
|  | for (i = 0; i < ETH_ALEN; i++) | 
|  | point += sprintf(point, i == (ETH_ALEN-1) ? "%02x" : "%02x:", | 
|  | addr[i]); | 
|  |  | 
|  | return current; | 
|  | } | 
|  |  | 
|  | /* get IP address in user form */ | 
|  | static char* print_ip_addr(const u_char* addr) | 
|  | { | 
|  | int i; | 
|  | char* point = get_buf(); | 
|  | char* current = point; | 
|  |  | 
|  | for (i = 0; i < IP_ADDR_LEN; i++) | 
|  | point += sprintf(point, i == (IP_ADDR_LEN-1) ? "%u" : "%u.", | 
|  | addr[i]); | 
|  |  | 
|  | return current; | 
|  | } | 
|  |  | 
|  | /* Transform user -> computer friendly MAC address */ | 
|  | static int read_hw_addr(u_char* buf, const char* str) | 
|  | { | 
|  | int rc = -1; | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < 2*ETH_ALEN; i++) | 
|  | { | 
|  | char c, val; | 
|  |  | 
|  | c = tolower(*str++); | 
|  | if (!c) | 
|  | goto out; | 
|  |  | 
|  | if (isdigit(c)) | 
|  | val = c - '0'; | 
|  | else if (c >= 'a' && c <= 'f') | 
|  | val = c - 'a' + 10; | 
|  | else | 
|  | goto out; | 
|  |  | 
|  | if (i % 2) | 
|  | { | 
|  | *buf++ |= val; | 
|  | if (*str == ':') | 
|  | str++; | 
|  | } | 
|  | else | 
|  | *buf = val << 4; | 
|  | } | 
|  | rc = 0; | 
|  | out: | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /* Transform user -> computer friendly IP address */ | 
|  | static int read_ip_addr(struct in_addr* in_addr, const char* str) | 
|  | { | 
|  | in_addr->s_addr=inet_addr(str); | 
|  | if (in_addr->s_addr == INADDR_NONE) | 
|  | return -1; | 
|  | return 0; | 
|  | } |