arp.c revision 154162
119370Spst/* 2130803Smarcel * Copyright (c) 1984, 1993 3130803Smarcel * The Regents of the University of California. All rights reserved. 498944Sobrien * 519370Spst * This code is derived from software contributed to Berkeley by 698944Sobrien * Sun Microsystems, Inc. 719370Spst * 898944Sobrien * Redistribution and use in source and binary forms, with or without 998944Sobrien * modification, are permitted provided that the following conditions 1098944Sobrien * are met: 1198944Sobrien * 1. Redistributions of source code must retain the above copyright 1219370Spst * notice, this list of conditions and the following disclaimer. 1398944Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1498944Sobrien * notice, this list of conditions and the following disclaimer in the 1598944Sobrien * documentation and/or other materials provided with the distribution. 1698944Sobrien * 4. Neither the name of the University nor the names of its contributors 1719370Spst * may be used to endorse or promote products derived from this software 1898944Sobrien * without specific prior written permission. 1998944Sobrien * 2098944Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2198944Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2219370Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2319370Spst * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2419370Spst * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2519370Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2619370Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2719370Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2819370Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2998944Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3098944Sobrien * SUCH DAMAGE. 3198944Sobrien */ 32130803Smarcel 3398944Sobrien#if 0 3419370Spst#ifndef lint 3519370Spststatic char const copyright[] = 3619370Spst"@(#) Copyright (c) 1984, 1993\n\ 3719370Spst The Regents of the University of California. All rights reserved.\n"; 3819370Spst#endif /* not lint */ 3919370Spst 4019370Spst#ifndef lint 4119370Spststatic char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; 4219370Spst#endif /* not lint */ 4319370Spst#endif 4419370Spst#include <sys/cdefs.h> 4598944Sobrien__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 154162 2006-01-10 05:17:16Z brooks $"); 4698944Sobrien 4719370Spst/* 4898944Sobrien * arp - display, set, and delete arp table entries 4998944Sobrien */ 5098944Sobrien 5198944Sobrien 5298944Sobrien#include <sys/param.h> 5398944Sobrien#include <sys/file.h> 5498944Sobrien#include <sys/socket.h> 5598944Sobrien#include <sys/sockio.h> 5698944Sobrien#include <sys/sysctl.h> 5798944Sobrien#include <sys/ioctl.h> 5898944Sobrien#include <sys/time.h> 5998944Sobrien 6098944Sobrien#include <net/if.h> 6119370Spst#include <net/if_dl.h> 6298944Sobrien#include <net/if_types.h> 6398944Sobrien#include <net/route.h> 6498944Sobrien#include <net/iso88025.h> 6519370Spst 6698944Sobrien#include <netinet/in.h> 6798944Sobrien#include <netinet/if_ether.h> 6819370Spst 6998944Sobrien#include <arpa/inet.h> 7098944Sobrien 7119370Spst#include <ctype.h> 7298944Sobrien#include <err.h> 7319370Spst#include <errno.h> 7419370Spst#include <netdb.h> 7519370Spst#include <nlist.h> 7619370Spst#include <paths.h> 7719370Spst#include <stdio.h> 7819370Spst#include <stdlib.h> 7919370Spst#include <string.h> 8019370Spst#include <strings.h> 8198944Sobrien#include <unistd.h> 8219370Spst 8398944Sobrientypedef void (action_fn)(struct sockaddr_dl *sdl, 8498944Sobrien struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); 8598944Sobrien 8698944Sobrienstatic int search(u_long addr, action_fn *action); 8798944Sobrienstatic action_fn print_entry; 8898944Sobrienstatic action_fn nuke_entry; 8998944Sobrien 9098944Sobrienstatic int delete(char *host, int do_proxy); 9119370Spststatic void usage(void); 9298944Sobrienstatic int set(int argc, char **argv); 9398944Sobrienstatic int get(char *host); 9498944Sobrienstatic int file(char *name); 9519370Spststatic struct rt_msghdr *rtmsg(int cmd, 9698944Sobrien struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); 9798944Sobrienstatic int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); 9898944Sobrienstatic struct sockaddr_inarp *getaddr(char *host); 9998944Sobrienstatic int valid_type(int type); 10098944Sobrien 10146283Sdfrstatic int nflag; /* no reverse dns lookups */ 10298944Sobrienstatic char *rifname; 10398944Sobrien 10498944Sobrienstatic int expire_time, flags, doing_proxy, proxy_only; 10519370Spst 10698944Sobrien/* which function we're supposed to do */ 10798944Sobrien#define F_GET 1 10898944Sobrien#define F_SET 2 10998944Sobrien#define F_FILESET 3 11098944Sobrien#define F_REPLACE 4 11198944Sobrien#define F_DELETE 5 11298944Sobrien 11398944Sobrien#define SETFUNC(f) { if (func) usage(); func = (f); } 11498944Sobrien 11598944Sobrienint 11698944Sobrienmain(int argc, char *argv[]) 11798944Sobrien{ 11898944Sobrien int ch, func = 0; 11998944Sobrien int rtn = 0; 12098944Sobrien int aflag = 0; /* do it for all entries */ 12198944Sobrien 12298944Sobrien while ((ch = getopt(argc, argv, "andfsSi:")) != -1) 12398944Sobrien switch((char)ch) { 12498944Sobrien case 'a': 12598944Sobrien aflag = 1; 12698944Sobrien break; 12798944Sobrien case 'd': 12898944Sobrien SETFUNC(F_DELETE); 12998944Sobrien break; 13098944Sobrien case 'n': 13198944Sobrien nflag = 1; 13298944Sobrien break; 13398944Sobrien case 'S': 13498944Sobrien SETFUNC(F_REPLACE); 13598944Sobrien break; 13698944Sobrien case 's': 13798944Sobrien SETFUNC(F_SET); 13898944Sobrien break; 13998944Sobrien case 'f' : 14098944Sobrien SETFUNC(F_FILESET); 14198944Sobrien break; 14298944Sobrien case 'i': 14398944Sobrien rifname = optarg; 14498944Sobrien break; 14598944Sobrien case '?': 14619370Spst default: 14719370Spst usage(); 14898944Sobrien } 14998944Sobrien argc -= optind; 15098944Sobrien argv += optind; 15198944Sobrien 15298944Sobrien if (!func) 15398944Sobrien func = F_GET; 15498944Sobrien if (rifname) { 15598944Sobrien if (func != F_GET && !(func == F_DELETE && aflag)) 15698944Sobrien errx(1, "-i not applicable to this operation"); 15798944Sobrien if (if_nametoindex(rifname) == 0) { 15898944Sobrien if (errno == ENXIO) 15998944Sobrien errx(1, "interface %s does not exist", rifname); 16098944Sobrien else 16198944Sobrien err(1, "if_nametoindex(%s)", rifname); 16298944Sobrien } 16398944Sobrien } 16498944Sobrien switch (func) { 16598944Sobrien case F_GET: 16698944Sobrien if (aflag) { 16798944Sobrien if (argc != 0) 16819370Spst usage(); 16946283Sdfr search(0, print_entry); 17019370Spst } else { 17119370Spst if (argc != 1) 17298944Sobrien usage(); 17398944Sobrien rtn = get(argv[0]); 17498944Sobrien } 17598944Sobrien break; 17698944Sobrien case F_SET: 17798944Sobrien case F_REPLACE: 17898944Sobrien if (argc < 2 || argc > 6) 17919370Spst usage(); 18098944Sobrien if (func == F_REPLACE) 18198944Sobrien (void)delete(argv[0], 0); 18298944Sobrien rtn = set(argc, argv) ? 1 : 0; 18398944Sobrien break; 18498944Sobrien case F_DELETE: 18598944Sobrien if (aflag) { 18698944Sobrien if (argc != 0) 18798944Sobrien usage(); 188130803Smarcel search(0, nuke_entry); 189130803Smarcel } else { 190130803Smarcel if (argc == 2 && strncmp(argv[1], "pub", 3) == 0) 191130803Smarcel ch = SIN_PROXY; 192130803Smarcel else if (argc == 1) 193130803Smarcel ch = 0; 194130803Smarcel else 195130803Smarcel usage(); 196130803Smarcel rtn = delete(argv[0], ch); 197130803Smarcel } 198130803Smarcel break; 199130803Smarcel case F_FILESET: 200130803Smarcel if (argc != 1) 201130803Smarcel usage(); 202130803Smarcel rtn = file(argv[0]); 203130803Smarcel break; 204130803Smarcel } 205130803Smarcel 206130803Smarcel return (rtn); 207130803Smarcel} 208130803Smarcel 209130803Smarcel/* 210130803Smarcel * Process a file to set standard arp entries 211130803Smarcel */ 212130803Smarcelstatic int 213130803Smarcelfile(char *name) 214130803Smarcel{ 215130803Smarcel FILE *fp; 216130803Smarcel int i, retval; 217130803Smarcel char line[100], arg[5][50], *args[5], *p; 218130803Smarcel 219130803Smarcel if ((fp = fopen(name, "r")) == NULL) 220130803Smarcel err(1, "cannot open %s", name); 221130803Smarcel args[0] = &arg[0][0]; 222130803Smarcel args[1] = &arg[1][0]; 223130803Smarcel args[2] = &arg[2][0]; 224130803Smarcel args[3] = &arg[3][0]; 225130803Smarcel args[4] = &arg[4][0]; 226130803Smarcel retval = 0; 227130803Smarcel while(fgets(line, 100, fp) != NULL) { 228130803Smarcel if ((p = strchr(line, '#')) != NULL) 229130803Smarcel *p = '\0'; 230130803Smarcel for (p = line; isblank(*p); p++); 231130803Smarcel if (*p == '\n' || *p == '\0') 232130803Smarcel continue; 233130803Smarcel i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], 234130803Smarcel arg[2], arg[3], arg[4]); 235130803Smarcel if (i < 2) { 236130803Smarcel warnx("bad line: %s", line); 237130803Smarcel retval = 1; 238130803Smarcel continue; 239130803Smarcel } 240130803Smarcel if (set(i, args)) 241130803Smarcel retval = 1; 242130803Smarcel } 243130803Smarcel fclose(fp); 244130803Smarcel return (retval); 245130803Smarcel} 246130803Smarcel 247130803Smarcel/* 248130803Smarcel * Given a hostname, fills up a (static) struct sockaddr_inarp with 249130803Smarcel * the address of the host and returns a pointer to the 250130803Smarcel * structure. 251130803Smarcel */ 252130803Smarcelstatic struct sockaddr_inarp * 253130803Smarcelgetaddr(char *host) 254130803Smarcel{ 255130803Smarcel struct hostent *hp; 256130803Smarcel static struct sockaddr_inarp reply; 257130803Smarcel 258130803Smarcel bzero(&reply, sizeof(reply)); 259130803Smarcel reply.sin_len = sizeof(reply); 260130803Smarcel reply.sin_family = AF_INET; 261130803Smarcel reply.sin_addr.s_addr = inet_addr(host); 262130803Smarcel if (reply.sin_addr.s_addr == INADDR_NONE) { 263130803Smarcel if (!(hp = gethostbyname(host))) { 264130803Smarcel warnx("%s: %s", host, hstrerror(h_errno)); 265130803Smarcel return (NULL); 266130803Smarcel } 267130803Smarcel bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 268130803Smarcel sizeof reply.sin_addr); 269130803Smarcel } 270130803Smarcel return (&reply); 271130803Smarcel} 272130803Smarcel 273130803Smarcel/* 274130803Smarcel * Returns true if the type is a valid one for ARP. 275130803Smarcel */ 276130803Smarcelstatic int 277130803Smarcelvalid_type(int type) 278130803Smarcel{ 27919370Spst 28019370Spst switch (type) { 28119370Spst case IFT_ETHER: 28219370Spst case IFT_FDDI: 28319370Spst case IFT_ISO88023: 28419370Spst case IFT_ISO88024: 28519370Spst case IFT_ISO88025: 28619370Spst case IFT_L2VLAN: 28719370Spst case IFT_BRIDGE: 28898944Sobrien return (1); 28998944Sobrien default: 29098944Sobrien return (0); 29198944Sobrien } 29298944Sobrien} 29398944Sobrien 29498944Sobrien/* 29598944Sobrien * Set an individual arp entry 29698944Sobrien */ 29798944Sobrienstatic int 29819370Spstset(int argc, char **argv) 299130803Smarcel{ 300130803Smarcel struct sockaddr_inarp *addr; 30119370Spst struct sockaddr_inarp *dst; /* what are we looking for */ 30298944Sobrien struct sockaddr_dl *sdl; 30319370Spst struct rt_msghdr *rtm; 30498944Sobrien struct ether_addr *ea; 30519370Spst char *host = argv[0], *eaddr = argv[1]; 30698944Sobrien struct sockaddr_dl sdl_m; 30719370Spst 30898944Sobrien argc -= 2; 30919370Spst argv += 2; 31098944Sobrien 31198944Sobrien bzero(&sdl_m, sizeof(sdl_m)); 31298944Sobrien sdl_m.sdl_len = sizeof(sdl_m); 31398944Sobrien sdl_m.sdl_family = AF_LINK; 31498944Sobrien 31598944Sobrien dst = getaddr(host); 31698944Sobrien if (dst == NULL) 31798944Sobrien return (1); 31898944Sobrien doing_proxy = flags = proxy_only = expire_time = 0; 31998944Sobrien while (argc-- > 0) { 320130803Smarcel if (strncmp(argv[0], "temp", 4) == 0) { 32198944Sobrien struct timeval tv; 32298944Sobrien gettimeofday(&tv, 0); 32319370Spst expire_time = tv.tv_sec + 20 * 60; 32498944Sobrien } 32598944Sobrien else if (strncmp(argv[0], "pub", 3) == 0) { 32698944Sobrien flags |= RTF_ANNOUNCE; 32798944Sobrien doing_proxy = 1; 32898944Sobrien if (argc && strncmp(argv[1], "only", 3) == 0) { 32998944Sobrien proxy_only = 1; 33098944Sobrien dst->sin_other = SIN_PROXY; 33198944Sobrien argc--; argv++; 33298944Sobrien } 33398944Sobrien } else if (strncmp(argv[0], "trail", 5) == 0) { 33498944Sobrien /* XXX deprecated and undocumented feature */ 33519370Spst printf("%s: Sending trailers is no longer supported\n", 33698944Sobrien host); 33798944Sobrien } 33898944Sobrien argv++; 33998944Sobrien } 34098944Sobrien ea = (struct ether_addr *)LLADDR(&sdl_m); 34198944Sobrien if (doing_proxy && !strcmp(eaddr, "auto")) { 34298944Sobrien if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 34319370Spst printf("no interface found for %s\n", 34498944Sobrien inet_ntoa(dst->sin_addr)); 34598944Sobrien return (1); 34619370Spst } 34798944Sobrien sdl_m.sdl_alen = ETHER_ADDR_LEN; 34898944Sobrien } else { 34998944Sobrien struct ether_addr *ea1 = ether_aton(eaddr); 35098944Sobrien 35198944Sobrien if (ea1 == NULL) 35219370Spst warnx("invalid Ethernet address '%s'", eaddr); 353130803Smarcel else { 354130803Smarcel *ea = *ea1; 355130803Smarcel sdl_m.sdl_alen = ETHER_ADDR_LEN; 356130803Smarcel } 35719370Spst } 35898944Sobrien for (;;) { /* try at most twice */ 35998944Sobrien rtm = rtmsg(RTM_GET, dst, &sdl_m); 36019370Spst if (rtm == NULL) { 36198944Sobrien warn("%s", host); 36298944Sobrien return (1); 36398944Sobrien } 36498944Sobrien addr = (struct sockaddr_inarp *)(rtm + 1); 36598944Sobrien sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 36619370Spst if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) 36798944Sobrien break; 36898944Sobrien if (sdl->sdl_family == AF_LINK && 36998944Sobrien (rtm->rtm_flags & RTF_LLINFO) && 37098944Sobrien !(rtm->rtm_flags & RTF_GATEWAY) && 37198944Sobrien valid_type(sdl->sdl_type) ) 37298944Sobrien break; 37398944Sobrien if (doing_proxy == 0) { 37498944Sobrien printf("set: can only proxy for %s\n", host); 37598944Sobrien return (1); 37698944Sobrien } 37798944Sobrien if (dst->sin_other & SIN_PROXY) { 37898944Sobrien printf("set: proxy entry exists for non 802 device\n"); 37998944Sobrien return (1); 38098944Sobrien } 38198944Sobrien dst->sin_other = SIN_PROXY; 38298944Sobrien proxy_only = 1; 38398944Sobrien } 38498944Sobrien 38598944Sobrien if (sdl->sdl_family != AF_LINK) { 38698944Sobrien printf("cannot intuit interface index and type for %s\n", host); 387130803Smarcel return (1); 388130803Smarcel } 389130803Smarcel sdl_m.sdl_type = sdl->sdl_type; 390130803Smarcel sdl_m.sdl_index = sdl->sdl_index; 391130803Smarcel return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 392130803Smarcel} 393130803Smarcel 394130803Smarcel/* 395130803Smarcel * Display an individual arp entry 396130803Smarcel */ 397130803Smarcelstatic int 398130803Smarcelget(char *host) 399130803Smarcel{ 40098944Sobrien struct sockaddr_inarp *addr; 40119370Spst 40298944Sobrien addr = getaddr(host); 40398944Sobrien if (addr == NULL) 40498944Sobrien return (1); 40519370Spst if (0 == search(addr->sin_addr.s_addr, print_entry)) { 40619370Spst printf("%s (%s) -- no entry", 40719370Spst host, inet_ntoa(addr->sin_addr)); 40819370Spst if (rifname) 40919370Spst printf(" on %s", rifname); 41019370Spst printf("\n"); 41198944Sobrien return (1); 41219370Spst } 41319370Spst return (0); 41419370Spst} 41598944Sobrien 41619370Spst/* 417130803Smarcel * Delete an arp entry 41819370Spst */ 41919370Spststatic int 42019370Spstdelete(char *host, int do_proxy) 42119370Spst{ 42298944Sobrien struct sockaddr_inarp *addr, *dst; 42398944Sobrien struct rt_msghdr *rtm; 42498944Sobrien struct sockaddr_dl *sdl; 42598944Sobrien 42698944Sobrien dst = getaddr(host); 42798944Sobrien if (dst == NULL) 42819370Spst return (1); 42998944Sobrien dst->sin_other = do_proxy; 43098944Sobrien for (;;) { /* try twice */ 43198944Sobrien rtm = rtmsg(RTM_GET, dst, NULL); 43298944Sobrien if (rtm == NULL) { 43398944Sobrien warn("%s", host); 43419370Spst return (1); 43598944Sobrien } 43698944Sobrien addr = (struct sockaddr_inarp *)(rtm + 1); 43719370Spst sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 43898944Sobrien if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && 43998944Sobrien sdl->sdl_family == AF_LINK && 44019370Spst (rtm->rtm_flags & RTF_LLINFO) && 44198944Sobrien !(rtm->rtm_flags & RTF_GATEWAY) && 44298944Sobrien valid_type(sdl->sdl_type) ) 44398944Sobrien break; /* found it */ 44498944Sobrien if (dst->sin_other & SIN_PROXY) { 44598944Sobrien fprintf(stderr, "delete: cannot locate %s\n",host); 44619370Spst return (1); 44798944Sobrien } 44898944Sobrien dst->sin_other = SIN_PROXY; 44998944Sobrien } 45098944Sobrien if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 45198944Sobrien printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 45219370Spst return (0); 45398944Sobrien } 45498944Sobrien return (1); 45598944Sobrien} 45619370Spst 45798944Sobrien/* 45898944Sobrien * Search the arp table and do some action on matching entries 45919370Spst */ 46098944Sobrienstatic int 46198944Sobriensearch(u_long addr, action_fn *action) 46219370Spst{ 46398944Sobrien int mib[6]; 46498944Sobrien size_t needed; 46598944Sobrien char *lim, *buf, *newbuf, *next; 46619370Spst struct rt_msghdr *rtm; 46798944Sobrien struct sockaddr_inarp *sin2; 46898944Sobrien struct sockaddr_dl *sdl; 46998944Sobrien char ifname[IF_NAMESIZE]; 47046283Sdfr int st, found_entry = 0; 47198944Sobrien 47298944Sobrien mib[0] = CTL_NET; 47398944Sobrien mib[1] = PF_ROUTE; 47419370Spst mib[2] = 0; 47598944Sobrien mib[3] = AF_INET; 47698944Sobrien mib[4] = NET_RT_FLAGS; 47798944Sobrien mib[5] = RTF_LLINFO; 47819370Spst if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 47998944Sobrien err(1, "route-sysctl-estimate"); 48098944Sobrien if (needed == 0) /* empty table */ 48198944Sobrien return 0; 48219370Spst buf = NULL; 48398944Sobrien for (;;) { 48498944Sobrien newbuf = realloc(buf, needed); 48598944Sobrien if (newbuf == NULL) { 48698944Sobrien if (buf != NULL) 48798944Sobrien free(buf); 48898944Sobrien errx(1, "could not reallocate memory"); 48998944Sobrien } 49098944Sobrien buf = newbuf; 49198944Sobrien st = sysctl(mib, 6, buf, &needed, NULL, 0); 49298944Sobrien if (st == 0 || errno != ENOMEM) 49398944Sobrien break; 49498944Sobrien needed += needed / 8; 49598944Sobrien } 49698944Sobrien if (st == -1) 49798944Sobrien err(1, "actual retrieval of routing table"); 49898944Sobrien lim = buf + needed; 49998944Sobrien for (next = buf; next < lim; next += rtm->rtm_msglen) { 50019370Spst rtm = (struct rt_msghdr *)next; 50198944Sobrien sin2 = (struct sockaddr_inarp *)(rtm + 1); 50219370Spst sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 50319370Spst if (rifname && if_indextoname(sdl->sdl_index, ifname) && 50498944Sobrien strcmp(ifname, rifname)) 50519370Spst continue; 50646283Sdfr if (addr) { 50746283Sdfr if (addr != sin2->sin_addr.s_addr) 50846283Sdfr continue; 50946283Sdfr found_entry = 1; 51046283Sdfr } 51146283Sdfr (*action)(sdl, sin2, rtm); 51246283Sdfr } 51346283Sdfr free(buf); 51498944Sobrien return (found_entry); 51598944Sobrien} 51646283Sdfr 51719370Spst/* 51819370Spst * Display an arp entry 51919370Spst */ 52019370Spststatic void 52119370Spstprint_entry(struct sockaddr_dl *sdl, 52219370Spst struct sockaddr_inarp *addr, struct rt_msghdr *rtm) 52319370Spst{ 52419370Spst const char *host; 52519370Spst struct hostent *hp; 52698944Sobrien struct iso88025_sockaddr_dl_data *trld; 52719370Spst char ifname[IF_NAMESIZE]; 52846283Sdfr int seg; 52998944Sobrien 53046283Sdfr if (nflag == 0) 53119370Spst hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 53219370Spst sizeof addr->sin_addr, AF_INET); 53319370Spst else 53498944Sobrien hp = 0; 53519370Spst if (hp) 53619370Spst host = hp->h_name; 53719370Spst else { 53819370Spst host = "?"; 53919370Spst if (h_errno == TRY_AGAIN) 54019370Spst nflag = 1; 54198944Sobrien } 54219370Spst printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 54319370Spst if (sdl->sdl_alen) { 54419370Spst if ((sdl->sdl_type == IFT_ETHER || 54519370Spst sdl->sdl_type == IFT_L2VLAN || 54698944Sobrien sdl->sdl_type == IFT_BRIDGE) && 54719370Spst sdl->sdl_alen == ETHER_ADDR_LEN) 54819370Spst printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 54998944Sobrien else { 55019370Spst int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 55146283Sdfr 55246283Sdfr printf("%s", link_ntoa(sdl) + n); 55346283Sdfr } 55498944Sobrien } else 55598944Sobrien printf("(incomplete)"); 55698944Sobrien if (if_indextoname(sdl->sdl_index, ifname) != NULL) 55719370Spst printf(" on %s", ifname); 55898944Sobrien if (rtm->rtm_rmx.rmx_expire == 0) 55998944Sobrien printf(" permanent"); 56098944Sobrien if (addr->sin_other & SIN_PROXY) 56198944Sobrien printf(" published (proxy only)"); 56298944Sobrien if (rtm->rtm_addrs & RTA_NETMASK) { 56398944Sobrien addr = (struct sockaddr_inarp *) 56498944Sobrien (SA_SIZE(sdl) + (char *)sdl); 56598944Sobrien if (addr->sin_addr.s_addr == 0xffffffff) 56698944Sobrien printf(" published"); 56798944Sobrien if (addr->sin_len != 8) 56898944Sobrien printf("(weird)"); 56998944Sobrien } 57098944Sobrien switch(sdl->sdl_type) { 57198944Sobrien case IFT_ETHER: 57298944Sobrien printf(" [ethernet]"); 57398944Sobrien break; 57419370Spst case IFT_ISO88025: 57598944Sobrien printf(" [token-ring]"); 57698944Sobrien trld = SDL_ISO88025(sdl); 57798944Sobrien if (trld->trld_rcf != 0) { 57898944Sobrien printf(" rt=%x", ntohs(trld->trld_rcf)); 57998944Sobrien for (seg = 0; 58098944Sobrien seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 58198944Sobrien seg++) 58298944Sobrien printf(":%x", ntohs(*(trld->trld_route[seg]))); 58398944Sobrien } 58498944Sobrien break; 58519370Spst case IFT_FDDI: 58698944Sobrien printf(" [fddi]"); 58798944Sobrien break; 58819370Spst case IFT_ATM: 58998944Sobrien printf(" [atm]"); 59098944Sobrien break; 59119370Spst case IFT_L2VLAN: 59298944Sobrien printf(" [vlan]"); 59398944Sobrien break; 59498944Sobrien case IFT_IEEE1394: 59598944Sobrien printf(" [firewire]"); 59646283Sdfr break; 59746283Sdfr case IFT_BRIDGE: 59898944Sobrien printf(" [bridge]"); 59998944Sobrien break; 60098944Sobrien default: 60198944Sobrien break; 60298944Sobrien } 60346283Sdfr 60498944Sobrien printf("\n"); 60598944Sobrien 60698944Sobrien} 60798944Sobrien 60898944Sobrien/* 60998944Sobrien * Nuke an arp entry 61098944Sobrien */ 61198944Sobrienstatic void 61219370Spstnuke_entry(struct sockaddr_dl *sdl __unused, 61398944Sobrien struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused) 61419370Spst{ 61519370Spst char ip[20]; 61698944Sobrien 61719370Spst snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 61898944Sobrien (void)delete(ip, 0); 61946283Sdfr} 620130803Smarcel 62119370Spststatic void 622130803Smarcelusage(void) 623130803Smarcel{ 624130803Smarcel fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 625130803Smarcel "usage: arp [-n] [-i interface] hostname", 626130803Smarcel " arp [-n] [-i interface] -a", 627130803Smarcel " arp -d hostname [pub]", 628130803Smarcel " arp -d -a", 62998944Sobrien " arp -s hostname ether_addr [temp] [pub]", 63019370Spst " arp -S hostname ether_addr [temp] [pub]", 631130803Smarcel " arp -f filename"); 63219370Spst exit(1); 63398944Sobrien} 63419370Spst 63598944Sobrienstatic struct rt_msghdr * 63619370Spstrtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) 63798944Sobrien{ 63846283Sdfr static int seq; 63919370Spst int rlen; 640130803Smarcel int l; 64119370Spst struct sockaddr_in so_mask; 64298944Sobrien static int s = -1; 64319370Spst static pid_t pid; 64498944Sobrien 64519370Spst static struct { 64698944Sobrien struct rt_msghdr m_rtm; 64719370Spst char m_space[512]; 64898944Sobrien } m_rtmsg; 64919370Spst 65098944Sobrien struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 65119370Spst char *cp = m_rtmsg.m_space; 65298944Sobrien 65319370Spst if (s < 0) { /* first time: open socket, get pid */ 65498944Sobrien s = socket(PF_ROUTE, SOCK_RAW, 0); 65519370Spst if (s < 0) 65698944Sobrien err(1, "socket"); 65719370Spst pid = getpid(); 65898944Sobrien } 65946283Sdfr bzero(&so_mask, sizeof(so_mask)); 66098944Sobrien so_mask.sin_len = 8; 66119370Spst so_mask.sin_addr.s_addr = 0xffffffff; 66298944Sobrien 66398944Sobrien errno = 0; 66498944Sobrien /* 66598944Sobrien * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 66698944Sobrien * appropriately. 66798944Sobrien */ 66898944Sobrien if (cmd == RTM_DELETE) 66919370Spst goto doit; 67098944Sobrien bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 67198944Sobrien rtm->rtm_flags = flags; 67298944Sobrien rtm->rtm_version = RTM_VERSION; 67398944Sobrien 67446283Sdfr switch (cmd) { 67546283Sdfr default: 67646283Sdfr errx(1, "internal wrong cmd"); 67746283Sdfr case RTM_ADD: 67846283Sdfr rtm->rtm_addrs |= RTA_GATEWAY; 67998944Sobrien rtm->rtm_rmx.rmx_expire = expire_time; 68046283Sdfr rtm->rtm_inits = RTV_EXPIRE; 68146283Sdfr rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 68246283Sdfr dst->sin_other = 0; 68346283Sdfr if (doing_proxy) { 68446283Sdfr if (proxy_only) 68546283Sdfr dst->sin_other = SIN_PROXY; 68698944Sobrien else { 68798944Sobrien rtm->rtm_addrs |= RTA_NETMASK; 68898944Sobrien rtm->rtm_flags &= ~RTF_HOST; 68998944Sobrien } 69098944Sobrien } 69198944Sobrien /* FALLTHROUGH */ 69298944Sobrien case RTM_GET: 69398944Sobrien rtm->rtm_addrs |= RTA_DST; 69498944Sobrien } 69598944Sobrien#define NEXTADDR(w, s) \ 69698944Sobrien if ((s) != NULL && rtm->rtm_addrs & (w)) { \ 69746283Sdfr bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);} 69846283Sdfr 69946283Sdfr NEXTADDR(RTA_DST, dst); 70046283Sdfr NEXTADDR(RTA_GATEWAY, sdl); 70146283Sdfr NEXTADDR(RTA_NETMASK, &so_mask); 70246283Sdfr 70346283Sdfr rtm->rtm_msglen = cp - (char *)&m_rtmsg; 70498944Sobriendoit: 70546283Sdfr l = rtm->rtm_msglen; 70698944Sobrien rtm->rtm_seq = ++seq; 70798944Sobrien rtm->rtm_type = cmd; 70819370Spst if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 70998944Sobrien if (errno != ESRCH || cmd != RTM_DELETE) { 71098944Sobrien warn("writing to routing socket"); 71198944Sobrien return (NULL); 71298944Sobrien } 71319370Spst } 714130803Smarcel do { 71546283Sdfr l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 71646283Sdfr } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 71746283Sdfr if (l < 0) 71846283Sdfr warn("read from routing socket"); 719130803Smarcel return (rtm); 72046283Sdfr} 72146283Sdfr 72246283Sdfr/* 72346283Sdfr * get_ether_addr - get the hardware address of an interface on the 72446283Sdfr * the same subnet as ipaddr. 72546283Sdfr */ 72646283Sdfr#define MAX_IFS 32 72746283Sdfr 72846283Sdfrstatic int 72946283Sdfrget_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) 73098944Sobrien{ 73146283Sdfr struct ifreq *ifr, *ifend, *ifp; 73246283Sdfr in_addr_t ina, mask; 73346283Sdfr struct sockaddr_dl *dla; 73446283Sdfr struct ifreq ifreq; 73546283Sdfr struct ifconf ifc; 73698944Sobrien struct ifreq ifs[MAX_IFS]; 73746283Sdfr int sock; 73898944Sobrien int retval = 0; 73946283Sdfr 74019370Spst sock = socket(AF_INET, SOCK_DGRAM, 0); 74198944Sobrien if (sock < 0) 74298944Sobrien err(1, "socket"); 74398944Sobrien 74498944Sobrien ifc.ifc_len = sizeof(ifs); 74598944Sobrien ifc.ifc_req = ifs; 74698944Sobrien if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 74719370Spst warnx("ioctl(SIOCGIFCONF)"); 74819370Spst goto done; 74919370Spst } 75098944Sobrien 75119370Spst#define NEXTIFR(i) \ 75298944Sobrien ((struct ifreq *)((char *)&(i)->ifr_addr \ 75319370Spst + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 75498944Sobrien 75519370Spst /* 75698944Sobrien * Scan through looking for an interface with an Internet 75719370Spst * address on the same subnet as `ipaddr'. 75898944Sobrien */ 75919370Spst ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 76098944Sobrien for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 76119370Spst if (ifr->ifr_addr.sa_family != AF_INET) 76298944Sobrien continue; 76319370Spst /* XXX can't we use *ifr instead of ifreq ? */ 76498944Sobrien strncpy(ifreq.ifr_name, ifr->ifr_name, 76519370Spst sizeof(ifreq.ifr_name)); 76698944Sobrien /* 76746283Sdfr * Check that the interface is up, 76898944Sobrien * and not point-to-point or loopback. 76919370Spst */ 77098944Sobrien if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 77146283Sdfr continue; 77298944Sobrien if ((ifreq.ifr_flags & 77398944Sobrien (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 77498944Sobrien IFF_LOOPBACK|IFF_NOARP)) 77598944Sobrien != (IFF_UP|IFF_BROADCAST)) 77698944Sobrien continue; 77798944Sobrien /* 77898944Sobrien * Get its netmask and check that it's on 77998944Sobrien * the right subnet. 78098944Sobrien */ 78198944Sobrien if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 78298944Sobrien continue; 78398944Sobrien mask = ((struct sockaddr_in *) 78498944Sobrien &ifreq.ifr_addr)->sin_addr.s_addr; 78598944Sobrien ina = ((struct sockaddr_in *) 78698944Sobrien &ifr->ifr_addr)->sin_addr.s_addr; 78746283Sdfr if ((ipaddr & mask) == (ina & mask)) 78898944Sobrien break; /* ok, we got it! */ 78998944Sobrien } 79046283Sdfr 79146283Sdfr if (ifr >= ifend) 79246283Sdfr goto done; 79398944Sobrien 79446283Sdfr /* 79598944Sobrien * Now scan through again looking for a link-level address 79698944Sobrien * for this interface. 79798944Sobrien */ 79898944Sobrien ifp = ifr; 79998944Sobrien for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 80098944Sobrien if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 80198944Sobrien ifr->ifr_addr.sa_family == AF_LINK) 80298944Sobrien break; 80398944Sobrien if (ifr >= ifend) 80498944Sobrien goto done; 80519370Spst /* 806 * Found the link-level address - copy it out 807 */ 808 dla = (struct sockaddr_dl *) &ifr->ifr_addr; 809 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 810 printf("using interface %s for proxy with address ", 811 ifp->ifr_name); 812 printf("%s\n", ether_ntoa(hwaddr)); 813 retval = dla->sdl_alen; 814done: 815 close(sock); 816 return (retval); 817} 818