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>