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 <errno.h> 59 #include <netdb.h> 60 #include <nlist.h> 61 #include <paths.h> 62 #include <stdbool.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <strings.h> 67 #include <unistd.h> 68 #include <ifaddrs.h> 69 #include <libxo/xo.h> 70 #include "arp.h" 71 72 typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_in *s_in, 73 struct rt_msghdr *rtm); 74 static void nuke_entries(uint32_t ifindex, struct in_addr addr); 75 static int print_entries(uint32_t ifindex, struct in_addr addr); 76 77 static int delete(char *host); 78 static void usage(void) __dead2; 79 static int set(int argc, char **argv); 80 static int get(char *host); 81 static int file(char *name); 82 static struct rt_msghdr *rtmsg(int cmd, 83 struct sockaddr_in *dst, struct sockaddr_dl *sdl); 84 static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); 85 static int set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m, 86 char *host); 87 88 struct if_nameindex *ifnameindex; 89 90 struct arp_opts opts = {}; 91 92 /* which function we're supposed to do */ 93 #define F_GET 1 94 #define F_SET 2 95 #define F_FILESET 3 96 #define F_REPLACE 4 97 #define F_DELETE 5 98 99 #define SETFUNC(f) { if (func) usage(); func = (f); } 100 101 #define ARP_XO_VERSION "1" 102 103 int 104 main(int argc, char *argv[]) 105 { 106 int ch, func = 0; 107 int rtn = 0; 108 109 argc = xo_parse_args(argc, argv); 110 if (argc < 0) 111 exit(1); 112 113 while ((ch = getopt(argc, argv, "andfsSi:")) != -1) 114 switch(ch) { 115 case 'a': 116 opts.aflag = true; 117 break; 118 case 'd': 119 SETFUNC(F_DELETE); 120 break; 121 case 'n': 122 opts.nflag = true; 123 break; 124 case 'S': 125 SETFUNC(F_REPLACE); 126 break; 127 case 's': 128 SETFUNC(F_SET); 129 break; 130 case 'f' : 131 SETFUNC(F_FILESET); 132 break; 133 case 'i': 134 opts.rifname = optarg; 135 break; 136 case '?': 137 default: 138 usage(); 139 } 140 argc -= optind; 141 argv += optind; 142 143 if (!func) 144 func = F_GET; 145 if (opts.rifname) { 146 if (func != F_GET && func != F_SET && !(func == F_DELETE && opts.aflag)) 147 xo_errx(1, "-i not applicable to this operation"); 148 if ((opts.rifindex = if_nametoindex(opts.rifname)) == 0) { 149 if (errno == ENXIO) 150 xo_errx(1, "interface %s does not exist", 151 opts.rifname); 152 else 153 xo_err(1, "if_nametoindex(%s)", opts.rifname); 154 } 155 } 156 switch (func) { 157 case F_GET: 158 if (opts.aflag) { 159 if (argc != 0) 160 usage(); 161 162 xo_set_version(ARP_XO_VERSION); 163 xo_open_container("arp"); 164 xo_open_list("arp-cache"); 165 166 struct in_addr all_addrs = {}; 167 print_entries(opts.rifindex, all_addrs); 168 169 xo_close_list("arp-cache"); 170 xo_close_container("arp"); 171 if (xo_finish() < 0) 172 xo_err(1, "stdout"); 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 exit(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(opts.rifindex, 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 if (xo_finish() < 0) 450 xo_err(1, "stdout"); 451 452 return (found == 0); 453 } 454 455 /* 456 * Delete an arp entry 457 */ 458 #ifdef WITHOUT_NETLINK 459 static int 460 delete_rtsock(char *host) 461 { 462 struct sockaddr_in *addr, *dst; 463 struct rt_msghdr *rtm; 464 struct sockaddr_dl *sdl; 465 466 dst = getaddr(host); 467 if (dst == NULL) 468 return (1); 469 470 /* 471 * Perform a regular entry delete first. 472 */ 473 opts.flags &= ~RTF_ANNOUNCE; 474 475 for (;;) { /* try twice */ 476 rtm = rtmsg(RTM_GET, dst, NULL); 477 if (rtm == NULL) { 478 xo_warn("%s", host); 479 return (1); 480 } 481 addr = (struct sockaddr_in *)(rtm + 1); 482 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 483 484 /* 485 * With the new L2/L3 restructure, the route 486 * returned is a prefix route. The important 487 * piece of information from the previous 488 * RTM_GET is the interface index. In the 489 * case of ECMP, the kernel will traverse 490 * the route group for the given entry. 491 */ 492 if (sdl->sdl_family == AF_LINK && 493 !(rtm->rtm_flags & RTF_GATEWAY) && 494 valid_type(sdl->sdl_type) ) { 495 addr->sin_addr.s_addr = dst->sin_addr.s_addr; 496 break; 497 } 498 499 /* 500 * Regular entry delete failed, now check if there 501 * is a proxy-arp entry to remove. 502 */ 503 if (opts.flags & RTF_ANNOUNCE) { 504 xo_warnx("delete: cannot locate %s", host); 505 return (1); 506 } 507 508 opts.flags |= RTF_ANNOUNCE; 509 } 510 rtm->rtm_flags |= RTF_LLDATA; 511 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 512 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 513 return (0); 514 } 515 return (1); 516 } 517 #endif 518 519 static int 520 delete(char *host) 521 { 522 #ifdef WITHOUT_NETLINK 523 return (delete_rtsock(host)); 524 #else 525 return (delete_nl(0, host)); 526 #endif 527 } 528 529 530 /* 531 * Search the arp table and do some action on matching entries 532 */ 533 static int 534 search(u_long addr, action_fn *action) 535 { 536 int mib[6]; 537 size_t needed; 538 char *lim, *buf, *next; 539 struct rt_msghdr *rtm; 540 struct sockaddr_in *sin2; 541 struct sockaddr_dl *sdl; 542 int st, found_entry = 0; 543 544 mib[0] = CTL_NET; 545 mib[1] = PF_ROUTE; 546 mib[2] = 0; 547 mib[3] = AF_INET; 548 mib[4] = NET_RT_FLAGS; 549 #ifdef RTF_LLINFO 550 mib[5] = RTF_LLINFO; 551 #else 552 mib[5] = 0; 553 #endif 554 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 555 xo_err(1, "route-sysctl-estimate"); 556 if (needed == 0) /* empty table */ 557 return 0; 558 buf = NULL; 559 for (;;) { 560 buf = reallocf(buf, needed); 561 if (buf == NULL) 562 xo_errx(1, "could not reallocate memory"); 563 st = sysctl(mib, 6, buf, &needed, NULL, 0); 564 if (st == 0 || errno != ENOMEM) 565 break; 566 needed += needed / 8; 567 } 568 if (st == -1) 569 xo_err(1, "actual retrieval of routing table"); 570 lim = buf + needed; 571 for (next = buf; next < lim; next += rtm->rtm_msglen) { 572 rtm = (struct rt_msghdr *)next; 573 sin2 = (struct sockaddr_in *)(rtm + 1); 574 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 575 if (opts.rifindex && 576 (opts.rifindex != sdl->sdl_index)) 577 continue; 578 if (addr && 579 (addr != sin2->sin_addr.s_addr)) 580 continue; 581 found_entry = 1; 582 (*action)(sdl, sin2, rtm); 583 } 584 free(buf); 585 return (found_entry); 586 } 587 588 /* 589 * Display an arp entry 590 */ 591 592 static void 593 print_entry(struct sockaddr_dl *sdl, 594 struct sockaddr_in *addr, struct rt_msghdr *rtm) 595 { 596 const char *host; 597 struct hostent *hp; 598 struct if_nameindex *p; 599 600 if (ifnameindex == NULL) 601 if ((ifnameindex = if_nameindex()) == NULL) 602 xo_err(1, "cannot retrieve interface names"); 603 604 xo_open_instance("arp-cache"); 605 606 if (!opts.nflag) 607 hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 608 sizeof addr->sin_addr, AF_INET); 609 else 610 hp = 0; 611 if (hp) 612 host = hp->h_name; 613 else { 614 host = "?"; 615 if (h_errno == TRY_AGAIN) 616 opts.nflag = true; 617 } 618 xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host, 619 inet_ntoa(addr->sin_addr)); 620 if (sdl->sdl_alen) { 621 if ((sdl->sdl_type == IFT_ETHER || 622 sdl->sdl_type == IFT_L2VLAN || 623 sdl->sdl_type == IFT_BRIDGE) && 624 sdl->sdl_alen == ETHER_ADDR_LEN) 625 xo_emit("{:mac-address/%s}", 626 ether_ntoa((struct ether_addr *)LLADDR(sdl))); 627 else { 628 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 629 630 xo_emit("{:mac-address/%s}", link_ntoa(sdl) + n); 631 } 632 } else 633 xo_emit("{d:/(incomplete)}{en:incomplete/true}"); 634 635 for (p = ifnameindex; p && p->if_index && p->if_name; p++) { 636 if (p->if_index == sdl->sdl_index) { 637 xo_emit(" on {:interface/%s}", p->if_name); 638 break; 639 } 640 } 641 642 if (rtm->rtm_rmx.rmx_expire == 0) 643 xo_emit("{d:/ permanent}{en:permanent/true}"); 644 else { 645 static struct timespec tp; 646 time_t expire_time = 0; 647 648 if (tp.tv_sec == 0) 649 clock_gettime(CLOCK_MONOTONIC, &tp); 650 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0) 651 xo_emit(" expires in {:expires/%d} seconds", 652 (int)expire_time); 653 else 654 xo_emit("{d:/ expired}{en:expired/true}"); 655 } 656 657 if (rtm->rtm_flags & RTF_ANNOUNCE) 658 xo_emit("{d:/ published}{en:published/true}"); 659 660 switch(sdl->sdl_type) { 661 case IFT_ETHER: 662 xo_emit(" [{:type/ethernet}]"); 663 break; 664 case IFT_FDDI: 665 xo_emit(" [{:type/fddi}]"); 666 break; 667 case IFT_ATM: 668 xo_emit(" [{:type/atm}]"); 669 break; 670 case IFT_L2VLAN: 671 xo_emit(" [{:type/vlan}]"); 672 break; 673 case IFT_IEEE1394: 674 xo_emit(" [{:type/firewire}]"); 675 break; 676 case IFT_BRIDGE: 677 xo_emit(" [{:type/bridge}]"); 678 break; 679 case IFT_INFINIBAND: 680 xo_emit(" [{:type/infiniband}]"); 681 break; 682 default: 683 break; 684 } 685 686 xo_emit("\n"); 687 688 xo_close_instance("arp-cache"); 689 } 690 691 static int 692 print_entries(uint32_t ifindex, struct in_addr addr) 693 { 694 #ifndef WITHOUT_NETLINK 695 return (print_entries_nl(ifindex, addr)); 696 #else 697 return (search(addr.s_addr, print_entry)); 698 #endif 699 } 700 701 702 /* 703 * Nuke an arp entry 704 */ 705 static void 706 nuke_entry(struct sockaddr_dl *sdl __unused, 707 struct sockaddr_in *addr, struct rt_msghdr *rtm) 708 { 709 char ip[20]; 710 711 if (rtm->rtm_flags & RTF_PINNED) 712 return; 713 714 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 715 delete(ip); 716 } 717 718 static void 719 nuke_entries(uint32_t ifindex, struct in_addr addr) 720 { 721 search(addr.s_addr, nuke_entry); 722 } 723 724 static void 725 usage(void) 726 { 727 xo_error("%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 728 "usage: arp [-n] [-i interface] hostname", 729 " arp [-n] [-i interface] -a", 730 " arp -d hostname [pub]", 731 " arp -d [-i interface] -a", 732 " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 733 " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 734 " arp -f filename"); 735 exit(1); 736 } 737 738 static struct rt_msghdr * 739 rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) 740 { 741 static int seq; 742 int rlen; 743 int l; 744 static int s = -1; 745 static pid_t pid; 746 747 static struct { 748 struct rt_msghdr m_rtm; 749 char m_space[512]; 750 } m_rtmsg; 751 752 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 753 char *cp = m_rtmsg.m_space; 754 755 if (s < 0) { /* first time: open socket, get pid */ 756 s = socket(PF_ROUTE, SOCK_RAW, 0); 757 if (s < 0) 758 xo_err(1, "socket"); 759 pid = getpid(); 760 } 761 762 errno = 0; 763 /* 764 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 765 * appropriately. 766 */ 767 if (cmd == RTM_DELETE) 768 goto doit; 769 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 770 rtm->rtm_flags = opts.flags; 771 rtm->rtm_version = RTM_VERSION; 772 773 switch (cmd) { 774 default: 775 xo_errx(1, "internal wrong cmd"); 776 case RTM_ADD: 777 rtm->rtm_addrs |= RTA_GATEWAY; 778 if (opts.expire_time != 0) { 779 struct timespec tp; 780 781 clock_gettime(CLOCK_MONOTONIC, &tp); 782 rtm->rtm_rmx.rmx_expire = opts.expire_time + tp.tv_sec; 783 } 784 rtm->rtm_inits = RTV_EXPIRE; 785 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 786 /* FALLTHROUGH */ 787 case RTM_GET: 788 rtm->rtm_addrs |= RTA_DST; 789 } 790 #define NEXTADDR(w, s) \ 791 do { \ 792 if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 793 bcopy((s), cp, sizeof(*(s))); \ 794 cp += SA_SIZE(s); \ 795 } \ 796 } while (0) 797 798 NEXTADDR(RTA_DST, dst); 799 NEXTADDR(RTA_GATEWAY, sdl); 800 801 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 802 doit: 803 l = rtm->rtm_msglen; 804 rtm->rtm_seq = ++seq; 805 rtm->rtm_type = cmd; 806 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 807 if (errno != ESRCH || cmd != RTM_DELETE) { 808 xo_warn("writing to routing socket"); 809 return (NULL); 810 } 811 } 812 do { 813 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 814 } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || 815 rtm->rtm_pid != pid)); 816 if (l < 0) 817 xo_warn("read from routing socket"); 818 return (rtm); 819 } 820 821 /* 822 * get_ether_addr - get the hardware address of an interface on the 823 * same subnet as ipaddr. 824 */ 825 static int 826 get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 827 { 828 struct ifaddrs *ifa, *ifd, *ifas = NULL; 829 in_addr_t ina, mask; 830 struct sockaddr_dl *dla; 831 int retval = 0; 832 833 /* 834 * Scan through looking for an interface with an Internet 835 * address on the same subnet as `ipaddr'. 836 */ 837 if (getifaddrs(&ifas) < 0) { 838 xo_warnx("getifaddrs"); 839 goto done; 840 } 841 842 for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) { 843 if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL) 844 continue; 845 if (ifa->ifa_addr->sa_family != AF_INET) 846 continue; 847 /* 848 * Check that the interface is up, 849 * and not point-to-point or loopback. 850 */ 851 if ((ifa->ifa_flags & 852 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 853 IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST)) 854 continue; 855 /* Get its netmask and check that it's on the right subnet. */ 856 mask = ((struct sockaddr_in *) 857 ifa->ifa_netmask)->sin_addr.s_addr; 858 ina = ((struct sockaddr_in *) 859 ifa->ifa_addr)->sin_addr.s_addr; 860 if ((ipaddr & mask) == (ina & mask)) 861 break; /* ok, we got it! */ 862 } 863 if (ifa == NULL) 864 goto done; 865 866 /* 867 * Now scan through again looking for a link-level address 868 * for this interface. 869 */ 870 for (ifd = ifas; ifd != NULL; ifd = ifd->ifa_next) { 871 if (ifd->ifa_addr == NULL) 872 continue; 873 if (strcmp(ifa->ifa_name, ifd->ifa_name) == 0 && 874 ifd->ifa_addr->sa_family == AF_LINK) 875 break; 876 } 877 if (ifd == NULL) 878 goto done; 879 /* 880 * Found the link-level address - copy it out 881 */ 882 dla = (struct sockaddr_dl *)ifd->ifa_addr; 883 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 884 printf("using interface %s for proxy with address %s\n", ifa->ifa_name, 885 ether_ntoa(hwaddr)); 886 retval = dla->sdl_alen; 887 done: 888 if (ifas != NULL) 889 freeifaddrs(ifas); 890 return (retval); 891 } 892