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