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