]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/arp/arp.c
Merge llvm-project main llvmorg-17-init-19304-gd0b54bb50e51
[FreeBSD/FreeBSD.git] / usr.sbin / arp / arp.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1984, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Sun Microsystems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 /*
36  * arp - display, set, and delete arp table entries
37  */
38
39 #include <sys/param.h>
40 #include <sys/file.h>
41 #include <sys/socket.h>
42 #include <sys/sockio.h>
43 #include <sys/sysctl.h>
44 #include <sys/ioctl.h>
45 #include <sys/time.h>
46
47 #include <net/if.h>
48 #include <net/if_dl.h>
49 #include <net/if_types.h>
50 #include <net/route.h>
51
52 #include <netinet/in.h>
53 #include <netinet/if_ether.h>
54
55 #include <arpa/inet.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <netdb.h>
61 #include <nlist.h>
62 #include <paths.h>
63 #include <stdbool.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <strings.h>
68 #include <unistd.h>
69 #include <ifaddrs.h>
70 #include <libxo/xo.h>
71 #include "arp.h"
72
73 typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_in *s_in,
74     struct rt_msghdr *rtm);
75 static void nuke_entries(uint32_t ifindex, struct in_addr addr);
76 static int print_entries(uint32_t ifindex, struct in_addr addr);
77
78 static int delete(char *host);
79 static void usage(void) __dead2;
80 static int set(int argc, char **argv);
81 static int get(char *host);
82 static int file(char *name);
83 static struct rt_msghdr *rtmsg(int cmd,
84     struct sockaddr_in *dst, struct sockaddr_dl *sdl);
85 static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
86 static int set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m,
87     char *host);
88
89 struct if_nameindex *ifnameindex;
90
91 struct arp_opts opts = {};
92
93 /* which function we're supposed to do */
94 #define F_GET           1
95 #define F_SET           2
96 #define F_FILESET       3
97 #define F_REPLACE       4
98 #define F_DELETE        5
99
100 #define SETFUNC(f)      { if (func) usage(); func = (f); }
101
102 #define ARP_XO_VERSION  "1"
103
104 int
105 main(int argc, char *argv[])
106 {
107         int ch, func = 0;
108         int rtn = 0;
109
110         argc = xo_parse_args(argc, argv);
111         if (argc < 0)
112                 exit(1);
113
114         while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
115                 switch(ch) {
116                 case 'a':
117                         opts.aflag = true;
118                         break;
119                 case 'd':
120                         SETFUNC(F_DELETE);
121                         break;
122                 case 'n':
123                         opts.nflag = true;
124                         break;
125                 case 'S':
126                         SETFUNC(F_REPLACE);
127                         break;
128                 case 's':
129                         SETFUNC(F_SET);
130                         break;
131                 case 'f' :
132                         SETFUNC(F_FILESET);
133                         break;
134                 case 'i':
135                         opts.rifname = optarg;
136                         break;
137                 case '?':
138                 default:
139                         usage();
140                 }
141         argc -= optind;
142         argv += optind;
143
144         if (!func)
145                 func = F_GET;
146         if (opts.rifname) {
147                 if (func != F_GET && !(func == F_DELETE && opts.aflag))
148                         xo_errx(1, "-i not applicable to this operation");
149                 if ((opts.rifindex = if_nametoindex(opts.rifname)) == 0) {
150                         if (errno == ENXIO)
151                                 xo_errx(1, "interface %s does not exist",
152                                     opts.rifname);
153                         else
154                                 xo_err(1, "if_nametoindex(%s)", opts.rifname);
155                 }
156         }
157         switch (func) {
158         case F_GET:
159                 if (opts.aflag) {
160                         if (argc != 0)
161                                 usage();
162
163                         xo_set_version(ARP_XO_VERSION);
164                         xo_open_container("arp");
165                         xo_open_list("arp-cache");
166
167                         struct in_addr all_addrs = {};
168                         print_entries(opts.rifindex, all_addrs);
169
170                         xo_close_list("arp-cache");
171                         xo_close_container("arp");
172                         xo_finish();
173                 } else {
174                         if (argc != 1)
175                                 usage();
176                         rtn = get(argv[0]);
177                 }
178                 break;
179         case F_SET:
180         case F_REPLACE:
181                 if (argc < 2 || argc > 6)
182                         usage();
183                 if (func == F_REPLACE)
184                         (void)delete(argv[0]);
185                 rtn = set(argc, argv) ? 1 : 0;
186                 break;
187         case F_DELETE:
188                 if (opts.aflag) {
189                         if (argc != 0)
190                                 usage();
191                         struct in_addr all_addrs = {};
192                         nuke_entries(0, all_addrs);
193                 } else {
194                         if (argc != 1)
195                                 usage();
196                         rtn = delete(argv[0]);
197                 }
198                 break;
199         case F_FILESET:
200                 if (argc != 1)
201                         usage();
202                 rtn = file(argv[0]);
203                 break;
204         }
205
206         if (ifnameindex != NULL)
207                 if_freenameindex(ifnameindex);
208
209         return (rtn);
210 }
211
212 /*
213  * Process a file to set standard arp entries
214  */
215 static int
216 file(char *name)
217 {
218         FILE *fp;
219         int i, retval;
220         char line[100], arg[5][50], *args[5], *p;
221
222         if ((fp = fopen(name, "r")) == NULL)
223                 xo_err(1, "cannot open %s", name);
224         args[0] = &arg[0][0];
225         args[1] = &arg[1][0];
226         args[2] = &arg[2][0];
227         args[3] = &arg[3][0];
228         args[4] = &arg[4][0];
229         retval = 0;
230         while(fgets(line, sizeof(line), fp) != NULL) {
231                 if ((p = strchr(line, '#')) != NULL)
232                         *p = '\0';
233                 for (p = line; isblank(*p); p++);
234                 if (*p == '\n' || *p == '\0')
235                         continue;
236                 i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
237                     arg[2], arg[3], arg[4]);
238                 if (i < 2) {
239                         xo_warnx("bad line: %s", line);
240                         retval = 1;
241                         continue;
242                 }
243                 if (set(i, args))
244                         retval = 1;
245         }
246         fclose(fp);
247         return (retval);
248 }
249
250 /*
251  * Given a hostname, fills up a (static) struct sockaddr_in with
252  * the address of the host and returns a pointer to the
253  * structure.
254  */
255 struct sockaddr_in *
256 getaddr(char *host)
257 {
258         struct hostent *hp;
259         static struct sockaddr_in reply;
260
261         bzero(&reply, sizeof(reply));
262         reply.sin_len = sizeof(reply);
263         reply.sin_family = AF_INET;
264         reply.sin_addr.s_addr = inet_addr(host);
265         if (reply.sin_addr.s_addr == INADDR_NONE) {
266                 if (!(hp = gethostbyname(host))) {
267                         xo_warnx("%s: %s", host, hstrerror(h_errno));
268                         return (NULL);
269                 }
270                 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
271                         sizeof reply.sin_addr);
272         }
273         return (&reply);
274 }
275
276 int valid_type(int type);
277 /*
278  * Returns true if the type is a valid one for ARP.
279  */
280 int
281 valid_type(int type)
282 {
283
284         switch (type) {
285         case IFT_ETHER:
286         case IFT_FDDI:
287         case IFT_IEEE1394:
288         case IFT_INFINIBAND:
289         case IFT_ISO88023:
290         case IFT_ISO88024:
291         case IFT_L2VLAN:
292         case IFT_BRIDGE:
293                 return (1);
294         default:
295                 return (0);
296         }
297 }
298
299 /*
300  * Set an individual arp entry
301  */
302 static int
303 set(int argc, char **argv)
304 {
305         struct sockaddr_in *dst;        /* what are we looking for */
306         struct ether_addr *ea;
307         char *host = argv[0], *eaddr = argv[1];
308         struct sockaddr_dl sdl_m;
309
310         argc -= 2;
311         argv += 2;
312
313         bzero(&sdl_m, sizeof(sdl_m));
314         sdl_m.sdl_len = sizeof(sdl_m);
315         sdl_m.sdl_family = AF_LINK;
316
317         dst = getaddr(host);
318         if (dst == NULL)
319                 return (1);
320         while (argc-- > 0) {
321                 if (strcmp(argv[0], "temp") == 0) {
322                         int max_age;
323                         size_t len = sizeof(max_age);
324
325                         if (sysctlbyname("net.link.ether.inet.max_age",
326                             &max_age, &len, NULL, 0) != 0)
327                                 xo_err(1, "sysctlbyname");
328                         opts.expire_time = max_age;
329                 } else if (strcmp(argv[0], "pub") == 0) {
330                         opts.flags |= RTF_ANNOUNCE;
331                         if (argc && strcmp(argv[1], "only") == 0) {
332                                 /*
333                                  * Compatibility: in pre FreeBSD 8 times
334                                  * the "only" keyword used to mean that
335                                  * an ARP entry should be announced, but
336                                  * not installed into routing table.
337                                  */
338                                 argc--; argv++;
339                         }
340                 } else if (strcmp(argv[0], "blackhole") == 0) {
341                         if (opts.flags & RTF_REJECT) {
342                                 xo_errx(1, "Choose one of blackhole or reject, "
343                                     "not both.");
344                         }
345                         opts.flags |= RTF_BLACKHOLE;
346                 } else if (strcmp(argv[0], "reject") == 0) {
347                         if (opts.flags & RTF_BLACKHOLE) {
348                                 xo_errx(1, "Choose one of blackhole or reject, "
349                                     "not both.");
350                         }
351                         opts.flags |= RTF_REJECT;
352                 } else {
353                         xo_warnx("Invalid parameter '%s'", argv[0]);
354                         usage();
355                 }
356                 argv++;
357         }
358         ea = (struct ether_addr *)LLADDR(&sdl_m);
359         if ((opts.flags & RTF_ANNOUNCE) && !strcmp(eaddr, "auto")) {
360                 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
361                         xo_warnx("no interface found for %s",
362                                inet_ntoa(dst->sin_addr));
363                         return (1);
364                 }
365                 sdl_m.sdl_alen = ETHER_ADDR_LEN;
366         } else {
367                 struct ether_addr *ea1 = ether_aton(eaddr);
368
369                 if (ea1 == NULL) {
370                         xo_warnx("invalid Ethernet address '%s'", eaddr);
371                         return (1);
372                 } else {
373                         *ea = *ea1;
374                         sdl_m.sdl_alen = ETHER_ADDR_LEN;
375                 }
376         }
377 #ifndef WITHOUT_NETLINK
378         return (set_nl(0, dst, &sdl_m, host));
379 #else
380         return (set_rtsock(dst, &sdl_m, host));
381 #endif
382 }
383
384 #ifdef WITHOUT_NETLINK
385 static int
386 set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m, char *host)
387 {
388         struct sockaddr_in *addr;
389         struct sockaddr_dl *sdl;
390         struct rt_msghdr *rtm;
391
392         /*
393          * In the case a proxy-arp entry is being added for
394          * a remote end point, the RTF_ANNOUNCE flag in the
395          * RTM_GET command is an indication to the kernel
396          * routing code that the interface associated with
397          * the prefix route covering the local end of the
398          * PPP link should be returned, on which ARP applies.
399          */
400         rtm = rtmsg(RTM_GET, dst, NULL);
401         if (rtm == NULL) {
402                 xo_warn("%s", host);
403                 return (1);
404         }
405         addr = (struct sockaddr_in *)(rtm + 1);
406         sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
407
408         if ((sdl->sdl_family != AF_LINK) ||
409             (rtm->rtm_flags & RTF_GATEWAY) ||
410             !valid_type(sdl->sdl_type)) {
411                 xo_warnx("cannot intuit interface index and type for %s", host);
412                 return (1);
413         }
414         sdl_m->sdl_type = sdl->sdl_type;
415         sdl_m->sdl_index = sdl->sdl_index;
416         return (rtmsg(RTM_ADD, dst, sdl_m) == NULL);
417 }
418 #endif
419
420 /*
421  * Display an individual arp entry
422  */
423 static int
424 get(char *host)
425 {
426         struct sockaddr_in *addr;
427         int found;
428
429         addr = getaddr(host);
430         if (addr == NULL)
431                 return (1);
432
433         xo_set_version(ARP_XO_VERSION);
434         xo_open_container("arp");
435         xo_open_list("arp-cache");
436
437         found = print_entries(opts.rifindex, addr->sin_addr);
438
439         if (found == 0) {
440                 xo_emit("{d:hostname/%s} ({d:ip-address/%s}) -- no entry",
441                     host, inet_ntoa(addr->sin_addr));
442                 if (opts.rifname)
443                         xo_emit(" on {d:interface/%s}", opts.rifname);
444                 xo_emit("\n");
445         }
446
447         xo_close_list("arp-cache");
448         xo_close_container("arp");
449         xo_finish();
450
451         return (found == 0);
452 }
453
454 /*
455  * Delete an arp entry
456  */
457 #ifdef WITHOUT_NETLINK
458 static int
459 delete_rtsock(char *host)
460 {
461         struct sockaddr_in *addr, *dst;
462         struct rt_msghdr *rtm;
463         struct sockaddr_dl *sdl;
464
465         dst = getaddr(host);
466         if (dst == NULL)
467                 return (1);
468
469         /*
470          * Perform a regular entry delete first.
471          */
472         opts.flags &= ~RTF_ANNOUNCE;
473
474         for (;;) {      /* try twice */
475                 rtm = rtmsg(RTM_GET, dst, NULL);
476                 if (rtm == NULL) {
477                         xo_warn("%s", host);
478                         return (1);
479                 }
480                 addr = (struct sockaddr_in *)(rtm + 1);
481                 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
482
483                 /*
484                  * With the new L2/L3 restructure, the route
485                  * returned is a prefix route. The important
486                  * piece of information from the previous
487                  * RTM_GET is the interface index. In the
488                  * case of ECMP, the kernel will traverse
489                  * the route group for the given entry.
490                  */
491                 if (sdl->sdl_family == AF_LINK &&
492                     !(rtm->rtm_flags & RTF_GATEWAY) &&
493                     valid_type(sdl->sdl_type) ) {
494                         addr->sin_addr.s_addr = dst->sin_addr.s_addr;
495                         break;
496                 }
497
498                 /*
499                  * Regular entry delete failed, now check if there
500                  * is a proxy-arp entry to remove.
501                  */
502                 if (opts.flags & RTF_ANNOUNCE) {
503                         xo_warnx("delete: cannot locate %s", host);
504                         return (1);
505                 }
506
507                 opts.flags |= RTF_ANNOUNCE;
508         }
509         rtm->rtm_flags |= RTF_LLDATA;
510         if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
511                 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
512                 return (0);
513         }
514         return (1);
515 }
516 #endif
517
518 static int
519 delete(char *host)
520 {
521 #ifdef WITHOUT_NETLINK
522         return (delete_rtsock(host));
523 #else
524         return (delete_nl(0, host));
525 #endif
526 }
527
528
529 /*
530  * Search the arp table and do some action on matching entries
531  */
532 static int
533 search(u_long addr, action_fn *action)
534 {
535         int mib[6];
536         size_t needed;
537         char *lim, *buf, *next;
538         struct rt_msghdr *rtm;
539         struct sockaddr_in *sin2;
540         struct sockaddr_dl *sdl;
541         int st, found_entry = 0;
542
543         mib[0] = CTL_NET;
544         mib[1] = PF_ROUTE;
545         mib[2] = 0;
546         mib[3] = AF_INET;
547         mib[4] = NET_RT_FLAGS;
548 #ifdef RTF_LLINFO
549         mib[5] = RTF_LLINFO;
550 #else
551         mib[5] = 0;
552 #endif
553         if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
554                 xo_err(1, "route-sysctl-estimate");
555         if (needed == 0)        /* empty table */
556                 return 0;
557         buf = NULL;
558         for (;;) {
559                 buf = reallocf(buf, needed);
560                 if (buf == NULL)
561                         xo_errx(1, "could not reallocate memory");
562                 st = sysctl(mib, 6, buf, &needed, NULL, 0);
563                 if (st == 0 || errno != ENOMEM)
564                         break;
565                 needed += needed / 8;
566         }
567         if (st == -1)
568                 xo_err(1, "actual retrieval of routing table");
569         lim = buf + needed;
570         for (next = buf; next < lim; next += rtm->rtm_msglen) {
571                 rtm = (struct rt_msghdr *)next;
572                 sin2 = (struct sockaddr_in *)(rtm + 1);
573                 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
574                 if (opts.rifindex &&
575                     (opts.rifindex != sdl->sdl_index))
576                         continue;
577                 if (addr &&
578                     (addr != sin2->sin_addr.s_addr))
579                         continue;
580                 found_entry = 1;
581                 (*action)(sdl, sin2, rtm);
582         }
583         free(buf);
584         return (found_entry);
585 }
586
587 /*
588  * Display an arp entry
589  */
590
591 static void
592 print_entry(struct sockaddr_dl *sdl,
593         struct sockaddr_in *addr, struct rt_msghdr *rtm)
594 {
595         const char *host;
596         struct hostent *hp;
597         struct if_nameindex *p;
598
599         if (ifnameindex == NULL)
600                 if ((ifnameindex = if_nameindex()) == NULL)
601                         xo_err(1, "cannot retrieve interface names");
602
603         xo_open_instance("arp-cache");
604
605         if (!opts.nflag)
606                 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
607                     sizeof addr->sin_addr, AF_INET);
608         else
609                 hp = 0;
610         if (hp)
611                 host = hp->h_name;
612         else {
613                 host = "?";
614                 if (h_errno == TRY_AGAIN)
615                         opts.nflag = true;
616         }
617         xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
618             inet_ntoa(addr->sin_addr));
619         if (sdl->sdl_alen) {
620                 if ((sdl->sdl_type == IFT_ETHER ||
621                     sdl->sdl_type == IFT_L2VLAN ||
622                     sdl->sdl_type == IFT_BRIDGE) &&
623                     sdl->sdl_alen == ETHER_ADDR_LEN)
624                         xo_emit("{:mac-address/%s}",
625                             ether_ntoa((struct ether_addr *)LLADDR(sdl)));
626                 else {
627                         int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
628
629                         xo_emit("{:mac-address/%s}", link_ntoa(sdl) + n);
630                 }
631         } else
632                 xo_emit("{d:/(incomplete)}{en:incomplete/true}");
633
634         for (p = ifnameindex; p && p->if_index && p->if_name; p++) {
635                 if (p->if_index == sdl->sdl_index) {
636                         xo_emit(" on {:interface/%s}", p->if_name);
637                         break;
638                 }
639         }
640
641         if (rtm->rtm_rmx.rmx_expire == 0)
642                 xo_emit("{d:/ permanent}{en:permanent/true}");
643         else {
644                 static struct timespec tp;
645                 time_t expire_time = 0;
646
647                 if (tp.tv_sec == 0)
648                         clock_gettime(CLOCK_MONOTONIC, &tp);
649                 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
650                         xo_emit(" expires in {:expires/%d} seconds",
651                             (int)expire_time);
652                 else
653                         xo_emit("{d:/ expired}{en:expired/true}");
654         }
655
656         if (rtm->rtm_flags & RTF_ANNOUNCE)
657                 xo_emit("{d:/ published}{en:published/true}");
658
659         switch(sdl->sdl_type) {
660         case IFT_ETHER:
661                 xo_emit(" [{:type/ethernet}]");
662                 break;
663         case IFT_FDDI:
664                 xo_emit(" [{:type/fddi}]");
665                 break;
666         case IFT_ATM:
667                 xo_emit(" [{:type/atm}]");
668                 break;
669         case IFT_L2VLAN:
670                 xo_emit(" [{:type/vlan}]");
671                 break;
672         case IFT_IEEE1394:
673                 xo_emit(" [{:type/firewire}]");
674                 break;
675         case IFT_BRIDGE:
676                 xo_emit(" [{:type/bridge}]");
677                 break;
678         case IFT_INFINIBAND:
679                 xo_emit(" [{:type/infiniband}]");
680                 break;
681         default:
682                 break;
683         }
684
685         xo_emit("\n");
686
687         xo_close_instance("arp-cache");
688 }
689
690 static int
691 print_entries(uint32_t ifindex, struct in_addr addr)
692 {
693 #ifndef WITHOUT_NETLINK
694         return (print_entries_nl(ifindex, addr));
695 #else
696         return (search(addr.s_addr, print_entry));
697 #endif
698 }
699
700
701 /*
702  * Nuke an arp entry
703  */
704 static void
705 nuke_entry(struct sockaddr_dl *sdl __unused,
706         struct sockaddr_in *addr, struct rt_msghdr *rtm)
707 {
708         char ip[20];
709
710         if (rtm->rtm_flags & RTF_PINNED)
711                 return;
712
713         snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
714         delete(ip);
715 }
716
717 static void
718 nuke_entries(uint32_t ifindex, struct in_addr addr)
719 {
720         search(addr.s_addr, nuke_entry);
721 }
722
723 static void
724 usage(void)
725 {
726         fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
727             "usage: arp [-n] [-i interface] hostname",
728             "       arp [-n] [-i interface] -a",
729             "       arp -d hostname [pub]",
730             "       arp -d [-i interface] -a",
731             "       arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
732             "       arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
733             "       arp -f filename");
734         exit(1);
735 }
736
737 static struct rt_msghdr *
738 rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl)
739 {
740         static int seq;
741         int rlen;
742         int l;
743         static int s = -1;
744         static pid_t pid;
745
746         static struct   {
747                 struct  rt_msghdr m_rtm;
748                 char    m_space[512];
749         }       m_rtmsg;
750
751         struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
752         char *cp = m_rtmsg.m_space;
753
754         if (s < 0) {    /* first time: open socket, get pid */
755                 s = socket(PF_ROUTE, SOCK_RAW, 0);
756                 if (s < 0)
757                         xo_err(1, "socket");
758                 pid = getpid();
759         }
760
761         errno = 0;
762         /*
763          * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
764          * appropriately.
765          */
766         if (cmd == RTM_DELETE)
767                 goto doit;
768         bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
769         rtm->rtm_flags = opts.flags;
770         rtm->rtm_version = RTM_VERSION;
771
772         switch (cmd) {
773         default:
774                 xo_errx(1, "internal wrong cmd");
775         case RTM_ADD:
776                 rtm->rtm_addrs |= RTA_GATEWAY;
777                 if (opts.expire_time != 0) {
778                         struct timespec tp;
779
780                         clock_gettime(CLOCK_MONOTONIC, &tp);
781                         rtm->rtm_rmx.rmx_expire = opts.expire_time + tp.tv_sec;
782                 }
783                 rtm->rtm_inits = RTV_EXPIRE;
784                 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
785                 /* FALLTHROUGH */
786         case RTM_GET:
787                 rtm->rtm_addrs |= RTA_DST;
788         }
789 #define NEXTADDR(w, s)                                          \
790         do {                                                    \
791                 if ((s) != NULL && rtm->rtm_addrs & (w)) {      \
792                         bcopy((s), cp, sizeof(*(s)));           \
793                         cp += SA_SIZE(s);                       \
794                 }                                               \
795         } while (0)
796
797         NEXTADDR(RTA_DST, dst);
798         NEXTADDR(RTA_GATEWAY, sdl);
799
800         rtm->rtm_msglen = cp - (char *)&m_rtmsg;
801 doit:
802         l = rtm->rtm_msglen;
803         rtm->rtm_seq = ++seq;
804         rtm->rtm_type = cmd;
805         if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
806                 if (errno != ESRCH || cmd != RTM_DELETE) {
807                         xo_warn("writing to routing socket");
808                         return (NULL);
809                 }
810         }
811         do {
812                 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
813         } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq ||
814             rtm->rtm_pid != pid));
815         if (l < 0)
816                 xo_warn("read from routing socket");
817         return (rtm);
818 }
819
820 /*
821  * get_ether_addr - get the hardware address of an interface on the
822  * the same subnet as ipaddr.
823  */
824 static int
825 get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
826 {
827         struct ifaddrs *ifa, *ifd, *ifas = NULL;
828         in_addr_t ina, mask;
829         struct sockaddr_dl *dla;
830         int retval = 0;
831
832         /*
833          * Scan through looking for an interface with an Internet
834          * address on the same subnet as `ipaddr'.
835          */
836         if (getifaddrs(&ifas) < 0) {
837                 xo_warnx("getifaddrs");
838                 goto done;
839         }
840
841         for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
842                 if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL)
843                         continue;
844                 if (ifa->ifa_addr->sa_family != AF_INET)
845                         continue;
846                 /*
847                  * Check that the interface is up,
848                  * and not point-to-point or loopback.
849                  */
850                 if ((ifa->ifa_flags &
851                     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
852                     IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST))
853                         continue;
854                 /* Get its netmask and check that it's on the right subnet. */
855                 mask = ((struct sockaddr_in *)
856                         ifa->ifa_netmask)->sin_addr.s_addr;
857                 ina = ((struct sockaddr_in *)
858                         ifa->ifa_addr)->sin_addr.s_addr;
859                 if ((ipaddr & mask) == (ina & mask))
860                         break; /* ok, we got it! */
861         }
862         if (ifa == NULL)
863                 goto done;
864
865         /*
866          * Now scan through again looking for a link-level address
867          * for this interface.
868          */
869         for (ifd = ifas; ifd != NULL; ifd = ifd->ifa_next) {
870                 if (ifd->ifa_addr == NULL)
871                         continue;
872                 if (strcmp(ifa->ifa_name, ifd->ifa_name) == 0 &&
873                     ifd->ifa_addr->sa_family == AF_LINK)
874                         break;
875         }
876         if (ifd == NULL)
877                 goto done;
878         /*
879          * Found the link-level address - copy it out
880          */
881         dla = (struct sockaddr_dl *)ifd->ifa_addr;
882         memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
883         printf("using interface %s for proxy with address %s\n", ifa->ifa_name,
884             ether_ntoa(hwaddr));
885         retval = dla->sdl_alen;
886 done:
887         if (ifas != NULL)
888                 freeifaddrs(ifas);
889         return (retval);
890 }