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>