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