1: /*
2: choparp - cheap & omitted proxy arp
3:
4: Copyright (c) 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)
5: Copyright (c) 2002-2015 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: #define _GNU_SOURCE /* asprintf */
40:
41: #include <pcap.h>
42: #include <stdio.h>
43: #include <stdlib.h>
44: #include <string.h>
45: #include <signal.h>
46: #include <net/if.h>
47: #include <sys/types.h>
48: #include <sys/socket.h>
49: #include <netinet/in.h>
50: #include <netinet/if_ether.h>
51: #include <arpa/inet.h>
52: #include <fcntl.h>
53: #include <unistd.h>
54: #include <sys/ioctl.h>
55:
56: #ifndef __linux__
57: #include <ifaddrs.h>
58: #include <net/if_dl.h>
59: #endif
60:
61: /* ARP Header */
62: #define ARP_REQUEST 1 /* ARP Request */
63: #define ARP_REPLY 2 /* ARP Reply */
64:
65: struct cidr {
66: struct cidr *next;
67: struct in_addr addr; /* addr and mask are host order */
68: struct in_addr mask;
69: };
70:
71: struct cidr *targets = NULL, *excludes = NULL;
72: char errbuf[PCAP_ERRBUF_SIZE];
73: u_char target_mac[ETHER_ADDR_LEN]; /* target MAC address */
74:
75: static char *pidfile = NULL;
76: static pcap_t *pc;
77:
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: }
91:
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: }
108:
109: a = a->next;
110: }
111: return res;
112: }
113:
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);
135: }
136:
137: pcap_freecode(&filter);
138: return pc;
139: }
140:
141: void
142: gen_arpreply(u_char *buf) {
143: struct ether_arp *arp;
144: struct in_addr ipbuf;
145:
146: /* set ethernet dst/src address */
147: memcpy(buf, buf+ETHER_ADDR_LEN, ETHER_ADDR_LEN);
148: memcpy(buf+ETHER_ADDR_LEN, target_mac, ETHER_ADDR_LEN);
149:
150: /* set result of ARP request */
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 */
157: arp->arp_op = htons(ARPOP_REPLY);
158: }
159:
160: void
161: cleanup(int sig){
162: if (pidfile != NULL)
163: unlink(pidfile);
164:
165: pcap_breakloop(pc);
166: pcap_close(pc);
167: exit(0);
168: }
169:
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);
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")) {
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;
200:
201: getifaddrs (&ifas);
202: for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
203: #define SDL ((struct sockaddr_dl *)ifa->ifa_addr)
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);
231: return(-1);
232: }
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: }
250: if (sscanf(buf, "0x%lx", (unsigned long *) ip_addr) == 1)
251: return(0);
252:
253: return(-1);
254: }
255:
256: void
257: usage(void){
258: fprintf(stderr,"usage: choparp [-p PIDFILE] if_name mac_addr [-]addr/mask...\n");
259: exit(-1);
260: }
261:
262: int
263: main(int argc, char **argv){
264: int pidf, opt;
265: char *ifname;
266: char *filter, *targets_filter, *excludes_filter;
267: struct cidr **targets_tail = &targets, **excludes_tail = &excludes;
268: struct sigaction act;
269:
270: #define APPEND(LIST,ADDR,MASK) \
271: do { \
272: *(LIST ## _tail) = malloc(sizeof (struct cidr)); \
273: (*(LIST ## _tail))->addr.s_addr = htonl(ADDR); \
274: (*(LIST ## _tail))->mask.s_addr = htonl(MASK); \
275: (*(LIST ## _tail))->next = NULL; \
276: (LIST ## _tail) = &(*(LIST ## _tail))->next; \
277: } while (0)
278:
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: }
288:
289: if (pidfile == NULL) {
290: argv++;
291: argc--;
292: } else {
293: argv += 3;
294: argc -= 3;
295: }
296:
297: if (argc < 3)
298: usage();
299:
300: ifname = argv[0];
301: if (setmac(argv[1], ifname)) {
302: exit(1);
303: }
304: argv += 2; argc -= 2;
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);
331:
332: argv++, argc--;
333: }
334:
335: #ifdef DEBUG
336: #define SHOW(LIST) \
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: } \
344: } while (0)
345:
346: SHOW(targets);
347: SHOW(excludes);
348: exit (0);
349: #endif
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);
388: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>