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