Annotation of embedaddon/choparp/choparp.c, revision 1.1.1.1.2.2

1.1       misho       1: /*
                      2:    choparp - cheap & omitted proxy arp
                      3: 
                      4:    Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)
1.1.1.1.2.2! misho       5:    Copyright (c) 2002-2015 Thomas Quinot (thomas@cuivre.fr.eu.org)
1.1       misho       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: 
1.1.1.1.2.2! misho      39: #define _GNU_SOURCE /* asprintf */
        !            40: 
        !            41: #include <pcap.h>
1.1       misho      42: #include <stdio.h>
                     43: #include <stdlib.h>
                     44: #include <string.h>
1.1.1.1.2.2! misho      45: #include <signal.h>
        !            46: #include <net/if.h>
1.1       misho      47: #include <sys/types.h>
                     48: #include <sys/socket.h>
                     49: #include <netinet/in.h>
                     50: #include <netinet/if_ether.h>
1.1.1.1.2.2! misho      51: #include <arpa/inet.h>
        !            52: #include <fcntl.h>
        !            53: #include <unistd.h>
        !            54: #include <sys/ioctl.h>
        !            55: 
        !            56: #ifndef __linux__
1.1       misho      57: #include <ifaddrs.h>
                     58: #include <net/if_dl.h>
                     59: #endif
                     60: 
1.1.1.1.2.2! misho      61: /* ARP Header                                      */ 
        !            62: #define ARP_REQUEST 1   /* ARP Request             */ 
        !            63: #define ARP_REPLY 2     /* ARP Reply               */ 
1.1       misho      64: 
                     65: struct cidr {
                     66:        struct cidr *next;
1.1.1.1.2.2! misho      67:        struct in_addr addr;            /* addr and mask are host order */
        !            68:        struct in_addr mask;
1.1       misho      69: };
                     70: 
                     71: struct cidr *targets = NULL, *excludes = NULL;
1.1.1.1.2.2! misho      72: char errbuf[PCAP_ERRBUF_SIZE];
        !            73: u_char target_mac[ETHER_ADDR_LEN];     /* target MAC address */
1.1       misho      74: 
1.1.1.1.2.2! misho      75: static char *pidfile = NULL;
        !            76: static pcap_t *pc;
1.1       misho      77: 
1.1.1.1.2.2! misho      78: char* cidr_to_str(struct cidr *a) {
        !            79:     char buf[64];
        !            80:     char *res = NULL;
        !            81:     int res_alloc, res_len;
        !            82:     int len;
        !            83: 
        !            84:     while (a) {
        !            85:         if (a->mask.s_addr == INADDR_NONE) {
        !            86:             len = snprintf(buf, sizeof buf, "dst host %s", inet_ntoa(a->addr));
        !            87:         } else {
        !            88:             len = snprintf(buf, sizeof buf, "dst net %s mask ", inet_ntoa(a->addr));
        !            89:             len += snprintf(buf + len, sizeof buf - len, "%s", inet_ntoa(a->mask));
        !            90:         }
1.1       misho      91: 
1.1.1.1.2.2! misho      92:         if (!res) {
        !            93:             res_alloc = 1024;
        !            94:             res = malloc(res_alloc);
        !            95:             strncpy(res, buf, res_alloc);
        !            96:             res_len = len;
        !            97: 
        !            98:         } else {
        !            99:             if (res_len + len + 5 > res_alloc) {
        !           100:                 res_alloc *= 2;
        !           101:                 res = realloc(res, res_alloc);
        !           102:             }
        !           103:             strncat(res, " or ", res_alloc - res_len - 1);
        !           104:             res_len += 4;
        !           105:             strncat(res, buf, res_alloc - res_len - 1);
        !           106:             res_len += len;
        !           107:         }
1.1       misho     108: 
1.1.1.1.2.2! misho     109:         a = a->next;
1.1       misho     110:     }
1.1.1.1.2.2! misho     111:     return res;
1.1       misho     112: }
                    113: 
