Diff for /embedaddon/choparp/choparp.c between versions 1.1.1.1.2.1 and 1.1.1.1.2.2

version 1.1.1.1.2.1, 2013/07/22 01:01:52 version 1.1.1.1.2.2, 2016/10/18 13:39:35
Line 2 Line 2
    choparp - cheap & omitted proxy arp     choparp - cheap & omitted proxy arp
   
    Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)     Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)
   Copyright (c) 2002 Thomas Quinot (thomas@cuivre.fr.eu.org)   Copyright (c) 2002-2015 Thomas Quinot (thomas@cuivre.fr.eu.org)
         
    Redistribution and use in source and binary forms, with or without     Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions     modification, are permitted provided that the following conditions
Line 36 Line 36
   
 */  */
   
   #define _GNU_SOURCE /* asprintf */
   
   #include <pcap.h>
 #include <stdio.h>  #include <stdio.h>
 #include <unistd.h>  
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   #include <signal.h>
   #include <net/if.h>
 #include <sys/types.h>  #include <sys/types.h>
 #include <fcntl.h>  
 #include <sys/time.h>  
 #include <sys/ioctl.h>  
 #include <net/bpf.h>  
 #include <sys/socket.h>  #include <sys/socket.h>
 #include <net/if.h>  
 #include <netinet/in.h>  #include <netinet/in.h>
 /* #include <net/if_arp.h> */  
 #if (__FreeBSD__ >= 3)  
  #include <net/if_var.h>  
 #endif  
 #include <netinet/if_ether.h>  #include <netinet/if_ether.h>
#include <sys/param.h>#include <arpa/inet.h>
#include <errno.h>#include <fcntl.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 
 #ifndef __linux__
 #include <ifaddrs.h>  #include <ifaddrs.h>
 #include <net/if_dl.h>  #include <net/if_dl.h>
   
 #ifdef DEBUG  
 #include <arpa/inet.h>  
 #endif  #endif
   
#define BPFFILENAME     "/dev/bpf%d"    /* bpf file template *//* ARP Header                                      */ 
#ifndef  NBPFILTER                       /* number of available bpf */#define ARP_REQUEST 1   /* ARP Request             */ 
# define NBPFILTER (16)#define ARP_REPLY 2     /* ARP Reply               */ 
#endif 
   
 struct cidr {  struct cidr {
         struct cidr *next;          struct cidr *next;
        u_int32_t addr;          /* addr and mask are host order */        struct in_addr addr;          /* addr and mask are host order */
        u_int32_t mask;        struct in_addr mask;
 };  };
   
 struct cidr *targets = NULL, *excludes = NULL;  struct cidr *targets = NULL, *excludes = NULL;
u_char  target_mac[ETHER_ADDR_LEN];     /* target MAC address */char errbuf[PCAP_ERRBUF_SIZE];
 u_char target_mac[ETHER_ADDR_LEN];     /* target MAC address */
   
/*static char *pidfile = NULL;
   ARP filter programstatic pcap_t *pc;
*/ 
struct bpf_insn bpf_filter_arp[] = { 
    /* check Ethernet Encapsulation (RFC894) first */ 
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* load frame type */ 
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_ARP, 0, 3), /* check it */ 
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20), /* load OP code */ 
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 1),  /* check it */ 
    BPF_STMT(BPF_RET+BPF_K, 14+28),     /* return Ethernet encap ARP req. */ 
    /* XXX: IEEE 802.2/802.3 Encap (RFC1042) should be available... */ 
    BPF_STMT(BPF_RET+BPF_K, 0),         /* discard */ 
}; 
   
