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 return (rtn); 234 } 235 236 /* 237 * Process a file to set standard arp entries 238 */ 239 static int 240 file(char *name) 241 { 242 FILE *fp; 243 int i, retval; 244 char line[100], arg[5][50], *args[5], *p; 245 246 if ((fp = fopen(name, "r")) == NULL) 247 err(1, "cannot open %s", name); 248 args[0] = &arg[0][0]; 249 args[1] = &arg[1][0]; 250 args[2] = &arg[2][0]; 251 args[3] = &arg[3][0]; 252 args[4] = &arg[4][0]; 253 retval = 0; 254 while(fgets(line, sizeof(line), fp) != NULL) { 255 if ((p = strchr(line, '#')) != NULL) 256 *p = '\0'; 257 for (p = line; isblank(*p); p++); 258 if (*p == '\n' || *p == '\0') 259 continue; 260 i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], 261 arg[2], arg[3], arg[4]); 262 if (i < 2) { 263 warnx("bad line: %s", line); 264 retval = 1; 265 continue; 266 } 267 if (set(i, args)) 268 retval = 1; 269 } 270 fclose(fp); 271 return (retval); 272 } 273 274 /* 275 * Given a hostname, fills up a (static) struct sockaddr_in with 276 * the address of the host and returns a pointer to the 277 * structure. 278 */ 279 static struct sockaddr_in * 280 getaddr(char *host) 281 { 282 #ifndef FSTACK 283 struct hostent *hp; 284 #endif 285 static struct sockaddr_in reply; 286 287 bzero(&reply, sizeof(reply)); 288 reply.sin_len = sizeof(reply); 289 reply.sin_family = AF_INET; 290 reply.sin_addr.s_addr = inet_addr(host); 291 if (reply.sin_addr.s_addr == INADDR_NONE) { 292 #ifndef FSTACK 293 if (!(hp = gethostbyname(host))) { 294 warnx("%s: %s", host, hstrerror(h_errno)); 295 return (NULL); 296 } 297 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 298 sizeof reply.sin_addr); 299 #else 300 warnx("reply.sin_addr.s_addr == INADDR_NONE"); 301 #endif 302 } 303 return (&reply); 304 } 305 306 /* 307 * Returns true if the type is a valid one for ARP. 308 */ 309 static int 310 valid_type(int type) 311 { 312 313 switch (type) { 314 case IFT_ETHER: 315 case IFT_FDDI: 316 case IFT_INFINIBAND: 317 case IFT_ISO88023: 318 case IFT_ISO88024: 319 case IFT_ISO88025: 320 case IFT_L2VLAN: 321 case IFT_BRIDGE: 322 return (1); 323 default: 324 return (0); 325 } 326 } 327 328 /* 329 * Set an individual arp entry 330 */ 331 static int 332 set(int argc, char **argv) 333 { 334 struct sockaddr_in *addr; 335 struct sockaddr_in *dst; /* what are we looking for */ 336 struct sockaddr_dl *sdl; 337 struct rt_msghdr *rtm; 338 struct ether_addr *ea; 339 char *host = argv[0], *eaddr = argv[1]; 340 struct sockaddr_dl sdl_m; 341 342 argc -= 2; 343 argv += 2; 344 345 bzero(&sdl_m, sizeof(sdl_m)); 346 sdl_m.sdl_len = sizeof(sdl_m); 347 sdl_m.sdl_family = AF_LINK; 348 349 dst = getaddr(host); 350 if (dst == NULL) 351 return (1); 352 doing_proxy = flags = expire_time = 0; 353 while (argc-- > 0) { 354 if (strncmp(argv[0], "temp", 4) == 0) { 355 struct timespec tp; 356 int max_age; 357 size_t len = sizeof(max_age); 358 359 clock_gettime(CLOCK_MONOTONIC, &tp); 360 if (sysctlbyname("net.link.ether.inet.max_age", 361 &max_age, &len, NULL, 0) != 0) 362 err(1, "sysctlbyname"); 363 expire_time = tp.tv_sec + max_age; 364 } else if (strncmp(argv[0], "pub", 3) == 0) { 365 flags |= RTF_ANNOUNCE; 366 doing_proxy = 1; 367 if (argc && strncmp(argv[1], "only", 3) == 0) { 368 /* 369 * Compatibility: in pre FreeBSD 8 times 370 * the "only" keyword used to mean that 371 * an ARP entry should be announced, but 372 * not installed into routing table. 373 */ 374 argc--; argv++; 375 } 376 } else if (strncmp(argv[0], "blackhole", 9) == 0) { 377 if (flags & RTF_REJECT) { 378 printf("Choose one of blackhole or reject, not both.\n"); 379 } 380 flags |= RTF_BLACKHOLE; 381 } else if (strncmp(argv[0], "reject", 6) == 0) { 382 if (flags & RTF_BLACKHOLE) { 383 printf("Choose one of blackhole or reject, not both.\n"); 384 } 385 flags |= RTF_REJECT; 386 } else if (strncmp(argv[0], "trail", 5) == 0) { 387 /* XXX deprecated and undocumented feature */ 388 printf("%s: Sending trailers is no longer supported\n", 389 host); 390 } 391 argv++; 392 } 393 ea = (struct ether_addr *)LLADDR(&sdl_m); 394 if (doing_proxy && !strcmp(eaddr, "auto")) { 395 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 396 printf("no interface found for %s\n", 397 inet_ntoa(dst->sin_addr)); 398 return (1); 399 } 400 sdl_m.sdl_alen = ETHER_ADDR_LEN; 401 } else { 402 struct ether_addr *ea1 = ether_aton(eaddr); 403 404 if (ea1 == NULL) { 405 warnx("invalid Ethernet address '%s'", eaddr); 406 return (1); 407 } else { 408 *ea = *ea1; 409 sdl_m.sdl_alen = ETHER_ADDR_LEN; 410 } 411 } 412 413 /* 414 * In the case a proxy-arp entry is being added for 415 * a remote end point, the RTF_ANNOUNCE flag in the 416 * RTM_GET command is an indication to the kernel 417 * routing code that the interface associated with 418 * the prefix route covering the local end of the 419 * PPP link should be returned, on which ARP applies. 420 */ 421 rtm = rtmsg(RTM_GET, dst, &sdl_m); 422 if (rtm == NULL) { 423 warn("%s", host); 424 return (1); 425 } 426 addr = (struct sockaddr_in *)(rtm + 1); 427 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 428 429 if ((sdl->sdl_family != AF_LINK) || 430 (rtm->rtm_flags & RTF_GATEWAY) || 431 !valid_type(sdl->sdl_type)) { 432 printf("cannot intuit interface index and type for %s\n", host); 433 return (1); 434 } 435 sdl_m.sdl_type = sdl->sdl_type; 436 sdl_m.sdl_index = sdl->sdl_index; 437 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 438 } 439 440 /* 441 * Display an individual arp entry 442 */ 443 static int 444 get(char *host) 445 { 446 struct sockaddr_in *addr; 447 448 addr = getaddr(host); 449 if (addr == NULL) 450 return (1); 451 if (0 == search(addr->sin_addr.s_addr, print_entry)) { 452 printf("%s (%s) -- no entry", 453 host, inet_ntoa(addr->sin_addr)); 454 if (rifname) 455 printf(" on %s", rifname); 456 printf("\n"); 457 return (1); 458 } 459 return (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 struct sockaddr_dl sdl_m; 472 473 dst = getaddr(host); 474 if (dst == NULL) 475 return (1); 476 477 /* 478 * Perform a regular entry delete first. 479 */ 480 flags &= ~RTF_ANNOUNCE; 481 482 /* 483 * setup the data structure to notify the kernel 484 * it is the ARP entry the RTM_GET is interested 485 * in 486 */ 487 bzero(&sdl_m, sizeof(sdl_m)); 488 sdl_m.sdl_len = sizeof(sdl_m); 489 sdl_m.sdl_family = AF_LINK; 490 491 for (;;) { /* try twice */ 492 rtm = rtmsg(RTM_GET, dst, &sdl_m); 493 if (rtm == NULL) { 494 warn("%s", host); 495 return (1); 496 } 497 addr = (struct sockaddr_in *)(rtm + 1); 498 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 499 500 /* 501 * With the new L2/L3 restructure, the route 502 * returned is a prefix route. The important 503 * piece of information from the previous 504 * RTM_GET is the interface index. In the 505 * case of ECMP, the kernel will traverse 506 * the route group for the given entry. 507 */ 508 if (sdl->sdl_family == AF_LINK && 509 !(rtm->rtm_flags & RTF_GATEWAY) && 510 valid_type(sdl->sdl_type) ) { 511 addr->sin_addr.s_addr = dst->sin_addr.s_addr; 512 break; 513 } 514 515 /* 516 * Regualar entry delete failed, now check if there 517 * is a proxy-arp entry to remove. 518 */ 519 if (flags & RTF_ANNOUNCE) { 520 fprintf(stderr, "delete: cannot locate %s\n",host); 521 return (1); 522 } 523 524 flags |= RTF_ANNOUNCE; 525 } 526 rtm->rtm_flags |= RTF_LLDATA; 527 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 528 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 529 return (0); 530 } 531 return (1); 532 } 533 534 535 /* 536 * Search the arp table and do some action on matching entries 537 */ 538 static int 539 search(u_long addr, action_fn *action) 540 { 541 int mib[6]; 542 size_t needed; 543 char *lim, *buf, *next; 544 struct rt_msghdr *rtm; 545 struct sockaddr_in *sin2; 546 struct sockaddr_dl *sdl; 547 char ifname[IF_NAMESIZE]; 548 int st, found_entry = 0; 549 550 mib[0] = CTL_NET; 551 mib[1] = PF_ROUTE; 552 mib[2] = 0; 553 mib[3] = AF_INET; 554 mib[4] = NET_RT_FLAGS; 555 #ifdef RTF_LLINFO 556 mib[5] = RTF_LLINFO; 557 #else 558 mib[5] = 0; 559 #endif 560 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 561 err(1, "route-sysctl-estimate"); 562 if (needed == 0) /* empty table */ 563 return 0; 564 buf = NULL; 565 for (;;) { 566 buf = reallocf(buf, needed); 567 if (buf == NULL) 568 errx(1, "could not reallocate memory"); 569 st = sysctl(mib, 6, buf, &needed, NULL, 0); 570 if (st == 0 || errno != ENOMEM) 571 break; 572 needed += needed / 8; 573 } 574 if (st == -1) 575 err(1, "actual retrieval of routing table"); 576 lim = buf + needed; 577 for (next = buf; next < lim; next += rtm->rtm_msglen) { 578 rtm = (struct rt_msghdr *)next; 579 sin2 = (struct sockaddr_in *)(rtm + 1); 580 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 581 if (rifname && if_indextoname(sdl->sdl_index, ifname) && 582 strcmp(ifname, rifname)) 583 continue; 584 if (addr) { 585 if (addr != sin2->sin_addr.s_addr) 586 continue; 587 found_entry = 1; 588 } 589 (*action)(sdl, sin2, rtm); 590 } 591 free(buf); 592 return (found_entry); 593 } 594 595 /* 596 * Display an arp entry 597 */ 598 599 static void 600 print_entry(struct sockaddr_dl *sdl, 601 struct sockaddr_in *addr, struct rt_msghdr *rtm) 602 { 603 const char *host; 604 struct hostent *hp; 605 struct iso88025_sockaddr_dl_data *trld; 606 struct if_nameindex *p; 607 int seg; 608 609 if (ifnameindex == NULL) 610 if ((ifnameindex = if_nameindex()) == NULL) 611 err(1, "cannot retrieve interface names"); 612 613 if (nflag == 0) 614 hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 615 sizeof addr->sin_addr, AF_INET); 616 else 617 hp = 0; 618 if (hp) 619 host = hp->h_name; 620 else { 621 host = "?"; 622 if (h_errno == TRY_AGAIN) 623 nflag = 1; 624 } 625 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 626 if (sdl->sdl_alen) { 627 if ((sdl->sdl_type == IFT_ETHER || 628 sdl->sdl_type == IFT_L2VLAN || 629 sdl->sdl_type == IFT_BRIDGE) && 630 sdl->sdl_alen == ETHER_ADDR_LEN) 631 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 632 else { 633 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 634 635 printf("%s", link_ntoa(sdl) + n); 636 } 637 } else 638 printf("(incomplete)"); 639 640 for (p = ifnameindex; p && ifnameindex->if_index && 641 ifnameindex->if_name; p++) { 642 if (p->if_index == sdl->sdl_index) { 643 printf(" on %s", p->if_name); 644 break; 645 } 646 } 647 648 if (rtm->rtm_rmx.rmx_expire == 0) 649 printf(" permanent"); 650 else { 651 static struct timespec tp; 652 if (tp.tv_sec == 0) 653 clock_gettime(CLOCK_MONOTONIC, &tp); 654 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0) 655 printf(" expires in %d seconds", (int)expire_time); 656 else 657 printf(" expired"); 658 } 659 if (rtm->rtm_flags & RTF_ANNOUNCE) 660 printf(" published"); 661 switch(sdl->sdl_type) { 662 case IFT_ETHER: 663 printf(" [ethernet]"); 664 break; 665 case IFT_ISO88025: 666 printf(" [token-ring]"); 667 trld = SDL_ISO88025(sdl); 668 if (trld->trld_rcf != 0) { 669 printf(" rt=%x", ntohs(trld->trld_rcf)); 670 for (seg = 0; 671 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 672 seg++) 673 printf(":%x", ntohs(*(trld->trld_route[seg]))); 674 } 675 break; 676 case IFT_FDDI: 677 printf(" [fddi]"); 678 break; 679 case IFT_ATM: 680 printf(" [atm]"); 681 break; 682 case IFT_L2VLAN: 683 printf(" [vlan]"); 684 break; 685 case IFT_IEEE1394: 686 printf(" [firewire]"); 687 break; 688 case IFT_BRIDGE: 689 printf(" [bridge]"); 690 break; 691 case IFT_INFINIBAND: 692 printf(" [infiniband]"); 693 break; 694 default: 695 break; 696 } 697 698 printf("\n"); 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 usage(void) 720 { 721 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 722 #ifndef FSTACK 723 "usage: arp [-n] [-i interface] hostname", 724 " arp [-n] [-i interface] -a", 725 " arp -d hostname [pub]", 726 " arp -d [-i interface] -a", 727 " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 728 " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 729 " arp -f filename"); 730 #else 731 "usage: arp -p <f-stack proc_id> [-n] [-i interface] hostname", 732 " arp -p <f-stack proc_id> [-n] [-i interface] -a", 733 " arp -p <f-stack proc_id> -d hostname [pub]", 734 " arp -p <f-stack proc_id> -d [-i interface] -a", 735 " arp -p <f-stack proc_id> -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 736 " arp -p <f-stack proc_id> -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 737 " arp -p <f-stack proc_id> -f filename"); 738 #endif 739 exit(1); 740 } 741 742 static struct rt_msghdr * 743 rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) 744 { 745 static int seq; 746 int rlen; 747 int l; 748 struct sockaddr_in so_mask, *som = &so_mask; 749 static int s = -1; 750 static pid_t pid; 751 752 static struct { 753 struct rt_msghdr m_rtm; 754 char m_space[512]; 755 } m_rtmsg; 756 757 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 758 char *cp = m_rtmsg.m_space; 759 760 if (s < 0) { /* first time: open socket, get pid */ 761 s = socket(PF_ROUTE, SOCK_RAW, 0); 762 if (s < 0) 763 err(1, "socket"); 764 pid = getpid(); 765 } 766 bzero(&so_mask, sizeof(so_mask)); 767 so_mask.sin_len = 8; 768 so_mask.sin_addr.s_addr = 0xffffffff; 769 770 errno = 0; 771 /* 772 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 773 * appropriately. 774 */ 775 if (cmd == RTM_DELETE) 776 goto doit; 777 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 778 rtm->rtm_flags = flags; 779 rtm->rtm_version = RTM_VERSION; 780 781 switch (cmd) { 782 default: 783 errx(1, "internal wrong cmd"); 784 case RTM_ADD: 785 rtm->rtm_addrs |= RTA_GATEWAY; 786 rtm->rtm_rmx.rmx_expire = expire_time; 787 rtm->rtm_inits = RTV_EXPIRE; 788 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 789 if (doing_proxy) { 790 rtm->rtm_addrs |= RTA_NETMASK; 791 rtm->rtm_flags &= ~RTF_HOST; 792 } 793 /* FALLTHROUGH */ 794 case RTM_GET: 795 rtm->rtm_addrs |= RTA_DST; 796 } 797 #define NEXTADDR(w, s) \ 798 do { \ 799 if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 800 bcopy((s), cp, sizeof(*(s))); \ 801 cp += SA_SIZE(s); \ 802 } \ 803 } while (0) 804 805 NEXTADDR(RTA_DST, dst); 806 NEXTADDR(RTA_GATEWAY, sdl); 807 NEXTADDR(RTA_NETMASK, som); 808 809 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 810 doit: 811 l = rtm->rtm_msglen; 812 rtm->rtm_seq = ++seq; 813 rtm->rtm_type = cmd; 814 #ifndef FSTACK 815 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 816 if (errno != ESRCH || cmd != RTM_DELETE) { 817 warn("writing to routing socket"); 818 return (NULL); 819 } 820 } 821 do { 822 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 823 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 824 #else 825 l = rtioctl((char *)&m_rtmsg, l, sizeof(m_rtmsg)); 826 #endif 827 if (l < 0) 828 warn("read from routing socket"); 829 return (rtm); 830 } 831 832 /* 833 * get_ether_addr - get the hardware address of an interface on the 834 * the same subnet as ipaddr. 835 */ 836 #define MAX_IFS 32 837 838 static int 839 get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 840 { 841 struct ifreq *ifr, *ifend, *ifp; 842 in_addr_t ina, mask; 843 struct sockaddr_dl *dla; 844 struct ifreq ifreq; 845 struct ifconf ifc; 846 struct ifreq ifs[MAX_IFS]; 847 int sock; 848 int retval = 0; 849 850 sock = socket(AF_INET, SOCK_DGRAM, 0); 851 if (sock < 0) 852 err(1, "socket"); 853 854 ifc.ifc_len = sizeof(ifs); 855 ifc.ifc_req = ifs; 856 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 857 warnx("ioctl(SIOCGIFCONF)"); 858 goto done; 859 } 860 861 #define NEXTIFR(i) \ 862 ((struct ifreq *)((char *)&(i)->ifr_addr \ 863 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 864 865 /* 866 * Scan through looking for an interface with an Internet 867 * address on the same subnet as `ipaddr'. 868 */ 869 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 870 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 871 if (ifr->ifr_addr.sa_family != AF_INET) 872 continue; 873 strncpy(ifreq.ifr_name, ifr->ifr_name, 874 sizeof(ifreq.ifr_name)); 875 ifreq.ifr_addr = ifr->ifr_addr; 876 /* 877 * Check that the interface is up, 878 * and not point-to-point or loopback. 879 */ 880 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 881 continue; 882 if ((ifreq.ifr_flags & 883 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 884 IFF_LOOPBACK|IFF_NOARP)) 885 != (IFF_UP|IFF_BROADCAST)) 886 continue; 887 /* 888 * Get its netmask and check that it's on 889 * the right subnet. 890 */ 891 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 892 continue; 893 mask = ((struct sockaddr_in *) 894 &ifreq.ifr_addr)->sin_addr.s_addr; 895 ina = ((struct sockaddr_in *) 896 &ifr->ifr_addr)->sin_addr.s_addr; 897 if ((ipaddr & mask) == (ina & mask)) 898 break; /* ok, we got it! */ 899 } 900 901 if (ifr >= ifend) 902 goto done; 903 904 /* 905 * Now scan through again looking for a link-level address 906 * for this interface. 907 */ 908 ifp = ifr; 909 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 910 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 911 ifr->ifr_addr.sa_family == AF_LINK) 912 break; 913 if (ifr >= ifend) 914 goto done; 915 /* 916 * Found the link-level address - copy it out 917 */ 918 dla = (struct sockaddr_dl *) &ifr->ifr_addr; 919 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 920 printf("using interface %s for proxy with address ", 921 ifp->ifr_name); 922 printf("%s\n", ether_ntoa(hwaddr)); 923 retval = dla->sdl_alen; 924 done: 925 close(sock); 926 return (retval); 927 } 928