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 #include <net/iso88025.h> 66 67 #include <netinet/in.h> 68 #include <netinet/if_ether.h> 69 70 #include <arpa/inet.h> 71 72 #include <ctype.h> 73 #include <err.h> 74 #include <errno.h> 75 #include <netdb.h> 76 #include <nlist.h> 77 #include <paths.h> 78 #include <stdio.h> 79 #include <stdlib.h> 80 #include <string.h> 81 #include <strings.h> 82 #include <unistd.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_INFINIBAND: 304 case IFT_ISO88023: 305 case IFT_ISO88024: 306 case IFT_ISO88025: 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 iso88025_sockaddr_dl_data *trld; 596 struct if_nameindex *p; 597 int seg; 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 (nflag == 0) 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 nflag = 1; 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 && ifnameindex->if_index && 635 ifnameindex->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 if (tp.tv_sec == 0) 647 clock_gettime(CLOCK_MONOTONIC, &tp); 648 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0) 649 xo_emit(" expires in {:expires/%d} seconds", 650 (int)expire_time); 651 else 652 xo_emit("{d:/ expired}{en:expired/true}"); 653 } 654 655 if (rtm->rtm_flags & RTF_ANNOUNCE) 656 xo_emit("{d:/ published}{en:published/true}"); 657 658 switch(sdl->sdl_type) { 659 case IFT_ETHER: 660 xo_emit(" [{:type/ethernet}]"); 661 break; 662 case IFT_ISO88025: 663 xo_emit(" [{:type/token-ring}]"); 664 trld = SDL_ISO88025(sdl); 665 if (trld->trld_rcf != 0) { 666 xo_emit(" rt=%x", ntohs(trld->trld_rcf)); 667 for (seg = 0; 668 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 669 seg++) 670 xo_emit(":%x", ntohs(*(trld->trld_route[seg]))); 671 } 672 break; 673 case IFT_FDDI: 674 xo_emit(" [{:type/fddi}]"); 675 break; 676 case IFT_ATM: 677 xo_emit(" [{:type/atm}]"); 678 break; 679 case IFT_L2VLAN: 680 xo_emit(" [{:type/vlan}]"); 681 break; 682 case IFT_IEEE1394: 683 xo_emit(" [{:type/firewire}]"); 684 break; 685 case IFT_BRIDGE: 686 xo_emit(" [{:type/bridge}]"); 687 break; 688 case IFT_INFINIBAND: 689 xo_emit(" [{:type/infiniband}]"); 690 break; 691 default: 692 break; 693 } 694 695 xo_emit("\n"); 696 697 xo_close_instance("arp-cache"); 698 } 699 700 /* 701 * Nuke an arp entry 702 */ 703 static void 704 nuke_entry(struct sockaddr_dl *sdl __unused, 705 struct sockaddr_in *addr, struct rt_msghdr *rtm) 706 { 707 char ip[20]; 708 709 if (rtm->rtm_flags & RTF_PINNED) 710 return; 711 712 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 713 delete(ip); 714 } 715 716 static void 717 usage(void) 718 { 719 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 720 "usage: arp [-n] [-i interface] hostname", 721 " arp [-n] [-i interface] -a", 722 " arp -d hostname [pub]", 723 " arp -d [-i interface] -a", 724 " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 725 " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 726 " arp -f filename"); 727 exit(1); 728 } 729 730 static struct rt_msghdr * 731 rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) 732 { 733 static int seq; 734 int rlen; 735 int l; 736 struct sockaddr_in so_mask, *som = &so_mask; 737 static int s = -1; 738 static pid_t pid; 739 740 static struct { 741 struct rt_msghdr m_rtm; 742 char m_space[512]; 743 } m_rtmsg; 744 745 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 746 char *cp = m_rtmsg.m_space; 747 748 if (s < 0) { /* first time: open socket, get pid */ 749 s = socket(PF_ROUTE, SOCK_RAW, 0); 750 if (s < 0) 751 xo_err(1, "socket"); 752 pid = getpid(); 753 } 754 bzero(&so_mask, sizeof(so_mask)); 755 so_mask.sin_len = 8; 756 so_mask.sin_addr.s_addr = 0xffffffff; 757 758 errno = 0; 759 /* 760 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 761 * appropriately. 762 */ 763 if (cmd == RTM_DELETE) 764 goto doit; 765 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 766 rtm->rtm_flags = flags; 767 rtm->rtm_version = RTM_VERSION; 768 769 switch (cmd) { 770 default: 771 xo_errx(1, "internal wrong cmd"); 772 case RTM_ADD: 773 rtm->rtm_addrs |= RTA_GATEWAY; 774 rtm->rtm_rmx.rmx_expire = expire_time; 775 rtm->rtm_inits = RTV_EXPIRE; 776 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 777 if (doing_proxy) { 778 rtm->rtm_addrs |= RTA_NETMASK; 779 rtm->rtm_flags &= ~RTF_HOST; 780 } 781 /* FALLTHROUGH */ 782 case RTM_GET: 783 rtm->rtm_addrs |= RTA_DST; 784 } 785 #define NEXTADDR(w, s) \ 786 do { \ 787 if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 788 bcopy((s), cp, sizeof(*(s))); \ 789 cp += SA_SIZE(s); \ 790 } \ 791 } while (0) 792 793 NEXTADDR(RTA_DST, dst); 794 NEXTADDR(RTA_GATEWAY, sdl); 795 NEXTADDR(RTA_NETMASK, som); 796 797 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 798 doit: 799 l = rtm->rtm_msglen; 800 rtm->rtm_seq = ++seq; 801 rtm->rtm_type = cmd; 802 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 803 if (errno != ESRCH || cmd != RTM_DELETE) { 804 xo_warn("writing to routing socket"); 805 return (NULL); 806 } 807 } 808 do { 809 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 810 } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || 811 rtm->rtm_pid != pid)); 812 if (l < 0) 813 xo_warn("read from routing socket"); 814 return (rtm); 815 } 816 817 /* 818 * get_ether_addr - get the hardware address of an interface on the 819 * the same subnet as ipaddr. 820 */ 821 #define MAX_IFS 32 822 823 static int 824 get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 825 { 826 struct ifreq *ifr, *ifend, *ifp; 827 in_addr_t ina, mask; 828 struct sockaddr_dl *dla; 829 struct ifreq ifreq; 830 struct ifconf ifc; 831 struct ifreq ifs[MAX_IFS]; 832 int sock; 833 int retval = 0; 834 835 sock = socket(AF_INET, SOCK_DGRAM, 0); 836 if (sock < 0) 837 xo_err(1, "socket"); 838 839 ifc.ifc_len = sizeof(ifs); 840 ifc.ifc_req = ifs; 841 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 842 xo_warnx("ioctl(SIOCGIFCONF)"); 843 goto done; 844 } 845 846 #define NEXTIFR(i) \ 847 ((struct ifreq *)((char *)&(i)->ifr_addr \ 848 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 849 850 /* 851 * Scan through looking for an interface with an Internet 852 * address on the same subnet as `ipaddr'. 853 */ 854 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 855 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 856 if (ifr->ifr_addr.sa_family != AF_INET) 857 continue; 858 strncpy(ifreq.ifr_name, ifr->ifr_name, 859 sizeof(ifreq.ifr_name)); 860 ifreq.ifr_addr = ifr->ifr_addr; 861 /* 862 * Check that the interface is up, 863 * and not point-to-point or loopback. 864 */ 865 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 866 continue; 867 if ((ifreq.ifr_flags & 868 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 869 IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST)) 870 continue; 871 /* Get its netmask and check that it's on the right subnet. */ 872 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 873 continue; 874 mask = ((struct sockaddr_in *) 875 &ifreq.ifr_addr)->sin_addr.s_addr; 876 ina = ((struct sockaddr_in *) 877 &ifr->ifr_addr)->sin_addr.s_addr; 878 if ((ipaddr & mask) == (ina & mask)) 879 break; /* ok, we got it! */ 880 } 881 882 if (ifr >= ifend) 883 goto done; 884 885 /* 886 * Now scan through again looking for a link-level address 887 * for this interface. 888 */ 889 ifp = ifr; 890 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 891 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 892 ifr->ifr_addr.sa_family == AF_LINK) 893 break; 894 if (ifr >= ifend) 895 goto done; 896 /* 897 * Found the link-level address - copy it out 898 */ 899 dla = (struct sockaddr_dl *) &ifr->ifr_addr; 900 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 901 printf("using interface %s for proxy with address %s\n", ifp->ifr_name, 902 ether_ntoa(hwaddr)); 903 retval = dla->sdl_alen; 904 done: 905 close(sock); 906 return (retval); 907 } 908