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