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 * 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 } 891