route.c revision 253519
1/*
2 * Copyright (c) 1983, 1989, 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)route.c	8.6 (Berkeley) 4/28/95";
39#endif
40#endif /* not lint */
41
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD: head/sbin/route/route.c 253519 2013-07-21 14:27:07Z hrs $");
44
45#include <sys/param.h>
46#include <sys/file.h>
47#include <sys/socket.h>
48#include <sys/ioctl.h>
49#include <sys/sysctl.h>
50#include <sys/types.h>
51#include <sys/queue.h>
52
53#include <net/if.h>
54#include <net/route.h>
55#include <net/if_dl.h>
56#include <netinet/in.h>
57#include <netinet/if_ether.h>
58#include <netatalk/at.h>
59#include <arpa/inet.h>
60#include <netdb.h>
61
62#include <ctype.h>
63#include <err.h>
64#include <errno.h>
65#include <paths.h>
66#include <stdio.h>
67#include <stdlib.h>
68#include <string.h>
69#include <sysexits.h>
70#include <unistd.h>
71#include <ifaddrs.h>
72
73static struct keytab {
74	const char	*kt_cp;
75	int	kt_i;
76} keywords[] = {
77#include "keywords.h"
78	{0, 0}
79};
80
81static struct sockaddr_storage so[RTAX_MAX];
82static int	pid, rtm_addrs;
83static int	s;
84static int	forcehost, forcenet, nflag, af, qflag, tflag;
85static int	verbose, aflen;
86static int	locking, lockrest, debugonly;
87static struct rt_metrics rt_metrics;
88static u_long  rtm_inits;
89static uid_t	uid;
90static int	defaultfib;
91static int	numfibs;
92
93static int	atalk_aton(const char *, struct at_addr *);
94static char	*atalk_ntoa(struct at_addr);
95static void	printb(int, const char *);
96static void	flushroutes(int argc, char *argv[]);
97static int	flushroutes_fib(int);
98static int	getaddr(int, char *, struct hostent **, int);
99static int	keyword(const char *);
100#ifdef INET
101static void	inet_makenetandmask(u_long, struct sockaddr_in *,
102		    struct sockaddr_in *, u_long);
103#endif
104#ifdef INET6
105static int	inet6_makenetandmask(struct sockaddr_in6 *, const char *);
106#endif
107static void	interfaces(void);
108static void	monitor(int, char*[]);
109static const char	*netname(struct sockaddr *);
110static void	newroute(int, char **);
111static int	newroute_fib(int, char *, int);
112static void	pmsg_addrs(char *, int, size_t);
113static void	pmsg_common(struct rt_msghdr *, size_t);
114static int	prefixlen(const char *);
115static void	print_getmsg(struct rt_msghdr *, int, int);
116static void	print_rtmsg(struct rt_msghdr *, size_t);
117static const char	*routename(struct sockaddr *);
118static int	rtmsg(int, int, int);
119static void	set_metric(char *, int);
120static int	set_sofib(int);
121static void	sockaddr(char *, struct sockaddr *, size_t);
122static void	sodump(struct sockaddr *, const char *);
123
124struct fibl {
125	TAILQ_ENTRY(fibl)	fl_next;
126
127	int	fl_num;
128	int	fl_error;
129	int	fl_errno;
130};
131static TAILQ_HEAD(fibl_head_t, fibl) fibl_head;
132
133static int	fiboptlist_csv(const char *, struct fibl_head_t *);
134static int	fiboptlist_range(const char *, struct fibl_head_t *);
135
136static void usage(const char *) __dead2;
137
138static void
139usage(const char *cp)
140{
141	if (cp != NULL)
142		warnx("bad keyword: %s", cp);
143	errx(EX_USAGE, "usage: route [-dnqtv] command [[modifiers] args]");
144	/* NOTREACHED */
145}
146
147int
148main(int argc, char **argv)
149{
150	int ch;
151	size_t len;
152
153	if (argc < 2)
154		usage(NULL);
155
156	while ((ch = getopt(argc, argv, "nqdtv")) != -1)
157		switch(ch) {
158		case 'n':
159			nflag = 1;
160			break;
161		case 'q':
162			qflag = 1;
163			break;
164		case 'v':
165			verbose = 1;
166			break;
167		case 't':
168			tflag = 1;
169			break;
170		case 'd':
171			debugonly = 1;
172			break;
173		case '?':
174		default:
175			usage(NULL);
176		}
177	argc -= optind;
178	argv += optind;
179
180	pid = getpid();
181	uid = geteuid();
182	if (tflag)
183		s = open(_PATH_DEVNULL, O_WRONLY, 0);
184	else
185		s = socket(PF_ROUTE, SOCK_RAW, 0);
186	if (s < 0)
187		err(EX_OSERR, "socket");
188
189	len = sizeof(numfibs);
190	if (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) == -1)
191		numfibs = -1;
192
193	len = sizeof(defaultfib);
194	if (numfibs != -1 &&
195	    sysctlbyname("net.my_fibnum", (void *)&defaultfib, &len, NULL,
196		0) == -1)
197		defaultfib = -1;
198
199	if (*argv != NULL)
200		switch (keyword(*argv)) {
201		case K_GET:
202		case K_SHOW:
203			uid = 0;
204			/* FALLTHROUGH */
205
206		case K_CHANGE:
207		case K_ADD:
208		case K_DEL:
209		case K_DELETE:
210			newroute(argc, argv);
211			/* NOTREACHED */
212
213		case K_MONITOR:
214			monitor(argc, argv);
215			/* NOTREACHED */
216
217		case K_FLUSH:
218			flushroutes(argc, argv);
219			exit(0);
220			/* NOTREACHED */
221		}
222	usage(*argv);
223	/* NOTREACHED */
224}
225
226static int
227set_sofib(int fib)
228{
229
230	if (fib < 0)
231		return (0);
232	return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void *)&fib,
233	    sizeof(fib)));
234}
235
236static int
237fiboptlist_range(const char *arg, struct fibl_head_t *flh)
238{
239	struct fibl *fl;
240	char *str0, *str, *token, *endptr;
241	int fib[2], i, error;
242
243	str0 = str = strdup(arg);
244	error = 0;
245	i = 0;
246	while ((token = strsep(&str, "-")) != NULL) {
247		switch (i) {
248		case 0:
249		case 1:
250			errno = 0;
251			fib[i] = strtol(token, &endptr, 0);
252			if (errno == 0) {
253				if (*endptr != '\0' ||
254				    fib[i] < 0 ||
255				    (numfibs != -1 && fib[i] > numfibs - 1))
256					errno = EINVAL;
257			}
258			if (errno)
259				error = 1;
260			break;
261		default:
262			error = 1;
263		}
264		if (error)
265			goto fiboptlist_range_ret;
266		i++;
267	}
268	if (fib[0] >= fib[1]) {
269		error = 1;
270		goto fiboptlist_range_ret;
271	}
272	for (i = fib[0]; i <= fib[1]; i++) {
273		fl = calloc(1, sizeof(*fl));
274		if (fl == NULL) {
275			error = 1;
276			goto fiboptlist_range_ret;
277		}
278		fl->fl_num = i;
279		TAILQ_INSERT_TAIL(flh, fl, fl_next);
280	}
281fiboptlist_range_ret:
282	free(str0);
283	return (error);
284}
285
286#define	ALLSTRLEN	64
287static int
288fiboptlist_csv(const char *arg, struct fibl_head_t *flh)
289{
290	struct fibl *fl;
291	char *str0, *str, *token, *endptr;
292	int fib, error;
293
294	str0 = str = NULL;
295	if (strcmp("all", arg) == 0) {
296		str = calloc(1, ALLSTRLEN);
297		if (str == NULL) {
298			error = 1;
299			goto fiboptlist_csv_ret;
300		}
301		if (numfibs > 1)
302			snprintf(str, ALLSTRLEN - 1, "%d-%d", 0, numfibs - 1);
303		else
304			snprintf(str, ALLSTRLEN - 1, "%d", 0);
305	} else if (strcmp("default", arg) == 0) {
306		str0 = str = calloc(1, ALLSTRLEN);
307		if (str == NULL) {
308			error = 1;
309			goto fiboptlist_csv_ret;
310		}
311		snprintf(str, ALLSTRLEN - 1, "%d", defaultfib);
312	} else
313		str0 = str = strdup(arg);
314
315	error = 0;
316	while ((token = strsep(&str, ",")) != NULL) {
317		if (*token != '-' && strchr(token, '-') != NULL) {
318			error = fiboptlist_range(token, flh);
319			if (error)
320				goto fiboptlist_csv_ret;
321		} else {
322			errno = 0;
323			fib = strtol(token, &endptr, 0);
324			if (errno == 0) {
325				if (*endptr != '\0' ||
326				    fib < 0 ||
327				    (numfibs != -1 && fib > numfibs - 1))
328					errno = EINVAL;
329			}
330			if (errno) {
331				error = 1;
332				goto fiboptlist_csv_ret;
333			}
334			fl = calloc(1, sizeof(*fl));
335			if (fl == NULL) {
336				error = 1;
337				goto fiboptlist_csv_ret;
338			}
339			fl->fl_num = fib;
340			TAILQ_INSERT_TAIL(flh, fl, fl_next);
341		}
342	}
343fiboptlist_csv_ret:
344	if (str0 != NULL)
345		free(str0);
346	return (error);
347}
348
349/*
350 * Purge all entries in the routing tables not
351 * associated with network interfaces.
352 */
353static void
354flushroutes(int argc, char *argv[])
355{
356	struct fibl *fl;
357	int error;
358
359	if (uid != 0 && !debugonly && !tflag)
360		errx(EX_NOPERM, "must be root to alter routing table");
361	shutdown(s, SHUT_RD); /* Don't want to read back our messages */
362
363	TAILQ_INIT(&fibl_head);
364	while (argc > 1) {
365		argc--;
366		argv++;
367		if (**argv != '-')
368			usage(*argv);
369		switch (keyword(*argv + 1)) {
370#ifdef INET
371		case K_INET:
372			af = AF_INET;
373			break;
374#endif
375#ifdef INET6
376		case K_INET6:
377			af = AF_INET6;
378			break;
379#endif
380		case K_ATALK:
381			af = AF_APPLETALK;
382			break;
383		case K_LINK:
384			af = AF_LINK;
385			break;
386		case K_FIB:
387			if (!--argc)
388				usage(*argv);
389			error = fiboptlist_csv(*++argv, &fibl_head);
390			if (error)
391				errx(EX_USAGE, "invalid fib number: %s", *argv);
392			break;
393		default:
394			usage(*argv);
395		}
396	}
397	if (TAILQ_EMPTY(&fibl_head)) {
398		error = fiboptlist_csv("default", &fibl_head);
399		if (error)
400			errx(EX_OSERR, "fiboptlist_csv failed.");
401	}
402	TAILQ_FOREACH(fl, &fibl_head, fl_next)
403		flushroutes_fib(fl->fl_num);
404}
405
406static int
407flushroutes_fib(int fib)
408{
409	struct rt_msghdr *rtm;
410	size_t needed;
411	char *buf, *next, *lim;
412	int mib[7], rlen, seqno, count = 0;
413	int error;
414
415	error = set_sofib(fib);
416	if (error) {
417		warn("fib number %d is ignored", fib);
418		return (error);
419	}
420
421retry:
422	mib[0] = CTL_NET;
423	mib[1] = PF_ROUTE;
424	mib[2] = 0;		/* protocol */
425	mib[3] = AF_UNSPEC;
426	mib[4] = NET_RT_DUMP;
427	mib[5] = 0;		/* no flags */
428	mib[6] = fib;
429	if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0)
430		err(EX_OSERR, "route-sysctl-estimate");
431	if ((buf = malloc(needed)) == NULL)
432		errx(EX_OSERR, "malloc failed");
433	if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) {
434		if (errno == ENOMEM && count++ < 10) {
435			warnx("Routing table grew, retrying");
436			sleep(1);
437			free(buf);
438			goto retry;
439		}
440		err(EX_OSERR, "route-sysctl-get");
441	}
442	lim = buf + needed;
443	if (verbose)
444		(void)printf("Examining routing table from sysctl\n");
445	seqno = 0;		/* ??? */
446	for (next = buf; next < lim; next += rtm->rtm_msglen) {
447		rtm = (struct rt_msghdr *)(void *)next;
448		if (verbose)
449			print_rtmsg(rtm, rtm->rtm_msglen);
450		if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
451			continue;
452		if (af != 0) {
453			struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
454
455			if (sa->sa_family != af)
456				continue;
457		}
458		if (debugonly)
459			continue;
460		rtm->rtm_type = RTM_DELETE;
461		rtm->rtm_seq = seqno;
462		rlen = write(s, next, rtm->rtm_msglen);
463		if (rlen < 0 && errno == EPERM)
464			err(1, "write to routing socket");
465		if (rlen < (int)rtm->rtm_msglen) {
466			warn("write to routing socket");
467			(void)printf("got only %d for rlen\n", rlen);
468			free(buf);
469			goto retry;
470			break;
471		}
472		seqno++;
473		if (qflag)
474			continue;
475		if (verbose)
476			print_rtmsg(rtm, rlen);
477		else {
478			struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
479
480			printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ?
481			    routename(sa) : netname(sa));
482			sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa);
483			printf("%-20.20s ", routename(sa));
484			if (fib >= 0)
485				printf("-fib %-3d ", fib);
486			printf("done\n");
487		}
488	}
489	return (error);
490}
491
492static const char *
493routename(struct sockaddr *sa)
494{
495	struct sockaddr_dl *sdl;
496	const char *cp;
497	static char line[NI_MAXHOST];
498	static char domain[MAXHOSTNAMELEN + 1];
499	static int first = 1, n;
500
501	if (first) {
502		first = 0;
503		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
504		    (cp = strchr(domain, '.'))) {
505			domain[MAXHOSTNAMELEN] = '\0';
506			(void)strcpy(domain, cp + 1);
507		} else
508			domain[0] = '\0';
509	}
510
511	/* If the address is zero-filled, use "default". */
512	if (sa->sa_len == 0 && nflag == 0)
513		return ("default");
514#if defined(INET) || defined(INET6)
515	switch (sa->sa_family) {
516#ifdef INET
517	case AF_INET:
518		/* If the address is zero-filled, use "default". */
519		if (nflag == 0 &&
520		    ((struct sockaddr_in *)(void *)sa)->sin_addr.s_addr ==
521		    INADDR_ANY)
522			return("default");
523		break;
524#endif
525#ifdef INET6
526	case AF_INET6:
527		/* If the address is zero-filled, use "default". */
528		if (nflag == 0 &&
529		    IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)sa)->sin6_addr))
530			return("default");
531		break;
532#endif
533	}
534#endif
535
536	switch (sa->sa_family) {
537#if defined(INET) || defined(INET6)
538#ifdef INET
539	case AF_INET:
540#endif
541#ifdef INET6
542	case AF_INET6:
543#endif
544	{
545		struct sockaddr_storage ss;
546		int error;
547		char *p;
548
549		memset(&ss, 0, sizeof(ss));
550		if (sa->sa_len == 0)
551			ss.ss_family = sa->sa_family;
552		else
553			memcpy(&ss, sa, sa->sa_len);
554		/* Expand sa->sa_len because it could be shortened. */
555		if (sa->sa_family == AF_INET)
556			ss.ss_len = sizeof(struct sockaddr_in);
557		else if (sa->sa_family == AF_INET6)
558			ss.ss_len = sizeof(struct sockaddr_in6);
559		error = getnameinfo((struct sockaddr *)&ss, ss.ss_len,
560		    line, sizeof(line), NULL, 0,
561		    (nflag == 0) ? 0 : NI_NUMERICHOST);
562		if (error) {
563			warnx("getnameinfo(): %s", gai_strerror(error));
564			strncpy(line, "invalid", sizeof(line));
565		}
566
567		/* Remove the domain part if any. */
568		p = strchr(line, '.');
569		if (p != NULL && strcmp(p + 1, domain) == 0)
570			*p = '\0';
571
572		return (line);
573		break;
574	}
575#endif
576	case AF_APPLETALK:
577		(void)snprintf(line, sizeof(line), "atalk %s",
578		    atalk_ntoa(((struct sockaddr_at *)(void *)sa)->sat_addr));
579		break;
580
581	case AF_LINK:
582		sdl = (struct sockaddr_dl *)(void *)sa;
583
584		if (sdl->sdl_nlen == 0 &&
585		    sdl->sdl_alen == 0 &&
586		    sdl->sdl_slen == 0) {
587			n = snprintf(line, sizeof(line), "link#%d",
588			    sdl->sdl_index);
589			if (n > (int)sizeof(line))
590			    line[0] = '\0';
591			return (line);
592		} else
593			return (link_ntoa(sdl));
594		break;
595
596	default:
597	    {
598		u_short *sp = (u_short *)(void *)sa;
599		u_short *splim = sp + ((sa->sa_len + 1) >> 1);
600		char *cps = line + sprintf(line, "(%d)", sa->sa_family);
601		char *cpe = line + sizeof(line);
602
603		while (++sp < splim && cps < cpe) /* start with sa->sa_data */
604			if ((n = snprintf(cps, cpe - cps, " %x", *sp)) > 0)
605				cps += n;
606			else
607				*cps = '\0';
608		break;
609	    }
610	}
611	return (line);
612}
613
614/*
615 * Return the name of the network whose address is given.
616 * The address is assumed to be that of a net, not a host.
617 */
618static const char *
619netname(struct sockaddr *sa)
620{
621	struct sockaddr_dl *sdl;
622	static char line[MAXHOSTNAMELEN + 1];
623	int n;
624#ifdef INET
625	struct netent *np = NULL;
626	const char *cp = NULL;
627	u_long i;
628#endif
629
630	switch (sa->sa_family) {
631#ifdef INET
632	case AF_INET:
633	{
634		struct in_addr in;
635
636		in = ((struct sockaddr_in *)(void *)sa)->sin_addr;
637		i = in.s_addr = ntohl(in.s_addr);
638		if (in.s_addr == 0)
639			cp = "default";
640		else if (!nflag) {
641			np = getnetbyaddr(i, AF_INET);
642			if (np != NULL)
643				cp = np->n_name;
644		}
645#define C(x)	(unsigned)((x) & 0xff)
646		if (cp != NULL)
647			strncpy(line, cp, sizeof(line));
648		else if ((in.s_addr & 0xffffff) == 0)
649			(void)sprintf(line, "%u", C(in.s_addr >> 24));
650		else if ((in.s_addr & 0xffff) == 0)
651			(void)sprintf(line, "%u.%u", C(in.s_addr >> 24),
652			    C(in.s_addr >> 16));
653		else if ((in.s_addr & 0xff) == 0)
654			(void)sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
655			    C(in.s_addr >> 16), C(in.s_addr >> 8));
656		else
657			(void)sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
658			    C(in.s_addr >> 16), C(in.s_addr >> 8),
659			    C(in.s_addr));
660#undef C
661		break;
662	}
663#endif
664#ifdef INET6
665	case AF_INET6:
666	{
667		struct sockaddr_in6 sin6;
668		int niflags = 0;
669
670		memset(&sin6, 0, sizeof(sin6));
671		memcpy(&sin6, sa, sa->sa_len);
672		sin6.sin6_len = sizeof(sin6);
673		sin6.sin6_family = AF_INET6;
674		if (nflag)
675			niflags |= NI_NUMERICHOST;
676		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
677		    line, sizeof(line), NULL, 0, niflags) != 0)
678			strncpy(line, "invalid", sizeof(line));
679
680		return(line);
681	}
682#endif
683
684	case AF_APPLETALK:
685		(void)snprintf(line, sizeof(line), "atalk %s",
686		    atalk_ntoa(((struct sockaddr_at *)(void *)sa)->sat_addr));
687		break;
688
689	case AF_LINK:
690		sdl = (struct sockaddr_dl *)(void *)sa;
691
692		if (sdl->sdl_nlen == 0 &&
693		    sdl->sdl_alen == 0 &&
694		    sdl->sdl_slen == 0) {
695			n = snprintf(line, sizeof(line), "link#%d",
696			    sdl->sdl_index);
697			if (n > (int)sizeof(line))
698			    line[0] = '\0';
699			return (line);
700		} else
701			return (link_ntoa(sdl));
702		break;
703
704	default:
705	    {
706		u_short *sp = (u_short *)(void *)sa->sa_data;
707		u_short *splim = sp + ((sa->sa_len + 1)>>1);
708		char *cps = line + sprintf(line, "af %d:", sa->sa_family);
709		char *cpe = line + sizeof(line);
710
711		while (sp < splim && cps < cpe)
712			if ((n = snprintf(cps, cpe - cps, " %x", *sp++)) > 0)
713				cps += n;
714			else
715				*cps = '\0';
716		break;
717	    }
718	}
719	return (line);
720}
721
722static void
723set_metric(char *value, int key)
724{
725	int flag = 0;
726	u_long noval, *valp = &noval;
727
728	switch (key) {
729#define caseof(x, y, z)	case x: valp = &rt_metrics.z; flag = y; break
730	caseof(K_MTU, RTV_MTU, rmx_mtu);
731	caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
732	caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
733	caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
734	caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
735	caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
736	caseof(K_RTT, RTV_RTT, rmx_rtt);
737	caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
738	caseof(K_WEIGHT, RTV_WEIGHT, rmx_weight);
739	}
740	rtm_inits |= flag;
741	if (lockrest || locking)
742		rt_metrics.rmx_locks |= flag;
743	if (locking)
744		locking = 0;
745	*valp = atoi(value);
746}
747
748#define	F_ISHOST	0x01
749#define	F_FORCENET	0x02
750#define	F_FORCEHOST	0x04
751#define	F_PROXY		0x08
752#define	F_INTERFACE	0x10
753
754static void
755newroute(int argc, char **argv)
756{
757	struct hostent *hp;
758	struct fibl *fl;
759	char *cmd;
760	const char *dest, *gateway, *errmsg;
761	int key, error, flags, nrflags, fibnum;
762
763	if (uid != 0 && !debugonly && !tflag)
764		errx(EX_NOPERM, "must be root to alter routing table");
765	dest = NULL;
766	gateway = NULL;
767	flags = RTF_STATIC;
768	nrflags = 0;
769	hp = NULL;
770	TAILQ_INIT(&fibl_head);
771
772	cmd = argv[0];
773	if (*cmd != 'g' && *cmd != 's')
774		shutdown(s, SHUT_RD); /* Don't want to read back our messages */
775	while (--argc > 0) {
776		if (**(++argv)== '-') {
777			switch (key = keyword(1 + *argv)) {
778			case K_LINK:
779				af = AF_LINK;
780				aflen = sizeof(struct sockaddr_dl);
781				break;
782#ifdef INET
783			case K_INET:
784				af = AF_INET;
785				aflen = sizeof(struct sockaddr_in);
786				break;
787#endif
788#ifdef INET6
789			case K_INET6:
790				af = AF_INET6;
791				aflen = sizeof(struct sockaddr_in6);
792				break;
793#endif
794			case K_ATALK:
795				af = AF_APPLETALK;
796				aflen = sizeof(struct sockaddr_at);
797				break;
798			case K_SA:
799				af = PF_ROUTE;
800				aflen = sizeof(struct sockaddr_storage);
801				break;
802			case K_IFACE:
803			case K_INTERFACE:
804				nrflags |= F_INTERFACE;
805				break;
806			case K_NOSTATIC:
807				flags &= ~RTF_STATIC;
808				break;
809			case K_LOCK:
810				locking = 1;
811				break;
812			case K_LOCKREST:
813				lockrest = 1;
814				break;
815			case K_HOST:
816				nrflags |= F_FORCEHOST;
817				break;
818			case K_REJECT:
819				flags |= RTF_REJECT;
820				break;
821			case K_BLACKHOLE:
822				flags |= RTF_BLACKHOLE;
823				break;
824			case K_PROTO1:
825				flags |= RTF_PROTO1;
826				break;
827			case K_PROTO2:
828				flags |= RTF_PROTO2;
829				break;
830			case K_PROXY:
831				nrflags |= F_PROXY;
832				break;
833			case K_XRESOLVE:
834				flags |= RTF_XRESOLVE;
835				break;
836			case K_STATIC:
837				flags |= RTF_STATIC;
838				break;
839			case K_STICKY:
840				flags |= RTF_STICKY;
841				break;
842			case K_NOSTICK:
843				flags &= ~RTF_STICKY;
844				break;
845			case K_FIB:
846				if (!--argc)
847					usage(NULL);
848				error = fiboptlist_csv(*++argv, &fibl_head);
849				if (error)
850					errx(EX_USAGE,
851					    "invalid fib number: %s", *argv);
852				break;
853			case K_IFA:
854				if (!--argc)
855					usage(NULL);
856				getaddr(RTAX_IFA, *++argv, 0, nrflags);
857				break;
858			case K_IFP:
859				if (!--argc)
860					usage(NULL);
861				getaddr(RTAX_IFP, *++argv, 0, nrflags);
862				break;
863			case K_GENMASK:
864				if (!--argc)
865					usage(NULL);
866				getaddr(RTAX_GENMASK, *++argv, 0, nrflags);
867				break;
868			case K_GATEWAY:
869				if (!--argc)
870					usage(NULL);
871				getaddr(RTAX_GATEWAY, *++argv, 0, nrflags);
872				gateway = *argv;
873				break;
874			case K_DST:
875				if (!--argc)
876					usage(NULL);
877				if (getaddr(RTAX_DST, *++argv, &hp, nrflags))
878					nrflags |= F_ISHOST;
879				dest = *argv;
880				break;
881			case K_NETMASK:
882				if (!--argc)
883					usage(NULL);
884				getaddr(RTAX_NETMASK, *++argv, 0, nrflags);
885				/* FALLTHROUGH */
886			case K_NET:
887				nrflags |= F_FORCENET;
888				break;
889			case K_PREFIXLEN:
890				if (!--argc)
891					usage(NULL);
892				if (prefixlen(*++argv) == -1) {
893					nrflags &= ~F_FORCENET;
894					nrflags |= F_ISHOST;
895				} else {
896					nrflags |= F_FORCENET;
897					nrflags &= ~F_ISHOST;
898				}
899				break;
900			case K_MTU:
901			case K_HOPCOUNT:
902			case K_EXPIRE:
903			case K_RECVPIPE:
904			case K_SENDPIPE:
905			case K_SSTHRESH:
906			case K_RTT:
907			case K_RTTVAR:
908			case K_WEIGHT:
909				if (!--argc)
910					usage(NULL);
911				set_metric(*++argv, key);
912				break;
913			default:
914				usage(1+*argv);
915			}
916		} else {
917			if ((rtm_addrs & RTA_DST) == 0) {
918				dest = *argv;
919				if (getaddr(RTAX_DST, *argv, &hp, nrflags))
920					nrflags |= F_ISHOST;
921			} else if ((rtm_addrs & RTA_GATEWAY) == 0) {
922				gateway = *argv;
923				getaddr(RTAX_GATEWAY, *argv, &hp, nrflags);
924			} else {
925				getaddr(RTAX_NETMASK, *argv, 0, nrflags);
926				nrflags |= F_FORCENET;
927			}
928		}
929	}
930
931	if (nrflags & F_FORCEHOST) {
932		nrflags |= F_ISHOST;
933#ifdef INET6
934		if (af == AF_INET6) {
935			rtm_addrs &= ~RTA_NETMASK;
936			memset(&so[RTAX_NETMASK], 0, sizeof(so[RTAX_NETMASK]));
937		}
938#endif
939	}
940	if (nrflags & F_FORCENET)
941		nrflags &= ~F_ISHOST;
942	flags |= RTF_UP;
943	if (nrflags & F_ISHOST)
944		flags |= RTF_HOST;
945	if ((nrflags & F_INTERFACE) == 0)
946		flags |= RTF_GATEWAY;
947	if (nrflags & F_PROXY)
948		flags |= RTF_ANNOUNCE;
949	if (dest == NULL)
950		dest = "";
951	if (gateway == NULL)
952		gateway = "";
953
954	if (TAILQ_EMPTY(&fibl_head)) {
955		error = fiboptlist_csv("default", &fibl_head);
956		if (error)
957			errx(EX_OSERR, "fiboptlist_csv failed.");
958	}
959	error = 0;
960	TAILQ_FOREACH(fl, &fibl_head, fl_next) {
961		fl->fl_error = newroute_fib(fl->fl_num, cmd, flags);
962		if (fl->fl_error)
963			fl->fl_errno = errno;
964		error += fl->fl_error;
965	}
966	if (*cmd == 'g' || *cmd == 's')
967		exit(error);
968
969	error = 0;
970	if (!qflag) {
971		fibnum = 0;
972		TAILQ_FOREACH(fl, &fibl_head, fl_next) {
973			if (fl->fl_error == 0)
974				fibnum++;
975		}
976		if (fibnum > 0) {
977			int firstfib = 1;
978
979			printf("%s %s %s", cmd,
980			    (nrflags & F_ISHOST) ? "host" : "net", dest);
981			if (*gateway)
982				printf(": gateway %s", gateway);
983
984			if (numfibs > 1) {
985				TAILQ_FOREACH(fl, &fibl_head, fl_next) {
986					if (fl->fl_error == 0
987					    && fl->fl_num >= 0) {
988						if (firstfib) {
989							printf(" fib ");
990							firstfib = 0;
991						}
992						printf("%d", fl->fl_num);
993						if (fibnum-- > 1)
994							printf(",");
995					}
996				}
997			}
998			printf("\n");
999		}
1000
1001		fibnum = 0;
1002		TAILQ_FOREACH(fl, &fibl_head, fl_next) {
1003			if (fl->fl_error != 0) {
1004				printf("%s %s %s", cmd, (nrflags & F_ISHOST)
1005				    ? "host" : "net", dest);
1006				if (*gateway)
1007					printf(": gateway %s", gateway);
1008
1009				if (fl->fl_num >= 0)
1010					printf(" fib %d", fl->fl_num);
1011
1012				switch (fl->fl_errno) {
1013				case ESRCH:
1014					errmsg = "not in table";
1015					break;
1016				case EBUSY:
1017					errmsg = "entry in use";
1018					break;
1019				case ENOBUFS:
1020					errmsg = "not enough memory";
1021					break;
1022				case EADDRINUSE:
1023					/*
1024					 * handle recursion avoidance
1025					 * in rt_setgate()
1026					 */
1027					errmsg = "gateway uses the same route";
1028					break;
1029				case EEXIST:
1030					errmsg = "route already in table";
1031					break;
1032				default:
1033					errmsg = strerror(fl->fl_errno);
1034					break;
1035				}
1036				printf(": %s\n", errmsg);
1037				error = 1;
1038			}
1039		}
1040	}
1041	exit(error);
1042}
1043
1044static int
1045newroute_fib(int fib, char *cmd, int flags)
1046{
1047	int error;
1048
1049	error = set_sofib(fib);
1050	if (error) {
1051		warn("fib number %d is ignored", fib);
1052		return (error);
1053	}
1054
1055	error = rtmsg(*cmd, flags, fib);
1056	return (error);
1057}
1058
1059#ifdef INET
1060static void
1061inet_makenetandmask(u_long net, struct sockaddr_in *sin,
1062    struct sockaddr_in *sin_mask, u_long bits)
1063{
1064	u_long mask = 0;
1065
1066	rtm_addrs |= RTA_NETMASK;
1067
1068	/*
1069	 * MSB of net should be meaningful. 0/0 is exception.
1070	 */
1071	if (net > 0)
1072		while ((net & 0xff000000) == 0)
1073			net <<= 8;
1074
1075	/*
1076	 * If no /xx was specified we must calculate the
1077	 * CIDR address.
1078	 */
1079	if ((bits == 0) && (net != 0)) {
1080		u_long i, j;
1081
1082		for(i = 0, j = 0xff; i < 4; i++)  {
1083			if (net & j) {
1084				break;
1085			}
1086			j <<= 8;
1087		}
1088		/* i holds the first non zero bit */
1089		bits = 32 - (i*8);
1090	}
1091	if (bits != 0)
1092		mask = 0xffffffff << (32 - bits);
1093
1094	sin->sin_addr.s_addr = htonl(net);
1095	sin_mask->sin_addr.s_addr = htonl(mask);
1096	sin_mask->sin_len = sizeof(struct sockaddr_in);
1097	sin_mask->sin_family = AF_INET;
1098}
1099#endif
1100
1101#ifdef INET6
1102/*
1103 * XXX the function may need more improvement...
1104 */
1105static int
1106inet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen)
1107{
1108	struct in6_addr in6;
1109
1110	if (plen == NULL) {
1111		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
1112		    sin6->sin6_scope_id == 0) {
1113			plen = "0";
1114		} else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) {
1115			/* aggregatable global unicast - RFC2374 */
1116			memset(&in6, 0, sizeof(in6));
1117			if (!memcmp(&sin6->sin6_addr.s6_addr[8],
1118				    &in6.s6_addr[8], 8))
1119				plen = "64";
1120		}
1121	}
1122
1123	if (plen == NULL || strcmp(plen, "128") == 0)
1124		return (1);
1125	rtm_addrs |= RTA_NETMASK;
1126	prefixlen(plen);
1127	return (0);
1128}
1129#endif
1130
1131/*
1132 * Interpret an argument as a network address of some kind,
1133 * returning 1 if a host address, 0 if a network address.
1134 */
1135static int
1136getaddr(int idx, char *str, struct hostent **hpp, int nrflags)
1137{
1138	struct sockaddr *sa;
1139#if defined(INET)
1140	struct sockaddr_in *sin;
1141	struct hostent *hp;
1142	struct netent *np;
1143	u_long val;
1144	char *q;
1145#elif defined(INET6)
1146	char *q;
1147#endif
1148
1149	if (af == 0) {
1150#if defined(INET)
1151		af = AF_INET;
1152		aflen = sizeof(struct sockaddr_in);
1153#elif defined(INET6)
1154		af = AF_INET6;
1155		aflen = sizeof(struct sockaddr_in6);
1156#else
1157		af = AF_LINK;
1158		aflen = sizeof(struct sockaddr_dl);
1159#endif
1160	}
1161#ifndef INET
1162	hpp = NULL;
1163#endif
1164	rtm_addrs |= (1 << idx);
1165
1166	if (idx > RTAX_MAX)
1167		usage("internal error");
1168	sa = (struct sockaddr *)&so[idx];
1169	sa->sa_family = af;
1170	sa->sa_len = aflen;
1171
1172	switch (idx) {
1173	case RTAX_GATEWAY:
1174		if (nrflags & F_INTERFACE) {
1175			struct ifaddrs *ifap, *ifa;
1176			struct sockaddr_dl *sdl0 = (struct sockaddr_dl *)(void *)sa;
1177			struct sockaddr_dl *sdl = NULL;
1178
1179			if (getifaddrs(&ifap))
1180				err(EX_OSERR, "getifaddrs");
1181
1182			for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1183				if (ifa->ifa_addr->sa_family != AF_LINK)
1184					continue;
1185
1186				if (strcmp(str, ifa->ifa_name) != 0)
1187					continue;
1188
1189				sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
1190			}
1191			/* If we found it, then use it */
1192			if (sdl != NULL) {
1193				/*
1194				 * Note that we need to copy before calling
1195				 * freeifaddrs().
1196				 */
1197				memcpy(sdl0, sdl, sdl->sdl_len);
1198			}
1199			freeifaddrs(ifap);
1200			if (sdl != NULL)
1201				return(1);
1202		}
1203		break;
1204	case RTAX_IFP:
1205		sa->sa_family = AF_LINK;
1206		break;
1207	}
1208	if (strcmp(str, "default") == 0) {
1209		/*
1210		 * Default is net 0.0.0.0/0
1211		 */
1212		switch (idx) {
1213		case RTAX_DST:
1214			forcenet++;
1215			getaddr(RTAX_NETMASK, str, 0, nrflags);
1216			break;
1217		}
1218		return (0);
1219	}
1220	switch (sa->sa_family) {
1221#ifdef INET6
1222	case AF_INET6:
1223	{
1224		struct addrinfo hints, *res;
1225		int ecode;
1226
1227		q = NULL;
1228		if (idx == RTAX_DST && (q = strchr(str, '/')) != NULL)
1229			*q = '\0';
1230		memset(&hints, 0, sizeof(hints));
1231		hints.ai_family = sa->sa_family;
1232		hints.ai_socktype = SOCK_DGRAM;
1233		ecode = getaddrinfo(str, NULL, &hints, &res);
1234		if (ecode != 0 || res->ai_family != AF_INET6 ||
1235		    res->ai_addrlen != sizeof(struct sockaddr_in6))
1236			errx(EX_OSERR, "%s: %s", str, gai_strerror(ecode));
1237		memcpy(sa, res->ai_addr, res->ai_addrlen);
1238		freeaddrinfo(res);
1239		if (q != NULL)
1240			*q++ = '/';
1241		if (idx == RTAX_DST)
1242			return (inet6_makenetandmask((struct sockaddr_in6 *)(void *)sa, q));
1243		return (0);
1244	}
1245#endif /* INET6 */
1246
1247	case AF_APPLETALK:
1248	{
1249		struct sockaddr_at *sat = (struct sockaddr_at *)(void *)sa;
1250
1251		if (!atalk_aton(str, &sat->sat_addr))
1252			errx(EX_NOHOST, "bad address: %s", str);
1253		rtm_addrs |= RTA_NETMASK;
1254		return(forcehost || sat->sat_addr.s_node != 0);
1255	}
1256	case AF_LINK:
1257		link_addr(str, (struct sockaddr_dl *)(void *)sa);
1258		return (1);
1259
1260	case PF_ROUTE:
1261		sockaddr(str, sa, sizeof(struct sockaddr_storage));
1262		return (1);
1263#ifdef INET
1264	case AF_INET:
1265#endif
1266	default:
1267		break;
1268	}
1269
1270#ifdef INET
1271	sin = (struct sockaddr_in *)(void *)sa;
1272	if (hpp == NULL)
1273		hpp = &hp;
1274	*hpp = NULL;
1275
1276	q = strchr(str,'/');
1277	if (q != NULL && idx == RTAX_DST) {
1278		*q = '\0';
1279		if ((val = inet_network(str)) != INADDR_NONE) {
1280			inet_makenetandmask(val, sin,
1281			    (struct sockaddr_in *)&so[RTAX_NETMASK],
1282			    strtoul(q+1, 0, 0));
1283			return (0);
1284		}
1285		*q = '/';
1286	}
1287	if ((idx != RTAX_DST || forcenet == 0) &&
1288	    inet_aton(str, &sin->sin_addr)) {
1289		val = sin->sin_addr.s_addr;
1290		if (idx != RTAX_DST || forcehost ||
1291		    inet_lnaof(sin->sin_addr) != INADDR_ANY)
1292			return (1);
1293		else {
1294			val = ntohl(val);
1295			goto netdone;
1296		}
1297	}
1298	if (idx == RTAX_DST && forcehost == 0 &&
1299	    ((val = inet_network(str)) != INADDR_NONE ||
1300	    ((np = getnetbyname(str)) != NULL && (val = np->n_net) != 0))) {
1301netdone:
1302		inet_makenetandmask(val, sin,
1303		    (struct sockaddr_in *)&so[RTAX_NETMASK], 0);
1304		return (0);
1305	}
1306	hp = gethostbyname(str);
1307	if (hp != NULL) {
1308		*hpp = hp;
1309		sin->sin_family = hp->h_addrtype;
1310		memmove((char *)&sin->sin_addr, hp->h_addr,
1311		    MIN((size_t)hp->h_length, sizeof(sin->sin_addr)));
1312		return (1);
1313	}
1314#endif
1315	errx(EX_NOHOST, "bad address: %s", str);
1316}
1317
1318static int
1319prefixlen(const char *str)
1320{
1321	int len = atoi(str), q, r;
1322	int max;
1323	char *p;
1324
1325	rtm_addrs |= RTA_NETMASK;
1326	switch (af) {
1327#ifdef INET6
1328	case AF_INET6:
1329	{
1330		struct sockaddr_in6 *sin6 =
1331		    (struct sockaddr_in6 *)&so[RTAX_NETMASK];
1332
1333		max = 128;
1334		p = (char *)&sin6->sin6_addr;
1335		sin6->sin6_family = AF_INET6;
1336		sin6->sin6_len = sizeof(*sin6);
1337		break;
1338	}
1339#endif
1340#ifdef INET
1341	case AF_INET:
1342	{
1343		struct sockaddr_in *sin =
1344		    (struct sockaddr_in *)&so[RTAX_NETMASK];
1345
1346		max = 32;
1347		p = (char *)&sin->sin_addr;
1348		sin->sin_family = AF_INET;
1349		sin->sin_len = sizeof(*sin);
1350		break;
1351	}
1352#endif
1353	default:
1354		errx(EX_OSERR, "prefixlen not supported in this af");
1355	}
1356
1357	if (len < 0 || max < len)
1358		errx(EX_USAGE, "%s: invalid prefixlen", str);
1359
1360	q = len >> 3;
1361	r = len & 7;
1362	memset((void *)p, 0, max / 8);
1363	if (q > 0)
1364		memset((void *)p, 0xff, q);
1365	if (r > 0)
1366		*((u_char *)p + q) = (0xff00 >> r) & 0xff;
1367	if (len == max)
1368		return (-1);
1369	else
1370		return (len);
1371}
1372
1373static void
1374interfaces(void)
1375{
1376	size_t needed;
1377	int mib[6];
1378	char *buf, *lim, *next, count = 0;
1379	struct rt_msghdr *rtm;
1380
1381retry2:
1382	mib[0] = CTL_NET;
1383	mib[1] = PF_ROUTE;
1384	mib[2] = 0;		/* protocol */
1385	mib[3] = AF_UNSPEC;
1386	mib[4] = NET_RT_IFLIST;
1387	mib[5] = 0;		/* no flags */
1388	if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0)
1389		err(EX_OSERR, "route-sysctl-estimate");
1390	if ((buf = malloc(needed)) == NULL)
1391		errx(EX_OSERR, "malloc failed");
1392	if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) {
1393		if (errno == ENOMEM && count++ < 10) {
1394			warnx("Routing table grew, retrying");
1395			sleep(1);
1396			free(buf);
1397			goto retry2;
1398		}
1399		err(EX_OSERR, "actual retrieval of interface table");
1400	}
1401	lim = buf + needed;
1402	for (next = buf; next < lim; next += rtm->rtm_msglen) {
1403		rtm = (struct rt_msghdr *)(void *)next;
1404		print_rtmsg(rtm, rtm->rtm_msglen);
1405	}
1406}
1407
1408static void
1409monitor(int argc, char *argv[])
1410{
1411	int n, fib, error;
1412	char msg[2048], *endptr;
1413
1414	fib = defaultfib;
1415	while (argc > 1) {
1416		argc--;
1417		argv++;
1418		if (**argv != '-')
1419			usage(*argv);
1420		switch (keyword(*argv + 1)) {
1421		case K_FIB:
1422			if (!--argc)
1423				usage(*argv);
1424			errno = 0;
1425			fib = strtol(*++argv, &endptr, 0);
1426			if (errno == 0) {
1427				if (*endptr != '\0' ||
1428				    fib < 0 ||
1429				    (numfibs != -1 && fib > numfibs - 1))
1430					errno = EINVAL;
1431			}
1432			if (errno)
1433				errx(EX_USAGE, "invalid fib number: %s", *argv);
1434			break;
1435		default:
1436			usage(*argv);
1437		}
1438	}
1439	error = set_sofib(fib);
1440	if (error)
1441		errx(EX_USAGE, "invalid fib number: %d", fib);
1442
1443	verbose = 1;
1444	if (debugonly) {
1445		interfaces();
1446		exit(0);
1447	}
1448	for (;;) {
1449		time_t now;
1450		n = read(s, msg, 2048);
1451		now = time(NULL);
1452		(void)printf("\ngot message of size %d on %s", n, ctime(&now));
1453		print_rtmsg((struct rt_msghdr *)(void *)msg, n);
1454	}
1455}
1456
1457static struct {
1458	struct	rt_msghdr m_rtm;
1459	char	m_space[512];
1460} m_rtmsg;
1461
1462static int
1463rtmsg(int cmd, int flags, int fib)
1464{
1465	static int seq;
1466	int rlen;
1467	char *cp = m_rtmsg.m_space;
1468	int l;
1469
1470#define NEXTADDR(w, u)							\
1471	if (rtm_addrs & (w)) {						\
1472		l = (((struct sockaddr *)&(u))->sa_len == 0) ?		\
1473		    sizeof(long) :					\
1474		    1 + ((((struct sockaddr *)&(u))->sa_len - 1)	\
1475			| (sizeof(long) - 1));				\
1476		memmove(cp, (char *)&(u), l);				\
1477		cp += l;						\
1478		if (verbose)						\
1479			sodump((struct sockaddr *)&(u), #w);		\
1480	}
1481
1482	errno = 0;
1483	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
1484	if (cmd == 'a')
1485		cmd = RTM_ADD;
1486	else if (cmd == 'c')
1487		cmd = RTM_CHANGE;
1488	else if (cmd == 'g' || cmd == 's') {
1489		cmd = RTM_GET;
1490		if (so[RTAX_IFP].ss_family == 0) {
1491			so[RTAX_IFP].ss_family = AF_LINK;
1492			so[RTAX_IFP].ss_len = sizeof(struct sockaddr_dl);
1493			rtm_addrs |= RTA_IFP;
1494		}
1495	} else
1496		cmd = RTM_DELETE;
1497#define rtm m_rtmsg.m_rtm
1498	rtm.rtm_type = cmd;
1499	rtm.rtm_flags = flags;
1500	rtm.rtm_version = RTM_VERSION;
1501	rtm.rtm_seq = ++seq;
1502	rtm.rtm_addrs = rtm_addrs;
1503	rtm.rtm_rmx = rt_metrics;
1504	rtm.rtm_inits = rtm_inits;
1505
1506	NEXTADDR(RTA_DST, so[RTAX_DST]);
1507	NEXTADDR(RTA_GATEWAY, so[RTAX_GATEWAY]);
1508	NEXTADDR(RTA_NETMASK, so[RTAX_NETMASK]);
1509	NEXTADDR(RTA_GENMASK, so[RTAX_GENMASK]);
1510	NEXTADDR(RTA_IFP, so[RTAX_IFP]);
1511	NEXTADDR(RTA_IFA, so[RTAX_IFA]);
1512	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
1513	if (verbose)
1514		print_rtmsg(&rtm, l);
1515	if (debugonly)
1516		return (0);
1517	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
1518		if (errno == EPERM)
1519			err(1, "writing to routing socket");
1520		warn("writing to routing socket");
1521		return (-1);
1522	}
1523	if (cmd == RTM_GET) {
1524		do {
1525			l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
1526		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
1527		if (l < 0)
1528			warn("read from routing socket");
1529		else
1530			print_getmsg(&rtm, l, fib);
1531	}
1532#undef rtm
1533	return (0);
1534}
1535
1536static const char *msgtypes[] = {
1537	"",
1538	"RTM_ADD: Add Route",
1539	"RTM_DELETE: Delete Route",
1540	"RTM_CHANGE: Change Metrics or flags",
1541	"RTM_GET: Report Metrics",
1542	"RTM_LOSING: Kernel Suspects Partitioning",
1543	"RTM_REDIRECT: Told to use different route",
1544	"RTM_MISS: Lookup failed on this address",
1545	"RTM_LOCK: fix specified metrics",
1546	"RTM_OLDADD: caused by SIOCADDRT",
1547	"RTM_OLDDEL: caused by SIOCDELRT",
1548	"RTM_RESOLVE: Route created by cloning",
1549	"RTM_NEWADDR: address being added to iface",
1550	"RTM_DELADDR: address being removed from iface",
1551	"RTM_IFINFO: iface status change",
1552	"RTM_NEWMADDR: new multicast group membership on iface",
1553	"RTM_DELMADDR: multicast group membership removed from iface",
1554	"RTM_IFANNOUNCE: interface arrival/departure",
1555	"RTM_IEEE80211: IEEE 802.11 wireless event",
1556};
1557
1558static const char metricnames[] =
1559    "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire"
1560    "\1mtu";
1561static const char routeflags[] =
1562    "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE"
1563    "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
1564    "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3"
1565    "\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY";
1566static const char ifnetflags[] =
1567    "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
1568    "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
1569    "\017LINK2\020MULTICAST";
1570static const char addrnames[] =
1571    "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
1572
1573static const char errfmt[] =
1574    "\n%s: truncated route message, only %zu bytes left\n";
1575
1576static void
1577print_rtmsg(struct rt_msghdr *rtm, size_t msglen)
1578{
1579	struct if_msghdr *ifm;
1580	struct ifa_msghdr *ifam;
1581#ifdef RTM_NEWMADDR
1582	struct ifma_msghdr *ifmam;
1583#endif
1584	struct if_announcemsghdr *ifan;
1585	const char *state;
1586
1587	if (verbose == 0)
1588		return;
1589	if (rtm->rtm_version != RTM_VERSION) {
1590		(void)printf("routing message version %d not understood\n",
1591		    rtm->rtm_version);
1592		return;
1593	}
1594	if (rtm->rtm_type < nitems(msgtypes))
1595		(void)printf("%s: ", msgtypes[rtm->rtm_type]);
1596	else
1597		(void)printf("unknown type %d: ", rtm->rtm_type);
1598	(void)printf("len %d, ", rtm->rtm_msglen);
1599
1600#define	REQUIRE(x)	do {		\
1601	if (msglen < sizeof(x))		\
1602		goto badlen;		\
1603	else				\
1604		msglen -= sizeof(x);	\
1605	} while (0)
1606
1607	switch (rtm->rtm_type) {
1608	case RTM_IFINFO:
1609		REQUIRE(struct if_msghdr);
1610		ifm = (struct if_msghdr *)rtm;
1611		(void)printf("if# %d, ", ifm->ifm_index);
1612		switch (ifm->ifm_data.ifi_link_state) {
1613		case LINK_STATE_DOWN:
1614			state = "down";
1615			break;
1616		case LINK_STATE_UP:
1617			state = "up";
1618			break;
1619		default:
1620			state = "unknown";
1621			break;
1622		}
1623		(void)printf("link: %s, flags:", state);
1624		printb(ifm->ifm_flags, ifnetflags);
1625		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs, msglen);
1626		break;
1627	case RTM_NEWADDR:
1628	case RTM_DELADDR:
1629		REQUIRE(struct ifa_msghdr);
1630		ifam = (struct ifa_msghdr *)rtm;
1631		(void)printf("metric %d, flags:", ifam->ifam_metric);
1632		printb(ifam->ifam_flags, routeflags);
1633		pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs, msglen);
1634		break;
1635#ifdef RTM_NEWMADDR
1636	case RTM_NEWMADDR:
1637	case RTM_DELMADDR:
1638		REQUIRE(struct ifma_msghdr);
1639		ifmam = (struct ifma_msghdr *)rtm;
1640		pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs, msglen);
1641		break;
1642#endif
1643	case RTM_IFANNOUNCE:
1644		REQUIRE(struct if_announcemsghdr);
1645		ifan = (struct if_announcemsghdr *)rtm;
1646		(void)printf("if# %d, what: ", ifan->ifan_index);
1647		switch (ifan->ifan_what) {
1648		case IFAN_ARRIVAL:
1649			(void)printf("arrival");
1650			break;
1651		case IFAN_DEPARTURE:
1652			printf("departure");
1653			break;
1654		default:
1655			printf("#%d", ifan->ifan_what);
1656			break;
1657		}
1658		printf("\n");
1659		fflush(stdout);
1660		break;
1661
1662	default:
1663		printf("pid: %ld, seq %d, errno %d, flags:",
1664			(long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
1665		printb(rtm->rtm_flags, routeflags);
1666		pmsg_common(rtm, msglen);
1667	}
1668
1669	return;
1670
1671badlen:
1672	(void)printf(errfmt, __func__, msglen);
1673#undef	REQUIRE
1674}
1675
1676static void
1677print_getmsg(struct rt_msghdr *rtm, int msglen, int fib)
1678{
1679	struct sockaddr *sp[RTAX_MAX];
1680	char *cp;
1681	int i;
1682
1683	memset(sp, 0, sizeof(sp));
1684	(void)printf("   route to: %s\n",
1685	    routename((struct sockaddr *)&so[RTAX_DST]));
1686	if (rtm->rtm_version != RTM_VERSION) {
1687		warnx("routing message version %d not understood",
1688		     rtm->rtm_version);
1689		return;
1690	}
1691	if (rtm->rtm_msglen > msglen) {
1692		warnx("message length mismatch, in packet %d, returned %d",
1693		      rtm->rtm_msglen, msglen);
1694		return;
1695	}
1696	if (rtm->rtm_errno)  {
1697		errno = rtm->rtm_errno;
1698		warn("message indicates error %d", errno);
1699		return;
1700	}
1701	cp = ((char *)(rtm + 1));
1702	for (i = 0; i < RTAX_MAX; i++) {
1703		if (rtm->rtm_addrs & (1 << i))
1704			sp[i] = (struct sockaddr *)cp;
1705		cp += SA_SIZE((struct sockaddr *)cp);
1706	}
1707	if (rtm->rtm_addrs & RTA_IFP) {
1708		if (sp[RTAX_IFP]->sa_family != AF_LINK ||
1709		   ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen == 0)
1710			sp[RTAX_IFP] = NULL;
1711	}
1712	if (sp[RTAX_DST] && sp[RTAX_NETMASK])
1713		sp[RTAX_NETMASK]->sa_family = sp[RTAX_DST]->sa_family; /* XXX */
1714	if (sp[RTAX_DST])
1715		(void)printf("destination: %s\n", routename(sp[RTAX_DST]));
1716	if (sp[RTAX_NETMASK])
1717		(void)printf("       mask: %s\n", routename(sp[RTAX_NETMASK]));
1718	if (sp[RTAX_GATEWAY] && (rtm->rtm_flags & RTF_GATEWAY))
1719		(void)printf("    gateway: %s\n", routename(sp[RTAX_GATEWAY]));
1720	if (fib >= 0)
1721		(void)printf("        fib: %u\n", (unsigned int)fib);
1722	if (sp[RTAX_IFP])
1723		(void)printf("  interface: %.*s\n",
1724		    ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen,
1725		    ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_data);
1726	(void)printf("      flags: ");
1727	printb(rtm->rtm_flags, routeflags);
1728
1729#define lock(f)	((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ')
1730#define msec(u)	(((u) + 500) / 1000)		/* usec to msec */
1731	printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe",
1732	    "sendpipe", "ssthresh", "rtt,msec", "mtu   ", "weight", "expire");
1733	printf("%8ld%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
1734	printf("%8ld%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
1735	printf("%8ld%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
1736	printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
1737	printf("%8ld%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
1738	printf("%8ld%c ", rtm->rtm_rmx.rmx_weight, lock(WEIGHT));
1739	if (rtm->rtm_rmx.rmx_expire)
1740		rtm->rtm_rmx.rmx_expire -= time(0);
1741	printf("%8ld%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
1742#undef lock
1743#undef msec
1744#define	RTA_IGN	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD)
1745	if (verbose)
1746		pmsg_common(rtm, msglen);
1747	else if (rtm->rtm_addrs &~ RTA_IGN) {
1748		(void)printf("sockaddrs: ");
1749		printb(rtm->rtm_addrs, addrnames);
1750		putchar('\n');
1751	}
1752#undef	RTA_IGN
1753}
1754
1755static void
1756pmsg_common(struct rt_msghdr *rtm, size_t msglen)
1757{
1758
1759	(void)printf("\nlocks: ");
1760	printb(rtm->rtm_rmx.rmx_locks, metricnames);
1761	(void)printf(" inits: ");
1762	printb(rtm->rtm_inits, metricnames);
1763	if (msglen > sizeof(struct rt_msghdr))
1764		pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs,
1765		    msglen - sizeof(struct rt_msghdr));
1766	else
1767		(void)fflush(stdout);
1768}
1769
1770static void
1771pmsg_addrs(char *cp, int addrs, size_t len)
1772{
1773	struct sockaddr *sa;
1774	int i;
1775
1776	if (addrs == 0) {
1777		(void)putchar('\n');
1778		return;
1779	}
1780	(void)printf("\nsockaddrs: ");
1781	printb(addrs, addrnames);
1782	putchar('\n');
1783	for (i = 0; i < RTAX_MAX; i++)
1784		if (addrs & (1 << i)) {
1785			sa = (struct sockaddr *)cp;
1786			if (len == 0 || len < SA_SIZE(sa)) {
1787				(void)printf(errfmt, __func__, len);
1788				break;
1789			}
1790			(void)printf(" %s", routename(sa));
1791			len -= SA_SIZE(sa);
1792			cp += SA_SIZE(sa);
1793		}
1794	(void)putchar('\n');
1795	(void)fflush(stdout);
1796}
1797
1798static void
1799printb(int b, const char *str)
1800{
1801	int i;
1802	int gotsome = 0;
1803
1804	if (b == 0)
1805		return;
1806	while ((i = *str++) != 0) {
1807		if (b & (1 << (i-1))) {
1808			if (gotsome == 0)
1809				i = '<';
1810			else
1811				i = ',';
1812			putchar(i);
1813			gotsome = 1;
1814			for (; (i = *str) > 32; str++)
1815				putchar(i);
1816		} else
1817			while (*str > 32)
1818				str++;
1819	}
1820	if (gotsome)
1821		putchar('>');
1822}
1823
1824int
1825keyword(const char *cp)
1826{
1827	struct keytab *kt = keywords;
1828
1829	while (kt->kt_cp != NULL && strcmp(kt->kt_cp, cp) != 0)
1830		kt++;
1831	return (kt->kt_i);
1832}
1833
1834static void
1835sodump(struct sockaddr *sa, const char *which)
1836{
1837#ifdef INET6
1838	char nbuf[INET6_ADDRSTRLEN];
1839#endif
1840
1841	switch (sa->sa_family) {
1842	case AF_LINK:
1843		(void)printf("%s: link %s; ", which,
1844		    link_ntoa((struct sockaddr_dl *)(void *)sa));
1845		break;
1846#ifdef INET
1847	case AF_INET:
1848		(void)printf("%s: inet %s; ", which,
1849		    inet_ntoa(((struct sockaddr_in *)(void *)sa)->sin_addr));
1850		break;
1851#endif
1852#ifdef INET6
1853	case AF_INET6:
1854		(void)printf("%s: inet6 %s; ", which, inet_ntop(sa->sa_family,
1855		    &((struct sockaddr_in6 *)(void *)sa)->sin6_addr, nbuf,
1856		    sizeof(nbuf)));
1857		break;
1858#endif
1859	case AF_APPLETALK:
1860		(void)printf("%s: atalk %s; ", which,
1861		    atalk_ntoa(((struct sockaddr_at *)(void *)sa)->sat_addr));
1862		break;
1863	}
1864	(void)fflush(stdout);
1865}
1866
1867/* States*/
1868#define VIRGIN	0
1869#define GOTONE	1
1870#define GOTTWO	2
1871/* Inputs */
1872#define	DIGIT	(4*0)
1873#define	END	(4*1)
1874#define DELIM	(4*2)
1875
1876static void
1877sockaddr(char *addr, struct sockaddr *sa, size_t size)
1878{
1879	char *cp = (char *)sa;
1880	char *cplim = cp + size;
1881	int byte = 0, state = VIRGIN, new = 0 /* foil gcc */;
1882
1883	memset(cp, 0, size);
1884	cp++;
1885	do {
1886		if ((*addr >= '0') && (*addr <= '9')) {
1887			new = *addr - '0';
1888		} else if ((*addr >= 'a') && (*addr <= 'f')) {
1889			new = *addr - 'a' + 10;
1890		} else if ((*addr >= 'A') && (*addr <= 'F')) {
1891			new = *addr - 'A' + 10;
1892		} else if (*addr == '\0')
1893			state |= END;
1894		else
1895			state |= DELIM;
1896		addr++;
1897		switch (state /* | INPUT */) {
1898		case GOTTWO | DIGIT:
1899			*cp++ = byte; /*FALLTHROUGH*/
1900		case VIRGIN | DIGIT:
1901			state = GOTONE; byte = new; continue;
1902		case GOTONE | DIGIT:
1903			state = GOTTWO; byte = new + (byte << 4); continue;
1904		default: /* | DELIM */
1905			state = VIRGIN; *cp++ = byte; byte = 0; continue;
1906		case GOTONE | END:
1907		case GOTTWO | END:
1908			*cp++ = byte; /* FALLTHROUGH */
1909		case VIRGIN | END:
1910			break;
1911		}
1912		break;
1913	} while (cp < cplim);
1914	sa->sa_len = cp - (char *)sa;
1915}
1916
1917static int
1918atalk_aton(const char *text, struct at_addr *addr)
1919{
1920	u_int net, node;
1921
1922	if (sscanf(text, "%u.%u", &net, &node) != 2
1923	    || net > 0xffff || node > 0xff)
1924		return(0);
1925	addr->s_net = htons(net);
1926	addr->s_node = node;
1927	return(1);
1928}
1929
1930static char *
1931atalk_ntoa(struct at_addr at)
1932{
1933	static char buf[20];
1934
1935	(void)snprintf(buf, sizeof(buf), "%u.%u", ntohs(at.s_net), at.s_node);
1936	buf[sizeof(buf) - 1] = '\0';
1937	return(buf);
1938}
1939