1 /* 2 * Copyright (c) 1984, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Sun Microsystems, Inc. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #if 0 34 #ifndef lint 35 static char const copyright[] = 36 "@(#) Copyright (c) 1984, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; 42 #endif /* not lint */ 43 #endif 44 #include <sys/cdefs.h> 45 __FBSDID("$FreeBSD$"); 46 47 /* 48 * arp - display, set, and delete arp table entries 49 */ 50 51 52 #include <sys/param.h> 53 #include <sys/file.h> 54 #include <sys/socket.h> 55 #include <sys/sockio.h> 56 #include <sys/sysctl.h> 57 #include <sys/ioctl.h> 58 #include <sys/time.h> 59 60 #include <net/if.h> 61 #include <net/if_dl.h> 62 #include <net/if_types.h> 63 #include <net/route.h> 64 #include <net/iso88025.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 83 #ifdef FSTACK 84 #include <time.h> 85 #include "rtioctl.h" 86 #include "ff_ipc.h" 87 88 #ifndef __unused 89 #define __unused __attribute__((__unused__)) 90 91 #define socket(a, b, c) rt_socket((a), (b), (c)) 92 #define close(a) rt_close(a) 93 94 #endif 95 96 #endif 97 98 typedef void (action_fn)(struct sockaddr_dl *sdl, 99 struct sockaddr_in *s_in, struct rt_msghdr *rtm); 100 101 static int search(u_long addr, action_fn *action); 102 static action_fn print_entry; 103 static action_fn nuke_entry; 104 105 static int delete(char *host); 106 static void usage(void); 107 static int set(int argc, char **argv); 108 static int get(char *host); 109 static int file(char *name); 110 static struct rt_msghdr *rtmsg(int cmd, 111 struct sockaddr_in *dst, struct sockaddr_dl *sdl); 112 static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); 113 static struct sockaddr_in *getaddr(char *host); 114 static int valid_type(int type); 115 116 static int nflag; /* no reverse dns lookups */ 117 static char *rifname; 118 119 static time_t expire_time; 120 static int flags, doing_proxy; 121 122 struct if_nameindex *ifnameindex; 123 124 /* which function we're supposed to do */ 125 #define F_GET 1 126 #define F_SET 2 127 #define F_FILESET 3 128 #define F_REPLACE 4 129 #define F_DELETE 5 130 131 #define SETFUNC(f) { if (func) usage(); func = (f); } 132 133 int 134 main(int argc, char *argv[]) 135 { 136 int ch, func = 0; 137 int rtn = 0; 138 int aflag = 0; /* do it for all entries */ 139 140 #ifndef FSTACK 141 while ((ch = getopt(argc, argv, "andfsSi:")) != -1) 142 #else 143 ff_ipc_init(); 144 while ((ch = getopt(argc, argv, "andfsSi:p:")) != -1) 145 #endif 146 switch(ch) { 147 case 'a': 148 aflag = 1; 149 break; 150 case 'd': 151 SETFUNC(F_DELETE); 152 break; 153 case 'n': 154 nflag = 1; 155 break; 156 case 'S': 157 SETFUNC(F_REPLACE); 158 break; 159 case 's': 160 SETFUNC(F_SET); 161 break; 162 case 'f' : 163 SETFUNC(F_FILESET); 164 break; 165 case 'i': 166 rifname = optarg; 167 break; 168 #ifdef FSTACK 169 case 'p': 170 ff_set_proc_id(atoi(optarg)); 171 break; 172 #endif 173 case '?': 174 default: 175 usage(); 176 } 177 argc -= optind; 178 argv += optind; 179 180 if (!func) 181 func = F_GET; 182 if (rifname) { 183 if (func != F_GET && !(func == F_DELETE && aflag)) 184 errx(1, "-i not applicable to this operation"); 185 if (if_nametoindex(rifname) == 0) { 186 if (errno == ENXIO) 187 errx(1, "interface %s does not exist", rifname); 188 else 189 err(1, "if_nametoindex(%s)", rifname); 190 } 191 } 192 switch (func) { 193 case F_GET: 194 if (aflag) { 195 if (argc != 0) 196 usage(); 197 search(0, print_entry); 198 } else { 199 if (argc != 1) 200 usage(); 201 rtn = get(argv[0]); 202 } 203 break; 204 case F_SET: 205 case F_REPLACE: 206 if (argc < 2 || argc > 6) 207 usage(); 208 if (func == F_REPLACE) 209 (void)delete(argv[0]); 210 rtn = set(argc, argv) ? 1 : 0; 211 break; 212 case F_DELETE: 213 if (aflag) { 214 if (argc != 0) 215 usage(); 216 search(0, nuke_entry); 217 } else { 218 if (argc != 1) 219 usage(); 220 rtn = delete(argv[0]); 221 } 222 break; 223 case F_FILESET: 224 if (argc != 1) 225 usage(); 226 rtn = file(argv[0]); 227 break; 228 } 229 230 if (ifnameindex != NULL) 231 if_freenameindex(ifnameindex); 232 233 #ifdef FSTACK 234 ff_ipc_exit(); 235 #endif 236 237 return (rtn); 238 } 239 240 /* 241 * Process a file to set standard arp entries 242 */ 243 static int 244 file(char *name) 245 { 246 FILE *fp; 247 int i, retval; 248 char line[100], arg[5][50], *args[5], *p; 249 250 if ((fp = fopen(name, "r")) == NULL) 251 err(1, "cannot open %s", name); 252 args[0] = &arg[0][0]; 253 args[1] = &arg[1][0]; 254 args[2] = &arg[2][0]; 255 args[3] = &arg[3][0]; 256 args[4] = &arg[4][0]; 257 retval = 0; 258 while(fgets(line, sizeof(line), fp) != NULL) { 259 if ((p = strchr(line, '#')) != NULL) 260 *p = '\0'; 261 for (p = line; isblank(*p); p++); 262 if (*p == '\n' || *p == '\0') 263 continue; 264 i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], 265 arg[2], arg[3], arg[4]); 266 if (i < 2) { 267 warnx("bad line: %s", line); 268 retval = 1; 269 continue; 270 } 271 if (set(i, args)) 272 retval = 1; 273 } 274 fclose(fp); 275 return (retval); 276 } 277 278 /* 279 * Given a hostname, fills up a (static) struct sockaddr_in with 280 * the address of the host and returns a pointer to the 281 * structure. 282 */ 283 static struct sockaddr_in * 284 getaddr(char *host) 285 { 286 #ifndef FSTACK 287 struct hostent *hp; 288 #endif 289 static struct sockaddr_in reply; 290 291 bzero(&reply, sizeof(reply)); 292 reply.sin_len = sizeof(reply); 293 reply.sin_family = AF_INET; 294 reply.sin_addr.s_addr = inet_addr(host); 295 if (reply.sin_addr.s_addr == INADDR_NONE) { 296 #ifndef FSTACK 297 if (!(hp = gethostbyname(host))) { 298 warnx("%s: %s", host, hstrerror(h_errno)); 299 return (NULL); 300 } 301 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 302 sizeof reply.sin_addr); 303 #else 304 warnx("reply.sin_addr.s_addr == INADDR_NONE"); 305 #endif 306 } 307 return (&reply); 308 } 309 310 /* 311 * Returns true if the type is a valid one for ARP. 312 */ 313 static int 314 valid_type(int type) 315 { 316 317 switch (type) { 318 case IFT_ETHER: 319 case IFT_FDDI: 320 case IFT_INFINIBAND: 321 case IFT_ISO88023: 322 case IFT_ISO88024: 323 case IFT_ISO88025: 324 case IFT_L2VLAN: 325 case IFT_BRIDGE: 326 return (1); 327 default: 328 return (0); 329 } 330 } 331 332 /* 333 * Set an individual arp entry 334 */ 335 static int 336 set(int argc, char **argv) 337 { 338 struct sockaddr_in *addr; 339 struct sockaddr_in *dst; /* what are we looking for */ 340 struct sockaddr_dl *sdl; 341 struct rt_msghdr *rtm; 342 struct ether_addr *ea; 343 char *host = argv[0], *eaddr = argv[1]; 344 struct sockaddr_dl sdl_m; 345 346 argc -= 2; 347 argv += 2; 348 349 bzero(&sdl_m, sizeof(sdl_m)); 350 sdl_m.sdl_len = sizeof(sdl_m); 351 sdl_m.sdl_family = AF_LINK; 352 353 dst = getaddr(host); 354 if (dst == NULL) 355 return (1); 356 doing_proxy = flags = expire_time = 0; 357 while (argc-- > 0) { 358 if (strncmp(argv[0], "temp", 4) == 0) { 359 struct timespec tp; 360 int max_age; 361 size_t len = sizeof(max_age); 362 363 clock_gettime(CLOCK_MONOTONIC, &tp); 364 if (sysctlbyname("net.link.ether.inet.max_age", 365 &max_age, &len, NULL, 0) != 0) 366 err(1, "sysctlbyname"); 367 expire_time = tp.tv_sec + max_age; 368 } else if (strncmp(argv[0], "pub", 3) == 0) { 369 flags |= RTF_ANNOUNCE; 370 doing_proxy = 1; 371 if (argc && strncmp(argv[1], "only", 3) == 0) { 372 /* 373 * Compatibility: in pre FreeBSD 8 times 374 * the "only" keyword used to mean that 375 * an ARP entry should be announced, but 376 * not installed into routing table. 377 */ 378 argc--; argv++; 379 } 380 } else if (strncmp(argv[0], "blackhole", 9) == 0) { 381 if (flags & RTF_REJECT) { 382 printf("Choose one of blackhole or reject, not both.\n"); 383 } 384 flags |= RTF_BLACKHOLE; 385 } else if (strncmp(argv[0], "reject", 6) == 0) { 386 if (flags & RTF_BLACKHOLE) { 387 printf("Choose one of blackhole or reject, not both.\n"); 388 } 389 flags |= RTF_REJECT; 390 } else if (strncmp(argv[0], "trail", 5) == 0) { 391 /* XXX deprecated and undocumented feature */ 392 printf("%s: Sending trailers is no longer supported\n", 393 host); 394 } 395 argv++; 396 } 397 ea = (struct ether_addr *)LLADDR(&sdl_m); 398 if (doing_proxy && !strcmp(eaddr, "auto")) { 399 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 400 printf("no interface found for %s\n", 401 inet_ntoa(dst->sin_addr)); 402 return (1); 403 } 404 sdl_m.sdl_alen = ETHER_ADDR_LEN; 405 } else { 406 struct ether_addr *ea1 = ether_aton(eaddr); 407 408 if (ea1 == NULL) { 409 warnx("invalid Ethernet address '%s'", eaddr); 410 return (1); 411 } else { 412 *ea = *ea1; 413 sdl_m.sdl_alen = ETHER_ADDR_LEN; 414 } 415 } 416 417 /* 418 * In the case a proxy-arp entry is being added for 419 * a remote end point, the RTF_ANNOUNCE flag in the 420 * RTM_GET command is an indication to the kernel 421 * routing code that the interface associated with 422 * the prefix route covering the local end of the 423 * PPP link should be returned, on which ARP applies. 424 */ 425 rtm = rtmsg(RTM_GET, dst, &sdl_m); 426 if (rtm == NULL) { 427 warn("%s", host); 428 return (1); 429 } 430 addr = (struct sockaddr_in *)(rtm + 1); 431 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 432 433 if ((sdl->sdl_family != AF_LINK) || 434 (rtm->rtm_flags & RTF_GATEWAY) || 435 !valid_type(sdl->sdl_type)) { 436 printf("cannot intuit interface index and type for %s\n", host); 437 return (1); 438 } 439 sdl_m.sdl_type = sdl->sdl_type; 440 sdl_m.sdl_index = sdl->sdl_index; 441 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 442 } 443 444 /* 445 * Display an individual arp entry 446 */ 447 static int 448 get(char *host) 449 { 450 struct sockaddr_in *addr; 451 452 addr = getaddr(host); 453 if (addr == NULL) 454 return (1); 455 if (0 == search(addr->sin_addr.s_addr, print_entry)) { 456 printf("%s (%s) -- no entry", 457 host, inet_ntoa(addr->sin_addr)); 458 if (rifname) 459 printf(" on %s", rifname); 460 printf("\n"); 461 return (1); 462 } 463 return (0); 464 } 465 466 /* 467 * Delete an arp entry 468 */ 469 static int 470 delete(char *host) 471 { 472 struct sockaddr_in *addr, *dst; 473 struct rt_msghdr *rtm; 474 struct sockaddr_dl *sdl; 475 struct sockaddr_dl sdl_m; 476 477 dst = getaddr(host); 478 if (dst == NULL) 479 return (1); 480 481 /* 482 * Perform a regular entry delete first. 483 */ 484 flags &= ~RTF_ANNOUNCE; 485 486 /* 487 * setup the data structure to notify the kernel 488 * it is the ARP entry the RTM_GET is interested 489 * in 490 */ 491 bzero(&sdl_m, sizeof(sdl_m)); 492 sdl_m.sdl_len = sizeof(sdl_m); 493 sdl_m.sdl_family = AF_LINK; 494 495 for (;;) { /* try twice */ 496 rtm = rtmsg(RTM_GET, dst, &sdl_m); 497 if (rtm == NULL) { 498 warn("%s", host); 499 return (1); 500 } 501 addr = (struct sockaddr_in *)(rtm + 1); 502 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 503 504 /* 505 * With the new L2/L3 restructure, the route 506 * returned is a prefix route. The important 507 * piece of information from the previous 508 * RTM_GET is the interface index. In the 509 * case of ECMP, the kernel will traverse 510 * the route group for the given entry. 511 */ 512 if (sdl->sdl_family == AF_LINK && 513 !(rtm->rtm_flags & RTF_GATEWAY) && 514 valid_type(sdl->sdl_type) ) { 515 addr->sin_addr.s_addr = dst->sin_addr.s_addr; 516 break; 517 } 518 519 /* 520 * Regualar entry delete failed, now check if there 521 * is a proxy-arp entry to remove. 522 */ 523 if (flags & RTF_ANNOUNCE) { 524 fprintf(stderr, "delete: cannot locate %s\n",host); 525 return (1); 526 } 527 528 flags |= RTF_ANNOUNCE; 529 } 530 rtm->rtm_flags |= RTF_LLDATA; 531 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 532 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 533 return (0); 534 } 535 return (1); 536 } 537 538 539 /* 540 * Search the arp table and do some action on matching entries 541 */ 542 static int 543 search(u_long addr, action_fn *action) 544 { 545 int mib[6]; 546 size_t needed; 547 char *lim, *buf, *next; 548 struct rt_msghdr *rtm; 549 struct sockaddr_in *sin2; 550 struct sockaddr_dl *sdl; 551 char ifname[IF_NAMESIZE]; 552 int st, found_entry = 0; 553 554 mib[0] = CTL_NET; 555 mib[1] = PF_ROUTE; 556 mib[2] = 0; 557 mib[3] = AF_INET; 558 mib[4] = NET_RT_FLAGS; 559 #ifdef RTF_LLINFO 560 mib[5] = RTF_LLINFO; 561 #else 562 mib[5] = 0; 563 #endif 564 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 565 err(1, "route-sysctl-estimate"); 566 if (needed == 0) /* empty table */ 567 return 0; 568 buf = NULL; 569 for (;;) { 570 buf = reallocf(buf, needed); 571 if (buf == NULL) 572 errx(1, "could not reallocate memory"); 573 st = sysctl(mib, 6, buf, &needed, NULL, 0); 574 if (st == 0 || errno != ENOMEM) 575 break; 576 needed += needed / 8; 577 } 578 if (st == -1) 579 err(1, "actual retrieval of routing table"); 580 lim = buf + needed; 581 for (next = buf; next < lim; next += rtm->rtm_msglen) { 582 rtm = (struct rt_msghdr *)next; 583 sin2 = (struct sockaddr_in *)(rtm + 1); 584 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 585 if (rifname && if_indextoname(sdl->sdl_index, ifname) && 586 strcmp(ifname, rifname)) 587 continue; 588 if (addr) { 589 if (addr != sin2->sin_addr.s_addr) 590 continue; 591 found_entry = 1; 592 } 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 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 iso88025_sockaddr_dl_data *trld; 610 struct if_nameindex *p; 611 int seg; 612 613 if (ifnameindex == NULL) 614 if ((ifnameindex = if_nameindex()) == NULL) 615 err(1, "cannot retrieve interface names"); 616 617 if (nflag == 0) 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 nflag = 1; 628 } 629 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 630 if (sdl->sdl_alen) { 631 if ((sdl->sdl_type == IFT_ETHER || 632 sdl->sdl_type == IFT_L2VLAN || 633 sdl->sdl_type == IFT_BRIDGE) && 634 sdl->sdl_alen == ETHER_ADDR_LEN) 635 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 636 else { 637 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 638 639 printf("%s", link_ntoa(sdl) + n); 640 } 641 } else 642 printf("(incomplete)"); 643 644 for (p = ifnameindex; p && ifnameindex->if_index && 645 ifnameindex->if_name; p++) { 646 if (p->if_index == sdl->sdl_index) { 647 printf(" on %s", p->if_name); 648 break; 649 } 650 } 651 652 if (rtm->rtm_rmx.rmx_expire == 0) 653 printf(" permanent"); 654 else { 655 static struct timespec tp; 656 if (tp.tv_sec == 0) 657 clock_gettime(CLOCK_MONOTONIC, &tp); 658 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0) 659 printf(" expires in %d seconds", (int)expire_time); 660 else 661 printf(" expired"); 662 } 663 if (rtm->rtm_flags & RTF_ANNOUNCE) 664 printf(" published"); 665 switch(sdl->sdl_type) { 666 case IFT_ETHER: 667 printf(" [ethernet]"); 668 break; 669 case IFT_ISO88025: 670 printf(" [token-ring]"); 671 trld = SDL_ISO88025(sdl); 672 if (trld->trld_rcf != 0) { 673 printf(" rt=%x", ntohs(trld->trld_rcf)); 674 for (seg = 0; 675 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 676 seg++) 677 printf(":%x", ntohs(*(trld->trld_route[seg]))); 678 } 679 break; 680 case IFT_FDDI: 681 printf(" [fddi]"); 682 break; 683 case IFT_ATM: 684 printf(" [atm]"); 685 break; 686 case IFT_L2VLAN: 687 printf(" [vlan]"); 688 break; 689 case IFT_IEEE1394: 690 printf(" [firewire]"); 691 break; 692 case IFT_BRIDGE: 693 printf(" [bridge]"); 694 break; 695 case IFT_INFINIBAND: 696 printf(" [infiniband]"); 697 break; 698 default: 699 break; 700 } 701 702 printf("\n"); 703 704 } 705 706 /* 707 * Nuke an arp entry 708 */ 709 static void 710 nuke_entry(struct sockaddr_dl *sdl __unused, 711 struct sockaddr_in *addr, struct rt_msghdr *rtm) 712 { 713 char ip[20]; 714 715 if (rtm->rtm_flags & RTF_PINNED) 716 return; 717 718 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 719 delete(ip); 720 } 721 722 static void 723 usage(void) 724 { 725 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 726 #ifndef FSTACK 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 #else 735 "usage: arp -p <f-stack proc_id> [-n] [-i interface] hostname", 736 " arp -p <f-stack proc_id> [-n] [-i interface] -a", 737 " arp -p <f-stack proc_id> -d hostname [pub]", 738 " arp -p <f-stack proc_id> -d [-i interface] -a", 739 " arp -p <f-stack proc_id> -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 740 " arp -p <f-stack proc_id> -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 741 " arp -p <f-stack proc_id> -f filename"); 742 #endif 743 #ifdef FSTACK 744 ff_ipc_exit(); 745 #endif 746 exit(1); 747 } 748 749 static struct rt_msghdr * 750 rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) 751 { 752 static int seq; 753 int rlen; 754 int l; 755 struct sockaddr_in so_mask, *som = &so_mask; 756 static int s = -1; 757 static pid_t pid; 758 759 static struct { 760 struct rt_msghdr m_rtm; 761 char m_space[512]; 762 } m_rtmsg; 763 764 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 765 char *cp = m_rtmsg.m_space; 766 767 if (s < 0) { /* first time: open socket, get pid */ 768 s = socket(PF_ROUTE, SOCK_RAW, 0); 769 if (s < 0) 770 err(1, "socket"); 771 pid = getpid(); 772 } 773 bzero(&so_mask, sizeof(so_mask)); 774 so_mask.sin_len = 8; 775 so_mask.sin_addr.s_addr = 0xffffffff; 776 777 errno = 0; 778 /* 779 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 780 * appropriately. 781 */ 782 if (cmd == RTM_DELETE) 783 goto doit; 784 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 785 rtm->rtm_flags = flags; 786 rtm->rtm_version = RTM_VERSION; 787 788 switch (cmd) { 789 default: 790 errx(1, "internal wrong cmd"); 791 case RTM_ADD: 792 rtm->rtm_addrs |= RTA_GATEWAY; 793 rtm->rtm_rmx.rmx_expire = expire_time; 794 rtm->rtm_inits = RTV_EXPIRE; 795 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 796 if (doing_proxy) { 797 rtm->rtm_addrs |= RTA_NETMASK; 798 rtm->rtm_flags &= ~RTF_HOST; 799 } 800 /* FALLTHROUGH */ 801 case RTM_GET: 802 rtm->rtm_addrs |= RTA_DST; 803 } 804 #define NEXTADDR(w, s) \ 805 do { \ 806 if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 807 bcopy((s), cp, sizeof(*(s))); \ 808 cp += SA_SIZE(s); \ 809 } \ 810 } while (0) 811 812 NEXTADDR(RTA_DST, dst); 813 NEXTADDR(RTA_GATEWAY, sdl); 814 NEXTADDR(RTA_NETMASK, som); 815 816 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 817 doit: 818 l = rtm->rtm_msglen; 819 rtm->rtm_seq = ++seq; 820 rtm->rtm_type = cmd; 821 #ifndef FSTACK 822 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 823 if (errno != ESRCH || cmd != RTM_DELETE) { 824 warn("writing to routing socket"); 825 return (NULL); 826 } 827 } 828 do { 829 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 830 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 831 #else 832 l = rtioctl((char *)&m_rtmsg, l, sizeof(m_rtmsg)); 833 #endif 834 if (l < 0) 835 warn("read from routing socket"); 836 return (rtm); 837 } 838 839 /* 840 * get_ether_addr - get the hardware address of an interface on the 841 * the same subnet as ipaddr. 842 */ 843 #define MAX_IFS 32 844 845 static int 846 get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 847 { 848 struct ifreq *ifr, *ifend, *ifp; 849 in_addr_t ina, mask; 850 struct sockaddr_dl *dla; 851 struct ifreq ifreq; 852 struct ifconf ifc; 853 struct ifreq ifs[MAX_IFS]; 854 int sock; 855 int retval = 0; 856 857 sock = socket(AF_INET, SOCK_DGRAM, 0); 858 if (sock < 0) 859 err(1, "socket"); 860 861 ifc.ifc_len = sizeof(ifs); 862 ifc.ifc_req = ifs; 863 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 864 warnx("ioctl(SIOCGIFCONF)"); 865 goto done; 866 } 867 868 #define NEXTIFR(i) \ 869 ((struct ifreq *)((char *)&(i)->ifr_addr \ 870 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 871 872 /* 873 * Scan through looking for an interface with an Internet 874 * address on the same subnet as `ipaddr'. 875 */ 876 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 877 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 878 if (ifr->ifr_addr.sa_family != AF_INET) 879 continue; 880 strncpy(ifreq.ifr_name, ifr->ifr_name, 881 sizeof(ifreq.ifr_name)); 882 ifreq.ifr_addr = ifr->ifr_addr; 883 /* 884 * Check that the interface is up, 885 * and not point-to-point or loopback. 886 */ 887 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 888 continue; 889 if ((ifreq.ifr_flags & 890 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 891 IFF_LOOPBACK|IFF_NOARP)) 892 != (IFF_UP|IFF_BROADCAST)) 893 continue; 894 /* 895 * Get its netmask and check that it's on 896 * the right subnet. 897 */ 898 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 899 continue; 900 mask = ((struct sockaddr_in *) 901 &ifreq.ifr_addr)->sin_addr.s_addr; 902 ina = ((struct sockaddr_in *) 903 &ifr->ifr_addr)->sin_addr.s_addr; 904 if ((ipaddr & mask) == (ina & mask)) 905 break; /* ok, we got it! */ 906 } 907 908 if (ifr >= ifend) 909 goto done; 910 911 /* 912 * Now scan through again looking for a link-level address 913 * for this interface. 914 */ 915 ifp = ifr; 916 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 917 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 918 ifr->ifr_addr.sa_family == AF_LINK) 919 break; 920 if (ifr >= ifend) 921 goto done; 922 /* 923 * Found the link-level address - copy it out 924 */ 925 dla = (struct sockaddr_dl *) &ifr->ifr_addr; 926 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 927 printf("using interface %s for proxy with address ", 928 ifp->ifr_name); 929 printf("%s\n", ether_ntoa(hwaddr)); 930 retval = dla->sdl_alen; 931 done: 932 close(sock); 933 return (retval); 934 } 935