arp.c revision 294203
1275970Scy/* 2275970Scy * Copyright (c) 1984, 1993 3275970Scy * The Regents of the University of California. All rights reserved. 4275970Scy * 5275970Scy * This code is derived from software contributed to Berkeley by 6275970Scy * Sun Microsystems, Inc. 7275970Scy * 8275970Scy * Redistribution and use in source and binary forms, with or without 9275970Scy * modification, are permitted provided that the following conditions 10275970Scy * are met: 11275970Scy * 1. Redistributions of source code must retain the above copyright 12275970Scy * notice, this list of conditions and the following disclaimer. 13275970Scy * 2. Redistributions in binary form must reproduce the above copyright 14275970Scy * notice, this list of conditions and the following disclaimer in the 15275970Scy * documentation and/or other materials provided with the distribution. 16275970Scy * 4. Neither the name of the University nor the names of its contributors 17275970Scy * may be used to endorse or promote products derived from this software 18275970Scy * without specific prior written permission. 19275970Scy * 20275970Scy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23275970Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30275970Scy * SUCH DAMAGE. 31275970Scy */ 32275970Scy 33275970Scy#if 0 34275970Scy#ifndef lint 35275970Scystatic char const copyright[] = 36275970Scy"@(#) Copyright (c) 1984, 1993\n\ 37275970Scy The Regents of the University of California. All rights reserved.\n"; 38275970Scy#endif /* not lint */ 39275970Scy 40275970Scy#ifndef lint 41275970Scystatic char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; 42275970Scy#endif /* not lint */ 43275970Scy#endif 44275970Scy#include <sys/cdefs.h> 45275970Scy__FBSDID("$FreeBSD: stable/10/usr.sbin/arp/arp.c 294203 2016-01-17 06:02:59Z melifaro $"); 46275970Scy 47275970Scy/* 48275970Scy * arp - display, set, and delete arp table entries 49275970Scy */ 50275970Scy 51275970Scy 52275970Scy#include <sys/param.h> 53275970Scy#include <sys/file.h> 54275970Scy#include <sys/socket.h> 55275970Scy#include <sys/sockio.h> 56275970Scy#include <sys/sysctl.h> 57275970Scy#include <sys/ioctl.h> 58275970Scy#include <sys/time.h> 59275970Scy 60275970Scy#include <net/if.h> 61275970Scy#include <net/if_dl.h> 62275970Scy#include <net/if_types.h> 63275970Scy#include <net/route.h> 64275970Scy#include <net/iso88025.h> 65275970Scy 66275970Scy#include <netinet/in.h> 67275970Scy#include <netinet/if_ether.h> 68275970Scy 69275970Scy#include <arpa/inet.h> 70275970Scy 71275970Scy#include <ctype.h> 72275970Scy#include <err.h> 73275970Scy#include <errno.h> 74275970Scy#include <netdb.h> 75275970Scy#include <nlist.h> 76275970Scy#include <paths.h> 77275970Scy#include <stdio.h> 78275970Scy#include <stdlib.h> 79275970Scy#include <string.h> 80275970Scy#include <strings.h> 81275970Scy#include <unistd.h> 82275970Scy 83275970Scytypedef void (action_fn)(struct sockaddr_dl *sdl, 84275970Scy struct sockaddr_in *s_in, struct rt_msghdr *rtm); 85275970Scy 86275970Scystatic int search(u_long addr, action_fn *action); 87275970Scystatic action_fn print_entry; 88275970Scystatic action_fn nuke_entry; 89275970Scy 90275970Scystatic int delete(char *host); 91275970Scystatic void usage(void); 92285612Sdelphijstatic int set(int argc, char **argv); 93285612Sdelphijstatic int get(char *host); 94275970Scystatic int file(char *name); 95275970Scystatic struct rt_msghdr *rtmsg(int cmd, 96275970Scy struct sockaddr_in *dst, struct sockaddr_dl *sdl); 97275970Scystatic int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); 98285612Sdelphijstatic struct sockaddr_in *getaddr(char *host); 99275970Scystatic int valid_type(int type); 100275970Scy 101275970Scystatic int nflag; /* no reverse dns lookups */ 102275970Scystatic char *rifname; 103275970Scy 104275970Scystatic time_t expire_time; 105275970Scystatic int flags, doing_proxy; 106275970Scy 107275970Scystruct if_nameindex *ifnameindex; 108275970Scy 109275970Scy/* which function we're supposed to do */ 110275970Scy#define F_GET 1 111275970Scy#define F_SET 2 112285612Sdelphij#define F_FILESET 3 113285612Sdelphij#define F_REPLACE 4 114285612Sdelphij#define F_DELETE 5 115285612Sdelphij 116285612Sdelphij#define SETFUNC(f) { if (func) usage(); func = (f); } 117285612Sdelphij 118285612Sdelphijint 119285612Sdelphijmain(int argc, char *argv[]) 120285612Sdelphij{ 121285612Sdelphij int ch, func = 0; 122285612Sdelphij int rtn = 0; 123275970Scy int aflag = 0; /* do it for all entries */ 124275970Scy 125275970Scy while ((ch = getopt(argc, argv, "andfsSi:")) != -1) 126275970Scy switch(ch) { 127275970Scy case 'a': 128275970Scy aflag = 1; 129275970Scy break; 130275970Scy case 'd': 131275970Scy SETFUNC(F_DELETE); 132275970Scy break; 133275970Scy case 'n': 134275970Scy nflag = 1; 135275970Scy break; 136275970Scy case 'S': 137275970Scy SETFUNC(F_REPLACE); 138275970Scy break; 139275970Scy case 's': 140275970Scy SETFUNC(F_SET); 141275970Scy break; 142275970Scy case 'f' : 143275970Scy SETFUNC(F_FILESET); 144275970Scy break; 145275970Scy case 'i': 146275970Scy rifname = optarg; 147275970Scy break; 148275970Scy case '?': 149275970Scy default: 150275970Scy usage(); 151275970Scy } 152275970Scy argc -= optind; 153275970Scy argv += optind; 154275970Scy 155275970Scy if (!func) 156275970Scy func = F_GET; 157275970Scy if (rifname) { 158275970Scy if (func != F_GET && !(func == F_DELETE && aflag)) 159275970Scy errx(1, "-i not applicable to this operation"); 160275970Scy if (if_nametoindex(rifname) == 0) { 161275970Scy if (errno == ENXIO) 162275970Scy errx(1, "interface %s does not exist", rifname); 163275970Scy else 164275970Scy err(1, "if_nametoindex(%s)", rifname); 165275970Scy } 166275970Scy } 167275970Scy switch (func) { 168275970Scy case F_GET: 169275970Scy if (aflag) { 170275970Scy if (argc != 0) 171275970Scy usage(); 172275970Scy search(0, print_entry); 173275970Scy } else { 174275970Scy if (argc != 1) 175275970Scy usage(); 176275970Scy rtn = get(argv[0]); 177275970Scy } 178275970Scy break; 179275970Scy case F_SET: 180275970Scy case F_REPLACE: 181275970Scy if (argc < 2 || argc > 6) 182275970Scy usage(); 183275970Scy if (func == F_REPLACE) 184275970Scy (void)delete(argv[0]); 185275970Scy rtn = set(argc, argv) ? 1 : 0; 186275970Scy break; 187275970Scy case F_DELETE: 188275970Scy if (aflag) { 189275970Scy if (argc != 0) 190275970Scy usage(); 191275970Scy search(0, nuke_entry); 192285612Sdelphij } else { 193285612Sdelphij if (argc != 1) 194285612Sdelphij usage(); 195285612Sdelphij rtn = delete(argv[0]); 196285612Sdelphij } 197275970Scy break; 198285612Sdelphij case F_FILESET: 199275970Scy if (argc != 1) 200285612Sdelphij usage(); 201275970Scy rtn = file(argv[0]); 202275970Scy break; 203275970Scy } 204275970Scy 205275970Scy if (ifnameindex != NULL) 206275970Scy if_freenameindex(ifnameindex); 207275970Scy 208275970Scy return (rtn); 209275970Scy} 210275970Scy 211275970Scy/* 212275970Scy * Process a file to set standard arp entries 213275970Scy */ 214275970Scystatic int 215275970Scyfile(char *name) 216275970Scy{ 217285612Sdelphij FILE *fp; 218275970Scy int i, retval; 219275970Scy char line[100], arg[5][50], *args[5], *p; 220275970Scy 221275970Scy if ((fp = fopen(name, "r")) == NULL) 222275970Scy err(1, "cannot open %s", name); 223275970Scy args[0] = &arg[0][0]; 224275970Scy args[1] = &arg[1][0]; 225275970Scy args[2] = &arg[2][0]; 226275970Scy args[3] = &arg[3][0]; 227275970Scy args[4] = &arg[4][0]; 228275970Scy retval = 0; 229275970Scy while(fgets(line, sizeof(line), fp) != NULL) { 230275970Scy if ((p = strchr(line, '#')) != NULL) 231275970Scy *p = '\0'; 232275970Scy for (p = line; isblank(*p); p++); 233275970Scy if (*p == '\n' || *p == '\0') 234275970Scy continue; 235275970Scy i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], 236275970Scy arg[2], arg[3], arg[4]); 237275970Scy if (i < 2) { 238275970Scy warnx("bad line: %s", line); 239275970Scy retval = 1; 240275970Scy continue; 241275970Scy } 242275970Scy if (set(i, args)) 243275970Scy retval = 1; 244275970Scy } 245275970Scy fclose(fp); 246275970Scy return (retval); 247275970Scy} 248275970Scy 249275970Scy/* 250275970Scy * Given a hostname, fills up a (static) struct sockaddr_in with 251275970Scy * the address of the host and returns a pointer to the 252275970Scy * structure. 253275970Scy */ 254275970Scystatic struct sockaddr_in * 255275970Scygetaddr(char *host) 256275970Scy{ 257275970Scy struct hostent *hp; 258275970Scy static struct sockaddr_in reply; 259275970Scy 260275970Scy bzero(&reply, sizeof(reply)); 261275970Scy reply.sin_len = sizeof(reply); 262275970Scy reply.sin_family = AF_INET; 263275970Scy reply.sin_addr.s_addr = inet_addr(host); 264275970Scy if (reply.sin_addr.s_addr == INADDR_NONE) { 265275970Scy if (!(hp = gethostbyname(host))) { 266275970Scy warnx("%s: %s", host, hstrerror(h_errno)); 267275970Scy return (NULL); 268275970Scy } 269275970Scy bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 270275970Scy sizeof reply.sin_addr); 271275970Scy } 272275970Scy return (&reply); 273275970Scy} 274275970Scy 275275970Scy/* 276275970Scy * Returns true if the type is a valid one for ARP. 277275970Scy */ 278275970Scystatic int 279275970Scyvalid_type(int type) 280275970Scy{ 281275970Scy 282275970Scy switch (type) { 283275970Scy case IFT_ETHER: 284275970Scy case IFT_FDDI: 285275970Scy case IFT_INFINIBAND: 286275970Scy case IFT_ISO88023: 287275970Scy case IFT_ISO88024: 288275970Scy case IFT_ISO88025: 289275970Scy case IFT_L2VLAN: 290275970Scy case IFT_BRIDGE: 291275970Scy return (1); 292275970Scy default: 293275970Scy return (0); 294275970Scy } 295275970Scy} 296275970Scy 297275970Scy/* 298275970Scy * Set an individual arp entry 299275970Scy */ 300275970Scystatic int 301275970Scyset(int argc, char **argv) 302275970Scy{ 303275970Scy struct sockaddr_in *addr; 304275970Scy struct sockaddr_in *dst; /* what are we looking for */ 305275970Scy struct sockaddr_dl *sdl; 306275970Scy struct rt_msghdr *rtm; 307285612Sdelphij struct ether_addr *ea; 308275970Scy char *host = argv[0], *eaddr = argv[1]; 309275970Scy struct sockaddr_dl sdl_m; 310275970Scy 311275970Scy argc -= 2; 312275970Scy argv += 2; 313275970Scy 314275970Scy bzero(&sdl_m, sizeof(sdl_m)); 315285612Sdelphij sdl_m.sdl_len = sizeof(sdl_m); 316285612Sdelphij sdl_m.sdl_family = AF_LINK; 317285612Sdelphij 318285612Sdelphij dst = getaddr(host); 319285612Sdelphij if (dst == NULL) 320285612Sdelphij return (1); 321285612Sdelphij doing_proxy = flags = expire_time = 0; 322275970Scy while (argc-- > 0) { 323285612Sdelphij if (strncmp(argv[0], "temp", 4) == 0) { 324285612Sdelphij struct timespec tp; 325285612Sdelphij int max_age; 326285612Sdelphij size_t len = sizeof(max_age); 327275970Scy 328285612Sdelphij clock_gettime(CLOCK_MONOTONIC, &tp); 329285612Sdelphij if (sysctlbyname("net.link.ether.inet.max_age", 330285612Sdelphij &max_age, &len, NULL, 0) != 0) 331285612Sdelphij err(1, "sysctlbyname"); 332285612Sdelphij expire_time = tp.tv_sec + max_age; 333285612Sdelphij } else if (strncmp(argv[0], "pub", 3) == 0) { 334285612Sdelphij flags |= RTF_ANNOUNCE; 335285612Sdelphij doing_proxy = 1; 336285612Sdelphij if (argc && strncmp(argv[1], "only", 3) == 0) { 337285612Sdelphij /* 338285612Sdelphij * Compatibility: in pre FreeBSD 8 times 339285612Sdelphij * the "only" keyword used to mean that 340285612Sdelphij * an ARP entry should be announced, but 341275970Scy * not installed into routing table. 342275970Scy */ 343275970Scy argc--; argv++; 344275970Scy } 345285612Sdelphij } else if (strncmp(argv[0], "blackhole", 9) == 0) { 346285612Sdelphij if (flags & RTF_REJECT) { 347275970Scy printf("Choose one of blackhole or reject, not both.\n"); 348275970Scy } 349275970Scy flags |= RTF_BLACKHOLE; 350275970Scy } else if (strncmp(argv[0], "reject", 6) == 0) { 351275970Scy if (flags & RTF_BLACKHOLE) { 352285612Sdelphij printf("Choose one of blackhole or reject, not both.\n"); 353275970Scy } 354275970Scy flags |= RTF_REJECT; 355275970Scy } else if (strncmp(argv[0], "trail", 5) == 0) { 356275970Scy /* XXX deprecated and undocumented feature */ 357275970Scy printf("%s: Sending trailers is no longer supported\n", 358275970Scy host); 359275970Scy } 360275970Scy argv++; 361275970Scy } 362275970Scy ea = (struct ether_addr *)LLADDR(&sdl_m); 363285612Sdelphij if (doing_proxy && !strcmp(eaddr, "auto")) { 364275970Scy if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 365275970Scy printf("no interface found for %s\n", 366275970Scy inet_ntoa(dst->sin_addr)); 367275970Scy return (1); 368275970Scy } 369275970Scy sdl_m.sdl_alen = ETHER_ADDR_LEN; 370275970Scy } else { 371275970Scy struct ether_addr *ea1 = ether_aton(eaddr); 372275970Scy 373275970Scy if (ea1 == NULL) { 374275970Scy warnx("invalid Ethernet address '%s'", eaddr); 375285612Sdelphij return (1); 376285612Sdelphij } else { 377285612Sdelphij *ea = *ea1; 378285612Sdelphij sdl_m.sdl_alen = ETHER_ADDR_LEN; 379285612Sdelphij } 380285612Sdelphij } 381285612Sdelphij 382285612Sdelphij /* 383285612Sdelphij * In the case a proxy-arp entry is being added for 384285612Sdelphij * a remote end point, the RTF_ANNOUNCE flag in the 385285612Sdelphij * RTM_GET command is an indication to the kernel 386285612Sdelphij * routing code that the interface associated with 387285612Sdelphij * the prefix route covering the local end of the 388285612Sdelphij * PPP link should be returned, on which ARP applies. 389285612Sdelphij */ 390285612Sdelphij rtm = rtmsg(RTM_GET, dst, &sdl_m); 391275970Scy if (rtm == NULL) { 392275970Scy warn("%s", host); 393275970Scy return (1); 394275970Scy } 395275970Scy addr = (struct sockaddr_in *)(rtm + 1); 396275970Scy sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 397275970Scy 398275970Scy if ((sdl->sdl_family != AF_LINK) || 399275970Scy (rtm->rtm_flags & RTF_GATEWAY) || 400275970Scy !valid_type(sdl->sdl_type)) { 401285612Sdelphij printf("cannot intuit interface index and type for %s\n", host); 402275970Scy return (1); 403275970Scy } 404275970Scy sdl_m.sdl_type = sdl->sdl_type; 405285612Sdelphij sdl_m.sdl_index = sdl->sdl_index; 406275970Scy return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 407275970Scy} 408275970Scy 409275970Scy/* 410275970Scy * Display an individual arp entry 411275970Scy */ 412275970Scystatic int 413275970Scyget(char *host) 414275970Scy{ 415275970Scy struct sockaddr_in *addr; 416275970Scy 417275970Scy addr = getaddr(host); 418275970Scy if (addr == NULL) 419275970Scy return (1); 420275970Scy if (0 == search(addr->sin_addr.s_addr, print_entry)) { 421275970Scy printf("%s (%s) -- no entry", 422275970Scy host, inet_ntoa(addr->sin_addr)); 423275970Scy if (rifname) 424275970Scy printf(" on %s", rifname); 425275970Scy printf("\n"); 426275970Scy return (1); 427275970Scy } 428275970Scy return (0); 429275970Scy} 430275970Scy 431275970Scy/* 432275970Scy * Delete an arp entry 433275970Scy */ 434275970Scystatic int 435275970Scydelete(char *host) 436275970Scy{ 437275970Scy struct sockaddr_in *addr, *dst; 438275970Scy struct rt_msghdr *rtm; 439275970Scy struct sockaddr_dl *sdl; 440275970Scy struct sockaddr_dl sdl_m; 441285612Sdelphij 442275970Scy dst = getaddr(host); 443275970Scy if (dst == NULL) 444275970Scy return (1); 445275970Scy 446275970Scy /* 447275970Scy * Perform a regular entry delete first. 448275970Scy */ 449275970Scy flags &= ~RTF_ANNOUNCE; 450275970Scy 451275970Scy /* 452275970Scy * setup the data structure to notify the kernel 453275970Scy * it is the ARP entry the RTM_GET is interested 454275970Scy * in 455275970Scy */ 456275970Scy bzero(&sdl_m, sizeof(sdl_m)); 457275970Scy sdl_m.sdl_len = sizeof(sdl_m); 458275970Scy sdl_m.sdl_family = AF_LINK; 459275970Scy 460275970Scy for (;;) { /* try twice */ 461275970Scy rtm = rtmsg(RTM_GET, dst, &sdl_m); 462275970Scy if (rtm == NULL) { 463275970Scy warn("%s", host); 464275970Scy return (1); 465275970Scy } 466275970Scy addr = (struct sockaddr_in *)(rtm + 1); 467275970Scy sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 468275970Scy 469275970Scy /* 470275970Scy * With the new L2/L3 restructure, the route 471275970Scy * returned is a prefix route. The important 472275970Scy * piece of information from the previous 473275970Scy * RTM_GET is the interface index. In the 474275970Scy * case of ECMP, the kernel will traverse 475275970Scy * the route group for the given entry. 476275970Scy */ 477275970Scy if (sdl->sdl_family == AF_LINK && 478275970Scy !(rtm->rtm_flags & RTF_GATEWAY) && 479275970Scy valid_type(sdl->sdl_type) ) { 480275970Scy addr->sin_addr.s_addr = dst->sin_addr.s_addr; 481275970Scy break; 482275970Scy } 483275970Scy 484275970Scy /* 485275970Scy * Regualar entry delete failed, now check if there 486275970Scy * is a proxy-arp entry to remove. 487275970Scy */ 488275970Scy if (flags & RTF_ANNOUNCE) { 489275970Scy fprintf(stderr, "delete: cannot locate %s\n",host); 490275970Scy return (1); 491285612Sdelphij } 492275970Scy 493275970Scy flags |= RTF_ANNOUNCE; 494275970Scy } 495275970Scy rtm->rtm_flags |= RTF_LLDATA; 496275970Scy if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 497275970Scy printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 498275970Scy return (0); 499275970Scy } 500275970Scy return (1); 501275970Scy} 502275970Scy 503275970Scy 504275970Scy/* 505275970Scy * Search the arp table and do some action on matching entries 506275970Scy */ 507275970Scystatic int 508275970Scysearch(u_long addr, action_fn *action) 509275970Scy{ 510285612Sdelphij int mib[6]; 511275970Scy size_t needed; 512275970Scy char *lim, *buf, *next; 513275970Scy struct rt_msghdr *rtm; 514275970Scy struct sockaddr_in *sin2; 515275970Scy struct sockaddr_dl *sdl; 516275970Scy char ifname[IF_NAMESIZE]; 517275970Scy int st, found_entry = 0; 518275970Scy 519275970Scy mib[0] = CTL_NET; 520275970Scy mib[1] = PF_ROUTE; 521275970Scy mib[2] = 0; 522275970Scy mib[3] = AF_INET; 523275970Scy mib[4] = NET_RT_FLAGS; 524275970Scy#ifdef RTF_LLINFO 525275970Scy mib[5] = RTF_LLINFO; 526275970Scy#else 527275970Scy mib[5] = 0; 528275970Scy#endif 529275970Scy if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 530275970Scy err(1, "route-sysctl-estimate"); 531275970Scy if (needed == 0) /* empty table */ 532275970Scy return 0; 533275970Scy buf = NULL; 534275970Scy for (;;) { 535275970Scy buf = reallocf(buf, needed); 536275970Scy if (buf == NULL) 537275970Scy errx(1, "could not reallocate memory"); 538275970Scy st = sysctl(mib, 6, buf, &needed, NULL, 0); 539275970Scy if (st == 0 || errno != ENOMEM) 540275970Scy break; 541275970Scy needed += needed / 8; 542275970Scy } 543275970Scy if (st == -1) 544275970Scy err(1, "actual retrieval of routing table"); 545275970Scy lim = buf + needed; 546275970Scy for (next = buf; next < lim; next += rtm->rtm_msglen) { 547275970Scy rtm = (struct rt_msghdr *)next; 548275970Scy sin2 = (struct sockaddr_in *)(rtm + 1); 549275970Scy sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 550275970Scy if (rifname && if_indextoname(sdl->sdl_index, ifname) && 551275970Scy strcmp(ifname, rifname)) 552275970Scy continue; 553275970Scy if (addr) { 554275970Scy if (addr != sin2->sin_addr.s_addr) 555275970Scy continue; 556275970Scy found_entry = 1; 557275970Scy } 558275970Scy (*action)(sdl, sin2, rtm); 559275970Scy } 560275970Scy free(buf); 561275970Scy return (found_entry); 562275970Scy} 563275970Scy 564275970Scy/* 565275970Scy * Display an arp entry 566275970Scy */ 567275970Scy 568275970Scystatic void 569275970Scyprint_entry(struct sockaddr_dl *sdl, 570275970Scy struct sockaddr_in *addr, struct rt_msghdr *rtm) 571275970Scy{ 572275970Scy const char *host; 573275970Scy struct hostent *hp; 574275970Scy struct iso88025_sockaddr_dl_data *trld; 575275970Scy struct if_nameindex *p; 576275970Scy int seg; 577275970Scy 578275970Scy if (ifnameindex == NULL) 579275970Scy if ((ifnameindex = if_nameindex()) == NULL) 580275970Scy err(1, "cannot retrieve interface names"); 581275970Scy 582275970Scy if (nflag == 0) 583275970Scy hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 584275970Scy sizeof addr->sin_addr, AF_INET); 585275970Scy else 586275970Scy hp = 0; 587275970Scy if (hp) 588275970Scy host = hp->h_name; 589275970Scy else { 590275970Scy host = "?"; 591275970Scy if (h_errno == TRY_AGAIN) 592275970Scy nflag = 1; 593275970Scy } 594275970Scy printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 595275970Scy if (sdl->sdl_alen) { 596275970Scy if ((sdl->sdl_type == IFT_ETHER || 597275970Scy sdl->sdl_type == IFT_L2VLAN || 598275970Scy sdl->sdl_type == IFT_BRIDGE) && 599275970Scy sdl->sdl_alen == ETHER_ADDR_LEN) 600285612Sdelphij printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 601275970Scy else { 602275970Scy int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 603275970Scy 604275970Scy printf("%s", link_ntoa(sdl) + n); 605275970Scy } 606275970Scy } else 607275970Scy printf("(incomplete)"); 608275970Scy 609275970Scy for (p = ifnameindex; p && ifnameindex->if_index && 610275970Scy ifnameindex->if_name; p++) { 611275970Scy if (p->if_index == sdl->sdl_index) { 612275970Scy printf(" on %s", p->if_name); 613275970Scy break; 614275970Scy } 615275970Scy } 616285612Sdelphij 617275970Scy if (rtm->rtm_rmx.rmx_expire == 0) 618275970Scy printf(" permanent"); 619275970Scy else { 620275970Scy static struct timespec tp; 621275970Scy if (tp.tv_sec == 0) 622275970Scy clock_gettime(CLOCK_MONOTONIC, &tp); 623275970Scy if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0) 624275970Scy printf(" expires in %d seconds", (int)expire_time); 625275970Scy else 626275970Scy printf(" expired"); 627275970Scy } 628275970Scy if (rtm->rtm_flags & RTF_ANNOUNCE) 629275970Scy printf(" published"); 630275970Scy switch(sdl->sdl_type) { 631275970Scy case IFT_ETHER: 632275970Scy printf(" [ethernet]"); 633285612Sdelphij break; 634275970Scy case IFT_ISO88025: 635275970Scy printf(" [token-ring]"); 636275970Scy trld = SDL_ISO88025(sdl); 637275970Scy if (trld->trld_rcf != 0) { 638275970Scy printf(" rt=%x", ntohs(trld->trld_rcf)); 639275970Scy for (seg = 0; 640275970Scy seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 641275970Scy seg++) 642275970Scy printf(":%x", ntohs(*(trld->trld_route[seg]))); 643275970Scy } 644275970Scy break; 645275970Scy case IFT_FDDI: 646275970Scy printf(" [fddi]"); 647275970Scy break; 648275970Scy case IFT_ATM: 649275970Scy printf(" [atm]"); 650275970Scy break; 651285612Sdelphij case IFT_L2VLAN: 652285612Sdelphij printf(" [vlan]"); 653285612Sdelphij break; 654285612Sdelphij case IFT_IEEE1394: 655285612Sdelphij printf(" [firewire]"); 656285612Sdelphij break; 657285612Sdelphij case IFT_BRIDGE: 658285612Sdelphij printf(" [bridge]"); 659285612Sdelphij break; 660285612Sdelphij case IFT_INFINIBAND: 661285612Sdelphij printf(" [infiniband]"); 662285612Sdelphij break; 663285612Sdelphij default: 664285612Sdelphij break; 665285612Sdelphij } 666285612Sdelphij 667285612Sdelphij printf("\n"); 668285612Sdelphij 669285612Sdelphij} 670285612Sdelphij 671285612Sdelphij/* 672285612Sdelphij * Nuke an arp entry 673285612Sdelphij */ 674285612Sdelphijstatic void 675285612Sdelphijnuke_entry(struct sockaddr_dl *sdl __unused, 676285612Sdelphij struct sockaddr_in *addr, struct rt_msghdr *rtm) 677285612Sdelphij{ 678285612Sdelphij char ip[20]; 679285612Sdelphij 680285612Sdelphij if (rtm->rtm_flags & RTF_PINNED) 681285612Sdelphij return; 682285612Sdelphij 683285612Sdelphij snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 684285612Sdelphij delete(ip); 685285612Sdelphij} 686285612Sdelphij 687285612Sdelphijstatic void 688285612Sdelphijusage(void) 689285612Sdelphij{ 690285612Sdelphij fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 691285612Sdelphij "usage: arp [-n] [-i interface] hostname", 692285612Sdelphij " arp [-n] [-i interface] -a", 693285612Sdelphij " arp -d hostname [pub]", 694285612Sdelphij " arp -d [-i interface] -a", 695285612Sdelphij " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 696285612Sdelphij " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 697285612Sdelphij " arp -f filename"); 698285612Sdelphij exit(1); 699285612Sdelphij} 700285612Sdelphij 701285612Sdelphijstatic struct rt_msghdr * 702285612Sdelphijrtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) 703285612Sdelphij{ 704285612Sdelphij static int seq; 705285612Sdelphij int rlen; 706285612Sdelphij int l; 707285612Sdelphij struct sockaddr_in so_mask, *som = &so_mask; 708285612Sdelphij static int s = -1; 709285612Sdelphij static pid_t pid; 710285612Sdelphij 711285612Sdelphij static struct { 712285612Sdelphij struct rt_msghdr m_rtm; 713285612Sdelphij char m_space[512]; 714285612Sdelphij } m_rtmsg; 715285612Sdelphij 716275970Scy struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 717275970Scy char *cp = m_rtmsg.m_space; 718275970Scy 719275970Scy if (s < 0) { /* first time: open socket, get pid */ 720275970Scy s = socket(PF_ROUTE, SOCK_RAW, 0); 721275970Scy if (s < 0) 722275970Scy err(1, "socket"); 723275970Scy pid = getpid(); 724275970Scy } 725275970Scy bzero(&so_mask, sizeof(so_mask)); 726275970Scy so_mask.sin_len = 8; 727275970Scy so_mask.sin_addr.s_addr = 0xffffffff; 728275970Scy 729275970Scy errno = 0; 730275970Scy /* 731275970Scy * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 732275970Scy * appropriately. 733275970Scy */ 734275970Scy if (cmd == RTM_DELETE) 735275970Scy goto doit; 736275970Scy bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 737275970Scy rtm->rtm_flags = flags; 738275970Scy rtm->rtm_version = RTM_VERSION; 739275970Scy 740275970Scy switch (cmd) { 741275970Scy default: 742275970Scy errx(1, "internal wrong cmd"); 743275970Scy case RTM_ADD: 744275970Scy rtm->rtm_addrs |= RTA_GATEWAY; 745275970Scy rtm->rtm_rmx.rmx_expire = expire_time; 746330141Sdelphij rtm->rtm_inits = RTV_EXPIRE; 747330141Sdelphij rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 748330141Sdelphij if (doing_proxy) { 749330141Sdelphij rtm->rtm_addrs |= RTA_NETMASK; 750330141Sdelphij rtm->rtm_flags &= ~RTF_HOST; 751330141Sdelphij } 752330141Sdelphij /* FALLTHROUGH */ 753330141Sdelphij case RTM_GET: 754330141Sdelphij rtm->rtm_addrs |= RTA_DST; 755330141Sdelphij } 756330141Sdelphij#define NEXTADDR(w, s) \ 757330141Sdelphij do { \ 758275970Scy if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 759285612Sdelphij bcopy((s), cp, sizeof(*(s))); \ 760330141Sdelphij cp += SA_SIZE(s); \ 761330141Sdelphij } \ 762330141Sdelphij } while (0) 763330141Sdelphij 764285612Sdelphij NEXTADDR(RTA_DST, dst); 765275970Scy NEXTADDR(RTA_GATEWAY, sdl); 766275970Scy NEXTADDR(RTA_NETMASK, som); 767275970Scy 768275970Scy rtm->rtm_msglen = cp - (char *)&m_rtmsg; 769275970Scydoit: 770285612Sdelphij l = rtm->rtm_msglen; 771275970Scy rtm->rtm_seq = ++seq; 772275970Scy rtm->rtm_type = cmd; 773275970Scy if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 774275970Scy if (errno != ESRCH || cmd != RTM_DELETE) { 775275970Scy warn("writing to routing socket"); 776275970Scy return (NULL); 777275970Scy } 778275970Scy } 779275970Scy do { 780275970Scy l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 781275970Scy } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 782275970Scy if (l < 0) 783275970Scy warn("read from routing socket"); 784275970Scy return (rtm); 785275970Scy} 786275970Scy 787275970Scy/* 788275970Scy * get_ether_addr - get the hardware address of an interface on the 789275970Scy * the same subnet as ipaddr. 790275970Scy */ 791275970Scy#define MAX_IFS 32 792275970Scy 793275970Scystatic int 794275970Scyget_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 795275970Scy{ 796275970Scy struct ifreq *ifr, *ifend, *ifp; 797275970Scy in_addr_t ina, mask; 798275970Scy struct sockaddr_dl *dla; 799275970Scy struct ifreq ifreq; 800275970Scy struct ifconf ifc; 801275970Scy struct ifreq ifs[MAX_IFS]; 802285612Sdelphij int sock; 803275970Scy int retval = 0; 804275970Scy 805275970Scy sock = socket(AF_INET, SOCK_DGRAM, 0); 806275970Scy if (sock < 0) 807275970Scy err(1, "socket"); 808275970Scy 809275970Scy ifc.ifc_len = sizeof(ifs); 810275970Scy ifc.ifc_req = ifs; 811275970Scy if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 812275970Scy warnx("ioctl(SIOCGIFCONF)"); 813275970Scy goto done; 814275970Scy } 815275970Scy 816275970Scy#define NEXTIFR(i) \ 817275970Scy ((struct ifreq *)((char *)&(i)->ifr_addr \ 818275970Scy + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 819275970Scy 820275970Scy /* 821275970Scy * Scan through looking for an interface with an Internet 822275970Scy * address on the same subnet as `ipaddr'. 823275970Scy */ 824275970Scy ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 825275970Scy for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 826275970Scy if (ifr->ifr_addr.sa_family != AF_INET) 827275970Scy continue; 828275970Scy strncpy(ifreq.ifr_name, ifr->ifr_name, 829275970Scy sizeof(ifreq.ifr_name)); 830285612Sdelphij ifreq.ifr_addr = ifr->ifr_addr; 831275970Scy /* 832275970Scy * Check that the interface is up, 833275970Scy * and not point-to-point or loopback. 834275970Scy */ 835275970Scy if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 836275970Scy continue; 837275970Scy if ((ifreq.ifr_flags & 838275970Scy (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 839275970Scy IFF_LOOPBACK|IFF_NOARP)) 840275970Scy != (IFF_UP|IFF_BROADCAST)) 841275970Scy continue; 842275970Scy /* 843275970Scy * Get its netmask and check that it's on 844275970Scy * the right subnet. 845275970Scy */ 846275970Scy if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 847275970Scy continue; 848275970Scy mask = ((struct sockaddr_in *) 849275970Scy &ifreq.ifr_addr)->sin_addr.s_addr; 850275970Scy ina = ((struct sockaddr_in *) 851275970Scy &ifr->ifr_addr)->sin_addr.s_addr; 852275970Scy if ((ipaddr & mask) == (ina & mask)) 853275970Scy break; /* ok, we got it! */ 854275970Scy } 855275970Scy 856275970Scy if (ifr >= ifend) 857275970Scy goto done; 858275970Scy 859275970Scy /* 860275970Scy * Now scan through again looking for a link-level address 861275970Scy * for this interface. 862275970Scy */ 863275970Scy ifp = ifr; 864275970Scy for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 865275970Scy if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 866275970Scy ifr->ifr_addr.sa_family == AF_LINK) 867275970Scy break; 868275970Scy if (ifr >= ifend) 869275970Scy goto done; 870275970Scy /* 871275970Scy * Found the link-level address - copy it out 872275970Scy */ 873275970Scy dla = (struct sockaddr_dl *) &ifr->ifr_addr; 874275970Scy memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 875275970Scy printf("using interface %s for proxy with address ", 876275970Scy ifp->ifr_name); 877275970Scy printf("%s\n", ether_ntoa(hwaddr)); 878275970Scy retval = dla->sdl_alen; 879275970Scydone: 880275970Scy close(sock); 881275970Scy return (retval); 882275970Scy} 883275970Scy