Deleted Added
sdiff udiff text old ( 233773 ) new ( 246143 )
full compact
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
33#if 0
34#ifndef lint
35static char const copyright[] =
36"@(#) Copyright (c) 1984, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
42#endif /* not lint */
43#endif
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 233773 2012-04-02 10:44:25Z glebius $");
46
47/*
48 * arp - display, set, and delete arp table entries
49 */
50
51
52#include <sys/param.h>
53#include <sys/file.h>
54#include <sys/socket.h>
55#include <sys/sockio.h>
56#include <sys/sysctl.h>
57#include <sys/ioctl.h>
58#include <sys/time.h>
59
60#include <net/if.h>
61#include <net/if_dl.h>
62#include <net/if_types.h>
63#include <net/route.h>
64#include <net/iso88025.h>
65
66#include <netinet/in.h>
67#include <netinet/if_ether.h>
68
69#include <arpa/inet.h>
70
71#include <ctype.h>
72#include <err.h>
73#include <errno.h>
74#include <netdb.h>
75#include <nlist.h>
76#include <paths.h>
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <strings.h>
81#include <unistd.h>
82
83typedef void (action_fn)(struct sockaddr_dl *sdl,
84 struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
85
86static int search(u_long addr, action_fn *action);
87static action_fn print_entry;
88static action_fn nuke_entry;
89
90static int delete(char *host, int do_proxy);
91static void usage(void);
92static int set(int argc, char **argv);
93static int get(char *host);
94static int file(char *name);
95static struct rt_msghdr *rtmsg(int cmd,
96 struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
97static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
98static struct sockaddr_inarp *getaddr(char *host);
99static int valid_type(int type);
100
101static int nflag; /* no reverse dns lookups */
102static char *rifname;
103
104static time_t expire_time;
105static int flags, doing_proxy, proxy_only;
106
107/* which function we're supposed to do */
108#define F_GET 1
109#define F_SET 2
110#define F_FILESET 3
111#define F_REPLACE 4
112#define F_DELETE 5
113
114#define SETFUNC(f) { if (func) usage(); func = (f); }
115
116int
117main(int argc, char *argv[])
118{
119 int ch, func = 0;
120 int rtn = 0;
121 int aflag = 0; /* do it for all entries */
122
123 while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
124 switch(ch) {
125 case 'a':
126 aflag = 1;
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 'i':
144 rifname = optarg;
145 break;
146 case '?':
147 default:
148 usage();
149 }
150 argc -= optind;
151 argv += optind;
152
153 if (!func)
154 func = F_GET;
155 if (rifname) {
156 if (func != F_GET && !(func == F_DELETE && aflag))
157 errx(1, "-i not applicable to this operation");
158 if (if_nametoindex(rifname) == 0) {
159 if (errno == ENXIO)
160 errx(1, "interface %s does not exist", rifname);
161 else
162 err(1, "if_nametoindex(%s)", rifname);
163 }
164 }
165 switch (func) {
166 case F_GET:
167 if (aflag) {
168 if (argc != 0)
169 usage();
170 search(0, print_entry);
171 } else {
172 if (argc != 1)
173 usage();
174 rtn = get(argv[0]);
175 }
176 break;
177 case F_SET:
178 case F_REPLACE:
179 if (argc < 2 || argc > 6)
180 usage();
181 if (func == F_REPLACE)
182 (void)delete(argv[0], 0);
183 rtn = set(argc, argv) ? 1 : 0;
184 break;
185 case F_DELETE:
186 if (aflag) {
187 if (argc != 0)
188 usage();
189 search(0, nuke_entry);
190 } else {
191 if (argc == 2 && strncmp(argv[1], "pub", 3) == 0)
192 ch = SIN_PROXY;
193 else if (argc == 1)
194 ch = 0;
195 else
196 usage();
197 rtn = delete(argv[0], ch);
198 }
199 break;
200 case F_FILESET:
201 if (argc != 1)
202 usage();
203 rtn = file(argv[0]);
204 break;
205 }
206
207 return (rtn);
208}
209
210/*
211 * Process a file to set standard arp entries
212 */
213static int
214file(char *name)
215{
216 FILE *fp;
217 int i, retval;
218 char line[100], arg[5][50], *args[5], *p;
219
220 if ((fp = fopen(name, "r")) == NULL)
221 err(1, "cannot open %s", name);
222 args[0] = &arg[0][0];
223 args[1] = &arg[1][0];
224 args[2] = &arg[2][0];
225 args[3] = &arg[3][0];
226 args[4] = &arg[4][0];
227 retval = 0;
228 while(fgets(line, sizeof(line), fp) != NULL) {
229 if ((p = strchr(line, '#')) != NULL)
230 *p = '\0';
231 for (p = line; isblank(*p); p++);
232 if (*p == '\n' || *p == '\0')
233 continue;
234 i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
235 arg[2], arg[3], arg[4]);
236 if (i < 2) {
237 warnx("bad line: %s", line);
238 retval = 1;
239 continue;
240 }
241 if (set(i, args))
242 retval = 1;
243 }
244 fclose(fp);
245 return (retval);
246}
247
248/*
249 * Given a hostname, fills up a (static) struct sockaddr_inarp with
250 * the address of the host and returns a pointer to the
251 * structure.
252 */
253static struct sockaddr_inarp *
254getaddr(char *host)
255{
256 struct hostent *hp;
257 static struct sockaddr_inarp reply;
258
259 bzero(&reply, sizeof(reply));
260 reply.sin_len = sizeof(reply);
261 reply.sin_family = AF_INET;
262 reply.sin_addr.s_addr = inet_addr(host);
263 if (reply.sin_addr.s_addr == INADDR_NONE) {
264 if (!(hp = gethostbyname(host))) {
265 warnx("%s: %s", host, hstrerror(h_errno));
266 return (NULL);
267 }
268 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
269 sizeof reply.sin_addr);
270 }
271 return (&reply);
272}
273
274/*
275 * Returns true if the type is a valid one for ARP.
276 */
277static int
278valid_type(int type)
279{
280
281 switch (type) {
282 case IFT_ETHER:
283 case IFT_FDDI:
284 case IFT_ISO88023:
285 case IFT_ISO88024:
286 case IFT_ISO88025:
287 case IFT_L2VLAN:
288 case IFT_BRIDGE:
289 return (1);
290 default:
291 return (0);
292 }
293}
294
295/*
296 * Set an individual arp entry
297 */
298static int
299set(int argc, char **argv)
300{
301 struct sockaddr_inarp *addr;
302 struct sockaddr_inarp *dst; /* what are we looking for */
303 struct sockaddr_dl *sdl;
304 struct rt_msghdr *rtm;
305 struct ether_addr *ea;
306 char *host = argv[0], *eaddr = argv[1];
307 struct sockaddr_dl sdl_m;
308
309 argc -= 2;
310 argv += 2;
311
312 bzero(&sdl_m, sizeof(sdl_m));
313 sdl_m.sdl_len = sizeof(sdl_m);
314 sdl_m.sdl_family = AF_LINK;
315
316 dst = getaddr(host);
317 if (dst == NULL)
318 return (1);
319 doing_proxy = flags = proxy_only = expire_time = 0;
320 while (argc-- > 0) {
321 if (strncmp(argv[0], "temp", 4) == 0) {
322 struct timespec tp;
323 int max_age;
324 size_t len = sizeof(max_age);
325
326 clock_gettime(CLOCK_MONOTONIC, &tp);
327 if (sysctlbyname("net.link.ether.inet.max_age",
328 &max_age, &len, NULL, 0) != 0)
329 err(1, "sysctlbyname");
330 expire_time = tp.tv_sec + max_age;
331 } else if (strncmp(argv[0], "pub", 3) == 0) {
332 flags |= RTF_ANNOUNCE;
333 doing_proxy = 1;
334 if (argc && strncmp(argv[1], "only", 3) == 0) {
335 proxy_only = 1;
336 argc--; argv++;
337 }
338 } else if (strncmp(argv[0], "blackhole", 9) == 0) {
339 if (flags & RTF_REJECT) {
340 printf("Choose one of blackhole or reject, not both.\n");
341 }
342 flags |= RTF_BLACKHOLE;
343 } else if (strncmp(argv[0], "reject", 6) == 0) {
344 if (flags & RTF_BLACKHOLE) {
345 printf("Choose one of blackhole or reject, not both.\n");
346 }
347 flags |= RTF_REJECT;
348 } else if (strncmp(argv[0], "trail", 5) == 0) {
349 /* XXX deprecated and undocumented feature */
350 printf("%s: Sending trailers is no longer supported\n",
351 host);
352 }
353 argv++;
354 }
355 ea = (struct ether_addr *)LLADDR(&sdl_m);
356 if (doing_proxy && !strcmp(eaddr, "auto")) {
357 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
358 printf("no interface found for %s\n",
359 inet_ntoa(dst->sin_addr));
360 return (1);
361 }
362 sdl_m.sdl_alen = ETHER_ADDR_LEN;
363 } else {
364 struct ether_addr *ea1 = ether_aton(eaddr);
365
366 if (ea1 == NULL) {
367 warnx("invalid Ethernet address '%s'", eaddr);
368 return (1);
369 } else {
370 *ea = *ea1;
371 sdl_m.sdl_alen = ETHER_ADDR_LEN;
372 }
373 }
374
375 /*
376 * In the case a proxy-arp entry is being added for
377 * a remote end point, the RTF_ANNOUNCE flag in the
378 * RTM_GET command is an indication to the kernel
379 * routing code that the interface associated with
380 * the prefix route covering the local end of the
381 * PPP link should be returned, on which ARP applies.
382 */
383 rtm = rtmsg(RTM_GET, dst, &sdl_m);
384 if (rtm == NULL) {
385 warn("%s", host);
386 return (1);
387 }
388 addr = (struct sockaddr_inarp *)(rtm + 1);
389 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
390
391 if ((sdl->sdl_family != AF_LINK) ||
392 (rtm->rtm_flags & RTF_GATEWAY) ||
393 !valid_type(sdl->sdl_type)) {
394 printf("cannot intuit interface index and type for %s\n", host);
395 return (1);
396 }
397 sdl_m.sdl_type = sdl->sdl_type;
398 sdl_m.sdl_index = sdl->sdl_index;
399 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
400}
401
402/*
403 * Display an individual arp entry
404 */
405static int
406get(char *host)
407{
408 struct sockaddr_inarp *addr;
409
410 addr = getaddr(host);
411 if (addr == NULL)
412 return (1);
413 if (0 == search(addr->sin_addr.s_addr, print_entry)) {
414 printf("%s (%s) -- no entry",
415 host, inet_ntoa(addr->sin_addr));
416 if (rifname)
417 printf(" on %s", rifname);
418 printf("\n");
419 return (1);
420 }
421 return (0);
422}
423
424/*
425 * Delete an arp entry
426 */
427static int
428delete(char *host, int do_proxy)
429{
430 struct sockaddr_inarp *addr, *dst;
431 struct rt_msghdr *rtm;
432 struct sockaddr_dl *sdl;
433 struct sockaddr_dl sdl_m;
434
435 dst = getaddr(host);
436 if (dst == NULL)
437 return (1);
438
439 /*
440 * Perform a regular entry delete first.
441 */
442 flags &= ~RTF_ANNOUNCE;
443
444 /*
445 * setup the data structure to notify the kernel
446 * it is the ARP entry the RTM_GET is interested
447 * in
448 */
449 bzero(&sdl_m, sizeof(sdl_m));
450 sdl_m.sdl_len = sizeof(sdl_m);
451 sdl_m.sdl_family = AF_LINK;
452
453 for (;;) { /* try twice */
454 rtm = rtmsg(RTM_GET, dst, &sdl_m);
455 if (rtm == NULL) {
456 warn("%s", host);
457 return (1);
458 }
459 addr = (struct sockaddr_inarp *)(rtm + 1);
460 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
461
462 /*
463 * With the new L2/L3 restructure, the route
464 * returned is a prefix route. The important
465 * piece of information from the previous
466 * RTM_GET is the interface index. In the
467 * case of ECMP, the kernel will traverse
468 * the route group for the given entry.
469 */
470 if (sdl->sdl_family == AF_LINK &&
471 !(rtm->rtm_flags & RTF_GATEWAY) &&
472 valid_type(sdl->sdl_type) ) {
473 addr->sin_addr.s_addr = dst->sin_addr.s_addr;
474 break;
475 }
476
477 /*
478 * Regualar entry delete failed, now check if there
479 * is a proxy-arp entry to remove.
480 */
481 if (flags & RTF_ANNOUNCE) {
482 fprintf(stderr, "delete: cannot locate %s\n",host);
483 return (1);
484 }
485
486 flags |= RTF_ANNOUNCE;
487 }
488 rtm->rtm_flags |= RTF_LLDATA;
489 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
490 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
491 return (0);
492 }
493 return (1);
494}
495
496
497/*
498 * Search the arp table and do some action on matching entries
499 */
500static int
501search(u_long addr, action_fn *action)
502{
503 int mib[6];
504 size_t needed;
505 char *lim, *buf, *next;
506 struct rt_msghdr *rtm;
507 struct sockaddr_inarp *sin2;
508 struct sockaddr_dl *sdl;
509 char ifname[IF_NAMESIZE];
510 int st, found_entry = 0;
511
512 mib[0] = CTL_NET;
513 mib[1] = PF_ROUTE;
514 mib[2] = 0;
515 mib[3] = AF_INET;
516 mib[4] = NET_RT_FLAGS;
517#ifdef RTF_LLINFO
518 mib[5] = RTF_LLINFO;
519#else
520 mib[5] = 0;
521#endif
522 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
523 err(1, "route-sysctl-estimate");
524 if (needed == 0) /* empty table */
525 return 0;
526 buf = NULL;
527 for (;;) {
528 buf = reallocf(buf, needed);
529 if (buf == NULL)
530 errx(1, "could not reallocate memory");
531 st = sysctl(mib, 6, buf, &needed, NULL, 0);
532 if (st == 0 || errno != ENOMEM)
533 break;
534 needed += needed / 8;
535 }
536 if (st == -1)
537 err(1, "actual retrieval of routing table");
538 lim = buf + needed;
539 for (next = buf; next < lim; next += rtm->rtm_msglen) {
540 rtm = (struct rt_msghdr *)next;
541 sin2 = (struct sockaddr_inarp *)(rtm + 1);
542 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
543 if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
544 strcmp(ifname, rifname))
545 continue;
546 if (addr) {
547 if (addr != sin2->sin_addr.s_addr)
548 continue;
549 found_entry = 1;
550 }
551 (*action)(sdl, sin2, rtm);
552 }
553 free(buf);
554 return (found_entry);
555}
556
557/*
558 * Display an arp entry
559 */
560static char lifname[IF_NAMESIZE];
561static int64_t lifindex = -1;
562
563static void
564print_entry(struct sockaddr_dl *sdl,
565 struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
566{
567 const char *host;
568 struct hostent *hp;
569 struct iso88025_sockaddr_dl_data *trld;
570 int seg;
571
572 if (nflag == 0)
573 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
574 sizeof addr->sin_addr, AF_INET);
575 else
576 hp = 0;
577 if (hp)
578 host = hp->h_name;
579 else {
580 host = "?";
581 if (h_errno == TRY_AGAIN)
582 nflag = 1;
583 }
584 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
585 if (sdl->sdl_alen) {
586 if ((sdl->sdl_type == IFT_ETHER ||
587 sdl->sdl_type == IFT_L2VLAN ||
588 sdl->sdl_type == IFT_BRIDGE) &&
589 sdl->sdl_alen == ETHER_ADDR_LEN)
590 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
591 else {
592 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
593
594 printf("%s", link_ntoa(sdl) + n);
595 }
596 } else
597 printf("(incomplete)");
598 if (sdl->sdl_index != lifindex &&
599 if_indextoname(sdl->sdl_index, lifname) != NULL) {
600 lifindex = sdl->sdl_index;
601 printf(" on %s", lifname);
602 } else if (sdl->sdl_index == lifindex)
603 printf(" on %s", lifname);
604 if (rtm->rtm_rmx.rmx_expire == 0)
605 printf(" permanent");
606 else {
607 static struct timespec tp;
608 if (tp.tv_sec == 0)
609 clock_gettime(CLOCK_MONOTONIC, &tp);
610 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
611 printf(" expires in %d seconds", (int)expire_time);
612 else
613 printf(" expired");
614 }
615 if (addr->sin_other & SIN_PROXY)
616 printf(" published (proxy only)");
617 if (rtm->rtm_flags & RTF_ANNOUNCE)
618 printf(" published");
619 switch(sdl->sdl_type) {
620 case IFT_ETHER:
621 printf(" [ethernet]");
622 break;
623 case IFT_ISO88025:
624 printf(" [token-ring]");
625 trld = SDL_ISO88025(sdl);
626 if (trld->trld_rcf != 0) {
627 printf(" rt=%x", ntohs(trld->trld_rcf));
628 for (seg = 0;
629 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
630 seg++)
631 printf(":%x", ntohs(*(trld->trld_route[seg])));
632 }
633 break;
634 case IFT_FDDI:
635 printf(" [fddi]");
636 break;
637 case IFT_ATM:
638 printf(" [atm]");
639 break;
640 case IFT_L2VLAN:
641 printf(" [vlan]");
642 break;
643 case IFT_IEEE1394:
644 printf(" [firewire]");
645 break;
646 case IFT_BRIDGE:
647 printf(" [bridge]");
648 break;
649 default:
650 break;
651 }
652
653 printf("\n");
654
655}
656
657/*
658 * Nuke an arp entry
659 */
660static void
661nuke_entry(struct sockaddr_dl *sdl __unused,
662 struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
663{
664 char ip[20];
665
666 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
667 (void)delete(ip, 0);
668}
669
670static void
671usage(void)
672{
673 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
674 "usage: arp [-n] [-i interface] hostname",
675 " arp [-n] [-i interface] -a",
676 " arp -d hostname [pub]",
677 " arp -d [-i interface] -a",
678 " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
679 " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
680 " arp -f filename");
681 exit(1);
682}
683
684static struct rt_msghdr *
685rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
686{
687 static int seq;
688 int rlen;
689 int l;
690 struct sockaddr_in so_mask, *som = &so_mask;
691 static int s = -1;
692 static pid_t pid;
693
694 static struct {
695 struct rt_msghdr m_rtm;
696 char m_space[512];
697 } m_rtmsg;
698
699 struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
700 char *cp = m_rtmsg.m_space;
701
702 if (s < 0) { /* first time: open socket, get pid */
703 s = socket(PF_ROUTE, SOCK_RAW, 0);
704 if (s < 0)
705 err(1, "socket");
706 pid = getpid();
707 }
708 bzero(&so_mask, sizeof(so_mask));
709 so_mask.sin_len = 8;
710 so_mask.sin_addr.s_addr = 0xffffffff;
711
712 errno = 0;
713 /*
714 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
715 * appropriately.
716 */
717 if (cmd == RTM_DELETE)
718 goto doit;
719 bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
720 rtm->rtm_flags = flags;
721 rtm->rtm_version = RTM_VERSION;
722
723 switch (cmd) {
724 default:
725 errx(1, "internal wrong cmd");
726 case RTM_ADD:
727 rtm->rtm_addrs |= RTA_GATEWAY;
728 rtm->rtm_rmx.rmx_expire = expire_time;
729 rtm->rtm_inits = RTV_EXPIRE;
730 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
731 dst->sin_other = 0;
732 if (doing_proxy) {
733 if (proxy_only)
734 dst->sin_other = SIN_PROXY;
735 else {
736 rtm->rtm_addrs |= RTA_NETMASK;
737 rtm->rtm_flags &= ~RTF_HOST;
738 }
739 }
740 /* FALLTHROUGH */
741 case RTM_GET:
742 rtm->rtm_addrs |= RTA_DST;
743 }
744#define NEXTADDR(w, s) \
745 do { \
746 if ((s) != NULL && rtm->rtm_addrs & (w)) { \
747 bcopy((s), cp, sizeof(*(s))); \
748 cp += SA_SIZE(s); \
749 } \
750 } while (0)
751
752 NEXTADDR(RTA_DST, dst);
753 NEXTADDR(RTA_GATEWAY, sdl);
754 NEXTADDR(RTA_NETMASK, som);
755
756 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
757doit:
758 l = rtm->rtm_msglen;
759 rtm->rtm_seq = ++seq;
760 rtm->rtm_type = cmd;
761 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
762 if (errno != ESRCH || cmd != RTM_DELETE) {
763 warn("writing to routing socket");
764 return (NULL);
765 }
766 }
767 do {
768 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
769 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
770 if (l < 0)
771 warn("read from routing socket");
772 return (rtm);
773}
774
775/*
776 * get_ether_addr - get the hardware address of an interface on the
777 * the same subnet as ipaddr.
778 */
779#define MAX_IFS 32
780
781static int
782get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
783{
784 struct ifreq *ifr, *ifend, *ifp;
785 in_addr_t ina, mask;
786 struct sockaddr_dl *dla;
787 struct ifreq ifreq;
788 struct ifconf ifc;
789 struct ifreq ifs[MAX_IFS];
790 int sock;
791 int retval = 0;
792
793 sock = socket(AF_INET, SOCK_DGRAM, 0);
794 if (sock < 0)
795 err(1, "socket");
796
797 ifc.ifc_len = sizeof(ifs);
798 ifc.ifc_req = ifs;
799 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
800 warnx("ioctl(SIOCGIFCONF)");
801 goto done;
802 }
803
804#define NEXTIFR(i) \
805 ((struct ifreq *)((char *)&(i)->ifr_addr \
806 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
807
808 /*
809 * Scan through looking for an interface with an Internet
810 * address on the same subnet as `ipaddr'.
811 */
812 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
813 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
814 if (ifr->ifr_addr.sa_family != AF_INET)
815 continue;
816 strncpy(ifreq.ifr_name, ifr->ifr_name,
817 sizeof(ifreq.ifr_name));
818 ifreq.ifr_addr = ifr->ifr_addr;
819 /*
820 * Check that the interface is up,
821 * and not point-to-point or loopback.
822 */
823 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
824 continue;
825 if ((ifreq.ifr_flags &
826 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
827 IFF_LOOPBACK|IFF_NOARP))
828 != (IFF_UP|IFF_BROADCAST))
829 continue;
830 /*
831 * Get its netmask and check that it's on
832 * the right subnet.
833 */
834 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
835 continue;
836 mask = ((struct sockaddr_in *)
837 &ifreq.ifr_addr)->sin_addr.s_addr;
838 ina = ((struct sockaddr_in *)
839 &ifr->ifr_addr)->sin_addr.s_addr;
840 if ((ipaddr & mask) == (ina & mask))
841 break; /* ok, we got it! */
842 }
843
844 if (ifr >= ifend)
845 goto done;
846
847 /*
848 * Now scan through again looking for a link-level address
849 * for this interface.
850 */
851 ifp = ifr;
852 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
853 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
854 ifr->ifr_addr.sa_family == AF_LINK)
855 break;
856 if (ifr >= ifend)
857 goto done;
858 /*
859 * Found the link-level address - copy it out
860 */
861 dla = (struct sockaddr_dl *) &ifr->ifr_addr;
862 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
863 printf("using interface %s for proxy with address ",
864 ifp->ifr_name);
865 printf("%s\n", ether_ntoa(hwaddr));
866 retval = dla->sdl_alen;
867done:
868 close(sock);
869 return (retval);
870}