/*char* cidr_to_str(struct cidr *a) {
   openbpf:    char buf[64];
     char *res = NULL;
     int res_alloc, res_len;
     int len;
   
   open bpf & set ARP filter program for named interface &    while (a) {
   allocate enough buffer for BPF.        if (a->mask.s_addr == INADDR_NONE) {
   return file descripter or -1 for error            len = snprintf(buf, sizeof buf, "dst host %s", inet_ntoa(a->addr));
*/        } else {
int            len = snprintf(buf, sizeof buf, "dst net %s mask ", inet_ntoa(a->addr));
openbpf(char *ifname, char **bufp, size_t *buflen){            len += snprintf(buf + len, sizeof buf - len, "%s", inet_ntoa(a->mask));
    char bpffile[sizeof(BPFFILENAME)+5];       /* XXX: */        }
    int fd = -1; 
    int n; 
    struct bpf_version  bpf_version; 
    struct ifreq        bpf_ifreq; 
    u_int       ui; 
    struct bpf_program  bpf_program; 
   
    /* open BPF file */        if (!res) {
    for (n=0; n<NBPFILTER; n++){            res_alloc = 1024;
        sprintf(bpffile, BPFFILENAME, n);            res = malloc(res_alloc);
        if ((fd = open(bpffile, O_RDWR, 0)) >= 0)            strncpy(res, buf, res_alloc);
            break;            res_len = len;
    } 
    if (fd < 0){ 
        fprintf(stderr,"openbpf: Can't open BPF\n"); 
        return(-1);               /* error */ 
    } 
   
    /* check version number */        } else {
    if ((ioctl(fd, BIOCVERSION, &bpf_version) == -1) ||            if (res_len + len + 5 > res_alloc) {
        bpf_version.bv_major != BPF_MAJOR_VERSION ||                res_alloc *= 2;
        bpf_version.bv_minor < BPF_MINOR_VERSION){                res = realloc(res, res_alloc);
        fprintf(stderr,"openbpf: incorrect BPF version\n");            }
        close(fd);            strncat(res, " or ", res_alloc - res_len - 1);
        return(-1);            res_len += 4;
    }            strncat(res, buf, res_alloc - res_len - 1);
             res_len += len;
         }
   
    /* set interface name */        a = a->next;
    strncpy(bpf_ifreq.ifr_name, ifname, IFNAMSIZ); 
    bpf_ifreq.ifr_name[IFNAMSIZ-1] = '\0';      /* paranoia */ 
    if (ioctl(fd, BIOCSETIF, &bpf_ifreq) == -1){ 
        fprintf(stderr,"openbpf: BIOCSETIF failed for interface <%s>\n", 
                ifname); 
        close(fd); 
        return(-1); 
     }      }
       return res;
   }
   
    /* set BPF immediate mode */pcap_t *
    ui = 1;open_pcap(char *ifname, char *filter_str) {
    if (ioctl(fd, BIOCIMMEDIATE, &ui) == -1){    pcap_t *pc = NULL;
        fprintf(stderr,"openbpf: BIOCIMMEDIATE failed.\n");    struct bpf_program filter;
        close(fd); 
        return(-1); 
    } 
   
    /* set ARP request filter */    /* Set up PCAP */
    bpf_program.bf_len = sizeof(bpf_filter_arp) / sizeof(struct bpf_insn);    if ((pc = pcap_open_live(ifname, 128, 0,  512, errbuf))==NULL){
    bpf_program.bf_insns = bpf_filter_arp;       fprintf(stderr, "pcap_open_live failed: %s\n", errbuf);
    if (ioctl(fd, BIOCSETF, &bpf_program) == -1){       exit(1);
        fprintf(stderr,"openbpf: BIOCSETF failed.\n"); 
        close(fd); 
        return(-1); 
     }      }
    
    /* allocate reasonable size & alimented buffer */    /* Compiles the filter expression */ 
    if (ioctl(fd, BIOCGBLEN, &ui) == -1){    if (pcap_compile(pc, &filter, filter_str, 1, PCAP_NETMASK_UNKNOWN) == -1){
        fprintf(stderr,"openbpf: BIOCGBLEN failed.\n");       fprintf(stderr, "pcap_compile failed: %s\n", pcap_geterr(pc) );
        close(fd);       exit(1);
        return(-1); 
     }      }
     *buflen = (size_t)ui;  
     if ((*bufp = (char *)malloc((size_t) ui)) == NULL){  
         fprintf(stderr,"openbpf: malloc failed.\n");  
         close(fd);  
         return(-1);  
     }  
   
    return(fd);    /* Set filter program */ 
}    if (pcap_setfilter(pc, &filter) == -1){
       fprintf(stderr, "pcap_setfilter failed: %s\n", pcap_geterr(pc));
/*       exit(1);
   get ARP datalink frame pointer 
 
   NULL if no more ARP frame 
*/ 
char * 
getarp(char *bpfframe, ssize_t bpfflen, char **next, ssize_t *nextlen){ 
    int bias; 
    char *p; 
 
    if (bpfframe == NULL || bpfflen == 0) 
        return(NULL); 
 
    bias = BPF_WORDALIGN(((struct bpf_hdr *)bpfframe)->bh_hdrlen + 
                         ((struct bpf_hdr *)bpfframe)->bh_caplen); 
    if (bias < bpfflen){ 
        /* there is another packet packed into same bpf frame */ 
        *next = bpfframe + bias; 
        *nextlen = (size_t) bpfflen - bias; 
    } else { 
        /* no more packet */ 
        *next = NULL; 
        *nextlen = 0; 
     }      }
   
    /* cut off BPF header */    pcap_freecode(&filter);
    p = bpfframe + ((struct bpf_hdr *)bpfframe)->bh_hdrlen;    return pc;
    return(p); 
 }  }
   
