arp.c revision 147170
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1984, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * This code is derived from software contributed to Berkeley by 61553Srgrimes * Sun Microsystems, Inc. 71553Srgrimes * 81553Srgrimes * Redistribution and use in source and binary forms, with or without 91553Srgrimes * modification, are permitted provided that the following conditions 101553Srgrimes * are met: 111553Srgrimes * 1. Redistributions of source code must retain the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer. 131553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141553Srgrimes * notice, this list of conditions and the following disclaimer in the 151553Srgrimes * documentation and/or other materials provided with the distribution. 161553Srgrimes * 4. Neither the name of the University nor the names of its contributors 171553Srgrimes * may be used to endorse or promote products derived from this software 181553Srgrimes * without specific prior written permission. 191553Srgrimes * 201553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301553Srgrimes * SUCH DAMAGE. 311553Srgrimes */ 321553Srgrimes 33114601Sobrien#if 0 341553Srgrimes#ifndef lint 3513977Sphkstatic char const copyright[] = 361553Srgrimes"@(#) Copyright (c) 1984, 1993\n\ 371553Srgrimes The Regents of the University of California. All rights reserved.\n"; 381553Srgrimes#endif /* not lint */ 391553Srgrimes 401553Srgrimes#ifndef lint 4113977Sphkstatic char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; 42114601Sobrien#endif /* not lint */ 4329060Scharnier#endif 44114601Sobrien#include <sys/cdefs.h> 45114601Sobrien__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 147170 2005-06-09 15:00:31Z glebius $"); 461553Srgrimes 471553Srgrimes/* 481553Srgrimes * arp - display, set, and delete arp table entries 491553Srgrimes */ 501553Srgrimes 511553Srgrimes 521553Srgrimes#include <sys/param.h> 531553Srgrimes#include <sys/file.h> 541553Srgrimes#include <sys/socket.h> 5513977Sphk#include <sys/sockio.h> 561553Srgrimes#include <sys/sysctl.h> 5713977Sphk#include <sys/ioctl.h> 5820287Swollman#include <sys/time.h> 591553Srgrimes 601553Srgrimes#include <net/if.h> 611553Srgrimes#include <net/if_dl.h> 621553Srgrimes#include <net/if_types.h> 6396235Swpaul#include <net/route.h> 6496202Skbyanc#include <net/iso88025.h> 651553Srgrimes 661553Srgrimes#include <netinet/in.h> 671553Srgrimes#include <netinet/if_ether.h> 681553Srgrimes 691553Srgrimes#include <arpa/inet.h> 701553Srgrimes 71108314Sru#include <ctype.h> 7229060Scharnier#include <err.h> 7329060Scharnier#include <errno.h> 741553Srgrimes#include <netdb.h> 751553Srgrimes#include <nlist.h> 7629060Scharnier#include <paths.h> 771553Srgrimes#include <stdio.h> 7813977Sphk#include <stdlib.h> 7993590Smike#include <string.h> 8029060Scharnier#include <strings.h> 8113977Sphk#include <unistd.h> 821553Srgrimes 83128181Sluigitypedef void (action_fn)(struct sockaddr_dl *sdl, 84128192Sluigi struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); 8513977Sphk 86128181Sluigistatic int search(u_long addr, action_fn *action); 87128181Sluigistatic action_fn print_entry; 88128181Sluigistatic action_fn nuke_entry; 89128181Sluigi 90128192Sluigistatic int delete(char *host, int do_proxy); 91128181Sluigistatic void usage(void); 92128181Sluigistatic int set(int argc, char **argv); 93128192Sluigistatic void get(char *host); 94128181Sluigistatic int file(char *name); 95128192Sluigistatic struct rt_msghdr *rtmsg(int cmd, 96128192Sluigi struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); 97128181Sluigistatic int get_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr); 98128181Sluigi 9931145Sjulianstatic int nflag; /* no reverse dns lookups */ 100117729Syarstatic char *rifname; 1011553Srgrimes 102128181Sluigistatic int expire_time, flags, doing_proxy, proxy_only; 103128192Sluigi 10431145Sjulian/* which function we're supposed to do */ 10531145Sjulian#define F_GET 1 10631145Sjulian#define F_SET 2 10731145Sjulian#define F_FILESET 3 10831145Sjulian#define F_REPLACE 4 10931145Sjulian#define F_DELETE 5 11031145Sjulian 11131145Sjulian#define SETFUNC(f) { if (func) usage(); func = (f); } 11231145Sjulian 11313977Sphkint 11477870Srumain(int argc, char *argv[]) 1151553Srgrimes{ 11631145Sjulian int ch, func = 0; 11731145Sjulian int rtn = 0; 118128181Sluigi int aflag = 0; /* do it for all entries */ 1191553Srgrimes 120117729Syar while ((ch = getopt(argc, argv, "andfsSi:")) != -1) 1211553Srgrimes switch((char)ch) { 1221553Srgrimes case 'a': 12331145Sjulian aflag = 1; 12431145Sjulian break; 1251553Srgrimes case 'd': 12631145Sjulian SETFUNC(F_DELETE); 12731145Sjulian break; 1281553Srgrimes case 'n': 1291553Srgrimes nflag = 1; 13031145Sjulian break; 13113977Sphk case 'S': 13231145Sjulian SETFUNC(F_REPLACE); 13331145Sjulian break; 1341553Srgrimes case 's': 13531145Sjulian SETFUNC(F_SET); 13631145Sjulian break; 1379874Sjkh case 'f' : 13831145Sjulian SETFUNC(F_FILESET); 13931145Sjulian break; 140117729Syar case 'i': 141117729Syar rifname = optarg; 142117729Syar break; 1431553Srgrimes case '?': 1441553Srgrimes default: 1451553Srgrimes usage(); 1461553Srgrimes } 14731145Sjulian argc -= optind; 14831145Sjulian argv += optind; 14931145Sjulian 15031145Sjulian if (!func) 15131145Sjulian func = F_GET; 152117729Syar if (rifname) { 153117729Syar if (func != F_GET) 154117729Syar errx(1, "-i not applicable to this operation"); 155117729Syar if (if_nametoindex(rifname) == 0) { 156117729Syar if (errno == ENXIO) 157117729Syar errx(1, "interface %s does not exist", rifname); 158117729Syar else 159117729Syar err(1, "if_nametoindex(%s)", rifname); 160117729Syar } 161117729Syar } 16231145Sjulian switch (func) { 16331145Sjulian case F_GET: 16431145Sjulian if (aflag) { 16531145Sjulian if (argc != 0) 16631145Sjulian usage(); 16731145Sjulian search(0, print_entry); 16831145Sjulian } else { 16931145Sjulian if (argc != 1) 17031145Sjulian usage(); 17131145Sjulian get(argv[0]); 17231145Sjulian } 17331145Sjulian break; 17431145Sjulian case F_SET: 17531145Sjulian case F_REPLACE: 17677870Sru if (argc < 2 || argc > 6) 17731145Sjulian usage(); 17831145Sjulian if (func == F_REPLACE) 179128192Sluigi delete(argv[0], 0); 18031145Sjulian rtn = set(argc, argv) ? 1 : 0; 18131145Sjulian break; 18231145Sjulian case F_DELETE: 18331145Sjulian if (aflag) { 18431145Sjulian if (argc != 0) 18531145Sjulian usage(); 18631145Sjulian search(0, nuke_entry); 18731145Sjulian } else { 188128192Sluigi if (argc == 2 && strncmp(argv[1], "pub", 3) == 0) 189128192Sluigi ch = SIN_PROXY; 190128192Sluigi else if (argc == 1) 191128192Sluigi ch = 0; 192128192Sluigi else 19331145Sjulian usage(); 194128192Sluigi rtn = delete(argv[0], ch); 19531145Sjulian } 19631145Sjulian break; 19731145Sjulian case F_FILESET: 19831145Sjulian if (argc != 1) 19931145Sjulian usage(); 20031145Sjulian rtn = file(argv[0]); 20131145Sjulian break; 20231145Sjulian } 20331145Sjulian 20431145Sjulian return(rtn); 2051553Srgrimes} 2061553Srgrimes 2071553Srgrimes/* 2081553Srgrimes * Process a file to set standard arp entries 2091553Srgrimes */ 210128181Sluigistatic int 21113977Sphkfile(char *name) 2121553Srgrimes{ 2131553Srgrimes FILE *fp; 2141553Srgrimes int i, retval; 215109413Sru char line[100], arg[5][50], *args[5], *p; 2161553Srgrimes 21729060Scharnier if ((fp = fopen(name, "r")) == NULL) 218128054Smux err(1, "cannot open %s", name); 2191553Srgrimes args[0] = &arg[0][0]; 2201553Srgrimes args[1] = &arg[1][0]; 2211553Srgrimes args[2] = &arg[2][0]; 2221553Srgrimes args[3] = &arg[3][0]; 2231553Srgrimes args[4] = &arg[4][0]; 2241553Srgrimes retval = 0; 2251553Srgrimes while(fgets(line, 100, fp) != NULL) { 226109413Sru if ((p = strchr(line, '#')) != NULL) 227109413Sru *p = '\0'; 228109413Sru for (p = line; isblank(*p); p++); 229111910Sru if (*p == '\n' || *p == '\0') 230108314Sru continue; 231109413Sru i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], 23256134Sjkh arg[2], arg[3], arg[4]); 2331553Srgrimes if (i < 2) { 23429060Scharnier warnx("bad line: %s", line); 2351553Srgrimes retval = 1; 2361553Srgrimes continue; 2371553Srgrimes } 2381553Srgrimes if (set(i, args)) 2391553Srgrimes retval = 1; 2401553Srgrimes } 2411553Srgrimes fclose(fp); 2421553Srgrimes return (retval); 2431553Srgrimes} 2441553Srgrimes 2451553Srgrimes/* 246128192Sluigi * Given a hostname, fills up a (static) struct sockaddr_inarp with 247128192Sluigi * the address of the host and returns a pointer to the 248128192Sluigi * structure. 249128192Sluigi */ 250128192Sluigistatic struct sockaddr_inarp * 251128192Sluigigetaddr(char *host) 252128192Sluigi{ 253128192Sluigi struct hostent *hp; 254128192Sluigi static struct sockaddr_inarp reply; 255128192Sluigi 256128192Sluigi bzero(&reply, sizeof(reply)); 257128192Sluigi reply.sin_len = sizeof(reply); 258128192Sluigi reply.sin_family = AF_INET; 259128192Sluigi reply.sin_addr.s_addr = inet_addr(host); 260128192Sluigi if (reply.sin_addr.s_addr == INADDR_NONE) { 261128192Sluigi if (!(hp = gethostbyname(host))) { 262128192Sluigi warnx("%s: %s", host, hstrerror(h_errno)); 263128192Sluigi return NULL; 264128192Sluigi } 265128192Sluigi bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, 266128192Sluigi sizeof reply.sin_addr); 267128192Sluigi } 268128192Sluigi return &reply; 269128192Sluigi} 270128192Sluigi 271128192Sluigi/* 272128192Sluigi * returns true if the type is a valid one for ARP 273128192Sluigi */ 274128192Sluigistatic int 275128192Sluigivalid_type(int type) 276128192Sluigi{ 277128192Sluigi switch (type) { 278128192Sluigi default: 279128192Sluigi return 0; 280128192Sluigi case IFT_ETHER: 281128192Sluigi case IFT_FDDI: 282128192Sluigi case IFT_ISO88023: 283128192Sluigi case IFT_ISO88024: 284128192Sluigi case IFT_ISO88025: 285128192Sluigi case IFT_L2VLAN: 286128192Sluigi return 1; 287128192Sluigi } 288128192Sluigi} 289128192Sluigi 290128192Sluigi/* 2918857Srgrimes * Set an individual arp entry 2921553Srgrimes */ 293128192Sluigistatic int 29413977Sphkset(int argc, char **argv) 2951553Srgrimes{ 296128192Sluigi struct sockaddr_inarp *addr; 297128192Sluigi struct sockaddr_inarp *dst; /* what are we looking for */ 298128054Smux struct sockaddr_dl *sdl; 299128181Sluigi struct rt_msghdr *rtm; 30093952Sru struct ether_addr *ea; 3011553Srgrimes char *host = argv[0], *eaddr = argv[1]; 302128192Sluigi struct sockaddr_dl sdl_m; 3031553Srgrimes 3041553Srgrimes argc -= 2; 3051553Srgrimes argv += 2; 306128181Sluigi 307128181Sluigi bzero(&sdl_m, sizeof(sdl_m)); 308128181Sluigi sdl_m.sdl_len = sizeof(sdl_m); 309128181Sluigi sdl_m.sdl_family = AF_LINK; 310128181Sluigi 311128192Sluigi dst = getaddr(host); 312128192Sluigi if (dst == NULL) 313128192Sluigi return (1); 31477870Sru doing_proxy = flags = proxy_only = expire_time = 0; 3151553Srgrimes while (argc-- > 0) { 3161553Srgrimes if (strncmp(argv[0], "temp", 4) == 0) { 31777870Sru struct timeval tv; 31877870Sru gettimeofday(&tv, 0); 31977870Sru expire_time = tv.tv_sec + 20 * 60; 3201553Srgrimes } 3211553Srgrimes else if (strncmp(argv[0], "pub", 3) == 0) { 3221553Srgrimes flags |= RTF_ANNOUNCE; 32377870Sru doing_proxy = 1; 32477870Sru if (argc && strncmp(argv[1], "only", 3) == 0) { 32577870Sru proxy_only = 1; 326128192Sluigi dst->sin_other = SIN_PROXY; 32777870Sru argc--; argv++; 32877870Sru } 3291553Srgrimes } else if (strncmp(argv[0], "trail", 5) == 0) { 330128192Sluigi /* XXX deprecated and undocumented feature */ 3311553Srgrimes printf("%s: Sending trailers is no longer supported\n", 3321553Srgrimes host); 3331553Srgrimes } 3341553Srgrimes argv++; 3351553Srgrimes } 33693952Sru ea = (struct ether_addr *)LLADDR(&sdl_m); 33713977Sphk if (doing_proxy && !strcmp(eaddr, "auto")) { 338128192Sluigi if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { 33973135Sru printf("no interface found for %s\n", 340128192Sluigi inet_ntoa(dst->sin_addr)); 34113977Sphk return (1); 34213977Sphk } 34393952Sru sdl_m.sdl_alen = ETHER_ADDR_LEN; 34413977Sphk } else { 345128192Sluigi struct ether_addr *ea1 = ether_aton(eaddr); 346128192Sluigi 347128192Sluigi if (ea1 == NULL) 348128192Sluigi warnx("invalid Ethernet address '%s'", eaddr); 349128192Sluigi else { 350128192Sluigi *ea = *ea1; 35193952Sru sdl_m.sdl_alen = ETHER_ADDR_LEN; 352128192Sluigi } 35313977Sphk } 354128192Sluigi for (;;) { /* try at most twice */ 355128192Sluigi rtm = rtmsg(RTM_GET, dst, &sdl_m); 356128192Sluigi if (rtm == NULL) { 357128192Sluigi warn("%s", host); 358128192Sluigi return (1); 359128192Sluigi } 360128192Sluigi addr = (struct sockaddr_inarp *)(rtm + 1); 361128192Sluigi sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 362128192Sluigi if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) 363128192Sluigi break; 3641553Srgrimes if (sdl->sdl_family == AF_LINK && 3651553Srgrimes (rtm->rtm_flags & RTF_LLINFO) && 366128192Sluigi !(rtm->rtm_flags & RTF_GATEWAY) && 367128192Sluigi valid_type(sdl->sdl_type) ) 368128192Sluigi break; 3691553Srgrimes if (doing_proxy == 0) { 3701553Srgrimes printf("set: can only proxy for %s\n", host); 3711553Srgrimes return (1); 3721553Srgrimes } 373128192Sluigi if (dst->sin_other & SIN_PROXY) { 3741553Srgrimes printf("set: proxy entry exists for non 802 device\n"); 3751553Srgrimes return(1); 3761553Srgrimes } 377128192Sluigi dst->sin_other = SIN_PROXY; 37877870Sru proxy_only = 1; 3791553Srgrimes } 380128192Sluigi 3811553Srgrimes if (sdl->sdl_family != AF_LINK) { 3821553Srgrimes printf("cannot intuit interface index and type for %s\n", host); 3831553Srgrimes return (1); 3841553Srgrimes } 3851553Srgrimes sdl_m.sdl_type = sdl->sdl_type; 3861553Srgrimes sdl_m.sdl_index = sdl->sdl_index; 387147170Sglebius return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); 3881553Srgrimes} 3891553Srgrimes 3901553Srgrimes/* 3911553Srgrimes * Display an individual arp entry 3921553Srgrimes */ 393128192Sluigistatic void 39413977Sphkget(char *host) 3951553Srgrimes{ 396128192Sluigi struct sockaddr_inarp *addr; 3971553Srgrimes 398128192Sluigi addr = getaddr(host); 399128192Sluigi if (addr == NULL) 400128192Sluigi exit(1); 401128181Sluigi if (0 == search(addr->sin_addr.s_addr, print_entry)) { 402117729Syar printf("%s (%s) -- no entry", 40387598Smikeh host, inet_ntoa(addr->sin_addr)); 404117729Syar if (rifname) 405117729Syar printf(" on %s", rifname); 406117729Syar printf("\n"); 4071553Srgrimes } 4081553Srgrimes} 4091553Srgrimes 4101553Srgrimes/* 4118857Srgrimes * Delete an arp entry 4121553Srgrimes */ 413128181Sluigistatic int 414128192Sluigidelete(char *host, int do_proxy) 4151553Srgrimes{ 416128192Sluigi struct sockaddr_inarp *addr, *dst; 417128181Sluigi struct rt_msghdr *rtm; 4181553Srgrimes struct sockaddr_dl *sdl; 4191553Srgrimes 420128192Sluigi dst = getaddr(host); 421128192Sluigi if (dst == NULL) 422128192Sluigi return 1; 423128192Sluigi dst->sin_other = do_proxy; 424128192Sluigi for (;;) { /* try twice */ 425128192Sluigi rtm = rtmsg(RTM_GET, dst, NULL); 426128192Sluigi if (rtm == NULL) { 427128192Sluigi warn("%s", host); 4281553Srgrimes return (1); 4291553Srgrimes } 430128192Sluigi addr = (struct sockaddr_inarp *)(rtm + 1); 431128192Sluigi sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); 432128192Sluigi if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && 433128192Sluigi sdl->sdl_family == AF_LINK && 4341553Srgrimes (rtm->rtm_flags & RTF_LLINFO) && 435128192Sluigi !(rtm->rtm_flags & RTF_GATEWAY) && 436128192Sluigi valid_type(sdl->sdl_type) ) 437128192Sluigi break; /* found it */ 438128192Sluigi if (dst->sin_other & SIN_PROXY) { 439128192Sluigi fprintf(stderr, "delete: cannot locate %s\n",host); 440128192Sluigi return (1); 441128192Sluigi } 442128192Sluigi dst->sin_other = SIN_PROXY; 4431553Srgrimes } 444128192Sluigi if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { 44587598Smikeh printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); 44613977Sphk return (0); 44713977Sphk } 44813977Sphk return (1); 4491553Srgrimes} 4501553Srgrimes 4511553Srgrimes/* 45231145Sjulian * Search the arp table and do some action on matching entries 4531553Srgrimes */ 454128181Sluigistatic int 455128181Sluigisearch(u_long addr, action_fn *action) 4561553Srgrimes{ 4571553Srgrimes int mib[6]; 4581553Srgrimes size_t needed; 459140735Smaxim char *lim, *buf, *newbuf, *next; 4601553Srgrimes struct rt_msghdr *rtm; 46187598Smikeh struct sockaddr_inarp *sin2; 4621553Srgrimes struct sockaddr_dl *sdl; 463117729Syar char ifname[IF_NAMESIZE]; 464140735Smaxim int st, found_entry = 0; 4651553Srgrimes 4661553Srgrimes mib[0] = CTL_NET; 4671553Srgrimes mib[1] = PF_ROUTE; 4681553Srgrimes mib[2] = 0; 4691553Srgrimes mib[3] = AF_INET; 4701553Srgrimes mib[4] = NET_RT_FLAGS; 4711553Srgrimes mib[5] = RTF_LLINFO; 4721553Srgrimes if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 473128054Smux err(1, "route-sysctl-estimate"); 474128192Sluigi if (needed == 0) /* empty table */ 475128181Sluigi return 0; 476140735Smaxim buf = NULL; 477140748Smaxim for (;;) { 478140735Smaxim newbuf = realloc(buf, needed); 479140735Smaxim if (newbuf == NULL) { 480140735Smaxim if (buf != NULL) 481140735Smaxim free(buf); 482140735Smaxim errx(1, "could not reallocate memory"); 483140735Smaxim } 484140735Smaxim buf = newbuf; 485140735Smaxim st = sysctl(mib, 6, buf, &needed, NULL, 0); 486140748Smaxim if (st == 0 || errno != ENOMEM) 487140748Smaxim break; 488140748Smaxim needed += needed / 8; 489140748Smaxim } 490140735Smaxim if (st == -1) 491128054Smux err(1, "actual retrieval of routing table"); 4921553Srgrimes lim = buf + needed; 4931553Srgrimes for (next = buf; next < lim; next += rtm->rtm_msglen) { 4941553Srgrimes rtm = (struct rt_msghdr *)next; 49587598Smikeh sin2 = (struct sockaddr_inarp *)(rtm + 1); 496130246Sstefanf sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); 497117729Syar if (rifname && if_indextoname(sdl->sdl_index, ifname) && 498117729Syar strcmp(ifname, rifname)) 499117729Syar continue; 5001553Srgrimes if (addr) { 50187598Smikeh if (addr != sin2->sin_addr.s_addr) 5021553Srgrimes continue; 5031553Srgrimes found_entry = 1; 5041553Srgrimes } 50587598Smikeh (*action)(sdl, sin2, rtm); 5061553Srgrimes } 50776216Syar free(buf); 508128181Sluigi return found_entry; 5091553Srgrimes} 5101553Srgrimes 51131145Sjulian/* 51231145Sjulian * Display an arp entry 51331145Sjulian */ 514128181Sluigistatic void 51531145Sjulianprint_entry(struct sockaddr_dl *sdl, 51687598Smikeh struct sockaddr_inarp *addr, struct rt_msghdr *rtm) 51731145Sjulian{ 51877870Sru const char *host; 51931145Sjulian struct hostent *hp; 52096202Skbyanc struct iso88025_sockaddr_dl_data *trld; 52184666Sru char ifname[IF_NAMESIZE]; 52244627Sjulian int seg; 52331145Sjulian 52431145Sjulian if (nflag == 0) 52587598Smikeh hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 52687598Smikeh sizeof addr->sin_addr, AF_INET); 52731145Sjulian else 52831145Sjulian hp = 0; 52931145Sjulian if (hp) 53031145Sjulian host = hp->h_name; 53131145Sjulian else { 53231145Sjulian host = "?"; 53331145Sjulian if (h_errno == TRY_AGAIN) 53431145Sjulian nflag = 1; 53531145Sjulian } 53687598Smikeh printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); 537130408Sdfr if (sdl->sdl_alen) { 538139270Sru if ((sdl->sdl_type == IFT_ETHER || 539139270Sru sdl->sdl_type == IFT_L2VLAN) && 540130408Sdfr sdl->sdl_alen == ETHER_ADDR_LEN) 541130408Sdfr printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); 542130408Sdfr else { 543130408Sdfr int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; 544130408Sdfr 545130408Sdfr printf("%s", link_ntoa(sdl) + n); 546130408Sdfr } 547130408Sdfr } else 54831145Sjulian printf("(incomplete)"); 54984666Sru if (if_indextoname(sdl->sdl_index, ifname) != NULL) 55084666Sru printf(" on %s", ifname); 55131145Sjulian if (rtm->rtm_rmx.rmx_expire == 0) 55231145Sjulian printf(" permanent"); 55387598Smikeh if (addr->sin_other & SIN_PROXY) 55431145Sjulian printf(" published (proxy only)"); 55531145Sjulian if (rtm->rtm_addrs & RTA_NETMASK) { 55687598Smikeh addr = (struct sockaddr_inarp *) 557128186Sluigi (SA_SIZE(sdl) + (char *)sdl); 55887598Smikeh if (addr->sin_addr.s_addr == 0xffffffff) 55931145Sjulian printf(" published"); 56087598Smikeh if (addr->sin_len != 8) 56151250Sru printf("(weird)"); 56231145Sjulian } 56344627Sjulian switch(sdl->sdl_type) { 564128181Sluigi case IFT_ETHER: 56544627Sjulian printf(" [ethernet]"); 56644627Sjulian break; 567128181Sluigi case IFT_ISO88025: 56844627Sjulian printf(" [token-ring]"); 56996202Skbyanc trld = SDL_ISO88025(sdl); 57096202Skbyanc if (trld->trld_rcf != 0) { 57196202Skbyanc printf(" rt=%x", ntohs(trld->trld_rcf)); 57296202Skbyanc for (seg = 0; 57396202Skbyanc seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); 57496202Skbyanc seg++) 57596235Swpaul printf(":%x", ntohs(*(trld->trld_route[seg]))); 57696202Skbyanc } 57744627Sjulian break; 578128181Sluigi case IFT_FDDI: 57985123Smdodd printf(" [fddi]"); 58085123Smdodd break; 581128181Sluigi case IFT_ATM: 58285123Smdodd printf(" [atm]"); 58385123Smdodd break; 584128181Sluigi case IFT_L2VLAN: 58575180Syar printf(" [vlan]"); 58675180Syar break; 587130408Sdfr case IFT_IEEE1394: 588130408Sdfr printf(" [firewire]"); 589130408Sdfr break; 590128181Sluigi default: 59194075Smurray break; 59244627Sjulian } 59344627Sjulian 59431145Sjulian printf("\n"); 59544627Sjulian 59631145Sjulian} 59731145Sjulian 59831145Sjulian/* 59931145Sjulian * Nuke an arp entry 60031145Sjulian */ 601128181Sluigistatic void 60287598Smikehnuke_entry(struct sockaddr_dl *sdl __unused, 60387598Smikeh struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused) 60431145Sjulian{ 60531145Sjulian char ip[20]; 60631145Sjulian 60787598Smikeh snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); 608128192Sluigi delete(ip, 0); 60931145Sjulian} 61031145Sjulian 611128181Sluigistatic void 61213977Sphkusage(void) 6131553Srgrimes{ 61431145Sjulian fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", 615117729Syar "usage: arp [-n] [-i interface] hostname", 616117729Syar " arp [-n] [-i interface] -a", 61777870Sru " arp -d hostname [pub]", 61831145Sjulian " arp -d -a", 61929060Scharnier " arp -s hostname ether_addr [temp] [pub]", 62029060Scharnier " arp -S hostname ether_addr [temp] [pub]", 62129060Scharnier " arp -f filename"); 6221553Srgrimes exit(1); 6231553Srgrimes} 6241553Srgrimes 625128181Sluigistatic struct rt_msghdr * 626128192Sluigirtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) 6271553Srgrimes{ 6281553Srgrimes static int seq; 6291553Srgrimes int rlen; 630128181Sluigi int l; 631128181Sluigi struct sockaddr_in so_mask; 632128181Sluigi static int s = -1; 633128181Sluigi static pid_t pid; 634128181Sluigi 635128181Sluigi static struct { 636128181Sluigi struct rt_msghdr m_rtm; 637128181Sluigi char m_space[512]; 638128181Sluigi } m_rtmsg; 639128181Sluigi 640128054Smux struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 641128054Smux char *cp = m_rtmsg.m_space; 6421553Srgrimes 643128181Sluigi if (s < 0) { /* first time: open socket, get pid */ 644128181Sluigi s = socket(PF_ROUTE, SOCK_RAW, 0); 645128181Sluigi if (s < 0) 646128181Sluigi err(1, "socket"); 647128181Sluigi pid = getpid(); 648128181Sluigi } 649128181Sluigi bzero(&so_mask, sizeof(so_mask)); 650128181Sluigi so_mask.sin_len = 8; 651128181Sluigi so_mask.sin_addr.s_addr = 0xffffffff; 652128181Sluigi 6531553Srgrimes errno = 0; 654128192Sluigi /* 655128192Sluigi * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer 656128192Sluigi * appropriately. 657128192Sluigi */ 6581553Srgrimes if (cmd == RTM_DELETE) 6591553Srgrimes goto doit; 6601553Srgrimes bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 6611553Srgrimes rtm->rtm_flags = flags; 6621553Srgrimes rtm->rtm_version = RTM_VERSION; 6631553Srgrimes 6641553Srgrimes switch (cmd) { 6651553Srgrimes default: 66629060Scharnier errx(1, "internal wrong cmd"); 6671553Srgrimes case RTM_ADD: 6681553Srgrimes rtm->rtm_addrs |= RTA_GATEWAY; 6691553Srgrimes rtm->rtm_rmx.rmx_expire = expire_time; 6701553Srgrimes rtm->rtm_inits = RTV_EXPIRE; 6711553Srgrimes rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 672128192Sluigi dst->sin_other = 0; 6731553Srgrimes if (doing_proxy) { 67477870Sru if (proxy_only) 675128192Sluigi dst->sin_other = SIN_PROXY; 6761553Srgrimes else { 6771553Srgrimes rtm->rtm_addrs |= RTA_NETMASK; 6781553Srgrimes rtm->rtm_flags &= ~RTF_HOST; 6791553Srgrimes } 6801553Srgrimes } 6811553Srgrimes /* FALLTHROUGH */ 6821553Srgrimes case RTM_GET: 6831553Srgrimes rtm->rtm_addrs |= RTA_DST; 6841553Srgrimes } 6851553Srgrimes#define NEXTADDR(w, s) \ 686128192Sluigi if (s && rtm->rtm_addrs & (w)) { \ 687128192Sluigi bcopy(s, cp, sizeof(*s)); cp += SA_SIZE(s);} 6881553Srgrimes 689128192Sluigi NEXTADDR(RTA_DST, dst); 690128192Sluigi NEXTADDR(RTA_GATEWAY, sdl); 691128192Sluigi NEXTADDR(RTA_NETMASK, &so_mask); 6921553Srgrimes 6931553Srgrimes rtm->rtm_msglen = cp - (char *)&m_rtmsg; 6941553Srgrimesdoit: 6951553Srgrimes l = rtm->rtm_msglen; 6961553Srgrimes rtm->rtm_seq = ++seq; 6971553Srgrimes rtm->rtm_type = cmd; 6981553Srgrimes if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 6991553Srgrimes if (errno != ESRCH || cmd != RTM_DELETE) { 70029060Scharnier warn("writing to routing socket"); 701128181Sluigi return NULL; 7021553Srgrimes } 7031553Srgrimes } 7041553Srgrimes do { 7051553Srgrimes l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 7061553Srgrimes } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 7071553Srgrimes if (l < 0) 70829060Scharnier warn("read from routing socket"); 709128181Sluigi return rtm; 7101553Srgrimes} 7111553Srgrimes 71213977Sphk/* 71313977Sphk * get_ether_addr - get the hardware address of an interface on the 71413977Sphk * the same subnet as ipaddr. 71513977Sphk */ 71613977Sphk#define MAX_IFS 32 71713977Sphk 718128181Sluigistatic int 71993952Sruget_ether_addr(u_int32_t ipaddr, struct ether_addr *hwaddr) 72013977Sphk{ 72113977Sphk struct ifreq *ifr, *ifend, *ifp; 722128192Sluigi uint32_t ina, mask; 72313977Sphk struct sockaddr_dl *dla; 72413977Sphk struct ifreq ifreq; 72513977Sphk struct ifconf ifc; 72613977Sphk struct ifreq ifs[MAX_IFS]; 72787598Smikeh int sock; 728128192Sluigi int retval = 0; 72913977Sphk 73087598Smikeh sock = socket(AF_INET, SOCK_DGRAM, 0); 73187598Smikeh if (sock < 0) 73229060Scharnier err(1, "socket"); 73313977Sphk 73413977Sphk ifc.ifc_len = sizeof(ifs); 73513977Sphk ifc.ifc_req = ifs; 73692735Smikeh if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { 73729060Scharnier warnx("ioctl(SIOCGIFCONF)"); 738128192Sluigi goto done; 73913977Sphk } 74013977Sphk 741128192Sluigi#define NEXTIFR(i) \ 742128192Sluigi ((struct ifreq *)((char *)&(i)->ifr_addr \ 743128192Sluigi + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) 744128192Sluigi 74513977Sphk /* 746128192Sluigi * Scan through looking for an interface with an Internet 747128192Sluigi * address on the same subnet as `ipaddr'. 748128192Sluigi */ 749128192Sluigi ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); 750128192Sluigi for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { 751128192Sluigi if (ifr->ifr_addr.sa_family != AF_INET) 752128192Sluigi continue; 753128192Sluigi /* XXX can't we use *ifr instead of ifreq ? */ 754128192Sluigi strncpy(ifreq.ifr_name, ifr->ifr_name, 755128192Sluigi sizeof(ifreq.ifr_name)); 756128192Sluigi /* 757128192Sluigi * Check that the interface is up, 758128192Sluigi * and not point-to-point or loopback. 759128192Sluigi */ 760128192Sluigi if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) 761128192Sluigi continue; 762128192Sluigi if ((ifreq.ifr_flags & 763128192Sluigi (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| 764128192Sluigi IFF_LOOPBACK|IFF_NOARP)) 765128192Sluigi != (IFF_UP|IFF_BROADCAST)) 766128192Sluigi continue; 767128192Sluigi /* 768128192Sluigi * Get its netmask and check that it's on 769128192Sluigi * the right subnet. 770128192Sluigi */ 771128192Sluigi if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) 772128192Sluigi continue; 773128192Sluigi mask = ((struct sockaddr_in *) 774128192Sluigi &ifreq.ifr_addr)->sin_addr.s_addr; 775128192Sluigi ina = ((struct sockaddr_in *) 776128192Sluigi &ifr->ifr_addr)->sin_addr.s_addr; 777128192Sluigi if ((ipaddr & mask) == (ina & mask)) 778128192Sluigi break; /* ok, we got it! */ 77913977Sphk } 78013977Sphk 781128192Sluigi if (ifr >= ifend) 782128192Sluigi goto done; 78313977Sphk 78413977Sphk /* 785128192Sluigi * Now scan through again looking for a link-level address 786128192Sluigi * for this interface. 787128192Sluigi */ 78813977Sphk ifp = ifr; 789128192Sluigi for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) 790128192Sluigi if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && 791128192Sluigi ifr->ifr_addr.sa_family == AF_LINK) 792128192Sluigi break; 793128192Sluigi if (ifr >= ifend) 794128192Sluigi goto done; 795128192Sluigi /* 796128192Sluigi * Found the link-level address - copy it out 797128192Sluigi */ 798128192Sluigi dla = (struct sockaddr_dl *) &ifr->ifr_addr; 799128192Sluigi memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); 800128192Sluigi printf("using interface %s for proxy with address ", 801128192Sluigi ifp->ifr_name); 802128192Sluigi printf("%s\n", ether_ntoa(hwaddr)); 803128192Sluigi retval = dla->sdl_alen; 804128192Sluigidone: 805128192Sluigi close(sock); 806128192Sluigi return retval; 80713977Sphk} 808