1.1.1.1.2.2! misho     114: pcap_t *
        !           115: open_pcap(char *ifname, char *filter_str) {
        !           116:     pcap_t *pc = NULL;
        !           117:     struct bpf_program filter;
        !           118: 
        !           119:     /* Set up PCAP */
        !           120:     if ((pc = pcap_open_live(ifname, 128, 0,  512, errbuf))==NULL){
        !           121:        fprintf(stderr, "pcap_open_live failed: %s\n", errbuf);
        !           122:        exit(1);
        !           123:     }
        !           124:     
        !           125:     /* Compiles the filter expression */ 
        !           126:     if (pcap_compile(pc, &filter, filter_str, 1, PCAP_NETMASK_UNKNOWN) == -1){
        !           127:        fprintf(stderr, "pcap_compile failed: %s\n", pcap_geterr(pc) );
        !           128:        exit(1);
        !           129:     }
        !           130: 
        !           131:     /* Set filter program */ 
        !           132:     if (pcap_setfilter(pc, &filter) == -1){
        !           133:        fprintf(stderr, "pcap_setfilter failed: %s\n", pcap_geterr(pc));
        !           134:        exit(1);
1.1       misho     135:     }
                    136: 
1.1.1.1.2.2! misho     137:     pcap_freecode(&filter);
        !           138:     return pc;
1.1       misho     139: }
                    140: 
1.1.1.1.2.2! misho     141: void
        !           142: gen_arpreply(u_char *buf) {
        !           143:     struct ether_arp *arp;
        !           144:     struct in_addr ipbuf;
1.1       misho     145: 
                    146:     /* set ethernet dst/src address */
1.1.1.1.2.2! misho     147:     memcpy(buf, buf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);
        !           148:     memcpy(buf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);
        !           149: 
1.1       misho     150:     /* set result of ARP request */
1.1.1.1.2.2! misho     151:     arp = (struct ether_arp *)(buf + ETHER_HDR_LEN);
        !           152:     memcpy((char*) &ipbuf, arp->arp_tpa, sizeof(ipbuf));       /* save protocol addr */
        !           153:     memcpy(arp->arp_tha, arp->arp_sha, sizeof(arp->arp_tha)); /* set target hard addr */
        !           154:     memcpy(arp->arp_tpa, arp->arp_spa, sizeof(arp->arp_tpa)); /* set target proto addr */
        !           155:     memcpy(arp->arp_spa, (char *)&ipbuf, sizeof(ipbuf));                     /* set source protocol addr */
        !           156:     memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN);         /* set source hard addr */
1.1       misho     157:     arp->arp_op = htons(ARPOP_REPLY);
                    158: }
                    159: 
                    160: void
1.1.1.1.2.2! misho     161: cleanup(int sig){
        !           162:      if (pidfile != NULL)
        !           163:          unlink(pidfile);
        !           164: 
        !           165:      pcap_breakloop(pc);
        !           166:      pcap_close(pc);
        !           167:      exit(0);
        !           168: }
