1/*
2 * Copyright (c) 2003-2012 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
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.
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
61#if 0
62#ifndef lint
63static char const copyright[] =
64"@(#) Copyright (c) 1984, 1993\n\
65	The Regents of the University of California.  All rights reserved.\n";
66#endif /* not lint */
67#endif
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>
77#include <sys/sockio.h>
78#include <sys/sysctl.h>
79#include <sys/ioctl.h>
80#include <sys/time.h>
81
82#include <net/if.h>
83#include <net/if_dl.h>
84#include <net/if_types.h>
85#include <net/route.h>
86#if 0
87#include <net/iso88025.h>
88#endif
89
90#include <netinet/in.h>
91#include <netinet/if_ether.h>
92
93#include <arpa/inet.h>
94
95#include <ctype.h>
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>
103#include <string.h>
104#include <strings.h>
105#include <unistd.h>
106
107typedef void (action_fn)(struct sockaddr_dl *sdl,
108	struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
109typedef void (action_ext_fn)(struct sockaddr_dl *sdl,
110	struct sockaddr_inarp *s_in, struct rt_msghdr_ext *rtm);
111
112static int search(in_addr_t addr, action_fn *action);
113static int search_ext(in_addr_t addr, action_ext_fn *action);
114static action_fn print_entry;
115static action_fn nuke_entry;
116static action_ext_fn print_entry_ext;
117
118static char *print_lladdr(struct sockaddr_dl *);
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);
129static char *sec2str(time_t);
130
131static int nflag;	/* no reverse dns lookups */
132static int xflag;	/* extended link-layer reachability information */
133static char *rifname;
134
135static int	expire_time, flags, doing_proxy, proxy_only;
136
137static char *boundif = NULL;
138static unsigned int ifscope = 0;
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
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
154#define SETFUNC(f)	{ if (func) usage(); func = (f); }
155
156
157int
158main(int argc, char *argv[])
159{
160	int ch, func = 0;
161	int rtn = 0;
162	int aflag = 0;	/* do it for all entries */
163	int lflag = 0;
164	uint32_t ifindex = 0;
165
166	while ((ch = getopt(argc, argv, "andflsSi:x")) != -1)
167		switch((char)ch) {
168		case 'a':
169			aflag = 1;
170			break;
171		case 'd':
172			SETFUNC(F_DELETE);
173			break;
174		case 'n':
175			nflag = 1;
176			break;
177		case 'l':
178			lflag = 1;
179			break;
180		case 'S':
181			SETFUNC(F_REPLACE);
182			break;
183		case 's':
184			SETFUNC(F_SET);
185			break;
186		case 'f' :
187			SETFUNC(F_FILESET);
188			break;
189		case 'i':
190			rifname = optarg;
191			break;
192		case 'x':
193			xflag = 1;
194			lflag = 1;
195			break;
196		case '?':
197		default:
198			usage();
199		}
200	argc -= optind;
201	argv += optind;
202
203	if (!func)
204		func = F_GET;
205	if (rifname) {
206		if (func != F_GET && !(func == F_DELETE && aflag))
207			errx(1, "-i not applicable to this operation");
208		if ((ifindex = if_nametoindex(rifname)) == 0) {
209			if (errno == ENXIO)
210				errx(1, "interface %s does not exist", rifname);
211			else
212				err(1, "if_nametoindex(%s)", rifname);
213		}
214	}
215	switch (func) {
216	case F_GET:
217		if (aflag) {
218			if (argc != 0)
219				usage();
220			if (lflag) {
221				printf("%-23s %-17s %-9.9s %-9.9s %8.8s %4s "
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");
229				search_ext(0, print_entry_ext);
230			} else {
231				search(0, print_entry);
232			}
233		} else {
234			if (argc != 1)
235				usage();
236			rtn = get(argv[0]);
237		}
238		break;
239	case F_SET:
240	case F_REPLACE:
241		if (argc < 2 || argc > 6)
242			usage();
243		if (func == F_REPLACE)
244			(void)delete(argv[0], 0);
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 {
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			}
271			if (i > argc)
272				usage();
273			rtn = delete(argv[0], do_proxy);
274		}
275		break;
276	case F_FILESET:
277		if (argc != 1)
278			usage();
279		rtn = file(argv[0]);
280		break;
281	}
282
283	return (rtn);
284}
285
286/*
287 * Process a file to set standard arp entries
288 */
289static int
290file(char *name)
291{
292	FILE *fp;
293	int i, retval;
294	char line[128], arg[7][50], *args[7], *p;
295
296	if ((fp = fopen(name, "r")) == NULL)
297		err(1, "cannot open %s", name);
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];
303	args[5] = &arg[5][0];
304	args[6] = &arg[6][0];
305	retval = 0;
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]);
314		if (i < 2) {
315			warnx("bad line: %s", line);
316			retval = 1;
317			continue;
318		}
319		if (set(i, args))
320			retval = 1;
321	}
322	fclose(fp);
323	return (retval);
324}
325
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)
333{
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);
374	}
375}
376
377/*
378 * Set an individual arp entry
379 */
380static int
381set(int argc, char **argv)
382{
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;
387	struct ether_addr *ea;
388	char *host = argv[0], *eaddr = argv[1];
389	struct sockaddr_dl sdl_m;
390
391	argc -= 2;
392	argv += 2;
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);
401	doing_proxy = flags = proxy_only = expire_time = 0;
402	boundif = NULL;
403	ifscope = 0;
404	while (argc-- > 0) {
405		if (strncmp(argv[0], "temp", sizeof("temp")) == 0) {
406			struct timeval tv;
407			gettimeofday(&tv, 0);
408			expire_time = tv.tv_sec + 20 * 60;
409		} else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) {
410			flags |= RTF_ANNOUNCE;
411			doing_proxy = 1;
412			if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) {
413				proxy_only = 1;
414				dst->sin_other = SIN_PROXY;
415				argc--; argv++;
416			}
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 */
423			printf("%s: Sending trailers is no longer supported\n",
424				host);
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++;
434		}
435		argv++;
436	}
437	ea = (struct ether_addr *)LLADDR(&sdl_m);
438	if (doing_proxy && !strcmp(eaddr, "auto")) {
439		if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
440			printf("no interface found for %s\n",
441			       inet_ntoa(dst->sin_addr));
442			return (1);
443		}
444		sdl_m.sdl_alen = ETHER_ADDR_LEN;
445	} else {
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;
453			sdl_m.sdl_alen = ETHER_ADDR_LEN;
454		}
455	}
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;
466		if (sdl->sdl_family == AF_LINK &&
467		    (rtm->rtm_flags & RTF_LLINFO) &&
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;
478		if (doing_proxy == 0) {
479			printf("set: can only proxy for %s\n", host);
480			return (1);
481		}
482		if (dst->sin_other & SIN_PROXY) {
483			printf("set: proxy entry exists for non 802 device\n");
484			return (1);
485		}
486		dst->sin_other = SIN_PROXY;
487		proxy_only = 1;
488	}
489
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;
496	return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
497}
498
499/*
500 * Display an individual arp entry
501 */
502static int
503get(char *host)
504{
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",
512		    host, inet_ntoa(addr->sin_addr));
513		if (rifname)
514			printf(" on %s", rifname);
515		printf("\n");
516		return (1);
517	}
518	return (0);
519}
520
521/*
522 * Delete an arp entry
523 */
524static int
525delete(char *host, int do_proxy)
526{
527	struct sockaddr_inarp *addr, *dst;
528	struct rt_msghdr *rtm;
529	struct sockaddr_dl *sdl;
530
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);
539			return (1);
540		}
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 &&
545		    (rtm->rtm_flags & RTF_LLINFO) &&
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);
552		}
553		dst->sin_other = SIN_PROXY;
554	}
555	if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
556		printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
557		return (0);
558	}
559	return (1);
560}
561
562/*
563 * Search the arp table and do some action on matching entries
564 */
565static int
566search(in_addr_t addr, action_fn *action)
567{
568	int mib[6];
569	size_t needed;
570	char *lim, *buf, *newbuf, *next;
571	struct rt_msghdr *rtm;
572	struct sockaddr_inarp *sin2;
573	struct sockaddr_dl *sdl;
574	char ifname[IF_NAMESIZE];
575	int st, found_entry = 0;
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)
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");
603	lim = buf + needed;
604	for (next = buf; next < lim; next += rtm->rtm_msglen) {
605		rtm = (struct rt_msghdr *)next;
606		sin2 = (struct sockaddr_inarp *)(rtm + 1);
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;
611		if (addr) {
612			if (addr != sin2->sin_addr.s_addr)
613				continue;
614			found_entry = 1;
615		}
616		(*action)(sdl, sin2, rtm);
617	}
618	free(buf);
619	return (found_entry);
620}
621
622/*
623 * Stolen and adapted from ifconfig
624 */
625static char *
626print_lladdr(struct sockaddr_dl *sdl)
627{
628	static char buf[256];
629        char *cp;
630        int n, bufsize = sizeof (buf), p = 0;
631
632	bzero(buf, sizeof (buf));
633        cp = (char *)LLADDR(sdl);
634        if ((n = sdl->sdl_alen) > 0) {
635                while (--n >= 0)
636                        p += snprintf(buf + p, bufsize - p, "%x%s",
637			    *cp++ & 0xff, n > 0 ? ":" : "");
638        }
639	return (buf);
640}
641
642/*
643 * Display an arp entry
644 */
645static void
646print_entry(struct sockaddr_dl *sdl,
647	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
648{
649	const char *host;
650	struct hostent *hp;
651	char ifname[IF_NAMESIZE];
652#if 0
653	struct iso88025_sockaddr_dl_data *trld;
654	int seg;
655#endif
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) {
671#if 1
672		printf("%s", print_lladdr(sdl));
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
686		printf("(incomplete)");
687	if (if_indextoname(sdl->sdl_index, ifname) != NULL)
688		printf(" on %s", ifname);
689	if ((rtm->rtm_flags & RTF_IFSCOPE))
690		printf(" ifscope");
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 *)
697			(SA_SIZE(sdl) + (char *)sdl);
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) {
704	case IFT_ETHER:
705                printf(" [ethernet]");
706                break;
707#if 0
708	case IFT_ISO88025:
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		}
718                break;
719#endif
720	case IFT_FDDI:
721                printf(" [fddi]");
722                break;
723	case IFT_ATM:
724                printf(" [atm]");
725                break;
726	case IFT_L2VLAN:
727		printf(" [vlan]");
728		break;
729	case IFT_IEEE1394:
730                printf(" [firewire]");
731                break;
732#ifdef IFT_BRIDGE
733	case IFT_BRIDGE:
734		printf(" [bridge]");
735		break;
736#endif
737	default:
738		break;
739        }
740
741	printf("\n");
742
743}
744
745/*
746 * Nuke an arp entry
747 */
748static void
749nuke_entry(struct sockaddr_dl *sdl __unused,
750	struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
751{
752	char ip[20];
753
754	snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
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;
760	(void)delete(ip, 0);
761	ifscope = 0;
762}
763
764static void
765usage(void)
766{
767	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
768		"usage: arp [-n] [-i interface] hostname",
769		"       arp [-n] [-i interface] [-l] -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]",
774		"       arp -f filename");
775	exit(1);
776}
777
778static struct rt_msghdr *
779rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
780{
781	static int seq;
782	int rlen;
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;
805
806	errno = 0;
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	 */
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
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
827	switch (cmd) {
828	default:
829		errx(1, "internal wrong cmd");
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);
835		dst->sin_other = 0;
836		if (doing_proxy) {
837			if (proxy_only)
838				dst->sin_other = SIN_PROXY;
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) \
849	if ((s) != NULL && rtm->rtm_addrs & (w)) { \
850		bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);}
851
852	NEXTADDR(RTA_DST, dst);
853	NEXTADDR(RTA_GATEWAY, sdl);
854	NEXTADDR(RTA_NETMASK, so_mask_ptr);
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) {
863			warn("writing to routing socket");
864			return (NULL);
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)
871		warn("read from routing socket");
872	return (rtm);
873}
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
881static int
882get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
883{
884	struct ifreq *ifr, *ifend, *ifp;
885	in_addr_t ina, mask;
886	struct sockaddr_dl *dla;
887	struct ifreq ifreq;
888	struct ifconf ifc;
889	struct ifreq ifs[MAX_IFS];
890	int sock;
891	int retval = 0;
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)");
901		goto done;
902	}
903
904#define NEXTIFR(i)						\
905    ((struct ifreq *)((char *)&(i)->ifr_addr			\
906	+ MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
907
908	/*
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;
916		strncpy(ifreq.ifr_name, ifr->ifr_name,
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! */
942	}
943
944	if (ifr >= ifend)
945		goto done;
946
947	/*
948	 * Now scan through again looking for a link-level address
949	 * for this interface.
950	 */
951	ifp = ifr;
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);
970}
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);
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		}
1161	}
1162	printf("\n");
1163}
1164