File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / choparp / choparp.c
Revision 1.1.1.1.2.1: download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:01:52 2013 UTC (11 years, 1 month ago) by misho
Branches: v20021107p0
Diff to: branchpoint 1.1.1.1: preferred, unified
patch0

    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/time.h>
   46: #include <sys/ioctl.h>
   47: #include <net/bpf.h>
   48: #include <sys/socket.h>
   49: #include <net/if.h>
   50: #include <netinet/in.h>
   51: /* #include <net/if_arp.h> */
   52: #if (__FreeBSD__ >= 3)
   53:  #include <net/if_var.h>
   54: #endif
   55: #include <netinet/if_ether.h>
   56: #include <sys/param.h>
   57: #include <errno.h>
   58: #include <ifaddrs.h>
   59: #include <net/if_dl.h>
   60: 
   61: #ifdef DEBUG
   62: #include <arpa/inet.h>
   63: #endif
   64: 
   65: #define	BPFFILENAME	"/dev/bpf%d"	/* bpf file template */
   66: #ifndef	NBPFILTER			/* number of available bpf */
   67: # define NBPFILTER (16)
   68: #endif
   69: 
   70: struct cidr {
   71: 	struct cidr *next;
   72: 	u_int32_t addr;		/* addr and mask are host order */
   73: 	u_int32_t mask;
   74: };
   75: 
   76: struct cidr *targets = NULL, *excludes = NULL;
   77: u_char	target_mac[ETHER_ADDR_LEN];	/* target MAC address */
   78: 
   79: /*
   80:    ARP filter program
   81: */
   82: struct bpf_insn bpf_filter_arp[] = {
   83:     /* check Ethernet Encapsulation (RFC894) first */
   84:     BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),	/* load frame type */
   85:     BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_ARP, 0, 3), /* check it */
   86:     BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20),	/* load OP code */
   87:     BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 1),  /* check it */
   88:     BPF_STMT(BPF_RET+BPF_K, 14+28),	/* return Ethernet encap ARP req. */
   89:     /* XXX: IEEE 802.2/802.3 Encap (RFC1042) should be available... */
   90:     BPF_STMT(BPF_RET+BPF_K, 0),		/* discard */
   91: };
   92: 
   93: /*
   94:    openbpf:
   95: 
   96:    open bpf & set ARP filter program for named interface &
   97:    allocate enough buffer for BPF.
   98:    return file descripter or -1 for error
   99: */
  100: int
  101: openbpf(char *ifname, char **bufp, size_t *buflen){
  102:     char bpffile[sizeof(BPFFILENAME)+5];	/* XXX: */
  103:     int	fd = -1;
  104:     int	n;
  105:     struct bpf_version	bpf_version;
  106:     struct ifreq	bpf_ifreq;
  107:     u_int	ui;
  108:     struct bpf_program	bpf_program;
  109: 
  110:     /* open BPF file */
  111:     for (n=0; n<NBPFILTER; n++){
  112: 	sprintf(bpffile, BPFFILENAME, n);
  113: 	if ((fd = open(bpffile, O_RDWR, 0)) >= 0)
  114: 	    break;
  115:     }
  116:     if (fd < 0){
  117: 	fprintf(stderr,"openbpf: Can't open BPF\n");
  118: 	return(-1);		/* error */
  119:     }
  120: 
  121:     /* check version number */
  122:     if ((ioctl(fd, BIOCVERSION, &bpf_version) == -1) ||
  123: 	bpf_version.bv_major != BPF_MAJOR_VERSION ||
  124: 	bpf_version.bv_minor < BPF_MINOR_VERSION){
  125: 	fprintf(stderr,"openbpf: incorrect BPF version\n");
  126: 	close(fd);
  127: 	return(-1);
  128:     }
  129: 
  130:     /* set interface name */
  131:     strncpy(bpf_ifreq.ifr_name, ifname, IFNAMSIZ);
  132:     bpf_ifreq.ifr_name[IFNAMSIZ-1] = '\0';	/* paranoia */
  133:     if (ioctl(fd, BIOCSETIF, &bpf_ifreq) == -1){
  134: 	fprintf(stderr,"openbpf: BIOCSETIF failed for interface <%s>\n",
  135: 		ifname);
  136: 	close(fd);
  137: 	return(-1);
  138:     }
  139: 
  140:     /* set BPF immediate mode */
  141:     ui = 1;
  142:     if (ioctl(fd, BIOCIMMEDIATE, &ui) == -1){
  143: 	fprintf(stderr,"openbpf: BIOCIMMEDIATE failed.\n");
  144: 	close(fd);
  145: 	return(-1);
  146:     }
  147: 
  148:     /* set ARP request filter */
  149:     bpf_program.bf_len = sizeof(bpf_filter_arp) / sizeof(struct bpf_insn);
  150:     bpf_program.bf_insns = bpf_filter_arp;
  151:     if (ioctl(fd, BIOCSETF, &bpf_program) == -1){
  152: 	fprintf(stderr,"openbpf: BIOCSETF failed.\n");
  153: 	close(fd);
  154: 	return(-1);
  155:     }
  156: 
  157:     /* allocate reasonable size & alimented buffer */
  158:     if (ioctl(fd, BIOCGBLEN, &ui) == -1){
  159: 	fprintf(stderr,"openbpf: BIOCGBLEN failed.\n");
  160: 	close(fd);
  161: 	return(-1);
  162:     }
  163:     *buflen = (size_t)ui;
  164:     if ((*bufp = (char *)malloc((size_t) ui)) == NULL){
  165: 	fprintf(stderr,"openbpf: malloc failed.\n");
  166: 	close(fd);
  167: 	return(-1);
  168:     }
  169: 
  170:     return(fd);
  171: }
  172: 
  173: /*
  174:    get ARP datalink frame pointer
  175: 
  176:    NULL if no more ARP frame
  177: */
  178: char *
  179: getarp(char *bpfframe, ssize_t bpfflen, char **next, ssize_t *nextlen){
  180:     int	bias;
  181:     char *p;
  182: 
  183:     if (bpfframe == NULL || bpfflen == 0)
  184: 	return(NULL);
  185: 
  186:     bias = BPF_WORDALIGN(((struct bpf_hdr *)bpfframe)->bh_hdrlen +
  187: 			 ((struct bpf_hdr *)bpfframe)->bh_caplen);
  188:     if (bias < bpfflen){
  189: 	/* there is another packet packed into same bpf frame */
  190: 	*next = bpfframe + bias;
  191: 	*nextlen = (size_t) bpfflen - bias;
  192:     } else {
  193: 	/* no more packet */
  194: 	*next = NULL;
  195: 	*nextlen = 0;
  196:     }
  197: 
  198:     /* cut off BPF header */
  199:     p = bpfframe + ((struct bpf_hdr *)bpfframe)->bh_hdrlen;
  200:     return(p);
  201: }
  202: 
  203: /*
  204:    match
  205: 
  206:    match an IP address against a list of address/netmask pairs
  207: */
  208: 
  209: static int
  210: match (u_int32_t addr, struct cidr *list) {
  211:     while (list) {
  212: 	if ((addr & list->mask) == list->addr)
  213: 	    return 1;
  214: 	list = list->next;
  215:     }
  216:     return 0;
  217: }
  218: 
  219: /*
  220:    checkarp
  221: 
  222:    check responsibility of the ARP request
  223:    return true if responsible
  224: 
  225:    arpbuf is pointing top of link-level frame
  226: */
  227: 
  228: static int
  229: checkarp(char *arpbuf){
  230:     struct ether_arp	*arp;
  231:     u_int32_t	target_ip;
  232: 
  233:     arp = (struct ether_arp *)(arpbuf + 14);	/* skip ethernet header */
  234:     if (ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
  235: 	/* XXX: ARPHRD_802 */
  236: 	ntohs(arp->arp_pro) != ETHERTYPE_IP ||
  237: 	(int) (arp->arp_hln) != ETHER_ADDR_LEN || /* length of ethernet addr */
  238: 	(int) (arp->arp_pln) != 4){  /* length of protocol addr */
  239: 	fprintf(stderr,"checkarp: WARNING: received unknown type ARP request.\n");
  240: 	return(0);
  241:     }
  242:     target_ip = ntohl(*(u_int32_t *)(arp->arp_tpa));
  243:     return match(target_ip, targets) && !match(target_ip, excludes);
  244: }
  245: 
  246: /*
  247:    genarpreply
  248: 
  249:    generate arp reply link level frame
  250:    arpbuf is pointing top of link-level frame
  251:    this routine overwrite arpbuf
  252: 
  253:    return reply buffer & its length
  254: */
  255: char *
  256: gen_arpreply(char *arpbuf, size_t *rlen){
  257:     struct ether_arp	*arp;
  258:     u_char	ipbuf[4];	/* sender IP */
  259: 
  260:     /* set ethernet dst/src address */
  261:     memcpy(arpbuf, arpbuf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);
  262:     memcpy(arpbuf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);
  263:     /* set result of ARP request */
  264:     arp = (struct ether_arp *)(arpbuf + 14);	/* skip ethernet header */
  265:     memcpy(ipbuf, arp->arp_tpa, 4);		/* save protocol addr */
  266:     memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
  267:     memcpy(arp->arp_spa, ipbuf, 4);		/* set source protocol addr */
  268:     memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN); /* set source hard addr */
  269:     arp->arp_op = htons(ARPOP_REPLY);
  270: 
  271:     *rlen = 14 + 28;		/* ethernet header & arp reply */
  272:     return(arpbuf);
  273: }
  274: 
  275: void
  276: loop(int fd, char *buf, size_t buflen){
  277:     ssize_t  rlen;
  278:     char    *p, *nextp;
  279:     ssize_t  nextlen;
  280:     char    *rframe;
  281:     char    *sframe;
  282:     size_t  frame_len;
  283:     fd_set  fdset;
  284: 
  285:     FD_ZERO(&fdset);
  286:     FD_SET(fd,&fdset);
  287: 
  288:     for(;;){
  289:         int r = select(fd+1,&fdset, 0, 0, 0);
  290: 
  291:         if (r < 0) {
  292:             if (errno == EINTR)
  293:                 continue;
  294:             perror("select");
  295:             return;
  296:         }
  297: 
  298:         rlen = read(fd, buf, buflen);
  299:         if (rlen < 0) {
  300:             if (errno == EINTR)
  301:                 continue;
  302:             perror("read");
  303:             return;
  304:         }
  305: 
  306: 	p = buf;
  307: 	while((rframe = getarp(p, rlen, &nextp, &nextlen)) != NULL){
  308: 	    if (checkarp(rframe)){
  309: 		sframe = gen_arpreply(rframe, &frame_len);
  310: 		write(fd, sframe, frame_len);
  311: 	    }
  312: 	    p = nextp;
  313: 	    rlen = nextlen;
  314: 	}
  315:     }
  316:     /* not reach */
  317: }
  318: 
  319: int
  320: setmac(char *addr, char *ifname){
  321:     u_int m0, m1, m2, m3, m4, m5;
  322: 
  323:     if (!strcmp (addr, "auto")) {
  324: 	struct ifaddrs *ifas, *ifa;
  325: 
  326: 	getifaddrs (&ifas);
  327: 	for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
  328: #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)
  329: 	    if (strcmp (ifa->ifa_name, ifname)
  330: 	      || SDL->sdl_family != AF_LINK
  331: 	      || SDL->sdl_alen != 6)
  332: 		continue;
  333: 	    memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, 6);
  334: 	    return 0;
  335: 	}
  336: 	return -1;
  337:     }
  338:     if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6)
  339:         return(-1);
  340:     target_mac[0] = (u_char )m0;
  341:     target_mac[1] = (u_char )m1;
  342:     target_mac[2] = (u_char )m2;
  343:     target_mac[3] = (u_char )m3;
  344:     target_mac[4] = (u_char )m4;
  345:     target_mac[5] = (u_char )m5;
  346:     return(0);
  347: }
  348: 
  349: int
  350: atoip(char *buf, u_int32_t *ip_addr){
  351:     u_int	i0, i1, i2, i3;
  352: 
  353:     if (sscanf(buf, "%u.%u.%u.%u", &i0, &i1, &i2, &i3) == 4){
  354: 	*ip_addr = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3;
  355: 	return(0);
  356:     }
  357:     if (sscanf(buf, "0x%lx", ip_addr) == 1)
  358: 	return(0);
  359: 
  360:     return(-1);	
  361: }
  362: 
  363: void
  364: usage(void){
  365:     fprintf(stderr,"usage: choparp if_name mac_addr [-]addr/mask...\n");
  366:     exit(-1);
  367: }
  368: 
  369: int
  370: main(int argc, char **argv){
  371:     int	fd;
  372:     char *buf, *ifname;
  373:     struct cidr **targets_tail = &targets, **excludes_tail = &excludes;
  374: #define APPEND(LIST,ADDR,MASK) \
  375:     do {							\
  376: 	*(LIST ## _tail) = malloc(sizeof (struct cidr));	\
  377: 	(*(LIST ## _tail))->addr = ADDR;			\
  378: 	(*(LIST ## _tail))->mask = MASK;			\
  379: 	(*(LIST ## _tail))->next = NULL;			\
  380: 	(LIST ## _tail) = &(*(LIST ## _tail))->next;		\
  381:     } while (0)
  382:     size_t buflen;
  383: 
  384:     if (argc < 4)
  385: 	usage();
  386: 
  387:     ifname = argv[1];
  388:     if (setmac(argv[2], ifname))
  389: 	usage();
  390:     argv += 3; argc -= 3;
  391: 
  392:     while (argc > 0) {
  393: 	u_int32_t addr, mask = ~0;
  394:         char *slash = strchr (*argv, '/');
  395: 	int exclude = 0;
  396: 
  397: 	if (**argv == '-') {
  398: 	    (*argv)++;
  399: 	    exclude = 1;
  400: 	}
  401: 	if (slash != NULL)
  402: 	    *(slash++) = '\0';
  403: 	if (atoip (*argv, &addr))
  404: 	    usage();
  405: 	if (slash != NULL) {
  406: 	    char *end;
  407: 	    u_int32_t len = strtol (slash, &end, 10);
  408: 	    if (*end == '\0')
  409: 		mask <<= (32 - len);
  410: 	    else if (atoip (slash, &mask))
  411: 		usage();
  412: 	}
  413: 	if (exclude)
  414: 	    APPEND(excludes, addr, mask);
  415: 	else
  416: 	    APPEND(targets, addr, mask);
  417: 	argv++, argc--;
  418:     }
  419: 
  420: #ifdef DEBUG
  421: #define SHOW(LIST) \
  422:     do {								\
  423: 	struct cidr *t;							\
  424: 	printf (#LIST ":\n");						\
  425: 	for (t = LIST; t; t = t->next) {				\
  426: 	    u_int32_t x;							\
  427: 	    x = htonl (t->addr);					\
  428: 	    printf ("  %s", inet_ntoa (*(struct in_addr *)&x));		\
  429: 	    x = htonl (t->mask);					\
  430: 	    printf ("/%s\n", inet_ntoa (*(struct in_addr *)&x));	\
  431: 	}								\
  432:     } while (0)
  433: 
  434:     SHOW(targets);
  435:     SHOW(excludes);
  436:     exit (0);
  437: #endif
  438:     if ((fd = openbpf(ifname, &buf, &buflen)) < 0)
  439: 	return(-1);
  440:     loop(fd, buf, buflen);
  441:     return(-1);
  442: }

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