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