--- embedaddon/choparp/choparp.c 2012/02/21 16:49:51 1.1.1.1 +++ embedaddon/choparp/choparp.c 2016/10/18 13:39:35 1.1.1.1.2.2 @@ -2,7 +2,7 @@ choparp - cheap & omitted proxy arp Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp) - Copyright (c) 2002 Thomas Quinot (thomas@cuivre.fr.eu.org) + Copyright (c) 2002-2015 Thomas Quinot (thomas@cuivre.fr.eu.org) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -36,305 +36,141 @@ */ +#define _GNU_SOURCE /* asprintf */ + +#include #include -#include #include #include +#include +#include #include -#include -#include -#include -#include -#include #include -#include #include -/* #include */ -#if (__FreeBSD__ >= 3) - #include -#endif #include -#include -#include +#include +#include +#include +#include + +#ifndef __linux__ #include #include - -#ifdef DEBUG -#include #endif -#define BPFFILENAME "/dev/bpf%d" /* bpf file template */ -#ifndef NBPFILTER /* number of available bpf */ -# define NBPFILTER (16) -#endif +/* ARP Header */ +#define ARP_REQUEST 1 /* ARP Request */ +#define ARP_REPLY 2 /* ARP Reply */ struct cidr { struct cidr *next; - u_int32_t addr; /* addr and mask are host order */ - u_int32_t mask; + struct in_addr addr; /* addr and mask are host order */ + struct in_addr mask; }; struct cidr *targets = NULL, *excludes = NULL; -u_char target_mac[ETHER_ADDR_LEN]; /* target MAC address */ -int verbose = 0; +char errbuf[PCAP_ERRBUF_SIZE]; +u_char target_mac[ETHER_ADDR_LEN]; /* target MAC address */ -/* - ARP filter program -*/ -struct bpf_insn bpf_filter_arp[] = { - /* check Ethernet Encapsulation (RFC894) first */ - BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* load frame type */ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_ARP, 0, 3), /* check it */ - BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20), /* load OP code */ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 1), /* check it */ - BPF_STMT(BPF_RET+BPF_K, 14+28), /* return Ethernet encap ARP req. */ - /* XXX: IEEE 802.2/802.3 Encap (RFC1042) should be available... */ - BPF_STMT(BPF_RET+BPF_K, 0), /* discard */ -}; +static char *pidfile = NULL; +static pcap_t *pc; -/* - openbpf: +char* cidr_to_str(struct cidr *a) { + char buf[64]; + char *res = NULL; + int res_alloc, res_len; + int len; - open bpf & set ARP filter program for named interface & - allocate enough buffer for BPF. - return file descripter or -1 for error -*/ -int -openbpf(char *ifname, char **bufp, size_t *buflen){ - char bpffile[sizeof(BPFFILENAME)+5]; /* XXX: */ - int fd = -1; - int n; - struct bpf_version bpf_version; - struct ifreq bpf_ifreq; - u_int ui; - struct bpf_program bpf_program; + while (a) { + if (a->mask.s_addr == INADDR_NONE) { + len = snprintf(buf, sizeof buf, "dst host %s", inet_ntoa(a->addr)); + } else { + len = snprintf(buf, sizeof buf, "dst net %s mask ", inet_ntoa(a->addr)); + len += snprintf(buf + len, sizeof buf - len, "%s", inet_ntoa(a->mask)); + } - /* open BPF file */ - for (n=0; n= 0) - break; - } - if (fd < 0){ - fprintf(stderr,"openbpf: Can't open BPF\n"); - return(-1); /* error */ - } + if (!res) { + res_alloc = 1024; + res = malloc(res_alloc); + strncpy(res, buf, res_alloc); + res_len = len; - /* check version number */ - if ((ioctl(fd, BIOCVERSION, &bpf_version) == -1) || - bpf_version.bv_major != BPF_MAJOR_VERSION || - bpf_version.bv_minor < BPF_MINOR_VERSION){ - fprintf(stderr,"openbpf: incorrect BPF version\n"); - close(fd); - return(-1); - } + } else { + if (res_len + len + 5 > res_alloc) { + res_alloc *= 2; + res = realloc(res, res_alloc); + } + strncat(res, " or ", res_alloc - res_len - 1); + res_len += 4; + strncat(res, buf, res_alloc - res_len - 1); + res_len += len; + } - /* set interface name */ - strncpy(bpf_ifreq.ifr_name, ifname, IFNAMSIZ); - bpf_ifreq.ifr_name[IFNAMSIZ-1] = '\0'; /* paranoia */ - if (ioctl(fd, BIOCSETIF, &bpf_ifreq) == -1){ - fprintf(stderr,"openbpf: BIOCSETIF failed for interface <%s>\n", - ifname); - close(fd); - return(-1); + a = a->next; } + return res; +} - /* set BPF immediate mode */ - ui = 1; - if (ioctl(fd, BIOCIMMEDIATE, &ui) == -1){ - fprintf(stderr,"openbpf: BIOCIMMEDIATE failed.\n"); - close(fd); - return(-1); - } +pcap_t * +open_pcap(char *ifname, char *filter_str) { + pcap_t *pc = NULL; + struct bpf_program filter; - /* set ARP request filter */ - bpf_program.bf_len = sizeof(bpf_filter_arp) / sizeof(struct bpf_insn); - bpf_program.bf_insns = bpf_filter_arp; - if (ioctl(fd, BIOCSETF, &bpf_program) == -1){ - fprintf(stderr,"openbpf: BIOCSETF failed.\n"); - close(fd); - return(-1); + /* Set up PCAP */ + if ((pc = pcap_open_live(ifname, 128, 0, 512, errbuf))==NULL){ + fprintf(stderr, "pcap_open_live failed: %s\n", errbuf); + exit(1); } - - /* allocate reasonable size & alimented buffer */ - if (ioctl(fd, BIOCGBLEN, &ui) == -1){ - fprintf(stderr,"openbpf: BIOCGBLEN failed.\n"); - close(fd); - return(-1); + + /* Compiles the filter expression */ + if (pcap_compile(pc, &filter, filter_str, 1, PCAP_NETMASK_UNKNOWN) == -1){ + fprintf(stderr, "pcap_compile failed: %s\n", pcap_geterr(pc) ); + exit(1); } - *buflen = (size_t)ui; - if ((*bufp = (char *)malloc((size_t) ui)) == NULL){ - fprintf(stderr,"openbpf: malloc failed.\n"); - close(fd); - return(-1); - } - return(fd); -} - -/* - get ARP datalink frame pointer - - NULL if no more ARP frame -*/ -char * -getarp(char *bpfframe, ssize_t bpfflen, char **next, ssize_t *nextlen){ - int bias; - char *p; - - if (bpfframe == NULL || bpfflen == 0) - return(NULL); - - bias = BPF_WORDALIGN(((struct bpf_hdr *)bpfframe)->bh_hdrlen + - ((struct bpf_hdr *)bpfframe)->bh_caplen); - if (bias < bpfflen){ - /* there is another packet packed into same bpf frame */ - *next = bpfframe + bias; - *nextlen = (size_t) bpfflen - bias; - } else { - /* no more packet */ - *next = NULL; - *nextlen = 0; + /* Set filter program */ + if (pcap_setfilter(pc, &filter) == -1){ + fprintf(stderr, "pcap_setfilter failed: %s\n", pcap_geterr(pc)); + exit(1); } - /* cut off BPF header */ - p = bpfframe + ((struct bpf_hdr *)bpfframe)->bh_hdrlen; - return(p); + pcap_freecode(&filter); + return pc; } -/* - match +void +gen_arpreply(u_char *buf) { + struct ether_arp *arp; + struct in_addr ipbuf; - match an IP address against a list of address/netmask pairs -*/ - -static int -match (u_int32_t addr, struct cidr *list) { - while (list) { - if ((addr & list->mask) == list->addr) - return 1; - list = list->next; - } - return 0; -} - -/* - checkarp - - check responsibility of the ARP request - return true if responsible - - arpbuf is pointing top of link-level frame -*/ - -static int -checkarp(char *arpbuf){ - struct ether_arp *arp; - u_int32_t target_ip; - - arp = (struct ether_arp *)(arpbuf + 14); /* skip ethernet header */ - if (ntohs(arp->arp_hrd) != ARPHRD_ETHER || - /* XXX: ARPHRD_802 */ - ntohs(arp->arp_pro) != ETHERTYPE_IP || - (int) (arp->arp_hln) != ETHER_ADDR_LEN || /* length of ethernet addr */ - (int) (arp->arp_pln) != 4){ /* length of protocol addr */ - fprintf(stderr,"checkarp: WARNING: received unknown type ARP request.\n"); - return(0); - } - if (ntohl(*(u_int32_t *)(arp->arp_tpa)) == ntohl(*(u_int32_t *)(arp->arp_spa))) { - if (verbose != 0) - fprintf(stderr,"checkarp: WARNING: sender equal dest.\n"); - return(0); - } - if (0 == ntohl(*(u_int32_t *)(arp->arp_spa))) { - if (verbose != 0) - fprintf(stderr,"checkarp: WARNING: zero sender address.\n"); - return(0); - } - target_ip = ntohl(*(u_int32_t *)(arp->arp_tpa)); - return match(target_ip, targets) && !match(target_ip, excludes); -} - -/* - genarpreply - - generate arp reply link level frame - arpbuf is pointing top of link-level frame - this routine overwrite arpbuf - - return reply buffer & its length -*/ -char * -gen_arpreply(char *arpbuf, size_t *rlen){ - struct ether_arp *arp; - u_char ipbuf[4]; /* sender IP */ - /* set ethernet dst/src address */ - memcpy(arpbuf, arpbuf+ETHER_ADDR_LEN, ETHER_ADDR_LEN); - memcpy(arpbuf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN); + memcpy(buf, buf+ETHER_ADDR_LEN, ETHER_ADDR_LEN); + memcpy(buf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN); + /* set result of ARP request */ - arp = (struct ether_arp *)(arpbuf + 14); /* skip ethernet header */ - memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */ - memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */ - memcpy(arp->arp_spa, ipbuf, 4); /* set source protocol addr */ - memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN); /* set source hard addr */ + arp = (struct ether_arp *)(buf + ETHER_HDR_LEN); + memcpy((char*) &ipbuf, arp->arp_tpa, sizeof(ipbuf)); /* save protocol addr */ + memcpy(arp->arp_tha, arp->arp_sha, sizeof(arp->arp_tha)); /* set target hard addr */ + memcpy(arp->arp_tpa, arp->arp_spa, sizeof(arp->arp_tpa)); /* set target proto addr */ + memcpy(arp->arp_spa, (char *)&ipbuf, sizeof(ipbuf)); /* set source protocol addr */ + memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN); /* set source hard addr */ arp->arp_op = htons(ARPOP_REPLY); - - *rlen = 14 + 28; /* ethernet header & arp reply */ - return(arpbuf); } void -loop(int fd, char *buf, size_t buflen){ - ssize_t rlen; - char *p, *nextp; - ssize_t nextlen; - char *rframe; - char *sframe; - size_t frame_len; - int kq; - struct kevent kev; +cleanup(int sig){ + if (pidfile != NULL) + unlink(pidfile); - if ((kq = kqueue()) < 0) { - perror("kqueue"); - return; - } + pcap_breakloop(pc); + pcap_close(pc); + exit(0); +} - EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); - if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0 ) { - perror("kevent"); - return; - } - - for(;;){ - int r = kevent(kq, NULL, 0, &kev, 1, NULL); - - if (r < 0) { - if (errno == EINTR) - continue; - perror("select"); - return; - } - - rlen = read(kev.ident, buf, buflen); - if (rlen < 0) { - if (errno == EINTR) - continue; - perror("read"); - return; - } - - p = buf; - while((rframe = getarp(p, rlen, &nextp, &nextlen)) != NULL){ - if (checkarp(rframe)){ - sframe = gen_arpreply(rframe, &frame_len); - write(kev.ident, sframe, frame_len); - } - p = nextp; - rlen = nextlen; - } - } - /* not reach */ +void +process_arp(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *packet) { + gen_arpreply((u_char *)packet); + pcap_inject((pcap_t *)user, packet, pkthdr->len); } int @@ -342,22 +178,58 @@ setmac(char *addr, char *ifname){ u_int m0, m1, m2, m3, m4, m5; if (!strcmp (addr, "auto")) { - struct ifaddrs *ifas, *ifa; +#ifdef __linux__ + int fd; + struct ifreq ifr; - getifaddrs (&ifas); - for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) { + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return -1; + } + + strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); + + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + memcpy(target_mac, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); + return 0; +#else + struct ifaddrs *ifas, *ifa; + + getifaddrs (&ifas); + for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) { #define SDL ((struct sockaddr_dl *)ifa->ifa_addr) - if (strcmp (ifa->ifa_name, ifname) - || SDL->sdl_family != AF_LINK - || SDL->sdl_alen != 6) - continue; - memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, 6); - return 0; - } - return -1; - } - if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6) + if (strcmp (ifa->ifa_name, ifname) + || SDL->sdl_family != AF_LINK + || SDL->sdl_alen != ETHER_ADDR_LEN) + continue; + memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, ETHER_ADDR_LEN); + return 0; + } +#endif + fprintf(stderr, "%s: not found\n", ifname); + return -1; + + } else if (!strncmp (addr, "vhid:", 4)) { + /* + * Virtual router mac address + * CARP address format: 00:00:5e:00:01: + */ + char *vhid = addr + 5; + if (!*vhid) + return(-1); + m0 = 0; + m1 = 0; + m2 = 0x5e; + m3 = 0; + m4 = 1; + m5 = atoi(vhid); + } else if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6) { + fprintf(stderr, "invalid MAC address: %s", addr); return(-1); + } target_mac[0] = (u_char )m0; target_mac[1] = (u_char )m1; target_mac[2] = (u_char )m2; @@ -375,7 +247,7 @@ atoip(char *buf, u_int32_t *ip_addr){ *ip_addr = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3; return(0); } - if (sscanf(buf, "0x%lx", ip_addr) == 1) + if (sscanf(buf, "0x%lx", (unsigned long *) ip_addr) == 1) return(0); return(-1); @@ -383,42 +255,52 @@ atoip(char *buf, u_int32_t *ip_addr){ void usage(void){ - fprintf(stderr,"usage: choparp [-v] if_name mac_addr [-]addr/mask...\n"); + fprintf(stderr,"usage: choparp [-p PIDFILE] if_name mac_addr [-]addr/mask...\n"); exit(-1); } int main(int argc, char **argv){ - int ch, fd; - char *buf, *ifname; + int pidf, opt; + char *ifname; + char *filter, *targets_filter, *excludes_filter; struct cidr **targets_tail = &targets, **excludes_tail = &excludes; + struct sigaction act; + #define APPEND(LIST,ADDR,MASK) \ do { \ *(LIST ## _tail) = malloc(sizeof (struct cidr)); \ - (*(LIST ## _tail))->addr = ADDR; \ - (*(LIST ## _tail))->mask = MASK; \ + (*(LIST ## _tail))->addr.s_addr = htonl(ADDR); \ + (*(LIST ## _tail))->mask.s_addr = htonl(MASK); \ (*(LIST ## _tail))->next = NULL; \ (LIST ## _tail) = &(*(LIST ## _tail))->next; \ } while (0) - size_t buflen; - while ((ch = getopt(argc, argv, "v")) != -1) - switch (ch) { - case 'v': - verbose++; + while ((opt = getopt(argc, argv, "p:")) != -1) { + switch (opt) { + case 'p': + pidfile = optarg; break; - default: + case '?': usage(); } - argc -= optind; - argv += optind; + } + if (pidfile == NULL) { + argv++; + argc--; + } else { + argv += 3; + argc -= 3; + } + if (argc < 3) usage(); ifname = argv[0]; - if (setmac(argv[1], ifname)) - usage(); + if (setmac(argv[1], ifname)) { + exit(1); + } argv += 2; argc -= 2; while (argc > 0) { @@ -446,32 +328,61 @@ main(int argc, char **argv){ APPEND(excludes, addr, mask); else APPEND(targets, addr, mask); + argv++, argc--; } #ifdef DEBUG #define SHOW(LIST) \ - do { \ - struct cidr *t; \ - printf (#LIST ":\n"); \ - for (t = LIST; t; t = t->next) { \ - u_int32_t x; \ - x = htonl (t->addr); \ - printf (" %s", inet_ntoa (*(struct in_addr *)&x)); \ - x = htonl (t->mask); \ - printf ("/%s\n", inet_ntoa (*(struct in_addr *)&x)); \ - } \ + do { \ + struct cidr *t; \ + fprintf (stderr, #LIST ":\n"); \ + for (t = LIST; t; t = t->next) { \ + fprintf (stderr, " %s", inet_ntoa (t->addr)); \ + fprintf (stderr, "/%s\n", inet_ntoa (t->mask)); \ + } \ } while (0) SHOW(targets); SHOW(excludes); exit (0); #endif - if ((fd = openbpf(ifname, &buf, &buflen)) < 0) - return(-1); - #ifndef DEBUG - daemon(0, 0); - #endif - loop(fd, buf, buflen); - return(-1); + + targets_filter = cidr_to_str(targets); + excludes_filter = cidr_to_str(excludes); + +#define TMPL_FILTER "arp[2:2] == 0x0800 " /* Protocol: IPv4 */ \ + "and arp[4] == 6 " /* Hw addr length: 6 */ \ + "and arp[5] == 4 " /* Proto addr length: 4 */ \ + "and arp[6:2] == 1 " /* Operation: Request */ \ + "and (%s)" + +#define EXCL_FILTER TMPL_FILTER " and not (%s)" + if (excludes_filter == NULL) + asprintf (&filter, TMPL_FILTER, targets_filter); + else + asprintf (&filter, EXCL_FILTER, targets_filter, excludes_filter); + +#ifdef DEBUG + fprintf(stderr, "Filter on %s: %s\n", ifname, filter); +#endif + if ((pc = open_pcap(ifname, filter)) < 0) + exit(1); + free(filter); + + if (pidfile != NULL) { + pidf = open(pidfile, O_RDWR | O_CREAT | O_FSYNC, 0600); + if (pidf > 0) { + ftruncate(pidf, 0); + dprintf(pidf, "%u\n", getpid()); + close(pidf); + memset(&act, 0, sizeof(act)); + act.sa_handler = cleanup; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + } + } + + pcap_loop(pc, 0, process_arp, (u_char*)pc); + exit(1); }