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>