/*void
   matchgen_arpreply(u_char *buf) {
     struct ether_arp *arp;
     struct in_addr ipbuf;
   
    match an IP address against a list of address/netmask pairs  
 */  
   
 static int  
 match (u_int32_t addr, struct cidr *list) {  
     while (list) {  
         if ((addr & list->mask) == list->addr)  
             return 1;  
         list = list->next;  
     }  
     return 0;  
 }  
   
 /*  
    checkarp  
   
    check responsibility of the ARP request  
    return true if responsible  
   
    arpbuf is pointing top of link-level frame  
 */  
   
 static int  
 checkarp(char *arpbuf){  
     struct ether_arp    *arp;  
     u_int32_t   target_ip;  
   
     arp = (struct ether_arp *)(arpbuf + 14);    /* skip ethernet header */  
     if (ntohs(arp->arp_hrd) != ARPHRD_ETHER ||  
         /* XXX: ARPHRD_802 */  
         ntohs(arp->arp_pro) != ETHERTYPE_IP ||  
         (int) (arp->arp_hln) != ETHER_ADDR_LEN || /* length of ethernet addr */  
         (int) (arp->arp_pln) != 4){  /* length of protocol addr */  
         fprintf(stderr,"checkarp: WARNING: received unknown type ARP request.\n");  
         return(0);  
     }  
     target_ip = ntohl(*(u_int32_t *)(arp->arp_tpa));  
     return match(target_ip, targets) && !match(target_ip, excludes);  
 }  
   
 /*  
    genarpreply  
   
    generate arp reply link level frame  
    arpbuf is pointing top of link-level frame  
    this routine overwrite arpbuf  
   
    return reply buffer & its length  
 */  
 char *  
 gen_arpreply(char *arpbuf, size_t *rlen){  
     struct ether_arp    *arp;  
     u_char      ipbuf[4];       /* sender IP */  
   
     /* set ethernet dst/src address */      /* set ethernet dst/src address */
    memcpy(arpbuf, arpbuf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);    memcpy(buf, buf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);
    memcpy(arpbuf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);    memcpy(buf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);
 
     /* set result of ARP request */      /* set result of ARP request */
    arp = (struct ether_arp *)(arpbuf + 14);    /* skip ethernet header */    arp = (struct ether_arp *)(buf + ETHER_HDR_LEN);
    memcpy(ipbuf, arp->arp_tpa, 4);             /* save protocol addr */    memcpy((char*) &ipbuf, arp->arp_tpa, sizeof(ipbuf));        /* save protocol addr */
    memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */    memcpy(arp->arp_tha, arp->arp_sha, sizeof(arp->arp_tha)); /* set target hard addr */
    memcpy(arp->arp_spa, ipbuf, 4);                /* set source protocol addr */    memcpy(arp->arp_tpa, arp->arp_spa, sizeof(arp->arp_tpa)); /* set target proto addr */
    memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN); /* set source hard addr */    memcpy(arp->arp_spa, (char *)&ipbuf, sizeof(ipbuf));                      /* set source protocol addr */
     memcpy(arp->arp_sha, target_mac, ETHER_ADDR_LEN);         /* set source hard addr */
     arp->arp_op = htons(ARPOP_REPLY);      arp->arp_op = htons(ARPOP_REPLY);
   
     *rlen = 14 + 28;            /* ethernet header & arp reply */  
     return(arpbuf);  
 }  }
   
 void  void
loop(int fd, char *buf, size_t buflen){cleanup(int sig){
    ssize_t  rlen;     if (pidfile != NULL)
    char    *p, *nextp;         unlink(pidfile);
    ssize_t  nextlen; 
    char    *rframe; 
    char    *sframe; 
    size_t  frame_len; 
    fd_set  fdset; 
   
    FD_ZERO(&fdset);     pcap_breakloop(pc);
    FD_SET(fd,&fdset);     pcap_close(pc);
      exit(0);
 }
   
    for(;;){void
        int r = select(fd+1,&fdset, 0, 0, 0);process_arp(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *packet) {
    gen_arpreply((u_char *)packet);
        if (r < 0) {    pcap_inject((pcap_t *)user, packet, pkthdr->len);
            if (errno == EINTR) 
                continue; 
            perror("select"); 
            return; 
        } 
 
        rlen = read(fd, buf, buflen); 
        if (rlen < 0) { 
            if (errno == EINTR) 
                continue; 
            perror("read"); 
            return; 
        } 
 
        p = buf; 
        while((rframe = getarp(p, rlen, &nextp, &nextlen)) != NULL){ 
            if (checkarp(rframe)){ 
                sframe = gen_arpreply(rframe, &frame_len); 
                write(fd, sframe, frame_len); 
            } 
            p = nextp; 
            rlen = nextlen; 
        } 
    } 
    /* not reach */ 
 }  }
   
 int  int
