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