Annotation of embedaddon/choparp/choparp.c, revision 1.1
1.1 ! misho 1: /*
! 2: choparp - cheap & omitted proxy arp
! 3:
! 4: Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)
! 5: Copyright (c) 2002 Thomas Quinot (thomas@cuivre.fr.eu.org)
! 6:
! 7: Redistribution and use in source and binary forms, with or without
! 8: modification, are permitted provided that the following conditions
! 9: are met:
! 10: 1. Redistributions of source code must retain the above copyright
! 11: notice, this list of conditions and the following disclaimer.
! 12: 2. Redistributions in binary form must reproduce the above copyright
! 13: notice, this list of conditions and the following disclaimer in the
! 14: documentation and/or other materials provided with the distribution.
! 15: 3. Neither the name of the authors nor the names of their contributors
! 16: may be used to endorse or promote products derived from this software
! 17: without specific prior written permission.
! 18:
! 19: THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
! 20: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 21: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 22: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 23: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 24: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 25: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 26: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 27: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 28: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 29: SUCH DAMAGE.
! 30:
! 31:
! 32: History:
! 33: 17 Jun 1997 Creation (tate)
! 34: 7 Oct 1997 fix some comments (tate)
! 35: 19 Jun 1998 fix read result as ssize_t (tate / pointed by msaitoh)
! 36:
! 37: */
! 38:
! 39: #include <stdio.h>
! 40: #include <unistd.h>
! 41: #include <stdlib.h>
! 42: #include <string.h>
! 43: #include <sys/types.h>
! 44: #include <fcntl.h>
! 45: #include <sys/event.h>
! 46: #include <sys/time.h>
! 47: #include <sys/ioctl.h>
! 48: #include <net/bpf.h>
! 49: #include <sys/socket.h>
! 50: #include <net/if.h>
! 51: #include <netinet/in.h>
! 52: /* #include <net/if_arp.h> */
! 53: #if (__FreeBSD__ >= 3)
! 54: #include <net/if_var.h>
! 55: #endif
! 56: #include <netinet/if_ether.h>
! 57: #include <sys/param.h>
! 58: #include <errno.h>
! 59: #include <ifaddrs.h>
! 60: #include <net/if_dl.h>
! 61:
! 62: #ifdef DEBUG
! 63: #include <arpa/inet.h>
! 64: #endif
! 65:
! 66: #define BPFFILENAME "/dev/bpf%d" /* bpf file template */
! 67: #ifndef NBPFILTER /* number of available bpf */
! 68: # define NBPFILTER (16)
! 69: #endif
! 70:
! 71: struct cidr {
! 72: struct cidr *next;
! 73: u_int32_t addr; /* addr and mask are host order */
! 74: u_int32_t mask;
! 75: };
! 76:
! 77: struct cidr *targets = NULL, *excludes = NULL;
! 78: u_char target_mac[ETHER_ADDR_LEN]; /* target MAC address */
! 79: int verbose = 0;
! 80:
! 81: /*
! 82: ARP filter program
! 83: */
! 84: struct bpf_insn bpf_filter_arp[] = {
! 85: /* check Ethernet Encapsulation (RFC894) first */
! 86: BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* load frame type */
! 87: BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_ARP, 0, 3), /* check it */
! 88: BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20), /* load OP code */
! 89: BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 1), /* check it */
! 90: BPF_STMT(BPF_RET+BPF_K, 14+28), /* return Ethernet encap ARP req. */
! 91: /* XXX: IEEE 802.2/802.3 Encap (RFC1042) should be available... */
! 92: BPF_STMT(BPF_RET+BPF_K, 0), /* discard */
! 93: };
! 94:
! 95: /*
! 96: openbpf:
! 97:
! 98: open bpf & set ARP filter program for named interface &
! 99: allocate enough buffer for BPF.
! 100: return file descripter or -1 for error
! 101: */
! 102: int
! 103: openbpf(char *ifname, char **bufp, size_t *buflen){
! 104: char bpffile[sizeof(BPFFILENAME)+5]; /* XXX: */
! 105: int fd = -1;
! 106: int n;
! 107: struct bpf_version bpf_version;
! 108: struct ifreq bpf_ifreq;
! 109: u_int ui;
! 110: struct bpf_program bpf_program;
! 111:
! 112: /* open BPF file */
! 113: for (n=0; n<NBPFILTER; n++){
! 114: sprintf(bpffile, BPFFILENAME, n);
! 115: if ((fd = open(bpffile, O_RDWR, 0)) >= 0)
! 116: break;
! 117: }
! 118: if (fd < 0){
! 119: fprintf(stderr,"openbpf: Can't open BPF\n");
! 120: return(-1); /* error */
! 121: }
! 122:
! 123: /* check version number */
! 124: if ((ioctl(fd, BIOCVERSION, &bpf_version) == -1) ||
! 125: bpf_version.bv_major != BPF_MAJOR_VERSION ||
! 126: bpf_version.bv_minor < BPF_MINOR_VERSION){
! 127: fprintf(stderr,"openbpf: incorrect BPF version\n");
! 128: close(fd);
! 129: return(-1);
! 130: }
! 131:
! 132: /* set interface name */
! 133: strncpy(bpf_ifreq.ifr_name, ifname, IFNAMSIZ);
! 134: bpf_ifreq.ifr_name[IFNAMSIZ-1] = '\0'; /* paranoia */
! 135: if (ioctl(fd, BIOCSETIF, &bpf_ifreq) == -1){
! 136: fprintf(stderr,"openbpf: BIOCSETIF failed for interface <%s>\n",
! 137: ifname);
! 138: close(fd);
! 139: return(-1);
! 140: }
! 141:
! 142: /* set BPF immediate mode */
! 143: ui = 1;
! 144: if (ioctl(fd, BIOCIMMEDIATE, &ui) == -1){
! 145: fprintf(stderr,"openbpf: BIOCIMMEDIATE failed.\n");
! 146: close(fd);
! 147: return(-1);
! 148: }
! 149:
! 150: /* set ARP request filter */
! 151: bpf_program.bf_len = sizeof(bpf_filter_arp) / sizeof(struct bpf_insn);
! 152: bpf_program.bf_insns = bpf_filter_arp;
! 153: if (ioctl(fd, BIOCSETF, &bpf_program) == -1){
! 154: fprintf(stderr,"openbpf: BIOCSETF failed.\n");
! 155: close(fd);
! 156: return(-1);
! 157: }
! 158:
! 159: /* allocate reasonable size & alimented buffer */
! 160: if (ioctl(fd, BIOCGBLEN, &ui) == -1){
! 161: fprintf(stderr,"openbpf: BIOCGBLEN failed.\n");
! 162: close(fd);
! 163: return(-1);
! 164: }
! 165: *buflen = (size_t)ui;
! 166: if ((*bufp = (char *)malloc((size_t) ui)) == NULL){
! 167: fprintf(stderr,"openbpf: malloc failed.\n");
! 168: close(fd);
! 169: return(-1);
! 170: }
! 171:
! 172: return(fd);
! 173: }
! 174:
! 175: /*
! 176: get ARP datalink frame pointer
! 177:
! 178: NULL if no more ARP frame
! 179: */
! 180: char *
! 181: getarp(char *bpfframe, ssize_t bpfflen, char **next, ssize_t *nextlen){
! 182: int bias;
! 183: char *p;
! 184:
! 185: if (bpfframe == NULL || bpfflen == 0)
! 186: return(NULL);
! 187:
! 188: bias = BPF_WORDALIGN(((struct bpf_hdr *)bpfframe)->bh_hdrlen +
! 189: ((struct bpf_hdr *)bpfframe)->bh_caplen);
! 190: if (bias < bpfflen){
! 191: /* there is another packet packed into same bpf frame */
! 192: *next = bpfframe + bias;
! 193: *nextlen = (size_t) bpfflen - bias;
! 194: } else {
! 195: /* no more packet */
! 196: *next = NULL;
! 197: *nextlen = 0;
! 198: }
! 199:
! 200: /* cut off BPF header */
! 201: p = bpfframe + ((struct bpf_hdr *)bpfframe)->bh_hdrlen;
! 202: return(p);
! 203: }
! 204:
! 205: /*
! 206: match
! 207:
! 208: match an IP address against a list of address/netmask pairs
! 209: */
! 210:
! 211: static int
! 212: match (u_int32_t addr, struct cidr *list) {
! 213: while (list) {
! 214: if ((addr & list->mask) == list->addr)
! 215: return 1;
! 216: list = list->next;
! 217: }
! 218: return 0;
! 219: }
! 220:
! 221: /*
! 222: checkarp
! 223:
! 224: check responsibility of the ARP request
! 225: return true if responsible
! 226:
! 227: arpbuf is pointing top of link-level frame
! 228: */
! 229:
! 230: static int
! 231: checkarp(char *arpbuf){
! 232: struct ether_arp *arp;
! 233: u_int32_t target_ip;
! 234:
! 235: arp = (struct ether_arp *)(arpbuf + 14); /* skip ethernet header */
! 236: if (ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
! 237: /* XXX: ARPHRD_802 */
! 238: ntohs(arp->arp_pro) != ETHERTYPE_IP ||
! 239: (int) (arp->arp_hln) != ETHER_ADDR_LEN || /* length of ethernet addr */
! 240: (int) (arp->arp_pln) != 4){ /* length of protocol addr */
! 241: fprintf(stderr,"checkarp: WARNING: received unknown type ARP request.\n");
! 242: return(0);
! 243: }
! 244: if (ntohl(*(u_int32_t *)(arp->arp_tpa)) == ntohl(*(u_int32_t *)(arp->arp_spa))) {
! 245: if (verbose != 0)
! 246: fprintf(stderr,"checkarp: WARNING: sender equal dest.\n");
! 247: return(0);
! 248: }
! 249: if (0 == ntohl(*(u_int32_t *)(arp->arp_spa))) {
! 250: if (verbose != 0)
! 251: fprintf(stderr,"checkarp: WARNING: zero sender address.\n");
! 252: return(0);
! 253: }
! 254: target_ip = ntohl(*(u_int32_t *)(arp->arp_tpa));
! 255: return match(target_ip, targets) && !match(target_ip, excludes);
! 256: }
! 257:
! 258: /*
! 259: genarpreply
! 260:
! 261: generate arp reply link level frame
! 262: arpbuf is pointing top of link-level frame
! 263: this routine overwrite arpbuf
! 264:
! 265: return reply buffer & its length
! 266: */
! 267: char *
! 268: gen_arpreply(char *arpbuf, size_t *rlen){
! 269: struct ether_arp *arp;
! 270: u_char ipbuf[4]; /* sender IP */
! 271:
! 272: /* set ethernet dst/src address */
! 273: memcpy(arpbuf, arpbuf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);
! 274: memcpy(arpbuf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);
! 275: /* set result of ARP request */
! 276: arp = (struct ether_arp *)(arpbuf + 14); /* skip ethernet header */
! 277: memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
! 278: memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
! 279: memcpy(arp->arp_spa, ipbuf, 4); /* set source protocol addr */
! 280: memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN); /* set source hard addr */
! 281: arp->arp_op = htons(ARPOP_REPLY);
! 282:
! 283: *rlen = 14 + 28; /* ethernet header & arp reply */
! 284: return(arpbuf);
! 285: }
! 286:
! 287: void
! 288: loop(int fd, char *buf, size_t buflen){
! 289: ssize_t rlen;
! 290: char *p, *nextp;
! 291: ssize_t nextlen;
! 292: char *rframe;
! 293: char *sframe;
! 294: size_t frame_len;
! 295: int kq;
! 296: struct kevent kev;
! 297:
! 298: if ((kq = kqueue()) < 0) {
! 299: perror("kqueue");
! 300: return;
! 301: }
! 302:
! 303: EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
! 304: if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0 ) {
! 305: perror("kevent");
! 306: return;
! 307: }
! 308:
! 309: for(;;){
! 310: int r = kevent(kq, NULL, 0, &kev, 1, NULL);
! 311:
! 312: if (r < 0) {
! 313: if (errno == EINTR)
! 314: continue;
! 315: perror("select");
! 316: return;
! 317: }
! 318:
! 319: rlen = read(kev.ident, buf, buflen);
! 320: if (rlen < 0) {
! 321: if (errno == EINTR)
! 322: continue;
! 323: perror("read");
! 324: return;
! 325: }
! 326:
! 327: p = buf;
! 328: while((rframe = getarp(p, rlen, &nextp, &nextlen)) != NULL){
! 329: if (checkarp(rframe)){
! 330: sframe = gen_arpreply(rframe, &frame_len);
! 331: write(kev.ident, sframe, frame_len);
! 332: }
! 333: p = nextp;
! 334: rlen = nextlen;
! 335: }
! 336: }
! 337: /* not reach */
! 338: }
! 339:
! 340: int
! 341: setmac(char *addr, char *ifname){
! 342: u_int m0, m1, m2, m3, m4, m5;
! 343:
! 344: if (!strcmp (addr, "auto")) {
! 345: struct ifaddrs *ifas, *ifa;
! 346:
! 347: getifaddrs (&ifas);
! 348: for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
! 349: #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)
! 350: if (strcmp (ifa->ifa_name, ifname)
! 351: || SDL->sdl_family != AF_LINK
! 352: || SDL->sdl_alen != 6)
! 353: continue;
! 354: memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, 6);
! 355: return 0;
! 356: }
! 357: return -1;
! 358: }
! 359: if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6)
! 360: return(-1);
! 361: target_mac[0] = (u_char )m0;
! 362: target_mac[1] = (u_char )m1;
! 363: target_mac[2] = (u_char )m2;
! 364: target_mac[3] = (u_char )m3;
! 365: target_mac[4] = (u_char )m4;
! 366: target_mac[5] = (u_char )m5;
! 367: return(0);
! 368: }
! 369:
! 370: int
! 371: atoip(char *buf, u_int32_t *ip_addr){
! 372: u_int i0, i1, i2, i3;
! 373:
! 374: if (sscanf(buf, "%u.%u.%u.%u", &i0, &i1, &i2, &i3) == 4){
! 375: *ip_addr = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3;
! 376: return(0);
! 377: }
! 378: if (sscanf(buf, "0x%lx", ip_addr) == 1)
! 379: return(0);
! 380:
! 381: return(-1);
! 382: }
! 383:
! 384: void
! 385: usage(void){
! 386: fprintf(stderr,"usage: choparp [-v] if_name mac_addr [-]addr/mask...\n");
! 387: exit(-1);
! 388: }
! 389:
! 390: int
! 391: main(int argc, char **argv){
! 392: int ch, fd;
! 393: char *buf, *ifname;
! 394: struct cidr **targets_tail = &targets, **excludes_tail = &excludes;
! 395: #define APPEND(LIST,ADDR,MASK) \
! 396: do { \
! 397: *(LIST ## _tail) = malloc(sizeof (struct cidr)); \
! 398: (*(LIST ## _tail))->addr = ADDR; \
! 399: (*(LIST ## _tail))->mask = MASK; \
! 400: (*(LIST ## _tail))->next = NULL; \
! 401: (LIST ## _tail) = &(*(LIST ## _tail))->next; \
! 402: } while (0)
! 403: size_t buflen;
! 404:
! 405: while ((ch = getopt(argc, argv, "v")) != -1)
! 406: switch (ch) {
! 407: case 'v':
! 408: verbose++;
! 409: break;
! 410: default:
! 411: usage();
! 412: }
! 413: argc -= optind;
! 414: argv += optind;
! 415:
! 416: if (argc < 3)
! 417: usage();
! 418:
! 419: ifname = argv[0];
! 420: if (setmac(argv[1], ifname))
! 421: usage();
! 422: argv += 2; argc -= 2;
! 423:
! 424: while (argc > 0) {
! 425: u_int32_t addr, mask = ~0;
! 426: char *slash = strchr (*argv, '/');
! 427: int exclude = 0;
! 428:
! 429: if (**argv == '-') {
! 430: (*argv)++;
! 431: exclude = 1;
! 432: }
! 433: if (slash != NULL)
! 434: *(slash++) = '\0';
! 435: if (atoip (*argv, &addr))
! 436: usage();
! 437: if (slash != NULL) {
! 438: char *end;
! 439: u_int32_t len = strtol (slash, &end, 10);
! 440: if (*end == '\0')
! 441: mask <<= (32 - len);
! 442: else if (atoip (slash, &mask))
! 443: usage();
! 444: }
! 445: if (exclude)
! 446: APPEND(excludes, addr, mask);
! 447: else
! 448: APPEND(targets, addr, mask);
! 449: argv++, argc--;
! 450: }
! 451:
! 452: #ifdef DEBUG
! 453: #define SHOW(LIST) \
! 454: do { \
! 455: struct cidr *t; \
! 456: printf (#LIST ":\n"); \
! 457: for (t = LIST; t; t = t->next) { \
! 458: u_int32_t x; \
! 459: x = htonl (t->addr); \
! 460: printf (" %s", inet_ntoa (*(struct in_addr *)&x)); \
! 461: x = htonl (t->mask); \
! 462: printf ("/%s\n", inet_ntoa (*(struct in_addr *)&x)); \
! 463: } \
! 464: } while (0)
! 465:
! 466: SHOW(targets);
! 467: SHOW(excludes);
! 468: exit (0);
! 469: #endif
! 470: if ((fd = openbpf(ifname, &buf, &buflen)) < 0)
! 471: return(-1);
! 472: #ifndef DEBUG
! 473: daemon(0, 0);
! 474: #endif
! 475: loop(fd, buf, buflen);
! 476: return(-1);
! 477: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>