Line 321  setmac(char *addr, char *ifname){ Line 178  setmac(char *addr, char *ifname){
     u_int m0, m1, m2, m3, m4, m5;      u_int m0, m1, m2, m3, m4, m5;
   
     if (!strcmp (addr, "auto")) {      if (!strcmp (addr, "auto")) {
        struct ifaddrs *ifas, *ifa;#ifdef __linux__
         int fd;
         struct ifreq ifr;
   
        getifaddrs (&ifas);        if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {            perror("socket");
             return -1;
         }
 
         strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
 
         if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
             perror("ioctl(SIOCGIFHWADDR)");
             return -1;
         }
         memcpy(target_mac, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
         return 0;
 #else
         struct ifaddrs *ifas, *ifa;
 
         getifaddrs (&ifas);
         for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
 #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)  #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)
            if (strcmp (ifa->ifa_name, ifname)            if (strcmp (ifa->ifa_name, ifname)
              || SDL->sdl_family != AF_LINK              || SDL->sdl_family != AF_LINK
              || SDL->sdl_alen != 6)              || SDL->sdl_alen != ETHER_ADDR_LEN)
                continue;                continue;
            memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, 6);            memcpy (target_mac, SDL->sdl_data + SDL->sdl_nlen, ETHER_ADDR_LEN);
            return 0;            return 0;
        }        }
        return -1;#endif
    }        fprintf(stderr, "%s: not found\n", ifname);
    if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6)        return -1;
 
     } else if (!strncmp (addr, "vhid:", 4)) {
         /*
          * Virtual router mac address
          * CARP address format: 00:00:5e:00:01:<VHID>
          */
         char *vhid = addr + 5;
         if (!*vhid)
             return(-1);
         m0 = 0;
         m1 = 0;
         m2 = 0x5e;
         m3 = 0;
         m4 = 1;
         m5 = atoi(vhid);
     } else if (sscanf(addr, "%x:%x:%x:%x:%x:%x", &m0, &m1, &m2, &m3, &m4, &m5) < 6) {
         fprintf(stderr, "invalid MAC address: %s", addr);
         return(-1);          return(-1);
       }
     target_mac[0] = (u_char )m0;      target_mac[0] = (u_char )m0;
     target_mac[1] = (u_char )m1;      target_mac[1] = (u_char )m1;
     target_mac[2] = (u_char )m2;      target_mac[2] = (u_char )m2;
Line 354  atoip(char *buf, u_int32_t *ip_addr){ Line 247  atoip(char *buf, u_int32_t *ip_addr){
         *ip_addr = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3;          *ip_addr = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3;
         return(0);          return(0);
     }      }
    if (sscanf(buf, "0x%lx", ip_addr) == 1)    if (sscanf(buf, "0x%lx", (unsigned long *) ip_addr) == 1)
         return(0);          return(0);
   
     return(-1);       return(-1); 
