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