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