File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / choparp / choparp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:49:51 2012 UTC (12 years, 3 months ago) by misho
Branches: choparp, MAIN
CVS tags: v20021107p0, v20021107, HEAD
choparp

    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>