xref: /dragonfly/usr.sbin/arp/arp.c (revision 3170ffd7)
1 /*
2  * Copyright (c) 1984, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Sun Microsystems, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#) Copyright (c) 1984, 1993 The Regents of the University of California.  All rights reserved.
33  * @(#)from: arp.c	8.2 (Berkeley) 1/2/94
34  * $FreeBSD: src/usr.sbin/arp/arp.c,v 1.22.2.12 2003/04/16 10:02:37 ru Exp $
35  */
36 
37 /*
38  * arp - display, set, and delete arp table entries
39  */
40 
41 
42 #include <sys/param.h>
43 #include <sys/file.h>
44 #include <sys/socket.h>
45 #include <sys/sockio.h>
46 #include <sys/sysctl.h>
47 #include <sys/ioctl.h>
48 #include <sys/time.h>
49 
50 #include <net/if.h>
51 #include <net/if_dl.h>
52 #include <net/if_types.h>
53 #include <net/route.h>
54 
55 #include <netinet/in.h>
56 #include <netinet/if_ether.h>
57 
58 #include <arpa/inet.h>
59 
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <netdb.h>
64 #include <nlist.h>
65 #include <paths.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 
71 void search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
72 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
73 void print_entry(struct sockaddr_dl *sdl,
74 	struct sockaddr_inarp *addr, struct rt_msghdr *rtm);
75 void nuke_entry(struct sockaddr_dl *sdl,
76 	struct sockaddr_inarp *addr, struct rt_msghdr *rtm);
77 int delete(char *host, char *info);
78 void usage(void);
79 int set(int argc, char **argv);
80 int get(char *host);
81 int file(char *name);
82 void getsocket(void);
83 int my_ether_aton(char *a, struct ether_addr *n);
84 int rtmsg(int cmd);
85 int get_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr);
86 
87 static int pid;
88 static int nflag;	/* no reverse dns lookups */
89 static int aflag;	/* do it for all entries */
90 static int cpuflag = -1;
91 static int s = -1;
92 
93 struct	sockaddr_in so_mask;
94 struct	sockaddr_inarp blank_sin, sin_m;
95 struct	sockaddr_dl blank_sdl, sdl_m;
96 int	expire_time, flags, doing_proxy, proxy_only, found_entry;
97 struct	{
98 	struct	rt_msghdr m_rtm;
99 	char	m_space[512];
100 }	m_rtmsg;
101 
102 /* which function we're supposed to do */
103 #define F_GET		1
104 #define F_SET		2
105 #define F_FILESET	3
106 #define F_REPLACE	4
107 #define F_DELETE	5
108 
109 #define ROUNDUP(a) \
110 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
111 #define SETFUNC(f)	{ if (func) usage(); func = (f); }
112 
113 int
114 main(int argc, char **argv)
115 {
116 	int ch, func = 0;
117 	int rtn = 0;
118 
119 	pid = getpid();
120 	while ((ch = getopt(argc, argv, "ac:ndfsS")) != -1)
121 		switch((char)ch) {
122 		case 'a':
123 			aflag = 1;
124 			break;
125 		case 'c':
126 			cpuflag = strtol(optarg, NULL, 0);
127 			break;
128 		case 'd':
129 			SETFUNC(F_DELETE);
130 			break;
131 		case 'n':
132 			nflag = 1;
133 			break;
134 		case 'S':
135 			SETFUNC(F_REPLACE);
136 			break;
137 		case 's':
138 			SETFUNC(F_SET);
139 			break;
140 		case 'f' :
141 			SETFUNC(F_FILESET);
142 			break;
143 		case '?':
144 		default:
145 			usage();
146 		}
147 	argc -= optind;
148 	argv += optind;
149 
150 	bzero(&so_mask, sizeof(so_mask));
151 	so_mask.sin_len = 8;
152 	so_mask.sin_addr.s_addr = 0xffffffff;
153 	bzero(&blank_sin, sizeof(blank_sin));
154 	blank_sin.sin_len = sizeof(blank_sin);
155 	blank_sin.sin_family = AF_INET;
156 	bzero(&blank_sdl, sizeof(blank_sdl));
157 	blank_sdl.sdl_len = sizeof(blank_sdl);
158 	blank_sdl.sdl_family = AF_LINK;
159 
160 	if (!func)
161 		func = F_GET;
162 	switch (func) {
163 	case F_GET:
164 		if (aflag) {
165 			if (argc != 0)
166 				usage();
167 			search(0, print_entry);
168 		} else {
169 			if (argc != 1)
170 				usage();
171 			get(argv[0]);
172 		}
173 		break;
174 	case F_SET:
175 	case F_REPLACE:
176 		if (argc < 2 || argc > 6)
177 			usage();
178 		if (func == F_REPLACE)
179 			delete(argv[0], NULL);
180 		rtn = set(argc, argv) ? 1 : 0;
181 		break;
182 	case F_DELETE:
183 		if (aflag) {
184 			if (argc != 0)
185 				usage();
186 			search(0, nuke_entry);
187 		} else {
188 			if (argc < 1 || argc > 2)
189 				usage();
190 			rtn = delete(argv[0], argv[1]);
191 		}
192 		break;
193 	case F_FILESET:
194 		if (argc != 1)
195 			usage();
196 		rtn = file(argv[0]);
197 		break;
198 	}
199 
200 	return(rtn);
201 }
202 
203 /*
204  * Process a file to set standard arp entries
205  */
206 int
207 file(char *name)
208 {
209 	FILE *fp;
210 	int i, retval;
211 	char line[100], arg[5][50], *args[5], *p;
212 
213 	if ((fp = fopen(name, "r")) == NULL)
214 		errx(1, "cannot open %s", name);
215 	args[0] = &arg[0][0];
216 	args[1] = &arg[1][0];
217 	args[2] = &arg[2][0];
218 	args[3] = &arg[3][0];
219 	args[4] = &arg[4][0];
220 	retval = 0;
221 	while(fgets(line, 100, fp) != NULL) {
222 		if ((p = strchr(line, '#')) != NULL)
223 			*p = '\0';
224 		for (p = line; isblank(*p); p++);
225 		if (*p == '\n' || *p == '\0')
226 			continue;
227 		i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
228 		    arg[2], arg[3], arg[4]);
229 		if (i < 2) {
230 			warnx("bad line: %s", line);
231 			retval = 1;
232 			continue;
233 		}
234 		if (set(i, args))
235 			retval = 1;
236 	}
237 	fclose(fp);
238 	return(retval);
239 }
240 
241 void
242 getsocket(void)
243 {
244 	if (s < 0) {
245 		s = socket(PF_ROUTE, SOCK_RAW, 0);
246 		if (s < 0)
247 			err(1, "socket");
248 	}
249 }
250 
251 /*
252  * Set an individual arp entry
253  */
254 int
255 set(int argc, char **argv)
256 {
257 	struct hostent *hp;
258 	struct sockaddr_inarp *addr = &sin_m;
259 	struct sockaddr_dl *sdl;
260 	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
261 	struct ether_addr *ea;
262 	char *host = argv[0], *eaddr = argv[1];
263 
264 	getsocket();
265 	argc -= 2;
266 	argv += 2;
267 	sdl_m = blank_sdl;
268 	sin_m = blank_sin;
269 	addr->sin_addr.s_addr = inet_addr(host);
270 	if (addr->sin_addr.s_addr == INADDR_NONE) {
271 		if (!(hp = gethostbyname(host))) {
272 			warnx("%s: %s", host, hstrerror(h_errno));
273 			return(1);
274 		}
275 		bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
276 		    sizeof(addr->sin_addr));
277 	}
278 	doing_proxy = flags = proxy_only = expire_time = 0;
279 	while (argc-- > 0) {
280 		if (strncmp(argv[0], "temp", 4) == 0) {
281 			struct timeval tv;
282 			gettimeofday(&tv, 0);
283 			expire_time = tv.tv_sec + 20 * 60;
284 		}
285 		else if (strncmp(argv[0], "pub", 3) == 0) {
286 			flags |= RTF_ANNOUNCE;
287 			doing_proxy = 1;
288 			if (argc && strncmp(argv[1], "only", 3) == 0) {
289 				proxy_only = 1;
290 				sin_m.sin_other = SIN_PROXY;
291 				argc--; argv++;
292 			}
293 		} else if (strncmp(argv[0], "trail", 5) == 0) {
294 			printf("%s: Sending trailers is no longer supported\n",
295 				host);
296 		}
297 		argv++;
298 	}
299 	ea = (struct ether_addr *)LLADDR(&sdl_m);
300 	if (doing_proxy && !strcmp(eaddr, "auto")) {
301 		if (!get_ether_addr(addr->sin_addr.s_addr, ea)) {
302 			printf("no interface found for %s\n",
303 			       inet_ntoa(addr->sin_addr));
304 			return(1);
305 		}
306 		sdl_m.sdl_alen = ETHER_ADDR_LEN;
307 	} else {
308 		if (my_ether_aton(eaddr, ea) == 0)
309 			sdl_m.sdl_alen = ETHER_ADDR_LEN;
310 	}
311 tryagain:
312 	if (rtmsg(RTM_GET) < 0) {
313 		warn("%s", host);
314 		return(1);
315 	}
316 	addr = (struct sockaddr_inarp *)(rtm + 1);
317 	sdl = (struct sockaddr_dl *)(ROUNDUP(addr->sin_len) + (char *)addr);
318 	if (addr->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
319 		if (sdl->sdl_family == AF_LINK &&
320 		    (rtm->rtm_flags & RTF_LLINFO) &&
321 		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
322 		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
323 		case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN:
324 		case IFT_CARP:
325 			goto overwrite;
326 		}
327 		if (doing_proxy == 0) {
328 			printf("set: can only proxy for %s\n", host);
329 			return(1);
330 		}
331 		if (sin_m.sin_other & SIN_PROXY) {
332 			printf("set: proxy entry exists for non 802 device\n");
333 			return(1);
334 		}
335 		sin_m.sin_other = SIN_PROXY;
336 		proxy_only = 1;
337 		goto tryagain;
338 	}
339 overwrite:
340 	if (sdl->sdl_family != AF_LINK) {
341 		printf("cannot intuit interface index and type for %s\n", host);
342 		return(1);
343 	}
344 	sdl_m.sdl_type = sdl->sdl_type;
345 	sdl_m.sdl_index = sdl->sdl_index;
346 	return(rtmsg(RTM_ADD));
347 }
348 
349 /*
350  * Display an individual arp entry
351  */
352 int
353 get(char *host)
354 {
355 	struct hostent *hp;
356 	struct sockaddr_inarp *addr = &sin_m;
357 
358 	sin_m = blank_sin;
359 	addr->sin_addr.s_addr = inet_addr(host);
360 	if (addr->sin_addr.s_addr == INADDR_NONE) {
361 		if (!(hp = gethostbyname(host)))
362 			errx(1, "%s: %s", host, hstrerror(h_errno));
363 		bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
364 		    sizeof(addr->sin_addr));
365 	}
366 	search(addr->sin_addr.s_addr, print_entry);
367 	if (found_entry == 0) {
368 		printf("%s (%s) -- no entry\n",
369 		    host, inet_ntoa(addr->sin_addr));
370 		return(1);
371 	}
372 	return(0);
373 }
374 
375 /*
376  * Delete an arp entry
377  */
378 int
379 delete(char *host, char *info)
380 {
381 	struct hostent *hp;
382 	struct sockaddr_inarp *addr = &sin_m;
383 	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
384 	struct sockaddr_dl *sdl;
385 
386 	getsocket();
387 	sin_m = blank_sin;
388 	if (info) {
389 		if (strncmp(info, "pub", 3) == 0)
390 			sin_m.sin_other = SIN_PROXY;
391 		else
392 			usage();
393 	}
394 	addr->sin_addr.s_addr = inet_addr(host);
395 	if (addr->sin_addr.s_addr == INADDR_NONE) {
396 		if (!(hp = gethostbyname(host))) {
397 			warnx("%s: %s", host, hstrerror(h_errno));
398 			return(1);
399 		}
400 		bcopy((char *)hp->h_addr, (char *)&addr->sin_addr,
401 		    sizeof(addr->sin_addr));
402 	}
403 tryagain:
404 	if (rtmsg(RTM_GET) < 0) {
405 		warn("%s", host);
406 		return(1);
407 	}
408 	addr = (struct sockaddr_inarp *)(rtm + 1);
409 	sdl = (struct sockaddr_dl *)(ROUNDUP(addr->sin_len) + (char *)addr);
410 	if (addr->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
411 		if (sdl->sdl_family == AF_LINK &&
412 		    (rtm->rtm_flags & RTF_LLINFO) &&
413 		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
414 		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
415 		case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN:
416 		case IFT_CARP:
417 			goto delete;
418 		}
419 	}
420 	if (sin_m.sin_other & SIN_PROXY) {
421 		fprintf(stderr, "delete: can't locate %s\n",host);
422 		return(1);
423 	} else {
424 		sin_m.sin_other = SIN_PROXY;
425 		goto tryagain;
426 	}
427 delete:
428 	if (sdl->sdl_family != AF_LINK) {
429 		printf("cannot locate %s\n", host);
430 		return(1);
431 	}
432 	if (aflag && (rtm->rtm_flags & RTF_STATIC)) {
433 		sdl->sdl_alen = 0;
434 		sdl->sdl_slen = 0;
435 		if (rtmsg(RTM_CHANGE) == 0) {
436 			printf("%s (%s) cleared\n",
437 				host, inet_ntoa(addr->sin_addr));
438 			return(0);
439 		}
440 	} else {
441 		if (rtmsg(RTM_DELETE) == 0) {
442 			printf("%s (%s) deleted\n",
443 				host, inet_ntoa(addr->sin_addr));
444 			return(0);
445 		}
446 	}
447 	return(1);
448 }
449 
450 /*
451  * Search the arp table and do some action on matching entries
452  */
453 void
454 search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
455 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
456 {
457 	int mib[7];
458 	int miblen;
459 	size_t needed;
460 	char *lim, *buf, *next;
461 	struct rt_msghdr *rtm;
462 	struct sockaddr_inarp *sin2;
463 	struct sockaddr_dl *sdl;
464 
465 	mib[0] = CTL_NET;
466 	mib[1] = PF_ROUTE;
467 	mib[2] = 0;
468 	mib[3] = AF_INET;
469 	mib[4] = NET_RT_FLAGS;
470 	mib[5] = RTF_LLINFO;
471 	if (cpuflag >= 0) {
472 	    mib[6] = cpuflag;
473 	    miblen = 7;
474 	} else {
475 	    miblen = 6;
476 	}
477 	if (sysctl(mib, miblen, NULL, &needed, NULL, 0) < 0)
478 		errx(1, "route-sysctl-estimate");
479 	if ((buf = malloc(needed)) == NULL)
480 		errx(1, "malloc");
481 	if (sysctl(mib, miblen, buf, &needed, NULL, 0) < 0)
482 		errx(1, "actual retrieval of routing table");
483 	lim = buf + needed;
484 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
485 		rtm = (struct rt_msghdr *)next;
486 		sin2 = (struct sockaddr_inarp *)(rtm + 1);
487 		sdl = (struct sockaddr_dl *)((char *)sin2 + ROUNDUP(sin2->sin_len));
488 		if (addr) {
489 			if (addr != sin2->sin_addr.s_addr)
490 				continue;
491 			found_entry = 1;
492 		}
493 		(*action)(sdl, sin2, rtm);
494 	}
495 	free(buf);
496 }
497 
498 /*
499  * Display an arp entry
500  */
501 void
502 print_entry(struct sockaddr_dl *sdl,
503 	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
504 {
505 	const char *host;
506 	struct hostent *hp;
507 	char ifname[IF_NAMESIZE];
508 
509 	if (nflag == 0)
510 		hp = gethostbyaddr(&(addr->sin_addr), sizeof(addr->sin_addr),
511 		    AF_INET);
512 	else
513 		hp = NULL;
514 	if (hp)
515 		host = hp->h_name;
516 	else {
517 		host = "?";
518 		if (h_errno == TRY_AGAIN)
519 			nflag = 1;
520 	}
521 	printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
522 	if (sdl->sdl_alen)
523 		printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
524 	else
525 		printf("(incomplete)");
526 	if (if_indextoname(sdl->sdl_index, ifname) != NULL)
527 		printf(" on %s", ifname);
528 	if (rtm->rtm_rmx.rmx_expire == 0)
529 		printf(" permanent");
530 	if (addr->sin_other & SIN_PROXY)
531 		printf(" published (proxy only)");
532 	if (rtm->rtm_addrs & RTA_NETMASK) {
533 		addr = (struct sockaddr_inarp *)
534 			(ROUNDUP(sdl->sdl_len) + (char *)sdl);
535 		if (addr->sin_addr.s_addr == 0xffffffff)
536 			printf(" published");
537 		if (addr->sin_len != 8)
538 			printf("(weird)");
539 	}
540         switch(sdl->sdl_type) {
541             case IFT_ETHER:
542                 printf(" [ethernet]");
543                 break;
544 	    case IFT_L2VLAN:
545 		printf(" [vlan]");
546 		break;
547 	    case IFT_CARP:
548 	    	printf(" [carp]");
549             default:
550 		break;
551         }
552 
553 	printf("\n");
554 
555 }
556 
557 /*
558  * Nuke an arp entry
559  */
560 void
561 nuke_entry(struct sockaddr_dl *sdl __unused,
562 	struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
563 {
564 	char ip[20];
565 
566 	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
567 	delete(ip, NULL);
568 }
569 
570 int
571 my_ether_aton(char *a, struct ether_addr *n)
572 {
573 	struct ether_addr *ea;
574 
575 	if ((ea = ether_aton(a)) == NULL) {
576 		warnx("invalid Ethernet address '%s'", a);
577 		return(1);
578 	}
579 	*n = *ea;
580 	return(0);
581 }
582 
583 void
584 usage(void)
585 {
586 	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
587 		"usage: arp [-n] [-c cpu] hostname",
588 		"       arp [-n] [-c cpu] -a",
589 		"       arp -d hostname [pub]",
590 		"       arp -d -a",
591 		"       arp -s hostname ether_addr [temp] [pub [only]]",
592 		"       arp -S hostname ether_addr [temp] [pub [only]]",
593 		"       arp -f filename");
594 	exit(1);
595 }
596 
597 int
598 rtmsg(int cmd)
599 {
600 	static int seq;
601 	int rlen;
602 	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
603 	char *cp = m_rtmsg.m_space;
604 	int l;
605 
606 	errno = 0;
607 	if (cmd == RTM_DELETE || cmd == RTM_CHANGE)
608 		goto doit;
609 	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
610 	rtm->rtm_flags = flags;
611 	rtm->rtm_version = RTM_VERSION;
612 
613 	switch (cmd) {
614 	default:
615 		errx(1, "internal wrong cmd");
616 	case RTM_ADD:
617 		rtm->rtm_addrs |= RTA_GATEWAY;
618 		rtm->rtm_rmx.rmx_expire = expire_time;
619 		rtm->rtm_inits = RTV_EXPIRE;
620 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
621 		sin_m.sin_other = 0;
622 		if (doing_proxy) {
623 			if (proxy_only)
624 				sin_m.sin_other = SIN_PROXY;
625 			else {
626 				rtm->rtm_addrs |= RTA_NETMASK;
627 				rtm->rtm_flags &= ~RTF_HOST;
628 			}
629 		}
630 		/* FALLTHROUGH */
631 	case RTM_GET:
632 		rtm->rtm_addrs |= RTA_DST;
633 	}
634 #define NEXTADDR(w, s) \
635 	if (rtm->rtm_addrs & (w)) { \
636 		bcopy((char *)&s, cp, sizeof(s)); cp += ROUNDUP(sizeof(s));}
637 
638 	NEXTADDR(RTA_DST, sin_m);
639 	NEXTADDR(RTA_GATEWAY, sdl_m);
640 	NEXTADDR(RTA_NETMASK, so_mask);
641 
642 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
643 doit:
644 	l = rtm->rtm_msglen;
645 	rtm->rtm_seq = ++seq;
646 	rtm->rtm_type = cmd;
647 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
648 		if (errno != ESRCH || (cmd != RTM_DELETE && cmd != RTM_CHANGE)) {
649 			warn("writing to routing socket");
650 			return(-1);
651 		}
652 	}
653 	do {
654 		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
655 	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
656 	if (l < 0)
657 		warn("read from routing socket");
658 	return(0);
659 }
660 
661 /*
662  * get_ether_addr - get the hardware address of an interface on the
663  * the same subnet as ipaddr.
664  */
665 #define MAX_IFS		32
666 
667 int
668 get_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr)
669 {
670 	struct ifreq *ifr, *ifend, *ifp;
671 	u_int32_t ina, mask;
672 	struct sockaddr_dl *dla;
673 	struct ifreq ifreq;
674 	struct ifconf ifc;
675 	struct ifreq ifs[MAX_IFS];
676 	int sock;
677 
678 	sock = socket(AF_INET, SOCK_DGRAM, 0);
679 	if (sock < 0)
680 		err(1, "socket");
681 
682 	ifc.ifc_len = sizeof(ifs);
683 	ifc.ifc_req = ifs;
684 	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
685 		warnx("ioctl(SIOCGIFCONF)");
686 		close(sock);
687 		return(0);
688 	}
689 
690 	/*
691 	* Scan through looking for an interface with an Internet
692 	* address on the same subnet as `ipaddr'.
693 	*/
694 	ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
695 	for (ifr = ifc.ifc_req; ifr < ifend; ) {
696 		if (ifr->ifr_addr.sa_family == AF_INET) {
697 			ina = ((struct sockaddr_in *)
698 				&ifr->ifr_addr)->sin_addr.s_addr;
699 			strncpy(ifreq.ifr_name, ifr->ifr_name,
700 				sizeof(ifreq.ifr_name));
701 			/*
702 			 * Check that the interface is up,
703 			 * and not point-to-point or loopback.
704 			 */
705 			if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
706 				continue;
707 			if ((ifreq.ifr_flags &
708 			     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
709 					IFF_LOOPBACK|IFF_NOARP))
710 			     != (IFF_UP|IFF_BROADCAST))
711 				goto nextif;
712 			/*
713 			 * Get its netmask and check that it's on
714 			 * the right subnet.
715 			 */
716 			if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
717 				continue;
718 			mask = ((struct sockaddr_in *)
719 				&ifreq.ifr_addr)->sin_addr.s_addr;
720 			if ((ipaddr & mask) != (ina & mask))
721 				goto nextif;
722 			break;
723 		}
724 nextif:
725 		ifr = (struct ifreq *) ((char *)&ifr->ifr_addr
726 		    + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
727 	}
728 
729 	if (ifr >= ifend) {
730 		close(sock);
731 		return(0);
732 	}
733 
734 	/*
735 	* Now scan through again looking for a link-level address
736 	* for this interface.
737 	*/
738 	ifp = ifr;
739 	for (ifr = ifc.ifc_req; ifr < ifend; ) {
740 		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
741 		    && ifr->ifr_addr.sa_family == AF_LINK) {
742 			/*
743 			 * Found the link-level address - copy it out
744 			 */
745 		 	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
746 			memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
747 			close (sock);
748 			printf("using interface %s for proxy with address ",
749 				ifp->ifr_name);
750 			printf("%s\n", ether_ntoa(hwaddr));
751 			return(dla->sdl_alen);
752 		}
753 		ifr = (struct ifreq *) ((char *)&ifr->ifr_addr
754 		    + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)));
755 	}
756 	return(0);
757 }
758