arp.c revision 201282
1250003Sadrian/* 2250003Sadrian * Copyright (c) 1984, 1993 3250003Sadrian * The Regents of the University of California. All rights reserved. 4250003Sadrian * 5250003Sadrian * This code is derived from software contributed to Berkeley by 6250003Sadrian * Sun Microsystems, Inc. 7250003Sadrian * 8250003Sadrian * Redistribution and use in source and binary forms, with or without 9250003Sadrian * modification, are permitted provided that the following conditions 10250003Sadrian * are met: 11250003Sadrian * 1. Redistributions of source code must retain the above copyright 12250003Sadrian * notice, this list of conditions and the following disclaimer. 13250003Sadrian * 2. Redistributions in binary form must reproduce the above copyright 14250003Sadrian * notice, this list of conditions and the following disclaimer in the 15250003Sadrian * documentation and/or other materials provided with the distribution. 16250003Sadrian * 4. Neither the name of the University nor the names of its contributors 17250003Sadrian * may be used to endorse or promote products derived from this software 18250003Sadrian * without specific prior written permission. 19250003Sadrian * 20250003Sadrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21250003Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22250003Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23250003Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24250003Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25250003Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26250003Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27250003Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28250003Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29250003Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30250003Sadrian * SUCH DAMAGE. 31250003Sadrian */ 32250003Sadrian 33250003Sadrian#if 0 34250003Sadrian#ifndef lint 35250003Sadrianstatic char const copyright[] = 36250003Sadrian"@(#) Copyright (c) 1984, 1993\n\ 37250003Sadrian The Regents of the University of California. All rights reserved.\n"; 38250003Sadrian#endif /* not lint */ 39250003Sadrian 40250003Sadrian#ifndef lint 41250003Sadrianstatic char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; 42250003Sadrian#endif /* not lint */ 43250003Sadrian#endif 44250003Sadrian#include <sys/cdefs.h> 45250003Sadrian__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 201282 2009-12-30 21:35:34Z qingli $"); 46250003Sadrian 47250003Sadrian/* 48250003Sadrian * arp - display, set, and delete arp table entries 49250003Sadrian */ 50250003Sadrian 51250003Sadrian 52250003Sadrian#include <sys/param.h> 53250003Sadrian#include <sys/file.h> 54250003Sadrian#include <sys/socket.h> 55250003Sadrian#include <sys/sockio.h> 56250003Sadrian#include <sys/sysctl.h> 57250003Sadrian#include <sys/ioctl.h> 58250003Sadrian#include <sys/time.h> 59250003Sadrian 60250003Sadrian#include <net/if.h> 61250003Sadrian#include <net/if_dl.h> 62250003Sadrian#include <net/if_types.h> 63250003Sadrian#include <net/route.h> 64250003Sadrian#include <net/iso88025.h> 65250003Sadrian 66250003Sadrian#include <netinet/in.h> 67250003Sadrian#include <netinet/if_ether.h> 68250003Sadrian 69250003Sadrian#include <arpa/inet.h> 70250003Sadrian 71250003Sadrian#include <ctype.h> 72250003Sadrian#include <err.h> 73250003Sadrian#include <errno.h> 74250003Sadrian#include <netdb.h> 75250003Sadrian#include <nlist.h> 76250003Sadrian#include <paths.h> 77250003Sadrian#include <stdio.h> 78250003Sadrian#include <stdlib.h> 79250003Sadrian#include <string.h> 80250003Sadrian#include <strings.h> 81250003Sadrian#include <unistd.h> 82250003Sadrian 83250003Sadriantypedef void (action_fn)(struct sockaddr_dl *sdl, 84250003Sadrian struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); 85250003Sadrian 86250003Sadrianstatic int search(u_long addr, action_fn *action); 87250003Sadrianstatic action_fn print_entry; 88250003Sadrianstatic action_fn nuke_entry; 89250003Sadrian 90250003Sadrianstatic int delete(char *host, int do_proxy); 91250003Sadrianstatic void usage(void); 92250003Sadrianstatic int set(int argc, char **argv); 93250003Sadrianstatic int get(char *host); 94250003Sadrianstatic int file(char *name); 95250003Sadrianstatic struct rt_msghdr *rtmsg(int cmd, 96250003Sadrian struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); 97250003Sadrianstatic int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); 98250003Sadrianstatic struct sockaddr_inarp *getaddr(char *host); 99250003Sadrianstatic int valid_type(int type); 100250003Sadrian 101250003Sadrianstatic int nflag; /* no reverse dns lookups */ 102250003Sadrianstatic char *rifname; 103250003Sadrian 104250003Sadrianstatic int expire_time, flags, doing_proxy, proxy_only; 105250003Sadrian 106250003Sadrian/* which function we're supposed to do */ 107250003Sadrian#define F_GET 1 108250003Sadrian#define F_SET 2 109250003Sadrian#define F_FILESET 3 110250003Sadrian#define F_REPLACE 4 111250003Sadrian#define F_DELETE 5 112250003Sadrian 113250003Sadrian#define SETFUNC(f) { if (func) usage(); func = (f); } 114250003Sadrian 115250003Sadrianint 116250003Sadrianmain(int argc, char *argv[]) 117250003Sadrian{ 118250003Sadrian int ch, func = 0; 119250003Sadrian int rtn = 0; 120250003Sadrian int aflag = 0; /* do it for all entries */ 121250003Sadrian 122250003Sadrian while ((ch = getopt(argc, argv, "andfsSi:")) != -1) 123250003Sadrian switch(ch) { 124250003Sadrian case 'a': 125250003Sadrian aflag = 1; 126250003Sadrian break; 127250003Sadrian case 'd': 128250003Sadrian SETFUNC(F_DELETE); 129250003Sadrian break; 130250003Sadrian case 'n': 131250003Sadrian nflag = 1; 132250003Sadrian break; 133250003Sadrian case 'S': 134250003Sadrian SETFUNC(F_REPLACE); 135250003Sadrian break; 136250003Sadrian case 's': 137250003Sadrian SETFUNC(F_SET); 138250003Sadrian break; 139250003Sadrian case 'f' : 140250003Sadrian SETFUNC(F_FILESET); 141250003Sadrian break; 142250003Sadrian case 'i': 143250003Sadrian rifname = optarg; 144250003Sadrian break; 145250003Sadrian case '?': 146250003Sadrian default: 147250003Sadrian usage(); 148250003Sadrian } 149250003Sadrian argc -= optind; 150250003Sadrian argv += optind; 151250003Sadrian 152250003Sadrian if (!func) 153250003Sadrian func = F_GET; 154250003Sadrian if (rifname) { 155250003Sadrian if (func != F_GET && !(func == F_DELETE && aflag)) 156250003Sadrian errx(1, "-i not applicable to this operation"); 157250003Sadrian if (if_nametoindex(rifname) == 0) { 158250003Sadrian if (errno == ENXIO) 159250003Sadrian errx(1, "interface %s does not exist", rifname); 160250003Sadrian else 161250003Sadrian err(1, "if_nametoindex(%s)", rifname); 162250008Sadrian } 163250008Sadrian } 164250003Sadrian switch (func) { 165250003Sadrian case F_GET: 166250003Sadrian if (aflag) { 167250003Sadrian if (argc != 0) 168250003Sadrian usage(); 169250003Sadrian search(0, print_entry); 170250003Sadrian } else { 171250003Sadrian if (argc != 1) 172250003Sadrian usage(); 173250008Sadrian rtn = get(argv[0]); 174250003Sadrian } 175250003Sadrian break; 176250003Sadrian case F_SET: 177250003Sadrian case F_REPLACE: 178250003Sadrian if (argc < 2 || argc > 6) 179250003Sadrian usage(); 180250003Sadrian if (func == F_REPLACE) 181250003Sadrian (void)delete(argv[0], 0); 182250003Sadrian rtn = set(argc, argv) ? 1 : 0; 183250003Sadrian break; 184250003Sadrian case F_DELETE: 185250003Sadrian if (aflag) { 186250003Sadrian if (argc != 0) 187250003Sadrian usage(); 188250003Sadrian search(0, nuke_entry); 189250003Sadrian } else { 190250003Sadrian if (argc == 2 && strncmp(argv[1], "pub", 3) == 0) 191250003Sadrian ch = SIN_PROXY; 192250003Sadrian else if (argc == 1) 193250003Sadrian ch = 0; 194250003Sadrian else 195250008Sadrian usage(); 196250003Sadrian rtn = delete(argv[0], ch); 197250003Sadrian } 198250003Sadrian break; 199250003Sadrian case F_FILESET: 200250003Sadrian if (argc != 1) 201250003Sadrian usage(); 202250003Sadrian rtn = file(argv[0]); 203250003Sadrian break; 204250003Sadrian } 205250003Sadrian 206250003Sadrian return (rtn); 207250003Sadrian} 208250003Sadrian 209250003Sadrian/* 210250003Sadrian * Process a file to set standard arp entries 211250003Sadrian */ 212250003Sadrianstatic int 213250008Sadrianfile(char *name) 214250008Sadrian{ 215250003Sadrian FILE *fp; 216250003Sadrian int i, retval; 217250003Sadrian char line[100], arg[5][50], *args[5], *p; 218250003Sadrian 219250003Sadrian if ((fp = fopen(name, "r")) == NULL) 220250003Sadrian err(1, "cannot open %s", name); 221250003Sadrian args[0] = &arg[0][0]; 222250003Sadrian args[1] = &arg[1][0]; 223250003Sadrian args[2] = &arg[2][0]; 224250003Sadrian args[3] = &arg[3][0]; 225250003Sadrian args[4] = &arg[4][0]; 226250003Sadrian retval = 0; 227250003Sadrian while(fgets(line, sizeof(line), fp) != NULL) { 228250003Sadrian if ((p = strchr(line, '#')) != NULL) 229250003Sadrian *p = '\0'; 230250003Sadrian for (p = line; isblank(*p); p++); 231250003Sadrian if (*p == '\n' || *p == '\0') 232250003Sadrian continue; 233250003Sadrian i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], 234250003Sadrian arg[2], arg[3], arg[4]); 235250003Sadrian if (i < 2) { 236250003Sadrian warnx("bad line: %s", line); 237250003Sadrian retval = 1; 238250003Sadrian continue; 239250003Sadrian } 240250003Sadrian if (set(i, args)) 241250003Sadrian retval = 1; 242250003Sadrian } 243250003Sadrian fclose(fp); 244250003Sadrian return (retval); 245250003Sadrian} 246250003Sadrian 247250003Sadrian/* 248250003Sadrian * Given a hostname, fills up a (static) struct sockaddr_inarp with 249250003Sadrian * the address of the host and returns a pointer to the 250250003Sadrian * structure. 251250003Sadrian */ 252250003Sadrianstatic struct sockaddr_inarp * 253250003Sadriangetaddr(char *host) 254250003Sadrian{ 255250003Sadrian struct hostent *hp; 256250003Sadrian static struct sockaddr_inarp reply; 257250003Sadrian 258250003Sadrian bzero(&reply, sizeof(reply)); 259250003Sadrian reply.sin_len = sizeof(reply); 260250003Sadrian reply.sin_family = AF_INET; 261250003Sadrian reply.sin_addr.s_addr = inet_addr(host); 262250003Sadrian if (reply.sin_addr.s_addr == INADDR_NONE) { 263250003Sadrian if (!(hp = gethostbyname(host))) { 264250003Sadrian warnx("%s: %s", host, hstrerror(h_errno)); 265250003Sadrian return (NULL); 266250003Sadrian } 267250003Sadrian bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 268250003Sadrian sizeof reply.sin_addr); 269250003Sadrian } 270250003Sadrian return (&reply); 271250003Sadrian} 272250003Sadrian 273250003Sadrian/* 274250003Sadrian * Returns true if the type is a valid one for ARP. 275250003Sadrian */ 276250003Sadrianstatic int 277250003Sadrianvalid_type(int type) 278250003Sadrian{ 279250003Sadrian 280250003Sadrian switch (type) { 281250003Sadrian case IFT_ETHER: 282250003Sadrian case IFT_FDDI: 283250003Sadrian case IFT_ISO88023: 284250003Sadrian case IFT_ISO88024: 285250003Sadrian case IFT_ISO88025: 286250003Sadrian case IFT_L2VLAN: 287250003Sadrian case IFT_BRIDGE: 288250003Sadrian return (1); 289250003Sadrian default: 290250003Sadrian return (0); 291250003Sadrian } 292250003Sadrian} 293250003Sadrian 294250003Sadrian/* 295250003Sadrian * Set an individual arp entry 296250003Sadrian */ 297250003Sadrianstatic int 298250003Sadrianset(int argc, char **argv) 299250003Sadrian{ 300250003Sadrian struct sockaddr_inarp *addr; 301250003Sadrian struct sockaddr_inarp *dst; /* what are we looking for */ 302250003Sadrian struct sockaddr_dl *sdl; 303250003Sadrian struct rt_msghdr *rtm; 304250003Sadrian struct ether_addr *ea; 305250003Sadrian char *host = argv[0], *eaddr = argv[1]; 306250003Sadrian struct sockaddr_dl sdl_m; 307250003Sadrian 308250003Sadrian argc -= 2; 309250003Sadrian argv += 2; 310250003Sadrian 311250003Sadrian bzero(&sdl_m, sizeof(sdl_m)); 312250003Sadrian sdl_m.sdl_len = sizeof(sdl_m); 313250003Sadrian sdl_m.sdl_family = AF_LINK; 314250003Sadrian 315250003Sadrian dst = getaddr(host); 316250003Sadrian if (dst == NULL) 317250003Sadrian return (1); 318250003Sadrian doing_proxy = flags = proxy_only = expire_time = 0; 319250008Sadrian while (argc-- > 0) { 320250003Sadrian if (strncmp(argv[0], "temp", 4) == 0) { 321250003Sadrian struct timeval tv; 322250003Sadrian gettimeofday(&tv, 0); 323250003Sadrian expire_time = tv.tv_sec + 20 * 60; 324250003Sadrian } else if (strncmp(argv[0], "pub", 3) == 0) { 325250008Sadrian flags |= RTF_ANNOUNCE; 326250003Sadrian doing_proxy = 1; 327250003Sadrian if (argc && strncmp(argv[1], "only", 3) == 0) { 328250003Sadrian proxy_only = 1; 329250003Sadrian argc--; argv++; 330250003Sadrian } 331250003Sadrian } else if (strncmp(argv[0], "blackhole", 9) == 0) { 332250003Sadrian if (flags & RTF_REJECT) { 333250003Sadrian printf("Choose one of blackhole or reject, not both.\n"); 334250003Sadrian } 335250003Sadrian flags |= RTF_BLACKHOLE; 336250003Sadrian } else if (strncmp(argv[0], "reject", 6) == 0) { 337250003Sadrian if (flags & RTF_BLACKHOLE) { 338250003Sadrian printf("Choose one of blackhole or reject, not both.\n"); 339250003Sadrian } 340250008Sadrian flags |= RTF_REJECT; 341250003Sadrian } else if (strncmp(argv[0], "trail", 5) == 0) { 342250003Sadrian /* XXX deprecated and undocumented feature */ 343250003Sadrian printf("%s: Sending trailers is no longer supported\n", 344250008Sadrian host); 345250003Sadrian } 346250003Sadrian argv++; 347250003Sadrian } 348250003Sadrian ea = (struct ether_addr *)LLADDR(&sdl_m); 349250003Sadrian if (doing_proxy && !strcmp(eaddr, "auto")) { 350250003Sadrian if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 351250003Sadrian printf("no interface found for %s\n", 352250003Sadrian inet_ntoa(dst->sin_addr)); 353250003Sadrian return (1); 354250003Sadrian } 355250003Sadrian sdl_m.sdl_alen = ETHER_ADDR_LEN; 356250003Sadrian } else { 357250003Sadrian struct ether_addr *ea1 = ether_aton(eaddr); 358250003Sadrian 359250003Sadrian if (ea1 == NULL) { 360250003Sadrian warnx("invalid Ethernet address '%s'", eaddr); 361250003Sadrian return (1); 362250003Sadrian } else { 363250003Sadrian *ea = *ea1; 364250003Sadrian sdl_m.sdl_alen = ETHER_ADDR_LEN; 365250003Sadrian } 366250003Sadrian } 367250003Sadrian 368250003Sadrian /* 369250003Sadrian * In the case a proxy-arp entry is being added for 370250003Sadrian * a remote end point, the RTF_ANNOUNCE flag in the 371250003Sadrian * RTM_GET command is an indication to the kernel 372250003Sadrian * routing code that the interface associated with 373250003Sadrian * the prefix route covering the local end of the 374250003Sadrian * PPP link should be returned, on which ARP applies. 375250003Sadrian */ 376250003Sadrian rtm = rtmsg(RTM_GET, dst, &sdl_m); 377250003Sadrian if (rtm == NULL) { 378250003Sadrian warn("%s", host); 379250003Sadrian return (1); 380250003Sadrian } 381250003Sadrian addr = (struct sockaddr_inarp *)(rtm + 1); 382250003Sadrian sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 383250003Sadrian if (addr->sin_addr.s_addr == dst->sin_addr.s_addr) { 384250003Sadrian printf("set: proxy entry exists for non 802 device\n"); 385250003Sadrian return (1); 386250003Sadrian } 387250003Sadrian 388250003Sadrian if ((sdl->sdl_family != AF_LINK) || 389250003Sadrian (rtm->rtm_flags & RTF_GATEWAY) || 390250003Sadrian !valid_type(sdl->sdl_type)) { 391250003Sadrian printf("cannot intuit interface index and type for %s\n", host); 392250003Sadrian return (1); 393250003Sadrian } 394250003Sadrian sdl_m.sdl_type = sdl->sdl_type; 395250003Sadrian sdl_m.sdl_index = sdl->sdl_index; 396250003Sadrian return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 397250003Sadrian} 398250003Sadrian 399250003Sadrian/* 400250003Sadrian * Display an individual arp entry 401250003Sadrian */ 402250003Sadrianstatic int 403250003Sadrianget(char *host) 404250003Sadrian{ 405250003Sadrian struct sockaddr_inarp *addr; 406250003Sadrian 407250003Sadrian addr = getaddr(host); 408250003Sadrian if (addr == NULL) 409250003Sadrian return (1); 410250003Sadrian if (0 == search(addr->sin_addr.s_addr, print_entry)) { 411250003Sadrian printf("%s (%s) -- no entry", 412250003Sadrian host, inet_ntoa(addr->sin_addr)); 413250003Sadrian if (rifname) 414250003Sadrian printf(" on %s", rifname); 415250003Sadrian printf("\n"); 416250003Sadrian return (1); 417250003Sadrian } 418250003Sadrian return (0); 419250003Sadrian} 420250003Sadrian 421250003Sadrian/* 422250003Sadrian * Delete an arp entry 423250003Sadrian */ 424250003Sadrianstatic int 425250003Sadriandelete(char *host, int do_proxy) 426250003Sadrian{ 427250003Sadrian struct sockaddr_inarp *addr, *dst; 428250003Sadrian struct rt_msghdr *rtm; 429250003Sadrian struct sockaddr_dl *sdl; 430250003Sadrian struct sockaddr_dl sdl_m; 431250003Sadrian 432250003Sadrian dst = getaddr(host); 433250003Sadrian if (dst == NULL) 434250003Sadrian return (1); 435250003Sadrian 436250003Sadrian /* 437250003Sadrian * Perform a regular entry delete first. 438250003Sadrian */ 439250003Sadrian flags &= ~RTF_ANNOUNCE; 440250003Sadrian 441250003Sadrian /* 442250003Sadrian * setup the data structure to notify the kernel 443250003Sadrian * it is the ARP entry the RTM_GET is interested 444250003Sadrian * in 445250003Sadrian */ 446250003Sadrian bzero(&sdl_m, sizeof(sdl_m)); 447250003Sadrian sdl_m.sdl_len = sizeof(sdl_m); 448250003Sadrian sdl_m.sdl_family = AF_LINK; 449250003Sadrian 450250003Sadrian for (;;) { /* try twice */ 451250003Sadrian rtm = rtmsg(RTM_GET, dst, &sdl_m); 452250003Sadrian if (rtm == NULL) { 453250003Sadrian warn("%s", host); 454250003Sadrian return (1); 455250003Sadrian } 456250003Sadrian addr = (struct sockaddr_inarp *)(rtm + 1); 457250003Sadrian sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 458250003Sadrian 459250003Sadrian /* 460250003Sadrian * With the new L2/L3 restructure, the route 461250003Sadrian * returned is a prefix route. The important 462250003Sadrian * piece of information from the previous 463250003Sadrian * RTM_GET is the interface index. In the 464250003Sadrian * case of ECMP, the kernel will traverse 465250003Sadrian * the route group for the given entry. 466250003Sadrian */ 467250003Sadrian if (sdl->sdl_family == AF_LINK && 468250003Sadrian !(rtm->rtm_flags & RTF_GATEWAY) && 469250003Sadrian valid_type(sdl->sdl_type) ) { 470250003Sadrian addr->sin_addr.s_addr = dst->sin_addr.s_addr; 471250003Sadrian break; 472250003Sadrian } 473250003Sadrian 474250003Sadrian /* 475250003Sadrian * Regualar entry delete failed, now check if there 476250003Sadrian * is a proxy-arp entry to remove. 477250003Sadrian */ 478250003Sadrian if (flags & RTF_ANNOUNCE) { 479250003Sadrian fprintf(stderr, "delete: cannot locate %s\n",host); 480250003Sadrian return (1); 481250003Sadrian } 482250003Sadrian 483250003Sadrian flags |= RTF_ANNOUNCE; 484250003Sadrian } 485250003Sadrian rtm->rtm_flags |= RTF_LLDATA; 486250003Sadrian if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 487250003Sadrian printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 488250003Sadrian return (0); 489250003Sadrian } 490250003Sadrian return (1); 491250003Sadrian} 492250003Sadrian 493250003Sadrian 494250003Sadrian/* 495250003Sadrian * Search the arp table and do some action on matching entries 496250003Sadrian */ 497250003Sadrianstatic int 498250003Sadriansearch(u_long addr, action_fn *action) 499250003Sadrian{ 500250003Sadrian int mib[6]; 501250003Sadrian size_t needed; 502250003Sadrian char *lim, *buf, *next; 503250003Sadrian struct rt_msghdr *rtm; 504250003Sadrian struct sockaddr_inarp *sin2; 505250003Sadrian struct sockaddr_dl *sdl; 506250003Sadrian char ifname[IF_NAMESIZE]; 507250008Sadrian int st, found_entry = 0; 508250003Sadrian 509250003Sadrian mib[0] = CTL_NET; 510250003Sadrian mib[1] = PF_ROUTE; 511250008Sadrian mib[2] = 0; 512250003Sadrian mib[3] = AF_INET; 513250003Sadrian mib[4] = NET_RT_FLAGS; 514250003Sadrian#ifdef RTF_LLINFO 515250003Sadrian mib[5] = RTF_LLINFO; 516250003Sadrian#else 517250003Sadrian mib[5] = 0; 518250003Sadrian#endif 519250003Sadrian if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 520250003Sadrian err(1, "route-sysctl-estimate"); 521250003Sadrian if (needed == 0) /* empty table */ 522250003Sadrian return 0; 523250003Sadrian buf = NULL; 524250003Sadrian for (;;) { 525250003Sadrian buf = reallocf(buf, needed); 526250003Sadrian if (buf == NULL) 527250003Sadrian errx(1, "could not reallocate memory"); 528250003Sadrian st = sysctl(mib, 6, buf, &needed, NULL, 0); 529250003Sadrian if (st == 0 || errno != ENOMEM) 530250003Sadrian break; 531250003Sadrian needed += needed / 8; 532250003Sadrian } 533250003Sadrian if (st == -1) 534250003Sadrian err(1, "actual retrieval of routing table"); 535250008Sadrian lim = buf + needed; 536250003Sadrian for (next = buf; next < lim; next += rtm->rtm_msglen) { 537250003Sadrian rtm = (struct rt_msghdr *)next; 538250003Sadrian sin2 = (struct sockaddr_inarp *)(rtm + 1); 539250003Sadrian sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 540250003Sadrian if (rifname && if_indextoname(sdl->sdl_index, ifname) && 541250003Sadrian strcmp(ifname, rifname)) 542250003Sadrian continue; 543250003Sadrian if (addr) { 544250003Sadrian if (addr != sin2->sin_addr.s_addr) 545250003Sadrian continue; 546250003Sadrian found_entry = 1; 547250003Sadrian } 548250003Sadrian (*action)(sdl, sin2, rtm); 549250003Sadrian } 550250003Sadrian free(buf); 551250003Sadrian return (found_entry); 552250003Sadrian} 553250003Sadrian 554250003Sadrian/* 555250003Sadrian * Display an arp entry 556250003Sadrian */ 557250003Sadrianstatic void 558250003Sadrianprint_entry(struct sockaddr_dl *sdl, 559250003Sadrian struct sockaddr_inarp *addr, struct rt_msghdr *rtm) 560250003Sadrian{ 561250003Sadrian const char *host; 562250003Sadrian struct hostent *hp; 563250003Sadrian struct iso88025_sockaddr_dl_data *trld; 564250003Sadrian char ifname[IF_NAMESIZE]; 565250008Sadrian int seg; 566250003Sadrian 567250003Sadrian if (nflag == 0) 568250003Sadrian hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 569250003Sadrian sizeof addr->sin_addr, AF_INET); 570250003Sadrian else 571250003Sadrian hp = 0; 572250003Sadrian if (hp) 573250003Sadrian host = hp->h_name; 574250003Sadrian else { 575250003Sadrian host = "?"; 576250003Sadrian if (h_errno == TRY_AGAIN) 577250003Sadrian nflag = 1; 578250003Sadrian } 579250003Sadrian printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 580250003Sadrian if (sdl->sdl_alen) { 581250003Sadrian if ((sdl->sdl_type == IFT_ETHER || 582250003Sadrian sdl->sdl_type == IFT_L2VLAN || 583250003Sadrian sdl->sdl_type == IFT_BRIDGE) && 584250003Sadrian sdl->sdl_alen == ETHER_ADDR_LEN) 585250003Sadrian printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 586250003Sadrian else { 587250003Sadrian int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 588250003Sadrian 589250003Sadrian printf("%s", link_ntoa(sdl) + n); 590250003Sadrian } 591250003Sadrian } else 592250003Sadrian printf("(incomplete)"); 593250003Sadrian if (if_indextoname(sdl->sdl_index, ifname) != NULL) 594250003Sadrian printf(" on %s", ifname); 595250003Sadrian if (rtm->rtm_rmx.rmx_expire == 0) 596250003Sadrian printf(" permanent"); 597250003Sadrian if (addr->sin_other & SIN_PROXY) 598250003Sadrian printf(" published (proxy only)"); 599250003Sadrian if (rtm->rtm_flags & RTF_ANNOUNCE) 600250003Sadrian printf(" published"); 601250003Sadrian switch(sdl->sdl_type) { 602250003Sadrian case IFT_ETHER: 603250003Sadrian printf(" [ethernet]"); 604250003Sadrian break; 605250003Sadrian case IFT_ISO88025: 606250003Sadrian printf(" [token-ring]"); 607250003Sadrian trld = SDL_ISO88025(sdl); 608250003Sadrian if (trld->trld_rcf != 0) { 609250003Sadrian printf(" rt=%x", ntohs(trld->trld_rcf)); 610250003Sadrian for (seg = 0; 611250003Sadrian seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 612250003Sadrian seg++) 613250003Sadrian printf(":%x", ntohs(*(trld->trld_route[seg]))); 614250003Sadrian } 615250003Sadrian break; 616250003Sadrian case IFT_FDDI: 617250003Sadrian printf(" [fddi]"); 618250003Sadrian break; 619250003Sadrian case IFT_ATM: 620250003Sadrian printf(" [atm]"); 621250003Sadrian break; 622250003Sadrian case IFT_L2VLAN: 623250003Sadrian printf(" [vlan]"); 624250003Sadrian break; 625250003Sadrian case IFT_IEEE1394: 626250003Sadrian printf(" [firewire]"); 627250003Sadrian break; 628250003Sadrian case IFT_BRIDGE: 629250003Sadrian printf(" [bridge]"); 630250003Sadrian break; 631250003Sadrian default: 632250003Sadrian break; 633250003Sadrian } 634250003Sadrian 635250003Sadrian printf("\n"); 636250003Sadrian 637250003Sadrian} 638250003Sadrian 639250003Sadrian/* 640250003Sadrian * Nuke an arp entry 641250003Sadrian */ 642250003Sadrianstatic void 643250003Sadriannuke_entry(struct sockaddr_dl *sdl __unused, 644250003Sadrian struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused) 645250003Sadrian{ 646250003Sadrian char ip[20]; 647250003Sadrian 648250003Sadrian snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 649250003Sadrian (void)delete(ip, 0); 650250003Sadrian} 651250003Sadrian 652250003Sadrianstatic void 653250003Sadrianusage(void) 654250003Sadrian{ 655250003Sadrian fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 656250003Sadrian "usage: arp [-n] [-i interface] hostname", 657250003Sadrian " arp [-n] [-i interface] -a", 658250003Sadrian " arp -d hostname [pub]", 659250003Sadrian " arp -d [-i interface] -a", 660250003Sadrian " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 661250003Sadrian " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", 662250003Sadrian " arp -f filename"); 663250003Sadrian exit(1); 664250003Sadrian} 665250003Sadrian 666250003Sadrianstatic struct rt_msghdr * 667250003Sadrianrtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) 668250003Sadrian{ 669250003Sadrian static int seq; 670250003Sadrian int rlen; 671250003Sadrian int l; 672250003Sadrian struct sockaddr_in so_mask, *som = &so_mask; 673250003Sadrian static int s = -1; 674250003Sadrian static pid_t pid; 675250003Sadrian 676250003Sadrian static struct { 677250003Sadrian struct rt_msghdr m_rtm; 678250003Sadrian char m_space[512]; 679250003Sadrian } m_rtmsg; 680250003Sadrian 681250003Sadrian struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 682250003Sadrian char *cp = m_rtmsg.m_space; 683250003Sadrian 684250003Sadrian if (s < 0) { /* first time: open socket, get pid */ 685250003Sadrian s = socket(PF_ROUTE, SOCK_RAW, 0); 686250003Sadrian if (s < 0) 687250003Sadrian err(1, "socket"); 688250003Sadrian pid = getpid(); 689250003Sadrian } 690250003Sadrian bzero(&so_mask, sizeof(so_mask)); 691250003Sadrian so_mask.sin_len = 8; 692250003Sadrian so_mask.sin_addr.s_addr = 0xffffffff; 693250003Sadrian 694250003Sadrian errno = 0; 695250003Sadrian /* 696250003Sadrian * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 697250003Sadrian * appropriately. 698250003Sadrian */ 699250003Sadrian if (cmd == RTM_DELETE) 700250003Sadrian goto doit; 701250003Sadrian bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 702250003Sadrian rtm->rtm_flags = flags; 703250003Sadrian rtm->rtm_version = RTM_VERSION; 704250003Sadrian 705250003Sadrian switch (cmd) { 706250003Sadrian default: 707250003Sadrian errx(1, "internal wrong cmd"); 708250003Sadrian case RTM_ADD: 709250003Sadrian rtm->rtm_addrs |= RTA_GATEWAY; 710250003Sadrian rtm->rtm_rmx.rmx_expire = expire_time; 711250003Sadrian rtm->rtm_inits = RTV_EXPIRE; 712250003Sadrian rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); 713250003Sadrian dst->sin_other = 0; 714250003Sadrian if (doing_proxy) { 715250003Sadrian if (proxy_only) 716250003Sadrian dst->sin_other = SIN_PROXY; 717250003Sadrian else { 718250003Sadrian rtm->rtm_addrs |= RTA_NETMASK; 719250003Sadrian rtm->rtm_flags &= ~RTF_HOST; 720250003Sadrian } 721250003Sadrian } 722250003Sadrian /* FALLTHROUGH */ 723250003Sadrian case RTM_GET: 724250003Sadrian rtm->rtm_addrs |= RTA_DST; 725250003Sadrian } 726250003Sadrian#define NEXTADDR(w, s) \ 727250003Sadrian do { \ 728250003Sadrian if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 729250003Sadrian bcopy((s), cp, sizeof(*(s))); \ 730250003Sadrian cp += SA_SIZE(s); \ 731250003Sadrian } \ 732250003Sadrian } while (0) 733250003Sadrian 734250003Sadrian NEXTADDR(RTA_DST, dst); 735250003Sadrian NEXTADDR(RTA_GATEWAY, sdl); 736250003Sadrian NEXTADDR(RTA_NETMASK, som); 737250003Sadrian 738250003Sadrian rtm->rtm_msglen = cp - (char *)&m_rtmsg; 739250003Sadriandoit: 740250003Sadrian l = rtm->rtm_msglen; 741250003Sadrian rtm->rtm_seq = ++seq; 742250003Sadrian rtm->rtm_type = cmd; 743250003Sadrian if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 744250003Sadrian if (errno != ESRCH || cmd != RTM_DELETE) { 745250003Sadrian warn("writing to routing socket"); 746250003Sadrian return (NULL); 747250003Sadrian } 748250003Sadrian } 749250003Sadrian do { 750250003Sadrian l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 751250003Sadrian } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 752250003Sadrian if (l < 0) 753250003Sadrian warn("read from routing socket"); 754250003Sadrian return (rtm); 755250003Sadrian} 756250003Sadrian 757250003Sadrian/* 758250003Sadrian * get_ether_addr - get the hardware address of an interface on the 759250003Sadrian * the same subnet as ipaddr. 760250003Sadrian */ 761250003Sadrian#define MAX_IFS 32 762250003Sadrian 763250003Sadrianstatic int 764250003Sadrianget_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 765250003Sadrian{ 766250003Sadrian struct ifreq *ifr, *ifend, *ifp; 767250003Sadrian in_addr_t ina, mask; 768250003Sadrian struct sockaddr_dl *dla; 769250003Sadrian struct ifreq ifreq; 770250003Sadrian struct ifconf ifc; 771250003Sadrian struct ifreq ifs[MAX_IFS]; 772250003Sadrian int sock; 773250003Sadrian int retval = 0; 774250003Sadrian 775250003Sadrian sock = socket(AF_INET, SOCK_DGRAM, 0); 776250003Sadrian if (sock < 0) 777250003Sadrian err(1, "socket"); 778250003Sadrian 779250003Sadrian ifc.ifc_len = sizeof(ifs); 780250003Sadrian ifc.ifc_req = ifs; 781250003Sadrian if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 782250003Sadrian warnx("ioctl(SIOCGIFCONF)"); 783250003Sadrian goto done; 784250003Sadrian } 785250003Sadrian 786250003Sadrian#define NEXTIFR(i) \ 787250003Sadrian ((struct ifreq *)((char *)&(i)->ifr_addr \ 788250003Sadrian + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 789250003Sadrian 790250003Sadrian /* 791250003Sadrian * Scan through looking for an interface with an Internet 792250003Sadrian * address on the same subnet as `ipaddr'. 793250003Sadrian */ 794250003Sadrian ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 795250003Sadrian for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 796250003Sadrian if (ifr->ifr_addr.sa_family != AF_INET) 797250003Sadrian continue; 798250003Sadrian strncpy(ifreq.ifr_name, ifr->ifr_name, 799250003Sadrian sizeof(ifreq.ifr_name)); 800250003Sadrian ifreq.ifr_addr = ifr->ifr_addr; 801250003Sadrian /* 802250003Sadrian * Check that the interface is up, 803250003Sadrian * and not point-to-point or loopback. 804250003Sadrian */ 805250003Sadrian if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 806250003Sadrian continue; 807250003Sadrian if ((ifreq.ifr_flags & 808250003Sadrian (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 809250003Sadrian IFF_LOOPBACK|IFF_NOARP)) 810250003Sadrian != (IFF_UP|IFF_BROADCAST)) 811250003Sadrian continue; 812250003Sadrian /* 813250003Sadrian * Get its netmask and check that it's on 814250003Sadrian * the right subnet. 815250003Sadrian */ 816250003Sadrian if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 817250003Sadrian continue; 818250003Sadrian mask = ((struct sockaddr_in *) 819250003Sadrian &ifreq.ifr_addr)->sin_addr.s_addr; 820250003Sadrian ina = ((struct sockaddr_in *) 821250003Sadrian &ifr->ifr_addr)->sin_addr.s_addr; 822250003Sadrian if ((ipaddr & mask) == (ina & mask)) 823250003Sadrian break; /* ok, we got it! */ 824250003Sadrian } 825250003Sadrian 826250003Sadrian if (ifr >= ifend) 827250003Sadrian goto done; 828250003Sadrian 829250003Sadrian /* 830250003Sadrian * Now scan through again looking for a link-level address 831250003Sadrian * for this interface. 832250003Sadrian */ 833250003Sadrian ifp = ifr; 834250003Sadrian for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 835250003Sadrian if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 836250003Sadrian ifr->ifr_addr.sa_family == AF_LINK) 837250003Sadrian break; 838250003Sadrian if (ifr >= ifend) 839250003Sadrian goto done; 840250003Sadrian /* 841250003Sadrian * Found the link-level address - copy it out 842250003Sadrian */ 843250003Sadrian dla = (struct sockaddr_dl *) &ifr->ifr_addr; 844250003Sadrian memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 845250003Sadrian printf("using interface %s for proxy with address ", 846250003Sadrian ifp->ifr_name); 847250003Sadrian printf("%s\n", ether_ntoa(hwaddr)); 848250003Sadrian retval = dla->sdl_alen; 849250003Sadriandone: 850250003Sadrian close(sock); 851250003Sadrian return (retval); 852250003Sadrian} 853250003Sadrian