1/* 2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * Copyright (c) 1984, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * This code is derived from software contributed to Berkeley by 34 * Sun Microsystems, Inc. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 4. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61#if 0 62#ifndef lint 63static char const copyright[] = 64"@(#) Copyright (c) 1984, 1993\n\ 65 The Regents of the University of California. All rights reserved.\n"; 66#endif /* not lint */ 67#endif 68 69/* 70 * arp - display, set, and delete arp table entries 71 */ 72 73 74#include <sys/param.h> 75#include <sys/file.h> 76#include <sys/socket.h> 77#include <sys/sockio.h> 78#include <sys/sysctl.h> 79#include <sys/ioctl.h> 80#include <sys/time.h> 81 82#include <net/if.h> 83#include <net/if_dl.h> 84#include <net/if_types.h> 85#include <net/route.h> 86#if 0 87#include <net/iso88025.h> 88#endif 89 90#include <netinet/in.h> 91#include <netinet/if_ether.h> 92 93#include <arpa/inet.h> 94 95#include <ctype.h> 96#include <err.h> 97#include <errno.h> 98#include <netdb.h> 99#include <nlist.h> 100#include <paths.h> 101#include <stdio.h> 102#include <stdlib.h> 103#include <string.h> 104#include <strings.h> 105#include <unistd.h> 106 107typedef void (action_fn)(struct sockaddr_dl *sdl, 108 struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); 109typedef void (action_ext_fn)(struct sockaddr_dl *sdl, 110 struct sockaddr_inarp *s_in, struct rt_msghdr_ext *rtm); 111 112static int search(in_addr_t addr, action_fn *action); 113static int search_ext(in_addr_t addr, action_ext_fn *action); 114static action_fn print_entry; 115static action_fn nuke_entry; 116static action_ext_fn print_entry_ext; 117 118static char *print_lladdr(struct sockaddr_dl *); 119static int delete(char *host, int do_proxy); 120static void usage(void); 121static int set(int argc, char **argv); 122static int get(char *host); 123static int file(char *name); 124static struct rt_msghdr *rtmsg(int cmd, 125 struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); 126static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); 127static struct sockaddr_inarp *getaddr(char *host); 128static int valid_type(int type); 129static char *sec2str(time_t); 130 131static int nflag; /* no reverse dns lookups */ 132static int xflag; /* extended link-layer reachability information */ 133static char *rifname; 134 135static int expire_time, flags, doing_proxy, proxy_only; 136 137static char *boundif = NULL; 138static unsigned int ifscope = 0; 139 140/* which function we're supposed to do */ 141#define F_GET 1 142#define F_SET 2 143#define F_FILESET 3 144#define F_REPLACE 4 145#define F_DELETE 5 146 147#ifndef SA_SIZE 148#define SA_SIZE(sa) \ 149 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ 150 sizeof(uint32_t) : \ 151 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) ) 152#endif 153 154#define SETFUNC(f) { if (func) usage(); func = (f); } 155 156 157int 158main(int argc, char *argv[]) 159{ 160 int ch, func = 0; 161 int rtn = 0; 162 int aflag = 0; /* do it for all entries */ 163 int lflag = 0; 164 uint32_t ifindex = 0; 165 166 while ((ch = getopt(argc, argv, "andflsSi:x")) != -1) 167 switch((char)ch) { 168 case 'a': 169 aflag = 1; 170 break; 171 case 'd': 172 SETFUNC(F_DELETE); 173 break; 174 case 'n': 175 nflag = 1; 176 break; 177 case 'l': 178 lflag = 1; 179 break; 180 case 'S': 181 SETFUNC(F_REPLACE); 182 break; 183 case 's': 184 SETFUNC(F_SET); 185 break; 186 case 'f' : 187 SETFUNC(F_FILESET); 188 break; 189 case 'i': 190 rifname = optarg; 191 break; 192 case 'x': 193 xflag = 1; 194 lflag = 1; 195 break; 196 case '?': 197 default: 198 usage(); 199 } 200 argc -= optind; 201 argv += optind; 202 203 if (!func) 204 func = F_GET; 205 if (rifname) { 206 if (func != F_GET && !(func == F_DELETE && aflag)) 207 errx(1, "-i not applicable to this operation"); 208 if ((ifindex = if_nametoindex(rifname)) == 0) { 209 if (errno == ENXIO) 210 errx(1, "interface %s does not exist", rifname); 211 else 212 err(1, "if_nametoindex(%s)", rifname); 213 } 214 } 215 switch (func) { 216 case F_GET: 217 if (aflag) { 218 if (argc != 0) 219 usage(); 220 if (lflag) { 221 printf("%-23s %-17s %-9.9s %-9.9s %8.8s %4s " 222 "%4s", "Neighbor", 223 "Linklayer Address", "Expire(O)", 224 "Expire(I)", "Netif", "Refs", "Prbs"); 225 if (xflag) 226 printf(" %-7.7s %-7.7s %-7.7s", 227 "RSSI", "LQM", "NPM"); 228 printf("\n"); 229 search_ext(0, print_entry_ext); 230 } else { 231 search(0, print_entry); 232 } 233 } else { 234 if (argc != 1) 235 usage(); 236 rtn = get(argv[0]); 237 } 238 break; 239 case F_SET: 240 case F_REPLACE: 241 if (argc < 2 || argc > 6) 242 usage(); 243 if (func == F_REPLACE) 244 (void)delete(argv[0], 0); 245 rtn = set(argc, argv) ? 1 : 0; 246 break; 247 case F_DELETE: 248 if (aflag) { 249 if (argc != 0) 250 usage(); 251 search(0, nuke_entry); 252 } else { 253 int do_proxy = 0; 254 int i; 255 256 for (i = 1; i < argc; i++) { 257 if (strncmp(argv[i], "pub", sizeof("pub")) == 0) { 258 do_proxy = SIN_PROXY; 259 } else if (strncmp(argv[i], "ifscope", sizeof("ifscope")) == 0) { 260 if (i + 1 >= argc) { 261 printf("ifscope needs an interface parameter\n"); 262 return (1); 263 } 264 boundif = argv[++i]; 265 if ((ifscope = if_nametoindex(boundif)) == 0) 266 errx(1, "ifscope has bad interface name: %s", boundif); 267 } else { 268 usage(); 269 } 270 } 271 if (i > argc) 272 usage(); 273 rtn = delete(argv[0], do_proxy); 274 } 275 break; 276 case F_FILESET: 277 if (argc != 1) 278 usage(); 279 rtn = file(argv[0]); 280 break; 281 } 282 283 return (rtn); 284} 285 286/* 287 * Process a file to set standard arp entries 288 */ 289static int 290file(char *name) 291{ 292 FILE *fp; 293 int i, retval; 294 char line[128], arg[7][50], *args[7], *p; 295 296 if ((fp = fopen(name, "r")) == NULL) 297 err(1, "cannot open %s", name); 298 args[0] = &arg[0][0]; 299 args[1] = &arg[1][0]; 300 args[2] = &arg[2][0]; 301 args[3] = &arg[3][0]; 302 args[4] = &arg[4][0]; 303 args[5] = &arg[5][0]; 304 args[6] = &arg[6][0]; 305 retval = 0; 306 while(fgets(line, sizeof(line), fp) != NULL) { 307 if ((p = strchr(line, '#')) != NULL) 308 *p = '\0'; 309 for (p = line; isblank(*p); p++); 310 if (*p == '\n' || *p == '\0') 311 continue; 312 i = sscanf(p, "%49s %49s %49s %49s %49s %49s %49s", arg[0], arg[1], 313 arg[2], arg[3], arg[4], arg[5], arg[6]); 314 if (i < 2) { 315 warnx("bad line: %s", line); 316 retval = 1; 317 continue; 318 } 319 if (set(i, args)) 320 retval = 1; 321 } 322 fclose(fp); 323 return (retval); 324} 325 326/* 327 * Given a hostname, fills up a (static) struct sockaddr_inarp with 328 * the address of the host and returns a pointer to the 329 * structure. 330 */ 331static struct sockaddr_inarp * 332getaddr(char *host) 333{ 334 struct hostent *hp; 335 static struct sockaddr_inarp reply; 336 337 bzero(&reply, sizeof(reply)); 338 reply.sin_len = sizeof(reply); 339 reply.sin_family = AF_INET; 340 reply.sin_addr.s_addr = inet_addr(host); 341 if (reply.sin_addr.s_addr == INADDR_NONE) { 342 if (!(hp = gethostbyname(host))) { 343 warnx("%s: %s", host, hstrerror(h_errno)); 344 return (NULL); 345 } 346 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 347 sizeof reply.sin_addr); 348 } 349 return (&reply); 350} 351 352/* 353 * Returns true if the type is a valid one for ARP. 354 */ 355static int 356valid_type(int type) 357{ 358 359 switch (type) { 360 case IFT_ETHER: 361 case IFT_FDDI: 362 case IFT_ISO88023: 363 case IFT_ISO88024: 364#if 0 365 case IFT_ISO88025: 366#endif 367 case IFT_L2VLAN: 368#ifdef IFT_BRIDGE 369 case IFT_BRIDGE: 370#endif 371 return (1); 372 default: 373 return (0); 374 } 375} 376 377/* 378 * Set an individual arp entry 379 */ 380static int 381set(int argc, char **argv) 382{ 383 struct sockaddr_inarp *addr; 384 struct sockaddr_inarp *dst; /* what are we looking for */ 385 struct sockaddr_dl *sdl; 386 struct rt_msghdr *rtm; 387 struct ether_addr *ea; 388 char *host = argv[0], *eaddr = argv[1]; 389 struct sockaddr_dl sdl_m; 390 391 argc -= 2; 392 argv += 2; 393 394 bzero(&sdl_m, sizeof(sdl_m)); 395 sdl_m.sdl_len = sizeof(sdl_m); 396 sdl_m.sdl_family = AF_LINK; 397 398 dst = getaddr(host); 399 if (dst == NULL) 400 return (1); 401 doing_proxy = flags = proxy_only = expire_time = 0; 402 boundif = NULL; 403 ifscope = 0; 404 while (argc-- > 0) { 405 if (strncmp(argv[0], "temp", sizeof("temp")) == 0) { 406 struct timeval tv; 407 gettimeofday(&tv, 0); 408 expire_time = tv.tv_sec + 20 * 60; 409 } else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) { 410 flags |= RTF_ANNOUNCE; 411 doing_proxy = 1; 412 if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) { 413 proxy_only = 1; 414 dst->sin_other = SIN_PROXY; 415 argc--; argv++; 416 } 417 } else if (strncmp(argv[0], "blackhole", sizeof("blackhole")) == 0) { 418 flags |= RTF_BLACKHOLE; 419 } else if (strncmp(argv[0], "reject", sizeof("reject")) == 0) { 420 flags |= RTF_REJECT; 421 } else if (strncmp(argv[0], "trail", sizeof("trail")) == 0) { 422 /* XXX deprecated and undocumented feature */ 423 printf("%s: Sending trailers is no longer supported\n", 424 host); 425 } else if (strncmp(argv[0], "ifscope", sizeof("ifscope")) == 0) { 426 if (argc < 1) { 427 printf("ifscope needs an interface parameter\n"); 428 return (1); 429 } 430 boundif = argv[1]; 431 if ((ifscope = if_nametoindex(boundif)) == 0) 432 errx(1, "ifscope has bad interface name: %s", boundif); 433 argc--; argv++; 434 } 435 argv++; 436 } 437 ea = (struct ether_addr *)LLADDR(&sdl_m); 438 if (doing_proxy && !strcmp(eaddr, "auto")) { 439 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 440 printf("no interface found for %s\n", 441 inet_ntoa(dst->sin_addr)); 442 return (1); 443 } 444 sdl_m.sdl_alen = ETHER_ADDR_LEN; 445 } else { 446 struct ether_addr *ea1 = ether_aton(eaddr); 447 448 if (ea1 == NULL) { 449 warnx("invalid Ethernet address '%s'", eaddr); 450 return (1); 451 } else { 452 *ea = *ea1; 453 sdl_m.sdl_alen = ETHER_ADDR_LEN; 454 } 455 } 456 for (;;) { /* try at most twice */ 457 rtm = rtmsg(RTM_GET, dst, &sdl_m); 458 if (rtm == NULL) { 459 warn("%s", host); 460 return (1); 461 } 462 addr = (struct sockaddr_inarp *)(rtm + 1); 463 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 464 if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) 465 break; 466 if (sdl->sdl_family == AF_LINK && 467 (rtm->rtm_flags & RTF_LLINFO) && 468 !(rtm->rtm_flags & RTF_GATEWAY) && 469 valid_type(sdl->sdl_type) ) 470 break; 471 /* 472 * If we asked for a scope entry and did not get one or 473 * did not asked for a scope entry and got one, we can 474 * proceed. 475 */ 476 if ((ifscope != 0) != (rtm->rtm_flags & RTF_IFSCOPE)) 477 break; 478 if (doing_proxy == 0) { 479 printf("set: can only proxy for %s\n", host); 480 return (1); 481 } 482 if (dst->sin_other & SIN_PROXY) { 483 printf("set: proxy entry exists for non 802 device\n"); 484 return (1); 485 } 486 dst->sin_other = SIN_PROXY; 487 proxy_only = 1; 488 } 489 490 if (sdl->sdl_family != AF_LINK) { 491 printf("cannot intuit interface index and type for %s\n", host); 492 return (1); 493 } 494 sdl_m.sdl_type = sdl->sdl_type; 495 sdl_m.sdl_index = sdl->sdl_index; 496 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 497} 498 499/* 500 * Display an individual arp entry 501 */ 502static int 503get(char *host) 504{ 505 struct sockaddr_inarp *addr; 506 507 addr = getaddr(host); 508 if (addr == NULL) 509 return (1); 510 if (0 == search(addr->sin_addr.s_addr, print_entry)) { 511 printf("%s (%s) -- no entry", 512 host, inet_ntoa(addr->sin_addr)); 513 if (rifname) 514 printf(" on %s", rifname); 515 printf("\n"); 516 return (1); 517 } 518 return (0); 519} 520 521/* 522 * Delete an arp entry 523 */ 524static int 525delete(char *host, int do_proxy) 526{ 527 struct sockaddr_inarp *addr, *dst; 528 struct rt_msghdr *rtm; 529 struct sockaddr_dl *sdl; 530 531 dst = getaddr(host); 532 if (dst == NULL) 533 return (1); 534 dst->sin_other = do_proxy; 535 for (;;) { /* try twice */ 536 rtm = rtmsg(RTM_GET, dst, NULL); 537 if (rtm == NULL) { 538 warn("%s", host); 539 return (1); 540 } 541 addr = (struct sockaddr_inarp *)(rtm + 1); 542 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 543 if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && 544 sdl->sdl_family == AF_LINK && 545 (rtm->rtm_flags & RTF_LLINFO) && 546 !(rtm->rtm_flags & RTF_GATEWAY) && 547 valid_type(sdl->sdl_type) ) 548 break; /* found it */ 549 if (dst->sin_other & SIN_PROXY) { 550 fprintf(stderr, "delete: cannot locate %s\n",host); 551 return (1); 552 } 553 dst->sin_other = SIN_PROXY; 554 } 555 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 556 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 557 return (0); 558 } 559 return (1); 560} 561 562/* 563 * Search the arp table and do some action on matching entries 564 */ 565static int 566search(in_addr_t addr, action_fn *action) 567{ 568 int mib[6]; 569 size_t needed; 570 char *lim, *buf, *newbuf, *next; 571 struct rt_msghdr *rtm; 572 struct sockaddr_inarp *sin2; 573 struct sockaddr_dl *sdl; 574 char ifname[IF_NAMESIZE]; 575 int st, found_entry = 0; 576 577 mib[0] = CTL_NET; 578 mib[1] = PF_ROUTE; 579 mib[2] = 0; 580 mib[3] = AF_INET; 581 mib[4] = NET_RT_FLAGS; 582 mib[5] = RTF_LLINFO; 583 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 584 err(1, "route-sysctl-estimate"); 585 if (needed == 0) /* empty table */ 586 return 0; 587 buf = NULL; 588 for (;;) { 589 newbuf = realloc(buf, needed); 590 if (newbuf == NULL) { 591 if (buf != NULL) 592 free(buf); 593 errx(1, "could not reallocate memory"); 594 } 595 buf = newbuf; 596 st = sysctl(mib, 6, buf, &needed, NULL, 0); 597 if (st == 0 || errno != ENOMEM) 598 break; 599 needed += needed / 8; 600 } 601 if (st == -1) 602 err(1, "actual retrieval of routing table"); 603 lim = buf + needed; 604 for (next = buf; next < lim; next += rtm->rtm_msglen) { 605 rtm = (struct rt_msghdr *)next; 606 sin2 = (struct sockaddr_inarp *)(rtm + 1); 607 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 608 if (rifname && if_indextoname(sdl->sdl_index, ifname) && 609 strcmp(ifname, rifname)) 610 continue; 611 if (addr) { 612 if (addr != sin2->sin_addr.s_addr) 613 continue; 614 found_entry = 1; 615 } 616 (*action)(sdl, sin2, rtm); 617 } 618 free(buf); 619 return (found_entry); 620} 621 622/* 623 * Stolen and adapted from ifconfig 624 */ 625static char * 626print_lladdr(struct sockaddr_dl *sdl) 627{ 628 static char buf[256]; 629 char *cp; 630 int n, bufsize = sizeof (buf), p = 0; 631 632 bzero(buf, sizeof (buf)); 633 cp = (char *)LLADDR(sdl); 634 if ((n = sdl->sdl_alen) > 0) { 635 while (--n >= 0) 636 p += snprintf(buf + p, bufsize - p, "%x%s", 637 *cp++ & 0xff, n > 0 ? ":" : ""); 638 } 639 return (buf); 640} 641 642/* 643 * Display an arp entry 644 */ 645static void 646print_entry(struct sockaddr_dl *sdl, 647 struct sockaddr_inarp *addr, struct rt_msghdr *rtm) 648{ 649 const char *host; 650 struct hostent *hp; 651 char ifname[IF_NAMESIZE]; 652#if 0 653 struct iso88025_sockaddr_dl_data *trld; 654 int seg; 655#endif 656 657 if (nflag == 0) 658 hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 659 sizeof addr->sin_addr, AF_INET); 660 else 661 hp = 0; 662 if (hp) 663 host = hp->h_name; 664 else { 665 host = "?"; 666 if (h_errno == TRY_AGAIN) 667 nflag = 1; 668 } 669 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 670 if (sdl->sdl_alen) { 671#if 1 672 printf("%s", print_lladdr(sdl)); 673#else 674 if ((sdl->sdl_type == IFT_ETHER || 675 sdl->sdl_type == IFT_L2VLAN || 676 sdl->sdl_type == IFT_BRIDGE) && 677 sdl->sdl_alen == ETHER_ADDR_LEN) 678 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 679 else { 680 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 681 682 printf("%s", link_ntoa(sdl) + n); 683 } 684#endif 685 } else 686 printf("(incomplete)"); 687 if (if_indextoname(sdl->sdl_index, ifname) != NULL) 688 printf(" on %s", ifname); 689 if ((rtm->rtm_flags & RTF_IFSCOPE)) 690 printf(" ifscope"); 691 if (rtm->rtm_rmx.rmx_expire == 0) 692 printf(" permanent"); 693 if (addr->sin_other & SIN_PROXY) 694 printf(" published (proxy only)"); 695 if (rtm->rtm_addrs & RTA_NETMASK) { 696 addr = (struct sockaddr_inarp *) 697 (SA_SIZE(sdl) + (char *)sdl); 698 if (addr->sin_addr.s_addr == 0xffffffff) 699 printf(" published"); 700 if (addr->sin_len != 8) 701 printf("(weird)"); 702 } 703 switch(sdl->sdl_type) { 704 case IFT_ETHER: 705 printf(" [ethernet]"); 706 break; 707#if 0 708 case IFT_ISO88025: 709 printf(" [token-ring]"); 710 trld = SDL_ISO88025(sdl); 711 if (trld->trld_rcf != 0) { 712 printf(" rt=%x", ntohs(trld->trld_rcf)); 713 for (seg = 0; 714 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 715 seg++) 716 printf(":%x", ntohs(*(trld->trld_route[seg]))); 717 } 718 break; 719#endif 720 case IFT_FDDI: 721 printf(" [fddi]"); 722 break; 723 case IFT_ATM: 724 printf(" [atm]"); 725 break; 726 case IFT_L2VLAN: 727 printf(" [vlan]"); 728 break; 729 case IFT_IEEE1394: 730 printf(" [firewire]"); 731 break; 732#ifdef IFT_BRIDGE 733 case IFT_BRIDGE: 734 printf(" [bridge]"); 735 break; 736#endif 737 default: 738 break; 739 } 740 741 printf("\n"); 742 743} 744 745/* 746 * Nuke an arp entry 747 */ 748static void 749nuke_entry(struct sockaddr_dl *sdl __unused, 750 struct sockaddr_inarp *addr, struct rt_msghdr *rtm) 751{ 752 char ip[20]; 753 754 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 755 /* 756 * When deleting all entries, specify the interface scope of each entry 757 */ 758 if ((rtm->rtm_flags & RTF_IFSCOPE)) 759 ifscope = rtm->rtm_index; 760 (void)delete(ip, 0); 761 ifscope = 0; 762} 763 764static void 765usage(void) 766{ 767 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 768 "usage: arp [-n] [-i interface] hostname", 769 " arp [-n] [-i interface] [-l] -a", 770 " arp -d hostname [pub] [ifscope interface]", 771 " arp -d [-i interface] -a", 772 " arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", 773 " arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", 774 " arp -f filename"); 775 exit(1); 776} 777 778static struct rt_msghdr * 779rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) 780{ 781 static int seq; 782 int rlen; 783 int l; 784 struct sockaddr_in so_mask, *so_mask_ptr = &so_mask; 785 static int s = -1; 786 static pid_t pid; 787 788 static struct { 789 struct rt_msghdr m_rtm; 790 char m_space[512]; 791 } m_rtmsg; 792 793 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 794 char *cp = m_rtmsg.m_space; 795 796 if (s < 0) { /* first time: open socket, get pid */ 797 s = socket(PF_ROUTE, SOCK_RAW, 0); 798 if (s < 0) 799 err(1, "socket"); 800 pid = getpid(); 801 } 802 bzero(&so_mask, sizeof(so_mask)); 803 so_mask.sin_len = 8; 804 so_mask.sin_addr.s_addr = 0xffffffff; 805 806 errno = 0; 807 /* 808 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 809 * appropriately (except for the mask set just above). 810 */ 811 if (cmd == RTM_DELETE) 812 goto doit; 813 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 814 rtm->rtm_flags = flags; 815 rtm->rtm_version = RTM_VERSION; 816 817 /* 818 * Note: On RTM_GET the kernel will return a scoped route when both a scoped route and 819 * a unscoped route exist. That means we cannot delete a unscoped route if there is 820 * also a matching scope route 821 */ 822 if (ifscope) { 823 rtm->rtm_index = ifscope; 824 rtm->rtm_flags |= RTF_IFSCOPE; 825 } 826 827 switch (cmd) { 828 default: 829 errx(1, "internal wrong cmd"); 830 case RTM_ADD: 831 rtm->rtm_addrs |= RTA_GATEWAY; 832 rtm->rtm_rmx.rmx_expire = expire_time; 833 rtm->rtm_inits = RTV_EXPIRE; 834 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 835 dst->sin_other = 0; 836 if (doing_proxy) { 837 if (proxy_only) 838 dst->sin_other = SIN_PROXY; 839 else { 840 rtm->rtm_addrs |= RTA_NETMASK; 841 rtm->rtm_flags &= ~RTF_HOST; 842 } 843 } 844 /* FALLTHROUGH */ 845 case RTM_GET: 846 rtm->rtm_addrs |= RTA_DST; 847 } 848#define NEXTADDR(w, s) \ 849 if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 850 bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);} 851 852 NEXTADDR(RTA_DST, dst); 853 NEXTADDR(RTA_GATEWAY, sdl); 854 NEXTADDR(RTA_NETMASK, so_mask_ptr); 855 856 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 857doit: 858 l = rtm->rtm_msglen; 859 rtm->rtm_seq = ++seq; 860 rtm->rtm_type = cmd; 861 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 862 if (errno != ESRCH || cmd != RTM_DELETE) { 863 warn("writing to routing socket"); 864 return (NULL); 865 } 866 } 867 do { 868 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 869 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 870 if (l < 0) 871 warn("read from routing socket"); 872 return (rtm); 873} 874 875/* 876 * get_ether_addr - get the hardware address of an interface on the 877 * the same subnet as ipaddr. 878 */ 879#define MAX_IFS 32 880 881static int 882get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 883{ 884 struct ifreq *ifr, *ifend, *ifp; 885 in_addr_t ina, mask; 886 struct sockaddr_dl *dla; 887 struct ifreq ifreq; 888 struct ifconf ifc; 889 struct ifreq ifs[MAX_IFS]; 890 int sock; 891 int retval = 0; 892 893 sock = socket(AF_INET, SOCK_DGRAM, 0); 894 if (sock < 0) 895 err(1, "socket"); 896 897 ifc.ifc_len = sizeof(ifs); 898 ifc.ifc_req = ifs; 899 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 900 warnx("ioctl(SIOCGIFCONF)"); 901 goto done; 902 } 903 904#define NEXTIFR(i) \ 905 ((struct ifreq *)((char *)&(i)->ifr_addr \ 906 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 907 908 /* 909 * Scan through looking for an interface with an Internet 910 * address on the same subnet as `ipaddr'. 911 */ 912 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 913 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 914 if (ifr->ifr_addr.sa_family != AF_INET) 915 continue; 916 strncpy(ifreq.ifr_name, ifr->ifr_name, 917 sizeof(ifreq.ifr_name)); 918 ifreq.ifr_addr = ifr->ifr_addr; 919 /* 920 * Check that the interface is up, 921 * and not point-to-point or loopback. 922 */ 923 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 924 continue; 925 if ((ifreq.ifr_flags & 926 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 927 IFF_LOOPBACK|IFF_NOARP)) 928 != (IFF_UP|IFF_BROADCAST)) 929 continue; 930 /* 931 * Get its netmask and check that it's on 932 * the right subnet. 933 */ 934 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 935 continue; 936 mask = ((struct sockaddr_in *) 937 &ifreq.ifr_addr)->sin_addr.s_addr; 938 ina = ((struct sockaddr_in *) 939 &ifr->ifr_addr)->sin_addr.s_addr; 940 if ((ipaddr & mask) == (ina & mask)) 941 break; /* ok, we got it! */ 942 } 943 944 if (ifr >= ifend) 945 goto done; 946 947 /* 948 * Now scan through again looking for a link-level address 949 * for this interface. 950 */ 951 ifp = ifr; 952 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 953 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 954 ifr->ifr_addr.sa_family == AF_LINK) 955 break; 956 if (ifr >= ifend) 957 goto done; 958 /* 959 * Found the link-level address - copy it out 960 */ 961 dla = (struct sockaddr_dl *) &ifr->ifr_addr; 962 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 963 printf("using interface %s for proxy with address ", 964 ifp->ifr_name); 965 printf("%s\n", ether_ntoa(hwaddr)); 966 retval = dla->sdl_alen; 967done: 968 close(sock); 969 return (retval); 970} 971 972static char * 973sec2str(total) 974 time_t total; 975{ 976 static char result[256]; 977 int days, hours, mins, secs; 978 int first = 1; 979 char *p = result; 980 981 days = total / 3600 / 24; 982 hours = (total / 3600) % 24; 983 mins = (total / 60) % 60; 984 secs = total % 60; 985 986 if (days) { 987 first = 0; 988 p += snprintf(p, sizeof(result) - (p - result), "%dd", days); 989 } 990 if (!first || hours) { 991 first = 0; 992 p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); 993 } 994 if (!first || mins) { 995 first = 0; 996 p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); 997 } 998 snprintf(p, sizeof(result) - (p - result), "%ds", secs); 999 1000 return(result); 1001} 1002 1003static int 1004search_ext(in_addr_t addr, action_ext_fn *action) 1005{ 1006 int mib[6]; 1007 size_t needed; 1008 char *lim, *buf, *newbuf, *next; 1009 struct rt_msghdr_ext *ertm; 1010 struct sockaddr_inarp *sin2; 1011 struct sockaddr_dl *sdl; 1012 char ifname[IF_NAMESIZE]; 1013 int st, found_entry = 0; 1014 1015 mib[0] = CTL_NET; 1016 mib[1] = PF_ROUTE; 1017 mib[2] = 0; 1018 mib[3] = AF_INET; 1019 mib[4] = NET_RT_DUMPX_FLAGS; 1020 mib[5] = RTF_LLINFO; 1021 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 1022 err(1, "route-sysctl-estimate"); 1023 if (needed == 0) /* empty table */ 1024 return 0; 1025 buf = NULL; 1026 for (;;) { 1027 newbuf = realloc(buf, needed); 1028 if (newbuf == NULL) { 1029 if (buf != NULL) 1030 free(buf); 1031 errx(1, "could not reallocate memory"); 1032 } 1033 buf = newbuf; 1034 st = sysctl(mib, 6, buf, &needed, NULL, 0); 1035 if (st == 0 || errno != ENOMEM) 1036 break; 1037 needed += needed / 8; 1038 } 1039 if (st == -1) 1040 err(1, "actual retrieval of routing table"); 1041 lim = buf + needed; 1042 for (next = buf; next < lim; next += ertm->rtm_msglen) { 1043 ertm = (struct rt_msghdr_ext *)next; 1044 sin2 = (struct sockaddr_inarp *)(ertm + 1); 1045 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 1046 if (rifname && if_indextoname(sdl->sdl_index, ifname) && 1047 strcmp(ifname, rifname)) 1048 continue; 1049 if (addr) { 1050 if (addr != sin2->sin_addr.s_addr) 1051 continue; 1052 found_entry = 1; 1053 } 1054 (*action)(sdl, sin2, ertm); 1055 } 1056 free(buf); 1057 return (found_entry); 1058} 1059 1060static void 1061print_entry_ext(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr, 1062 struct rt_msghdr_ext *ertm) 1063{ 1064 const char *host; 1065 struct hostent *hp; 1066 char ifname[IF_NAMESIZE]; 1067 struct timeval time; 1068 1069 if (nflag == 0) 1070 hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 1071 sizeof (addr->sin_addr), AF_INET); 1072 else 1073 hp = 0; 1074 1075 if (hp) 1076 host = hp->h_name; 1077 else 1078 host = inet_ntoa(addr->sin_addr); 1079 1080 printf("%-23s ", host); 1081 1082 if (sdl->sdl_alen) 1083 printf("%-17s ", print_lladdr(sdl)); 1084 else 1085 printf("%-17s ", "(incomplete)"); 1086 1087 gettimeofday(&time, 0); 1088 1089 if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_snd_expire == 0) 1090 printf("%-9.9s ", "(none)"); 1091 else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec) 1092 printf("%-9.9s ", 1093 sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec)); 1094 else 1095 printf("%-9.9s ", "expired"); 1096 1097 if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_rcv_expire == 0) 1098 printf("%-9.9s", "(none)"); 1099 else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec) 1100 printf("%-9.9s", 1101 sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec)); 1102 else 1103 printf("%-9.9s", "expired"); 1104 1105 if (if_indextoname(sdl->sdl_index, ifname) == NULL) 1106 snprintf(ifname, sizeof (ifname), "%s", "?"); 1107 printf(" %8.8s", ifname); 1108 1109 if (ertm->rtm_ri.ri_refcnt) { 1110 printf(" %4d", ertm->rtm_ri.ri_refcnt); 1111 if (ertm->rtm_ri.ri_probes) 1112 printf(" %4d", ertm->rtm_ri.ri_probes); 1113 1114 if (xflag) { 1115 if (!ertm->rtm_ri.ri_probes) 1116 printf(" %-4.4s", "none"); 1117 1118 if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN) 1119 printf(" %7d", ertm->rtm_ri.ri_rssi); 1120 else 1121 printf(" %-7.7s", "unknown"); 1122 1123 switch (ertm->rtm_ri.ri_lqm) 1124 { 1125 case IFNET_LQM_THRESH_OFF: 1126 printf(" %-7.7s", "off"); 1127 break; 1128 case IFNET_LQM_THRESH_UNKNOWN: 1129 printf(" %-7.7s", "unknown"); 1130 break; 1131 case IFNET_LQM_THRESH_POOR: 1132 printf(" %-7.7s", "poor"); 1133 break; 1134 case IFNET_LQM_THRESH_GOOD: 1135 printf(" %-7.7s", "good"); 1136 break; 1137 default: 1138 printf(" %7d", ertm->rtm_ri.ri_lqm); 1139 break; 1140 } 1141 1142 switch (ertm->rtm_ri.ri_npm) 1143 { 1144 case IFNET_NPM_THRESH_UNKNOWN: 1145 printf(" %-7.7s", "unknown"); 1146 break; 1147 case IFNET_NPM_THRESH_NEAR: 1148 printf(" %-7.7s", "near"); 1149 break; 1150 case IFNET_NPM_THRESH_GENERAL: 1151 printf(" %-7.7s", "general"); 1152 break; 1153 case IFNET_NPM_THRESH_FAR: 1154 printf(" %-7.7s", "far"); 1155 break; 1156 default: 1157 printf(" %7d", ertm->rtm_ri.ri_npm); 1158 break; 1159 } 1160 } 1161 } 1162 printf("\n"); 1163} 1164