File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / choparp / choparp.c
Revision 1.1.1.1.2.2: download - view: text, annotated - select for diffs - revision graph
Tue Oct 18 13:39:35 2016 UTC (7 years, 10 months ago) by misho
Branches: v20021107p0
CVS tags: v20150613p0
Diff to: branchpoint 1.1.1.1: preferred, unified
v20150613p0

    1: /*
    2:    choparp - cheap & omitted proxy arp
    3: 
    4:    Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)
    5:    Copyright (c) 2002-2015 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: #define _GNU_SOURCE /* asprintf */
   40: 
   41: #include <pcap.h>
   42: #include <stdio.h>
   43: #include <stdlib.h>
   44: #include <string.h>
   45: #include <signal.h>
   46: #include <net/if.h>
   47: #include <sys/types.h>
   48: #include <sys/socket.h>
   49: #include <netinet/in.h>
   50: #include <netinet/if_ether.h>
   51: #include <arpa/inet.h>
   52: #include <fcntl.h>
   53: #include <unistd.h>
   54: #include <sys/ioctl.h>
   55: 
   56: #ifndef __linux__
   57: #include <ifaddrs.h>
   58: #include <net/if_dl.h>
   59: #endif
   60: 
   61: /* ARP Header                                      */ 
   62: #define ARP_REQUEST 1   /* ARP Request             */ 
   63: #define ARP_REPLY 2     /* ARP Reply               */ 
   64: 
   65: struct cidr {
   66: 	struct cidr *next;
   67: 	struct in_addr addr;		/* addr and mask are host order */
   68: 	struct in_addr mask;
   69: };
   70: 
   71: struct cidr *targets = NULL, *excludes = NULL;
   72: char errbuf[PCAP_ERRBUF_SIZE];
   73: u_char target_mac[ETHER_ADDR_LEN];	/* target MAC address */
   74: 
   75: static char *pidfile = NULL;
   76: static pcap_t *pc;
   77: 
   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:         }
   91: 
   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:         }
  108: 
  109:         a = a->next;
  110:     }
  111:     return res;
  112: }
  113: 
  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);
  135:     }
  136: 
  137:     pcap_freecode(&filter);
  138:     return pc;
  139: }
  140: 
  141: void
  142: gen_arpreply(u_char *buf) {
  143:     struct ether_arp *arp;
  144:     struct in_addr ipbuf;
  145: 
  146:     /* set ethernet dst/src address */
  147:     memcpy(buf, buf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);
  148:     memcpy(buf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);
  149: 
  150:     /* set result of ARP request */
  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 */
  157:     arp->arp_op = htons(ARPOP_REPLY);
  158: }
  159: 
  160: void
  161: cleanup(int sig){
  162:      if (pidfile != NULL)
  163:          unlink(pidfile);
  164: 
  165:      pcap_breakloop(pc);
  166:      pcap_close(pc);
  167:      exit(0);
  168: }
  169: 
  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);
  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")) {
  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;
  200: 
  201:         getifaddrs (&ifas);
  202:         for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
  203: #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)
  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);
  231:         return(-1);
  232:     }
  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:     }
  250:     if (sscanf(buf, "0x%lx", (unsigned long *) ip_addr) == 1)
  251: 	return(0);
  252: 
  253:     return(-1);	
  254: }
  255: 
  256: void
  257: usage(void){
  258:     fprintf(stderr,"usage: choparp [-p PIDFILE] if_name mac_addr [-]addr/mask...\n");
  259:     exit(-1);
  260: }
  261: 
  262: int
  263: main(int argc, char **argv){
  264:     int pidf, opt;
  265:     char *ifname;
  266:     char *filter, *targets_filter, *excludes_filter;
  267:     struct cidr **targets_tail = &targets, **excludes_tail = &excludes;
  268:     struct sigaction act;
  269: 
  270: #define APPEND(LIST,ADDR,MASK) \
  271:     do {							\
  272: 	*(LIST ## _tail) = malloc(sizeof (struct cidr));	\
  273: 	(*(LIST ## _tail))->addr.s_addr = htonl(ADDR);		\
  274: 	(*(LIST ## _tail))->mask.s_addr = htonl(MASK);		\
  275: 	(*(LIST ## _tail))->next = NULL;			\
  276: 	(LIST ## _tail) = &(*(LIST ## _tail))->next;		\
  277:     } while (0)
  278: 
  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:     }
  288: 
  289:     if (pidfile == NULL) {
  290:         argv++;
  291:         argc--;
  292:     } else {
  293:         argv += 3;
  294:         argc -= 3;
  295:     }
  296: 
  297:     if (argc < 3)
  298: 	usage();
  299: 
  300:     ifname = argv[0];
  301:     if (setmac(argv[1], ifname)) {
  302:         exit(1);
  303:     }
  304:     argv += 2; argc -= 2;
  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);
  331: 
  332: 	argv++, argc--;
  333:     }
  334: 
  335: #ifdef DEBUG
  336: #define SHOW(LIST) \
  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: 	}							\
  344:     } while (0)
  345: 
  346:     SHOW(targets);
  347:     SHOW(excludes);
  348:     exit (0);
  349: #endif
  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);
  388: }

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