Line 362  atoip(char *buf, u_int32_t *ip_addr){ Line 255  atoip(char *buf, u_int32_t *ip_addr){
   
 void  void
 usage(void){  usage(void){
    fprintf(stderr,"usage: choparp if_name mac_addr [-]addr/mask...\n");    fprintf(stderr,"usage: choparp [-p PIDFILE] if_name mac_addr [-]addr/mask...\n");
     exit(-1);      exit(-1);
 }  }
   
 int  int
 main(int argc, char **argv){  main(int argc, char **argv){
    int fd;    int pidf, opt;
    char *buf, *ifname;    char *ifname;
     char *filter, *targets_filter, *excludes_filter;
     struct cidr **targets_tail = &targets, **excludes_tail = &excludes;      struct cidr **targets_tail = &targets, **excludes_tail = &excludes;
       struct sigaction act;
   
 #define APPEND(LIST,ADDR,MASK) \  #define APPEND(LIST,ADDR,MASK) \
     do {                                                        \      do {                                                        \
         *(LIST ## _tail) = malloc(sizeof (struct cidr));        \          *(LIST ## _tail) = malloc(sizeof (struct cidr));        \
        (*(LIST ## _tail))->addr = ADDR;                       \        (*(LIST ## _tail))->addr.s_addr = htonl(ADDR);                \
        (*(LIST ## _tail))->mask = MASK;                       \        (*(LIST ## _tail))->mask.s_addr = htonl(MASK);              \
         (*(LIST ## _tail))->next = NULL;                        \          (*(LIST ## _tail))->next = NULL;                        \
         (LIST ## _tail) = &(*(LIST ## _tail))->next;            \          (LIST ## _tail) = &(*(LIST ## _tail))->next;            \
     } while (0)      } while (0)
     size_t buflen;  
   
    if (argc < 4)    while ((opt = getopt(argc, argv, "p:")) != -1) {
        usage();        switch (opt) {
         case 'p':
             pidfile = optarg;
             break;
         case '?':
             usage();
         }
     }
   
    ifname = argv[1];    if (pidfile == NULL) {
    if (setmac(argv[2], ifname))        argv++;
         argc--;
     } else {
         argv += 3;
         argc -= 3;
     }
 
     if (argc < 3)
         usage();          usage();
     argv += 3; argc -= 3;  
   
       ifname = argv[0];
       if (setmac(argv[1], ifname)) {
           exit(1);
       }
       argv += 2; argc -= 2;
   
     while (argc > 0) {      while (argc > 0) {
         u_int32_t addr, mask = ~0;          u_int32_t addr, mask = ~0;
         char *slash = strchr (*argv, '/');          char *slash = strchr (*argv, '/');
Line 414  main(int argc, char **argv){ Line 328  main(int argc, char **argv){
             APPEND(excludes, addr, mask);              APPEND(excludes, addr, mask);
         else          else
             APPEND(targets, addr, mask);              APPEND(targets, addr, mask);
   
         argv++, argc--;          argv++, argc--;
     }      }
   
 #ifdef DEBUG  #ifdef DEBUG
 #define SHOW(LIST) \  #define SHOW(LIST) \
    do {                                                                \    do {                                                        \
        struct cidr *t;                                                 \        struct cidr *t;                                         \
        printf (#LIST ":\n");                                           \        fprintf (stderr, #LIST ":\n");                          \
        for (t = LIST; t; t = t->next) {                                \        for (t = LIST; t; t = t->next) {                        \
            u_int32_t x;                                                        \            fprintf (stderr, "  %s", inet_ntoa (t->addr));     \
            x = htonl (t->addr);                                        \            fprintf (stderr, "/%s\n", inet_ntoa (t->mask));     \
            printf ("  %s", inet_ntoa (*(struct in_addr *)&x));             \        }                                                       \
            x = htonl (t->mask);                                        \ 
            printf ("/%s\n", inet_ntoa (*(struct in_addr *)&x));     \ 
        }                                                               \ 
     } while (0)      } while (0)
   
     SHOW(targets);      SHOW(targets);
     SHOW(excludes);      SHOW(excludes);
     exit (0);      exit (0);
 #endif  #endif
    if ((fd = openbpf(ifname, &buf, &buflen)) < 0)
        return(-1);    targets_filter = cidr_to_str(targets);
    loop(fd, buf, buflen);    excludes_filter = cidr_to_str(excludes);
    return(-1);
 #define TMPL_FILTER "arp[2:2] == 0x0800 " /* Protocol: IPv4 */       \
                     "and arp[4] == 6 "    /* Hw addr length: 6 */    \
                     "and arp[5] == 4 "    /* Proto addr length: 4 */ \
                     "and arp[6:2] == 1 "  /* Operation: Request */   \
                     "and (%s)"
 
 #define EXCL_FILTER TMPL_FILTER " and not (%s)"
     if (excludes_filter == NULL)
         asprintf (&filter, TMPL_FILTER, targets_filter);
     else
         asprintf (&filter, EXCL_FILTER, targets_filter, excludes_filter);
 
 #ifdef DEBUG
         fprintf(stderr, "Filter on %s: %s\n", ifname, filter);
 #endif
     if ((pc = open_pcap(ifname, filter)) < 0)
         exit(1);
     free(filter);
 
     if (pidfile != NULL) {
         pidf = open(pidfile, O_RDWR | O_CREAT | O_FSYNC, 0600);
         if (pidf > 0) {
             ftruncate(pidf, 0);
             dprintf(pidf, "%u\n", getpid());
             close(pidf);
             memset(&act, 0, sizeof(act));
             act.sa_handler = cleanup;
             sigaction(SIGINT, &act, NULL);
             sigaction(SIGTERM, &act, NULL);
         }
     }
 
     pcap_loop(pc, 0, process_arp, (u_char*)pc);
     exit(1);
 }  }

Removed from v.1.1.1.1.2.1  
changed lines
  Added in v.1.1.1.1.2.2


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