1.1       misho     169: 
1.1.1.1.2.2! misho     170: void
        !           171: process_arp(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *packet) {
        !           172:     gen_arpreply((u_char *)packet);
        !           173:     pcap_inject((pcap_t *)user, packet, pkthdr->len);
1.1       misho     174: }
                    175: 
                    176: int
                    177: setmac(char *addr, char *ifname){
                    178:     u_int m0, m1, m2, m3, m4, m5;
                    179: 
                    180:     if (!strcmp (addr, "auto")) {
1.1.1.1.2.2! misho     181: #ifdef __linux__
        !           182:         int fd;
        !           183:         struct ifreq ifr;
        !           184: 
        !           185:         if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        !           186:             perror("socket");
        !           187:             return -1;
        !           188:         }
        !           189: 
        !           190:         strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
        !           191: 
        !           192:         if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
        !           193:             perror("ioctl(SIOCGIFHWADDR)");
        !           194:             return -1;
        !           195:         }
        !           196:         memcpy(target_mac, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
        !           197:         return 0;
        !           198: #else
        !           199:         struct ifaddrs *ifas, *ifa;
1.1       misho     200: 
1.1.1.1.2.2! misho     201:         getifaddrs (&ifas);
        !           202:         for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
1.1       misho     203: #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)
1.1.1.1.2.2! misho     204:             if (strcmp (ifa->ifa_name, ifname)
        !           205:               || SDL->sdl_family != AF_LINK
        !           206:               || SDL->sdl_alen != ETHER_ADDR_LEN)
        !           207:                 continue;
        !           208:             memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, ETHER_ADDR_LEN);
        !           209:             return 0;
        !           210:         }
        !           211: #endif
        !           212:         fprintf(stderr, "%s: not found\n", ifname);
        !           213:         return -1;
        !           214: 
        !           215:     } else if (!strncmp (addr, "vhid:", 4)) {
        !           216:         /*
        !           217:          * Virtual router mac address
        !           218:          * CARP address format: 00:00:5e:00:01:<VHID>
        !           219:          */
        !           220:         char *vhid = addr + 5;
        !           221:         if (!*vhid)
        !           222:             return(-1);
        !           223:         m0 = 0;
        !           224:         m1 = 0;
        !           225:         m2 = 0x5e;
        !           226:         m3 = 0;
        !           227:         m4 = 1;
        !           228:         m5 = atoi(vhid);
        !           229:     } else if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6) {
        !           230:         fprintf(stderr, "invalid MAC address: %s", addr);
1.1       misho     231:         return(-1);
1.1.1.1.2.2! misho     232:     }
1.1       misho     233:     target_mac[0] = (u_char )m0;
                    234:     target_mac[1] = (u_char )m1;
                    235:     target_mac[2] = (u_char )m2;
                    236:     target_mac[3] = (u_char )m3;
                    237:     target_mac[4] = (u_char )m4;
                    238:     target_mac[5] = (u_char )m5;
                    239:     return(0);
                    240: }
                    241: 
                    242: int
                    243: atoip(char *buf, u_int32_t *ip_addr){
                    244:     u_int      i0, i1, i2, i3;
                    245: 
                    246:     if (sscanf(buf, "%u.%u.%u.%u", &i0, &i1, &i2, &i3) == 4){
                    247:        *ip_addr = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3;
                    248:        return(0);
                    249:     }
1.1.1.1.2.2! misho     250:     if (sscanf(buf, "0x%lx", (unsigned long *) ip_addr) == 1)
1.1       misho     251:        return(0);
                    252: 
                    253:     return(-1);        
                    254: }
                    255: 
                    256: void
                    257: usage(void){
1.1.1.1.2.2! misho     258:     fprintf(stderr,"usage: choparp [-p PIDFILE] if_name mac_addr [-]addr/mask...\n");
1.1       misho     259:     exit(-1);
                    260: }
                    261: 
                    262: int
                    263: main(int argc, char **argv){
1.1.1.1.2.2! misho     264:     int pidf, opt;
        !           265:     char *ifname;
        !           266:     char *filter, *targets_filter, *excludes_filter;
1.1       misho     267:     struct cidr **targets_tail = &targets, **excludes_tail = &excludes;
1.1.1.1.2.2! misho     268:     struct sigaction act;
        !           269: 
1.1       misho     270: #define APPEND(LIST,ADDR,MASK) \
                    271:     do {                                                       \
                    272:        *(LIST ## _tail) = malloc(sizeof (struct cidr));        \
1.1.1.1.2.2! misho     273:        (*(LIST ## _tail))->addr.s_addr = htonl(ADDR);          \
        !           274:        (*(LIST ## _tail))->mask.s_addr = htonl(MASK);          \
1.1       misho     275:        (*(LIST ## _tail))->next = NULL;                        \
                    276:        (LIST ## _tail) = &(*(LIST ## _tail))->next;            \
                    277:     } while (0)
                    278: 
1.1.1.1.2.2! misho     279:     while ((opt = getopt(argc, argv, "p:")) != -1) {
        !           280:         switch (opt) {
        !           281:         case 'p':
        !           282:             pidfile = optarg;
        !           283:             break;
        !           284:         case '?':
        !           285:             usage();
        !           286:         }
        !           287:     }
1.1       misho     288: 
1.1.1.1.2.2! misho     289:     if (pidfile == NULL) {
        !           290:         argv++;
        !           291:         argc--;
        !           292:     } else {
        !           293:         argv += 3;
        !           294:         argc -= 3;
        !           295:     }
        !           296: 
        !           297:     if (argc < 3)
1.1       misho     298:        usage();
1.1.1.1.2.2! misho     299: 
        !           300:     ifname = argv[0];
        !           301:     if (setmac(argv[1], ifname)) {
        !           302:         exit(1);
        !           303:     }
        !           304:     argv += 2; argc -= 2;
1.1       misho     305: 
                    306:     while (argc > 0) {
                    307:        u_int32_t addr, mask = ~0;
                    308:         char *slash = strchr (*argv, '/');
                    309:        int exclude = 0;
                    310: 
                    311:        if (**argv == '-') {
                    312:            (*argv)++;
                    313:            exclude = 1;
                    314:        }
                    315:        if (slash != NULL)
                    316:            *(slash++) = '\0';
                    317:        if (atoip (*argv, &addr))
                    318:            usage();
                    319:        if (slash != NULL) {
                    320:            char *end;
                    321:            u_int32_t len = strtol (slash, &end, 10);
                    322:            if (*end == '\0')
                    323:                mask <<= (32 - len);
                    324:            else if (atoip (slash, &mask))
                    325:                usage();
                    326:        }
                    327:        if (exclude)
                    328:            APPEND(excludes, addr, mask);
                    329:        else
                    330:            APPEND(targets, addr, mask);
1.1.1.1.2.2! misho     331: 
1.1       misho     332:        argv++, argc--;
                    333:     }
                    334: 
                    335: #ifdef DEBUG
                    336: #define SHOW(LIST) \
1.1.1.1.2.2! misho     337:     do {                                                       \
        !           338:        struct cidr *t;                                         \
        !           339:        fprintf (stderr, #LIST ":\n");                          \
        !           340:        for (t = LIST; t; t = t->next) {                        \
        !           341:            fprintf (stderr, "  %s", inet_ntoa (t->addr));      \
        !           342:            fprintf (stderr, "/%s\n", inet_ntoa (t->mask));     \
        !           343:        }                                                       \
1.1       misho     344:     } while (0)
                    345: 
                    346:     SHOW(targets);
                    347:     SHOW(excludes);
                    348:     exit (0);
                    349: #endif
1.1.1.1.2.2! misho     350: 
        !           351:     targets_filter = cidr_to_str(targets);
        !           352:     excludes_filter = cidr_to_str(excludes);
        !           353: 
        !           354: #define TMPL_FILTER "arp[2:2] == 0x0800 " /* Protocol: IPv4 */       \
        !           355:                     "and arp[4] == 6 "    /* Hw addr length: 6 */    \
        !           356:                     "and arp[5] == 4 "    /* Proto addr length: 4 */ \
        !           357:                     "and arp[6:2] == 1 "  /* Operation: Request */   \
        !           358:                     "and (%s)"
        !           359: 
        !           360: #define EXCL_FILTER TMPL_FILTER " and not (%s)"
        !           361:     if (excludes_filter == NULL)
        !           362:         asprintf (&filter, TMPL_FILTER, targets_filter);
        !           363:     else
        !           364:         asprintf (&filter, EXCL_FILTER, targets_filter, excludes_filter);
        !           365: 
        !           366: #ifdef DEBUG
        !           367:         fprintf(stderr, "Filter on %s: %s\n", ifname, filter);
        !           368: #endif
        !           369:     if ((pc = open_pcap(ifname, filter)) < 0)
        !           370:        exit(1);
        !           371:     free(filter);
        !           372: 
        !           373:     if (pidfile != NULL) {
        !           374:         pidf = open(pidfile, O_RDWR | O_CREAT | O_FSYNC, 0600);
        !           375:         if (pidf > 0) {
        !           376:             ftruncate(pidf, 0);
        !           377:             dprintf(pidf, "%u\n", getpid());
        !           378:             close(pidf);
        !           379:             memset(&act, 0, sizeof(act));
        !           380:             act.sa_handler = cleanup;
        !           381:             sigaction(SIGINT, &act, NULL);
        !           382:             sigaction(SIGTERM, &act, NULL);
        !           383:         }
        !           384:     }
        !           385: 
        !           386:     pcap_loop(pc, 0, process_arp, (u_char*)pc);
        !           387:     exit(1);
1.1       misho     388: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>