route.c revision 243860
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1983, 1989, 1991, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
301558Srgrimes#ifndef lint
3113171Swollmanstatic const char copyright[] =
321558Srgrimes"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\
331558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
341558Srgrimes#endif /* not lint */
351558Srgrimes
361558Srgrimes#ifndef lint
3737907Scharnier#if 0
3885048Srustatic char sccsid[] = "@(#)route.c	8.6 (Berkeley) 4/28/95";
3937907Scharnier#endif
401558Srgrimes#endif /* not lint */
411558Srgrimes
42196527Scharnier#include <sys/cdefs.h>
43196527Scharnier__FBSDID("$FreeBSD: head/sbin/route/route.c 243860 2012-12-04 11:10:01Z glebius $");
44196527Scharnier
451558Srgrimes#include <sys/param.h>
461558Srgrimes#include <sys/file.h>
471558Srgrimes#include <sys/socket.h>
481558Srgrimes#include <sys/ioctl.h>
491558Srgrimes#include <sys/sysctl.h>
5051639Sbillf#include <sys/types.h>
51243185Shrs#include <sys/queue.h>
521558Srgrimes
531558Srgrimes#include <net/if.h>
541558Srgrimes#include <net/route.h>
551558Srgrimes#include <net/if_dl.h>
561558Srgrimes#include <netinet/in.h>
5778140Sru#include <netinet/if_ether.h>
5817046Sjulian#include <netatalk/at.h>
591558Srgrimes#include <arpa/inet.h>
601558Srgrimes#include <netdb.h>
611558Srgrimes
6220287Swollman#include <ctype.h>
6320287Swollman#include <err.h>
641558Srgrimes#include <errno.h>
6569793Sobrien#include <paths.h>
661558Srgrimes#include <stdio.h>
671558Srgrimes#include <stdlib.h>
681558Srgrimes#include <string.h>
6913171Swollman#include <sysexits.h>
7020287Swollman#include <unistd.h>
7178064Sume#include <ifaddrs.h>
721558Srgrimes
731558Srgrimesstruct keytab {
74204406Suqs	const char	*kt_cp;
751558Srgrimes	int	kt_i;
761558Srgrimes} keywords[] = {
771558Srgrimes#include "keywords.h"
781558Srgrimes	{0, 0}
791558Srgrimes};
801558Srgrimes
811558Srgrimesunion	sockunion {
821558Srgrimes	struct	sockaddr sa;
831558Srgrimes	struct	sockaddr_in sin;
8454263Sshin#ifdef INET6
8554263Sshin	struct	sockaddr_in6 sin6;
8654263Sshin#endif
8717046Sjulian	struct	sockaddr_at sat;
881558Srgrimes	struct	sockaddr_dl sdl;
8978140Sru	struct	sockaddr_inarp sinarp;
9078064Sume	struct	sockaddr_storage ss; /* added to avoid memory overrun */
911558Srgrimes} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp;
921558Srgrimes
931558Srgrimestypedef union sockunion *sup;
9482651Sruint	pid, rtm_addrs;
951558Srgrimesint	s;
96196527Scharnierint	forcehost, forcenet, doflush, nflag, af, qflag, tflag;
971558Srgrimesint	iflag, verbose, aflen = sizeof (struct sockaddr_in);
981558Srgrimesint	locking, lockrest, debugonly;
991558Srgrimesstruct	rt_metrics rt_metrics;
1001558Srgrimesu_long  rtm_inits;
10182651Sruuid_t	uid;
102243185Shrsstatic int	defaultfib;
103243185Shrsstatic int	numfibs;
104196527Scharnier
105204406Suqsstatic int	atalk_aton(const char *, struct at_addr *);
106204406Suqsstatic char	*atalk_ntoa(struct at_addr);
107204406Suqsstatic void	bprintf(FILE *, int, u_char *);
108204406Suqsstatic void	flushroutes(int argc, char *argv[]);
109243185Shrsstatic int	flushroutes_fib(int);
110204406Suqsstatic int	getaddr(int, char *, struct hostent **);
111204406Suqsstatic int	keyword(const char *);
112204406Suqsstatic void	inet_makenetandmask(u_long, struct sockaddr_in *, u_long);
11397062Sume#ifdef INET6
114204406Suqsstatic int inet6_makenetandmask(struct sockaddr_in6 *, const char *);
11597062Sume#endif
116204406Suqsstatic void	interfaces(void);
117204406Suqsstatic void	mask_addr(void);
118243185Shrsstatic void	monitor(int, char*[]);
119204406Suqsstatic const char	*netname(struct sockaddr *);
120204406Suqsstatic void	newroute(int, char **);
121243185Shrsstatic int	newroute_fib(int, char *, int);
122216297Sglebiusstatic void	pmsg_addrs(char *, int, size_t);
123216297Sglebiusstatic void	pmsg_common(struct rt_msghdr *, size_t);
124204406Suqsstatic int	prefixlen(const char *);
125243185Shrsstatic void	print_getmsg(struct rt_msghdr *, int, int);
126216297Sglebiusstatic void	print_rtmsg(struct rt_msghdr *, size_t);
127204406Suqsstatic const char	*routename(struct sockaddr *);
128243185Shrsstatic int	rtmsg(int, int, int);
129204406Suqsstatic void	set_metric(char *, int);
130243185Shrsstatic int	set_sofib(int);
131243185Shrsstatic int	set_procfib(int);
132204406Suqsstatic void	sockaddr(char *, struct sockaddr *);
133204406Suqsstatic void	sodump(sup, const char *);
134204406Suqsextern	char *iso_ntoa(void);
1351558Srgrimes
136243185Shrsstruct fibl {
137243185Shrs	TAILQ_ENTRY(fibl)	fl_next;
138243185Shrs
139243185Shrs	int	fl_num;
140243185Shrs	int	fl_error;
141243185Shrs	int	fl_errno;
142243185Shrs};
143243185ShrsTAILQ_HEAD(fibl_head_t, fibl) fibl_head;
144243185Shrs
145243185Shrsstatic int	fiboptlist_csv(const char *, struct fibl_head_t *);
146243185Shrsstatic int	fiboptlist_range(const char *, struct fibl_head_t *);
147243185Shrs
148204406Suqsstatic void usage(const char *) __dead2;
14913171Swollman
15018286Sbdevoid
151196527Scharnierusage(const char *cp)
1521558Srgrimes{
153204406Suqs	if (cp != NULL)
15413171Swollman		warnx("bad keyword: %s", cp);
1551558Srgrimes	(void) fprintf(stderr,
15637907Scharnier	    "usage: route [-dnqtv] command [[modifiers] args]\n");
15713171Swollman	exit(EX_USAGE);
1581558Srgrimes	/* NOTREACHED */
1591558Srgrimes}
1601558Srgrimes
1611558Srgrimesint
162196527Scharniermain(int argc, char **argv)
1631558Srgrimes{
1641558Srgrimes	int ch;
165243185Shrs	size_t len;
1661558Srgrimes
1671558Srgrimes	if (argc < 2)
168204406Suqs		usage(NULL);
1691558Srgrimes
17037907Scharnier	while ((ch = getopt(argc, argv, "nqdtv")) != -1)
1711558Srgrimes		switch(ch) {
1721558Srgrimes		case 'n':
1731558Srgrimes			nflag = 1;
1741558Srgrimes			break;
1751558Srgrimes		case 'q':
1761558Srgrimes			qflag = 1;
1771558Srgrimes			break;
1781558Srgrimes		case 'v':
1791558Srgrimes			verbose = 1;
1801558Srgrimes			break;
1811558Srgrimes		case 't':
1821558Srgrimes			tflag = 1;
1831558Srgrimes			break;
1841558Srgrimes		case 'd':
1851558Srgrimes			debugonly = 1;
1861558Srgrimes			break;
1871558Srgrimes		case '?':
1881558Srgrimes		default:
189204406Suqs			usage(NULL);
1901558Srgrimes		}
1911558Srgrimes	argc -= optind;
1921558Srgrimes	argv += optind;
1931558Srgrimes
1941558Srgrimes	pid = getpid();
195109811Skbyanc	uid = geteuid();
1961558Srgrimes	if (tflag)
19769793Sobrien		s = open(_PATH_DEVNULL, O_WRONLY, 0);
1981558Srgrimes	else
1991558Srgrimes		s = socket(PF_ROUTE, SOCK_RAW, 0);
2001558Srgrimes	if (s < 0)
20113171Swollman		err(EX_OSERR, "socket");
202243185Shrs
203243185Shrs	len = sizeof(numfibs);
204243185Shrs	if (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) == -1)
205243185Shrs		numfibs = -1;
206243185Shrs
207243185Shrs	len = sizeof(defaultfib);
208243185Shrs	if (numfibs != -1 &&
209243185Shrs	    sysctlbyname("net.my_fibnum", (void *)&defaultfib, &len, NULL,
210243185Shrs		0) == -1)
211243185Shrs		defaultfib = -1;
212243185Shrs
213204406Suqs	if (*argv != NULL)
2141558Srgrimes		switch (keyword(*argv)) {
2151558Srgrimes		case K_GET:
216191080Skmacy		case K_SHOW:
2171558Srgrimes			uid = 0;
2181558Srgrimes			/* FALLTHROUGH */
2191558Srgrimes
2201558Srgrimes		case K_CHANGE:
2211558Srgrimes		case K_ADD:
222150679Stobez		case K_DEL:
2231558Srgrimes		case K_DELETE:
2241558Srgrimes			newroute(argc, argv);
2251558Srgrimes			/* NOTREACHED */
2261558Srgrimes
2271558Srgrimes		case K_MONITOR:
228243185Shrs			monitor(argc, argv);
2291558Srgrimes			/* NOTREACHED */
2301558Srgrimes
2311558Srgrimes		case K_FLUSH:
2321558Srgrimes			flushroutes(argc, argv);
2331558Srgrimes			exit(0);
2341558Srgrimes			/* NOTREACHED */
2351558Srgrimes		}
2361558Srgrimes	usage(*argv);
2371558Srgrimes	/* NOTREACHED */
2381558Srgrimes}
2391558Srgrimes
240243185Shrsstatic int
241243185Shrsset_sofib(int fib)
242243185Shrs{
243243185Shrs
244243185Shrs	if (fib < 0)
245243185Shrs		return (0);
246243185Shrs	return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void *)&fib,
247243185Shrs	    sizeof(fib)));
248243185Shrs}
249243185Shrs
250243185Shrsstatic int
251243185Shrsset_procfib(int fib)
252243185Shrs{
253243185Shrs
254243185Shrs	if (fib < 0)
255243185Shrs		return (0);
256243185Shrs	return (setfib(fib));
257243185Shrs}
258243185Shrs
259243185Shrsstatic int
260243185Shrsfiboptlist_range(const char *arg, struct fibl_head_t *flh)
261243185Shrs{
262243185Shrs	struct fibl *fl;
263243185Shrs	char *str, *token, *endptr;
264243185Shrs	int fib[2], i, error;
265243185Shrs
266243185Shrs	str = strdup(arg);
267243185Shrs	error = 0;
268243185Shrs	i = 0;
269243185Shrs	while ((token = strsep(&str, "-")) != NULL) {
270243185Shrs		switch (i) {
271243185Shrs		case 0:
272243185Shrs		case 1:
273243185Shrs			fib[i] = strtol(token, &endptr, 0);
274243185Shrs			if (*endptr != '\0' || (fib[i] == 0 &&
275243185Shrs			    (errno == EINVAL || errno == ERANGE)))
276243185Shrs				error = 1;
277243185Shrs			break;
278243185Shrs		default:
279243185Shrs			error = 1;
280243185Shrs		}
281243185Shrs		if (error)
282243185Shrs			goto fiboptlist_range_ret;
283243185Shrs		i++;
284243185Shrs	}
285243185Shrs	if (fib[0] >= fib[1]) {
286243185Shrs		error = 1;
287243185Shrs		goto fiboptlist_range_ret;
288243185Shrs	}
289243185Shrs	for (i = fib[0]; i <= fib[1]; i++) {
290243185Shrs		fl = calloc(1, sizeof(*fl));
291243185Shrs		if (fl == NULL) {
292243185Shrs			error = 1;
293243185Shrs			goto fiboptlist_range_ret;
294243185Shrs		}
295243185Shrs		fl->fl_num = i;
296243185Shrs		TAILQ_INSERT_TAIL(flh, fl, fl_next);
297243185Shrs	}
298243185Shrsfiboptlist_range_ret:
299243185Shrs	free(str);
300243185Shrs	return (error);
301243185Shrs}
302243185Shrs
303243185Shrs#define	ALLSTRLEN	64
304243185Shrsstatic int
305243185Shrsfiboptlist_csv(const char *arg, struct fibl_head_t *flh)
306243185Shrs{
307243185Shrs	struct fibl *fl;
308243185Shrs	char *str, *token, *endptr;
309243185Shrs	int fib, error;
310243185Shrs
311243185Shrs	if (strcmp("all", arg) == 0) {
312243185Shrs		str = calloc(1, ALLSTRLEN);
313243185Shrs		if (str == NULL) {
314243185Shrs			error = 1;
315243185Shrs			goto fiboptlist_csv_ret;
316243185Shrs		}
317243185Shrs		if (numfibs > 1)
318243185Shrs			snprintf(str, ALLSTRLEN - 1, "%d-%d", 0, numfibs - 1);
319243185Shrs		else
320243185Shrs			snprintf(str, ALLSTRLEN - 1, "%d", 0);
321243185Shrs	} else if (strcmp("default", arg) == 0) {
322243185Shrs		str = calloc(1, ALLSTRLEN);
323243185Shrs		if (str == NULL) {
324243185Shrs			error = 1;
325243185Shrs			goto fiboptlist_csv_ret;
326243185Shrs		}
327243185Shrs		snprintf(str, ALLSTRLEN - 1, "%d", defaultfib);
328243185Shrs	} else
329243185Shrs		str = strdup(arg);
330243185Shrs
331243185Shrs	error = 0;
332243185Shrs	while ((token = strsep(&str, ",")) != NULL) {
333243185Shrs		if (*token != '-' && strchr(token, '-') != NULL) {
334243185Shrs			error = fiboptlist_range(token, flh);
335243185Shrs			if (error)
336243185Shrs				goto fiboptlist_csv_ret;
337243185Shrs		} else {
338243185Shrs			fib = strtol(token, &endptr, 0);
339243185Shrs			if (*endptr != '\0' || (fib == 0 &&
340243185Shrs			    (errno == EINVAL || errno == ERANGE))) {
341243185Shrs				error = 1;
342243185Shrs				goto fiboptlist_csv_ret;
343243185Shrs			}
344243185Shrs			fl = calloc(1, sizeof(*fl));
345243185Shrs			if (fl == NULL) {
346243185Shrs				error = 1;
347243185Shrs				goto fiboptlist_csv_ret;
348243185Shrs			}
349243185Shrs			fl->fl_num = fib;
350243185Shrs			TAILQ_INSERT_TAIL(flh, fl, fl_next);
351243185Shrs		}
352243185Shrs	}
353243185Shrsfiboptlist_csv_ret:
354243185Shrs	free(str);
355243185Shrs	return (error);
356243185Shrs}
357243185Shrs
3581558Srgrimes/*
3591558Srgrimes * Purge all entries in the routing tables not
3601558Srgrimes * associated with network interfaces.
3611558Srgrimes */
362204406Suqsstatic void
363196527Scharnierflushroutes(int argc, char *argv[])
3641558Srgrimes{
365243185Shrs	struct fibl *fl;
366243185Shrs	int error;
3671558Srgrimes
368243859Sglebius	if (uid != 0 && !debugonly && !tflag) {
36913171Swollman		errx(EX_NOPERM, "must be root to alter routing table");
3701558Srgrimes	}
371146079Sjmallett	shutdown(s, SHUT_RD); /* Don't want to read back our messages */
372243185Shrs
373243185Shrs	TAILQ_INIT(&fibl_head);
374243185Shrs	while (argc > 1) {
375243185Shrs		argc--;
3761558Srgrimes		argv++;
377243185Shrs		if (**argv != '-')
378243185Shrs			usage(*argv);
379243185Shrs		switch (keyword(*argv + 1)) {
380243185Shrs		case K_INET:
381243185Shrs			af = AF_INET;
382243185Shrs			break;
38354263Sshin#ifdef INET6
384243185Shrs		case K_INET6:
385243185Shrs			af = AF_INET6;
386243185Shrs			break;
38754263Sshin#endif
388243185Shrs		case K_ATALK:
389243185Shrs			af = AF_APPLETALK;
390243185Shrs			break;
391243185Shrs		case K_LINK:
392243185Shrs			af = AF_LINK;
393243185Shrs			break;
394243185Shrs		case K_FIB:
395243185Shrs			if (!--argc)
396243185Shrs				usage(*argv);
397243185Shrs			error = fiboptlist_csv(*++argv, &fibl_head);
398243185Shrs			if (error)
399243185Shrs				usage(*argv);
400243185Shrs			break;
401243185Shrs		default:
402243185Shrs			usage(*argv);
403243185Shrs		}
4041558Srgrimes	}
405243185Shrs	if (TAILQ_EMPTY(&fibl_head)) {
406243185Shrs		error = fiboptlist_csv("default", &fibl_head);
407243185Shrs		if (error)
408243185Shrs			errx(EX_OSERR, "fiboptlist_csv failed.");
409243185Shrs	}
410243185Shrs	TAILQ_FOREACH(fl, &fibl_head, fl_next)
411243185Shrs		flushroutes_fib(fl->fl_num);
412243185Shrs}
413243185Shrs
414243185Shrsstatic int
415243185Shrsflushroutes_fib(int fib)
416243185Shrs{
417243185Shrs	struct rt_msghdr *rtm;
418243185Shrs	size_t needed;
419243185Shrs	char *buf, *next, *lim;
420243185Shrs	int mib[6], rlen, seqno, count = 0;
421243185Shrs	int error;
422243185Shrs
423243185Shrs	error = set_sofib(fib);
424243185Shrs	error += set_procfib(fib);
425243185Shrs	if (error) {
426243185Shrs		warn("fib number %d is ignored", fib);
427243185Shrs		return (error);
428243185Shrs	}
429243185Shrs
430128782Sambriskoretry:
4311558Srgrimes	mib[0] = CTL_NET;
4321558Srgrimes	mib[1] = PF_ROUTE;
4331558Srgrimes	mib[2] = 0;		/* protocol */
4341558Srgrimes	mib[3] = 0;		/* wildcard address family */
4351558Srgrimes	mib[4] = NET_RT_DUMP;
4361558Srgrimes	mib[5] = 0;		/* no flags */
4371558Srgrimes	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
43813171Swollman		err(EX_OSERR, "route-sysctl-estimate");
4391558Srgrimes	if ((buf = malloc(needed)) == NULL)
44037907Scharnier		errx(EX_OSERR, "malloc failed");
441128782Sambrisko	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
442128782Sambrisko		if (errno == ENOMEM && count++ < 10) {
443204406Suqs			warnx("Routing table grew, retrying");
444128782Sambrisko			sleep(1);
445128782Sambrisko			free(buf);
446128782Sambrisko			goto retry;
447128782Sambrisko		}
44813171Swollman		err(EX_OSERR, "route-sysctl-get");
449128782Sambrisko	}
4501558Srgrimes	lim = buf + needed;
4511558Srgrimes	if (verbose)
4521558Srgrimes		(void) printf("Examining routing table from sysctl\n");
4531558Srgrimes	seqno = 0;		/* ??? */
4541558Srgrimes	for (next = buf; next < lim; next += rtm->rtm_msglen) {
4551558Srgrimes		rtm = (struct rt_msghdr *)next;
4561558Srgrimes		if (verbose)
4571558Srgrimes			print_rtmsg(rtm, rtm->rtm_msglen);
4581558Srgrimes		if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
4591558Srgrimes			continue;
460204406Suqs		if (af != 0) {
4611558Srgrimes			struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
4621558Srgrimes
4631558Srgrimes			if (sa->sa_family != af)
4641558Srgrimes				continue;
4651558Srgrimes		}
4661558Srgrimes		if (debugonly)
4671558Srgrimes			continue;
4681558Srgrimes		rtm->rtm_type = RTM_DELETE;
4691558Srgrimes		rtm->rtm_seq = seqno;
4701558Srgrimes		rlen = write(s, next, rtm->rtm_msglen);
471129034Scsjp		if (rlen < 0 && errno == EPERM)
472129034Scsjp			err(1, "write to routing socket");
4731558Srgrimes		if (rlen < (int)rtm->rtm_msglen) {
47413171Swollman			warn("write to routing socket");
4751558Srgrimes			(void) printf("got only %d for rlen\n", rlen);
476128782Sambrisko			free(buf);
477128782Sambrisko			goto retry;
4781558Srgrimes			break;
4791558Srgrimes		}
4801558Srgrimes		seqno++;
4811558Srgrimes		if (qflag)
4821558Srgrimes			continue;
4831558Srgrimes		if (verbose)
4841558Srgrimes			print_rtmsg(rtm, rlen);
4851558Srgrimes		else {
4861558Srgrimes			struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
487243185Shrs
488243185Shrs			printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ?
4891558Srgrimes			    routename(sa) : netname(sa));
490128186Sluigi			sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa);
491243185Shrs			printf("%-20.20s ", routename(sa));
492243185Shrs			if (fib >= 0)
493243185Shrs				printf("-fib %-3d ", fib);
494243185Shrs			printf("done\n");
4951558Srgrimes		}
4961558Srgrimes	}
497243185Shrs	return (error);
4981558Srgrimes}
4991558Srgrimes
50078064Sumeconst char *
501196527Scharnierroutename(struct sockaddr *sa)
5021558Srgrimes{
503204406Suqs	const char *cp;
50419209Sfenner	static char line[MAXHOSTNAMELEN + 1];
5051558Srgrimes	struct hostent *hp;
5061558Srgrimes	static char domain[MAXHOSTNAMELEN + 1];
50781976Sbrian	static int first = 1, n;
5081558Srgrimes
5091558Srgrimes	if (first) {
5101558Srgrimes		first = 0;
5111558Srgrimes		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
51285048Sru		    (cp = strchr(domain, '.'))) {
51319209Sfenner			domain[MAXHOSTNAMELEN] = '\0';
5141558Srgrimes			(void) strcpy(domain, cp + 1);
51519209Sfenner		} else
5161558Srgrimes			domain[0] = 0;
5171558Srgrimes	}
5181558Srgrimes
5191558Srgrimes	if (sa->sa_len == 0)
5201558Srgrimes		strcpy(line, "default");
5211558Srgrimes	else switch (sa->sa_family) {
5221558Srgrimes
5231558Srgrimes	case AF_INET:
5241558Srgrimes	    {	struct in_addr in;
5251558Srgrimes		in = ((struct sockaddr_in *)sa)->sin_addr;
5261558Srgrimes
527204406Suqs		cp = NULL;
5281558Srgrimes		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
5291558Srgrimes			cp = "default";
530204406Suqs		if (cp == NULL && !nflag) {
5311558Srgrimes			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
5321558Srgrimes				AF_INET);
533204406Suqs			if (hp != NULL) {
534204406Suqs				char *cptr;
535204406Suqs				cptr = strchr(hp->h_name, '.');
536204406Suqs				if (cptr != NULL &&
537204406Suqs				    strcmp(cptr + 1, domain) == 0)
538204406Suqs					*cptr = '\0';
5391558Srgrimes				cp = hp->h_name;
5401558Srgrimes			}
5411558Srgrimes		}
542204406Suqs		if (cp != NULL) {
54331958Simp			strncpy(line, cp, sizeof(line) - 1);
54431958Simp			line[sizeof(line) - 1] = '\0';
54577873Sru		} else
54677873Sru			(void) sprintf(line, "%s", inet_ntoa(in));
5471558Srgrimes		break;
5481558Srgrimes	    }
5491558Srgrimes
55054263Sshin#ifdef INET6
55154263Sshin	case AF_INET6:
55278064Sume	{
55378064Sume		struct sockaddr_in6 sin6; /* use static var for safety */
55478064Sume		int niflags = 0;
55554263Sshin
55678064Sume		memset(&sin6, 0, sizeof(sin6));
55778064Sume		memcpy(&sin6, sa, sa->sa_len);
55878064Sume		sin6.sin6_len = sizeof(struct sockaddr_in6);
55978064Sume		sin6.sin6_family = AF_INET6;
56078064Sume		if (nflag)
56178064Sume			niflags |= NI_NUMERICHOST;
56278064Sume		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
56378064Sume		    line, sizeof(line), NULL, 0, niflags) != 0)
56478064Sume			strncpy(line, "invalid", sizeof(line));
56578064Sume
56678064Sume		return(line);
56778064Sume	}
56878064Sume#endif
56978064Sume
57017046Sjulian	case AF_APPLETALK:
57117046Sjulian		(void) snprintf(line, sizeof(line), "atalk %s",
57217046Sjulian			atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
57317046Sjulian		break;
57417046Sjulian
5751558Srgrimes	case AF_LINK:
5761558Srgrimes		return (link_ntoa((struct sockaddr_dl *)sa));
5771558Srgrimes
5781558Srgrimes	default:
579204406Suqs	    {
580204406Suqs		u_short *sp = (u_short *)sa;
581204406Suqs		u_short *splim = sp + ((sa->sa_len + 1) >> 1);
582204406Suqs		char *cps = line + sprintf(line, "(%d)", sa->sa_family);
58319209Sfenner		char *cpe = line + sizeof(line);
5841558Srgrimes
585204406Suqs		while (++sp < splim && cps < cpe) /* start with sa->sa_data */
586204406Suqs			if ((n = snprintf(cps, cpe - cps, " %x", *sp)) > 0)
587204406Suqs				cps += n;
58881980Sbrian			else
589204406Suqs				*cps = '\0';
5901558Srgrimes		break;
5911558Srgrimes	    }
5921558Srgrimes	}
5931558Srgrimes	return (line);
5941558Srgrimes}
5951558Srgrimes
5961558Srgrimes/*
5971558Srgrimes * Return the name of the network whose address is given.
598243019Sglebius * The address is assumed to be that of a net, not a host.
5991558Srgrimes */
60078064Sumeconst char *
601196527Scharniernetname(struct sockaddr *sa)
6021558Srgrimes{
603204406Suqs	const char *cp = NULL;
60419209Sfenner	static char line[MAXHOSTNAMELEN + 1];
605204406Suqs	struct netent *np = NULL;
60692806Sobrien	u_long i;
607243019Sglebius	int n;
6081558Srgrimes
6091558Srgrimes	switch (sa->sa_family) {
6101558Srgrimes
6111558Srgrimes	case AF_INET:
6121558Srgrimes	    {	struct in_addr in;
6131558Srgrimes		in = ((struct sockaddr_in *)sa)->sin_addr;
6141558Srgrimes
6151558Srgrimes		i = in.s_addr = ntohl(in.s_addr);
6161558Srgrimes		if (in.s_addr == 0)
6171558Srgrimes			cp = "default";
6181558Srgrimes		else if (!nflag) {
619243019Sglebius			np = getnetbyaddr(i, AF_INET);
620204406Suqs			if (np != NULL)
6211558Srgrimes				cp = np->n_name;
6221558Srgrimes		}
62377873Sru#define C(x)	(unsigned)((x) & 0xff)
624204406Suqs		if (cp != NULL)
62519209Sfenner			strncpy(line, cp, sizeof(line));
6261558Srgrimes		else if ((in.s_addr & 0xffffff) == 0)
6271558Srgrimes			(void) sprintf(line, "%u", C(in.s_addr >> 24));
6281558Srgrimes		else if ((in.s_addr & 0xffff) == 0)
6291558Srgrimes			(void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
6301558Srgrimes			    C(in.s_addr >> 16));
6311558Srgrimes		else if ((in.s_addr & 0xff) == 0)
6321558Srgrimes			(void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
6331558Srgrimes			    C(in.s_addr >> 16), C(in.s_addr >> 8));
6341558Srgrimes		else
6351558Srgrimes			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
6361558Srgrimes			    C(in.s_addr >> 16), C(in.s_addr >> 8),
6371558Srgrimes			    C(in.s_addr));
63877873Sru#undef C
6391558Srgrimes		break;
6401558Srgrimes	    }
6411558Srgrimes
64254263Sshin#ifdef INET6
64354263Sshin	case AF_INET6:
64478064Sume	{
64578064Sume		struct sockaddr_in6 sin6; /* use static var for safety */
64678064Sume		int niflags = 0;
64754263Sshin
64878064Sume		memset(&sin6, 0, sizeof(sin6));
64978064Sume		memcpy(&sin6, sa, sa->sa_len);
65078064Sume		sin6.sin6_len = sizeof(struct sockaddr_in6);
65178064Sume		sin6.sin6_family = AF_INET6;
65278064Sume		if (nflag)
65378064Sume			niflags |= NI_NUMERICHOST;
65478064Sume		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
65578064Sume		    line, sizeof(line), NULL, 0, niflags) != 0)
65678064Sume			strncpy(line, "invalid", sizeof(line));
65778064Sume
65878064Sume		return(line);
65978064Sume	}
66078064Sume#endif
66178064Sume
66217046Sjulian	case AF_APPLETALK:
66317046Sjulian		(void) snprintf(line, sizeof(line), "atalk %s",
66417046Sjulian			atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
66517046Sjulian		break;
66617046Sjulian
6671558Srgrimes	case AF_LINK:
6681558Srgrimes		return (link_ntoa((struct sockaddr_dl *)sa));
6691558Srgrimes
6701558Srgrimes
6711558Srgrimes	default:
672204406Suqs	    {
673204406Suqs		u_short *sp = (u_short *)sa->sa_data;
674204406Suqs		u_short *splim = sp + ((sa->sa_len + 1)>>1);
675204406Suqs		char *cps = line + sprintf(line, "af %d:", sa->sa_family);
67619209Sfenner		char *cpe = line + sizeof(line);
6771558Srgrimes
678204406Suqs		while (sp < splim && cps < cpe)
679204406Suqs			if ((n = snprintf(cps, cpe - cps, " %x", *sp++)) > 0)
680204406Suqs				cps += n;
68181980Sbrian			else
682204406Suqs				*cps = '\0';
6831558Srgrimes		break;
6841558Srgrimes	    }
6851558Srgrimes	}
6861558Srgrimes	return (line);
6871558Srgrimes}
6881558Srgrimes
689204406Suqsstatic void
690196527Scharnierset_metric(char *value, int key)
6911558Srgrimes{
6921558Srgrimes	int flag = 0;
6931558Srgrimes	u_long noval, *valp = &noval;
6941558Srgrimes
6951558Srgrimes	switch (key) {
6961558Srgrimes#define caseof(x, y, z)	case x: valp = &rt_metrics.z; flag = y; break
6971558Srgrimes	caseof(K_MTU, RTV_MTU, rmx_mtu);
6981558Srgrimes	caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
6991558Srgrimes	caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
7001558Srgrimes	caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
7011558Srgrimes	caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
7021558Srgrimes	caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
7031558Srgrimes	caseof(K_RTT, RTV_RTT, rmx_rtt);
7041558Srgrimes	caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
705191080Skmacy	caseof(K_WEIGHT, RTV_WEIGHT, rmx_weight);
7061558Srgrimes	}
7071558Srgrimes	rtm_inits |= flag;
7081558Srgrimes	if (lockrest || locking)
7091558Srgrimes		rt_metrics.rmx_locks |= flag;
7101558Srgrimes	if (locking)
7111558Srgrimes		locking = 0;
7121558Srgrimes	*valp = atoi(value);
7131558Srgrimes}
7141558Srgrimes
715243185Shrs#define	F_ISHOST	0x01
716243185Shrs#define	F_FORCENET	0x02
717243185Shrs#define	F_FORCEHOST	0x04
718243185Shrs#define	F_PROXY		0x08
719243185Shrs#define	F_INTERFACE	0x10
720243185Shrs
721204406Suqsstatic void
722196527Scharniernewroute(int argc, char **argv)
7231558Srgrimes{
724243185Shrs	struct hostent *hp;
725243185Shrs	struct fibl *fl;
726204406Suqs	char *cmd;
727243185Shrs	const char *dest, *gateway, *errmsg;
728243185Shrs	int key, error, flags, nrflags, fibnum;
7291558Srgrimes
730243859Sglebius	if (uid != 0 && !debugonly && !tflag) {
73113171Swollman		errx(EX_NOPERM, "must be root to alter routing table");
7321558Srgrimes	}
733243185Shrs
734243185Shrs	dest = NULL;
735243185Shrs	gateway = NULL;
736243185Shrs	flags = RTF_STATIC;
737243185Shrs	nrflags = 0;
738243185Shrs	hp = NULL;
739243185Shrs	TAILQ_INIT(&fibl_head);
740243185Shrs
7411558Srgrimes	cmd = argv[0];
742191080Skmacy	if (*cmd != 'g' && *cmd != 's')
743146079Sjmallett		shutdown(s, SHUT_RD); /* Don't want to read back our messages */
744191080Skmacy
7451558Srgrimes	while (--argc > 0) {
7461558Srgrimes		if (**(++argv)== '-') {
7471558Srgrimes			switch (key = keyword(1 + *argv)) {
7481558Srgrimes			case K_LINK:
7491558Srgrimes				af = AF_LINK;
7501558Srgrimes				aflen = sizeof(struct sockaddr_dl);
7511558Srgrimes				break;
7521558Srgrimes			case K_INET:
7531558Srgrimes				af = AF_INET;
7541558Srgrimes				aflen = sizeof(struct sockaddr_in);
7551558Srgrimes				break;
75654263Sshin#ifdef INET6
75754263Sshin			case K_INET6:
75854263Sshin				af = AF_INET6;
75954263Sshin				aflen = sizeof(struct sockaddr_in6);
76054263Sshin				break;
76154263Sshin#endif
76217046Sjulian			case K_ATALK:
76317046Sjulian				af = AF_APPLETALK;
76417046Sjulian				aflen = sizeof(struct sockaddr_at);
76517046Sjulian				break;
7661558Srgrimes			case K_SA:
7671558Srgrimes				af = PF_ROUTE;
7681558Srgrimes				aflen = sizeof(union sockunion);
7691558Srgrimes				break;
7701558Srgrimes			case K_IFACE:
7711558Srgrimes			case K_INTERFACE:
772243185Shrs				nrflags |= F_INTERFACE;
7732787Spst				break;
7741558Srgrimes			case K_NOSTATIC:
7751558Srgrimes				flags &= ~RTF_STATIC;
7761558Srgrimes				break;
7771558Srgrimes			case K_LOCK:
7781558Srgrimes				locking = 1;
7791558Srgrimes				break;
7801558Srgrimes			case K_LOCKREST:
7811558Srgrimes				lockrest = 1;
7821558Srgrimes				break;
7831558Srgrimes			case K_HOST:
784243185Shrs				nrflags |= F_FORCEHOST;
7851558Srgrimes				break;
7861558Srgrimes			case K_REJECT:
7871558Srgrimes				flags |= RTF_REJECT;
7881558Srgrimes				break;
7891558Srgrimes			case K_BLACKHOLE:
7901558Srgrimes				flags |= RTF_BLACKHOLE;
7911558Srgrimes				break;
7921558Srgrimes			case K_PROTO1:
7931558Srgrimes				flags |= RTF_PROTO1;
7941558Srgrimes				break;
7951558Srgrimes			case K_PROTO2:
7961558Srgrimes				flags |= RTF_PROTO2;
7971558Srgrimes				break;
79878140Sru			case K_PROXY:
799243185Shrs				nrflags |= F_PROXY;
80078140Sru				break;
8011558Srgrimes			case K_XRESOLVE:
8021558Srgrimes				flags |= RTF_XRESOLVE;
8031558Srgrimes				break;
8041558Srgrimes			case K_STATIC:
8051558Srgrimes				flags |= RTF_STATIC;
8061558Srgrimes				break;
807191080Skmacy			case K_STICKY:
808191080Skmacy				flags |= RTF_STICKY;
809191080Skmacy				break;
810191080Skmacy			case K_NOSTICK:
811191080Skmacy				flags &= ~RTF_STICKY;
812191080Skmacy				break;
813243185Shrs			case K_FIB:
814243185Shrs				if (!--argc)
815243185Shrs					usage(NULL);
816243185Shrs				error = fiboptlist_csv(*++argv, &fibl_head);
817243185Shrs				if (error)
818243185Shrs					usage(NULL);
819243185Shrs				break;
8201558Srgrimes			case K_IFA:
82147668Sru				if (!--argc)
822204406Suqs					usage(NULL);
8231558Srgrimes				(void) getaddr(RTA_IFA, *++argv, 0);
8241558Srgrimes				break;
8251558Srgrimes			case K_IFP:
82647668Sru				if (!--argc)
827204406Suqs					usage(NULL);
8281558Srgrimes				(void) getaddr(RTA_IFP, *++argv, 0);
8291558Srgrimes				break;
8301558Srgrimes			case K_GENMASK:
83147668Sru				if (!--argc)
832204406Suqs					usage(NULL);
8331558Srgrimes				(void) getaddr(RTA_GENMASK, *++argv, 0);
8341558Srgrimes				break;
8351558Srgrimes			case K_GATEWAY:
83647668Sru				if (!--argc)
837204406Suqs					usage(NULL);
8381558Srgrimes				(void) getaddr(RTA_GATEWAY, *++argv, 0);
8391558Srgrimes				break;
8401558Srgrimes			case K_DST:
84147668Sru				if (!--argc)
842204406Suqs					usage(NULL);
843243185Shrs				if (getaddr(RTA_DST, *++argv, &hp))
844243185Shrs					nrflags |= F_ISHOST;
8451558Srgrimes				dest = *argv;
8461558Srgrimes				break;
8471558Srgrimes			case K_NETMASK:
84847668Sru				if (!--argc)
849204406Suqs					usage(NULL);
8501558Srgrimes				(void) getaddr(RTA_NETMASK, *++argv, 0);
8511558Srgrimes				/* FALLTHROUGH */
8521558Srgrimes			case K_NET:
853243185Shrs				nrflags |= F_FORCENET;
8541558Srgrimes				break;
85554263Sshin			case K_PREFIXLEN:
85654263Sshin				if (!--argc)
857204406Suqs					usage(NULL);
85854263Sshin				if (prefixlen(*++argv) == -1) {
859243185Shrs					nrflags &= ~F_FORCENET;
860243185Shrs					nrflags |= F_ISHOST;
86154263Sshin				} else {
862243185Shrs					nrflags |= F_FORCENET;
863243185Shrs					nrflags &= ~F_ISHOST;
86454263Sshin				}
86554263Sshin				break;
8661558Srgrimes			case K_MTU:
8671558Srgrimes			case K_HOPCOUNT:
8681558Srgrimes			case K_EXPIRE:
8691558Srgrimes			case K_RECVPIPE:
8701558Srgrimes			case K_SENDPIPE:
8711558Srgrimes			case K_SSTHRESH:
8721558Srgrimes			case K_RTT:
8731558Srgrimes			case K_RTTVAR:
874191080Skmacy			case K_WEIGHT:
87547668Sru				if (!--argc)
876204406Suqs					usage(NULL);
8771558Srgrimes				set_metric(*++argv, key);
8781558Srgrimes				break;
8791558Srgrimes			default:
8801558Srgrimes				usage(1+*argv);
8811558Srgrimes			}
8821558Srgrimes		} else {
8831558Srgrimes			if ((rtm_addrs & RTA_DST) == 0) {
8841558Srgrimes				dest = *argv;
885243185Shrs				if (getaddr(RTA_DST, *argv, &hp))
886243185Shrs					nrflags |= F_ISHOST;
8871558Srgrimes			} else if ((rtm_addrs & RTA_GATEWAY) == 0) {
8881558Srgrimes				gateway = *argv;
8891558Srgrimes				(void) getaddr(RTA_GATEWAY, *argv, &hp);
8901558Srgrimes			} else {
8911558Srgrimes				(void) getaddr(RTA_NETMASK, *argv, 0);
892243185Shrs				nrflags |= F_FORCENET;
8931558Srgrimes			}
8941558Srgrimes		}
8951558Srgrimes	}
896243185Shrs
897243185Shrs	if (nrflags & F_FORCEHOST) {
898243185Shrs		nrflags |= F_ISHOST;
89954263Sshin#ifdef INET6
90054263Sshin		if (af == AF_INET6) {
90154263Sshin			rtm_addrs &= ~RTA_NETMASK;
902204406Suqs			memset((void *)&so_mask, 0, sizeof(so_mask));
90354263Sshin		}
904204406Suqs#endif
90554263Sshin	}
906243185Shrs	if (nrflags & F_FORCENET)
907243185Shrs		nrflags &= ~F_ISHOST;
9081558Srgrimes	flags |= RTF_UP;
909243185Shrs	if (nrflags & F_ISHOST)
9101558Srgrimes		flags |= RTF_HOST;
911243185Shrs	if ((nrflags & F_INTERFACE) == 0)
9121558Srgrimes		flags |= RTF_GATEWAY;
913243185Shrs	if (nrflags & F_PROXY) {
91478140Sru		so_dst.sinarp.sin_other = SIN_PROXY;
91578140Sru		flags |= RTF_ANNOUNCE;
91678140Sru	}
917243185Shrs	if (dest == NULL)
918243185Shrs		dest = "";
919243185Shrs	if (gateway == NULL)
920243185Shrs		gateway = "";
921243185Shrs
922243185Shrs	if (TAILQ_EMPTY(&fibl_head)) {
923243185Shrs		error = fiboptlist_csv("default", &fibl_head);
924243185Shrs		if (error)
925243185Shrs			errx(EX_OSERR, "fiboptlist_csv failed.");
9261558Srgrimes	}
927243185Shrs	error = 0;
928243185Shrs	TAILQ_FOREACH(fl, &fibl_head, fl_next) {
929243185Shrs		fl->fl_error = newroute_fib(fl->fl_num, cmd, flags);
930243185Shrs		if (fl->fl_error)
931243185Shrs			fl->fl_errno = errno;
932243185Shrs		error += fl->fl_error;
933243185Shrs	}
934191080Skmacy	if (*cmd == 'g' || *cmd == 's')
935243185Shrs		exit(error);
936243185Shrs
937243185Shrs	error = 0;
93897278Sru	if (!qflag) {
939243185Shrs		fibnum = 0;
940243185Shrs		TAILQ_FOREACH(fl, &fibl_head, fl_next) {
941243185Shrs			if (fl->fl_error == 0)
942243185Shrs				fibnum++;
9431558Srgrimes		}
944243185Shrs		if (fibnum > 0) {
945243185Shrs			int firstfib = 1;
946243185Shrs
947243185Shrs			printf("%s %s %s", cmd,
948243185Shrs			    (nrflags & F_ISHOST) ? "host" : "net", dest);
949243185Shrs			if (*gateway)
950243185Shrs				printf(": gateway %s", gateway);
951243185Shrs
952243185Shrs			if (numfibs > 1) {
953243185Shrs				TAILQ_FOREACH(fl, &fibl_head, fl_next) {
954243185Shrs					if (fl->fl_error == 0
955243185Shrs					    && fl->fl_num >= 0) {
956243185Shrs						if (firstfib) {
957243185Shrs							printf(" fib ");
958243185Shrs							firstfib = 0;
959243185Shrs						}
960243185Shrs						printf("%d", fl->fl_num);
961243185Shrs						if (fibnum-- > 1)
962243185Shrs							printf(",");
963243185Shrs					}
964243185Shrs				}
96597278Sru			}
966243185Shrs			printf("\n");
96797278Sru		}
968243185Shrs
969243185Shrs		fibnum = 0;
970243185Shrs		TAILQ_FOREACH(fl, &fibl_head, fl_next) {
971243185Shrs			if (fl->fl_error != 0) {
972243185Shrs				printf("%s %s %s", cmd, (nrflags & F_ISHOST)
973243185Shrs				    ? "host" : "net", dest);
974243185Shrs				if (*gateway)
975243185Shrs					printf(": gateway %s", gateway);
976243185Shrs
977243185Shrs				if (fl->fl_num >= 0)
978243185Shrs					printf(" fib %d", fl->fl_num);
979243185Shrs
980243185Shrs				switch (fl->fl_errno) {
981243185Shrs				case ESRCH:
982243185Shrs					errmsg = "not in table";
983243185Shrs					break;
984243185Shrs				case EBUSY:
985243185Shrs					errmsg = "entry in use";
986243185Shrs					break;
987243185Shrs				case ENOBUFS:
988243185Shrs					errmsg = "not enough memory";
989243185Shrs					break;
990243185Shrs				case EADDRINUSE:
991243185Shrs					/*
992243185Shrs					 * handle recursion avoidance
993243185Shrs					 * in rt_setgate()
994243185Shrs					 */
995243185Shrs					errmsg = "gateway uses the same route";
996243185Shrs					break;
997243185Shrs				case EEXIST:
998243185Shrs					errmsg = "route already in table";
999243185Shrs					break;
1000243185Shrs				default:
1001243185Shrs					errmsg = strerror(fl->fl_errno);
1002243185Shrs					break;
1003243185Shrs				}
1004243185Shrs				printf(": %s\n", errmsg);
1005243185Shrs				error = 1;
1006243185Shrs			}
1007243185Shrs		}
10081558Srgrimes	}
1009243185Shrs	exit(error);
10101558Srgrimes}
10111558Srgrimes
1012243185Shrsstatic int
1013243185Shrsnewroute_fib(int fib, char *cmd, int flags)
1014243185Shrs{
1015243185Shrs	int error;
1016243185Shrs
1017243185Shrs	error = set_sofib(fib);
1018243185Shrs	if (error) {
1019243185Shrs		warn("fib number %d is ignored", fib);
1020243185Shrs		return (error);
1021243185Shrs	}
1022243185Shrs
1023243185Shrs	error = rtmsg(*cmd, flags, fib);
1024243185Shrs	return (error);
1025243185Shrs}
1026243185Shrs
1027204406Suqsstatic void
1028196527Scharnierinet_makenetandmask(u_long net, struct sockaddr_in *sin, u_long bits)
10291558Srgrimes{
1030243019Sglebius	u_long mask = 0;
103192806Sobrien	char *cp;
10321558Srgrimes
10331558Srgrimes	rtm_addrs |= RTA_NETMASK;
1034243019Sglebius
1035204406Suqs	/*
1036204406Suqs	 * If no /xx was specified we must calculate the
1037190758Srrs	 * CIDR address.
1038190758Srrs	 */
1039243019Sglebius	if ((bits == 0) && (net != 0)) {
1040190775Srrs		u_long i, j;
1041190775Srrs		for(i=0,j=0xff; i<4; i++)  {
1042243019Sglebius			if (net & j) {
1043190758Srrs				break;
1044190758Srrs			}
1045190775Srrs			j <<= 8;
1046190758Srrs		}
1047190758Srrs		/* i holds the first non zero bit */
1048190775Srrs		bits = 32 - (i*8);
1049190758Srrs	}
1050190913Srrs	if (bits != 0)
1051190913Srrs		mask = 0xffffffff << (32 - bits);
1052173124Smtm
1053243019Sglebius	sin->sin_addr.s_addr = htonl(net);
10541558Srgrimes	sin = &so_mask.sin;
10551558Srgrimes	sin->sin_addr.s_addr = htonl(mask);
10561558Srgrimes	sin->sin_len = 0;
10571558Srgrimes	sin->sin_family = 0;
10581558Srgrimes	cp = (char *)(&sin->sin_addr + 1);
10591558Srgrimes	while (*--cp == 0 && cp > (char *)sin)
10601558Srgrimes		;
10611558Srgrimes	sin->sin_len = 1 + cp - (char *)sin;
10621558Srgrimes}
10631558Srgrimes
106496997Sume#ifdef INET6
10651558Srgrimes/*
106696997Sume * XXX the function may need more improvement...
106796997Sume */
106897062Sumestatic int
1069204406Suqsinet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen)
107096997Sume{
107196997Sume	struct in6_addr in6;
107296997Sume
1073204406Suqs	if (plen == NULL) {
107497073Sume		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
107597073Sume		    sin6->sin6_scope_id == 0) {
107697073Sume			plen = "0";
107797073Sume		} else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) {
107897073Sume			/* aggregatable global unicast - RFC2374 */
107997073Sume			memset(&in6, 0, sizeof(in6));
108097073Sume			if (!memcmp(&sin6->sin6_addr.s6_addr[8],
108197073Sume				    &in6.s6_addr[8], 8))
108297073Sume				plen = "64";
108397073Sume		}
108496997Sume	}
108596997Sume
1086204406Suqs	if (plen == NULL || strcmp(plen, "128") == 0)
1087204406Suqs		return (1);
108898053Sume	rtm_addrs |= RTA_NETMASK;
1089204406Suqs	prefixlen(plen);
1090204406Suqs	return (0);
109196997Sume}
109296997Sume#endif
109396997Sume
109496997Sume/*
10951558Srgrimes * Interpret an argument as a network address of some kind,
10961558Srgrimes * returning 1 if a host address, 0 if a network address.
10971558Srgrimes */
1098204406Suqsstatic int
1099204406Suqsgetaddr(int which, char *str, struct hostent **hpp)
11001558Srgrimes{
110192806Sobrien	sup su;
11021558Srgrimes	struct hostent *hp;
11031558Srgrimes	struct netent *np;
11041558Srgrimes	u_long val;
110566449Sru	char *q;
110654263Sshin	int afamily;  /* local copy of af so we can change it */
11071558Srgrimes
11081558Srgrimes	if (af == 0) {
11091558Srgrimes		af = AF_INET;
11101558Srgrimes		aflen = sizeof(struct sockaddr_in);
11111558Srgrimes	}
111254263Sshin	afamily = af;
11131558Srgrimes	rtm_addrs |= which;
11141558Srgrimes	switch (which) {
11151558Srgrimes	case RTA_DST:
11161558Srgrimes		su = &so_dst;
11171558Srgrimes		break;
11181558Srgrimes	case RTA_GATEWAY:
11191558Srgrimes		su = &so_gate;
112017486Sjulian		if (iflag) {
112178064Sume			struct ifaddrs *ifap, *ifa;
112278064Sume			struct sockaddr_dl *sdl = NULL;
112317486Sjulian
112478064Sume			if (getifaddrs(&ifap))
112578064Sume				err(1, "getifaddrs");
112617486Sjulian
1127204406Suqs			for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
112878064Sume				if (ifa->ifa_addr->sa_family != AF_LINK)
112978064Sume					continue;
113017486Sjulian
1131204406Suqs				if (strcmp(str, ifa->ifa_name) != 0)
113278064Sume					continue;
113378064Sume
113478064Sume				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
113517486Sjulian			}
113617486Sjulian			/* If we found it, then use it */
1137204406Suqs			if (sdl != NULL) {
113878064Sume				/*
113978064Sume				 * Copy is safe since we have a
114078064Sume				 * sockaddr_storage member in sockunion{}.
114178064Sume				 * Note that we need to copy before calling
114278064Sume				 * freeifaddrs().
114378064Sume				 */
114478064Sume				memcpy(&su->sdl, sdl, sdl->sdl_len);
114578064Sume			}
114678064Sume			freeifaddrs(ifap);
1147204406Suqs			if (sdl != NULL)
114817486Sjulian				return(1);
114917486Sjulian		}
11501558Srgrimes		break;
11511558Srgrimes	case RTA_NETMASK:
11521558Srgrimes		su = &so_mask;
11531558Srgrimes		break;
11541558Srgrimes	case RTA_GENMASK:
11551558Srgrimes		su = &so_genmask;
11561558Srgrimes		break;
11571558Srgrimes	case RTA_IFP:
11581558Srgrimes		su = &so_ifp;
115954263Sshin		afamily = AF_LINK;
11601558Srgrimes		break;
11611558Srgrimes	case RTA_IFA:
11621558Srgrimes		su = &so_ifa;
11631558Srgrimes		break;
11641558Srgrimes	default:
116537907Scharnier		usage("internal error");
11661558Srgrimes		/*NOTREACHED*/
11671558Srgrimes	}
11681558Srgrimes	su->sa.sa_len = aflen;
116954263Sshin	su->sa.sa_family = afamily; /* cases that don't want it have left already */
1170204406Suqs	if (strcmp(str, "default") == 0) {
117127500Sjulian		/*
1172204406Suqs		 * Default is net 0.0.0.0/0
117327500Sjulian		 */
11741558Srgrimes		switch (which) {
11751558Srgrimes		case RTA_DST:
11761558Srgrimes			forcenet++;
117797637Swollman#if 0
117897637Swollman			bzero(su, sizeof(*su));	/* for readability */
117997637Swollman#endif
1180204406Suqs			getaddr(RTA_NETMASK, str, 0);
11811558Srgrimes			break;
118297637Swollman#if 0
11831558Srgrimes		case RTA_NETMASK:
11841558Srgrimes		case RTA_GENMASK:
118597637Swollman			bzero(su, sizeof(*su));	/* for readability */
118697637Swollman#endif
11871558Srgrimes		}
11881558Srgrimes		return (0);
11891558Srgrimes	}
119054263Sshin	switch (afamily) {
119154263Sshin#ifdef INET6
119254263Sshin	case AF_INET6:
119378064Sume	{
119457108Sshin		struct addrinfo hints, *res;
1195146546Sume		int ecode;
119657108Sshin
119797073Sume		q = NULL;
1198204406Suqs		if (which == RTA_DST && (q = strchr(str, '/')) != NULL)
119997073Sume			*q = '\0';
120078064Sume		memset(&hints, 0, sizeof(hints));
120178064Sume		hints.ai_family = afamily;	/*AF_INET6*/
120278064Sume		hints.ai_socktype = SOCK_DGRAM;		/*dummy*/
1203204406Suqs		ecode = getaddrinfo(str, NULL, &hints, &res);
1204146546Sume		if (ecode != 0 || res->ai_family != AF_INET6 ||
120578064Sume		    res->ai_addrlen != sizeof(su->sin6)) {
1206204406Suqs			(void) fprintf(stderr, "%s: %s\n", str,
1207146546Sume			    gai_strerror(ecode));
120854263Sshin			exit(1);
120954263Sshin		}
121078064Sume		memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6));
121197062Sume		freeaddrinfo(res);
121297073Sume		if (q != NULL)
121397073Sume			*q++ = '/';
121496997Sume		if (which == RTA_DST)
121598053Sume			return (inet6_makenetandmask(&su->sin6, q));
121678064Sume		return (0);
121778064Sume	}
121878064Sume#endif /* INET6 */
121954263Sshin
122017046Sjulian	case AF_APPLETALK:
1221204406Suqs		if (!atalk_aton(str, &su->sat.sat_addr))
1222204406Suqs			errx(EX_NOHOST, "bad address: %s", str);
122317265Sjulian		rtm_addrs |= RTA_NETMASK;
122417265Sjulian		return(forcehost || su->sat.sat_addr.s_node != 0);
122517046Sjulian
12261558Srgrimes	case AF_LINK:
1227204406Suqs		link_addr(str, &su->sdl);
12281558Srgrimes		return (1);
12291558Srgrimes
12301558Srgrimes
12311558Srgrimes	case PF_ROUTE:
12321558Srgrimes		su->sa.sa_len = sizeof(*su);
1233204406Suqs		sockaddr(str, &su->sa);
12341558Srgrimes		return (1);
12351558Srgrimes
12361558Srgrimes	case AF_INET:
12371558Srgrimes	default:
12381558Srgrimes		break;
12391558Srgrimes	}
12401558Srgrimes
12411558Srgrimes	if (hpp == NULL)
12421558Srgrimes		hpp = &hp;
12431558Srgrimes	*hpp = NULL;
124424558Sphk
1245204406Suqs	q = strchr(str,'/');
1246204406Suqs	if (q != NULL && which == RTA_DST) {
124724558Sphk		*q = '\0';
1248204406Suqs		if ((val = inet_network(str)) != INADDR_NONE) {
124924558Sphk			inet_makenetandmask(
125077904Sru				val, &su->sin, strtoul(q+1, 0, 0));
125124558Sphk			return (0);
125224558Sphk		}
125366449Sru		*q = '/';
125424558Sphk	}
125566449Sru	if ((which != RTA_DST || forcenet == 0) &&
1256204406Suqs	    inet_aton(str, &su->sin.sin_addr)) {
125779588Sru		val = su->sin.sin_addr.s_addr;
1258130569Sbms		if (which != RTA_DST || forcehost ||
125966449Sru		    inet_lnaof(su->sin.sin_addr) != INADDR_ANY)
12601558Srgrimes			return (1);
12611558Srgrimes		else {
12621558Srgrimes			val = ntohl(val);
12631558Srgrimes			goto netdone;
12641558Srgrimes		}
12651558Srgrimes	}
126666449Sru	if (which == RTA_DST && forcehost == 0 &&
1267204406Suqs	    ((val = inet_network(str)) != INADDR_NONE ||
1268204406Suqs	    ((np = getnetbyname(str)) != NULL && (val = np->n_net) != 0))) {
12691558Srgrimesnetdone:
127066449Sru		inet_makenetandmask(val, &su->sin, 0);
12711558Srgrimes		return (0);
12721558Srgrimes	}
1273204406Suqs	hp = gethostbyname(str);
1274204406Suqs	if (hp != NULL) {
12751558Srgrimes		*hpp = hp;
12761558Srgrimes		su->sin.sin_family = hp->h_addrtype;
127785048Sru		memmove((char *)&su->sin.sin_addr, hp->h_addr,
1278204406Suqs		    MIN((size_t)hp->h_length, sizeof(su->sin.sin_addr)));
12791558Srgrimes		return (1);
12801558Srgrimes	}
1281204406Suqs	errx(EX_NOHOST, "bad address: %s", str);
12821558Srgrimes}
12831558Srgrimes
1284204406Suqsstatic int
1285204406Suqsprefixlen(const char *str)
128654263Sshin{
1287204406Suqs	int len = atoi(str), q, r;
128854263Sshin	int max;
128954263Sshin	char *p;
12901558Srgrimes
129154263Sshin	rtm_addrs |= RTA_NETMASK;
129254263Sshin	switch (af) {
129354263Sshin#ifdef INET6
129454263Sshin	case AF_INET6:
129554263Sshin		max = 128;
129654263Sshin		p = (char *)&so_mask.sin6.sin6_addr;
129754263Sshin		break;
129854263Sshin#endif
129954263Sshin	case AF_INET:
130054263Sshin		max = 32;
130154263Sshin		p = (char *)&so_mask.sin.sin_addr;
130254263Sshin		break;
130354263Sshin	default:
1304204406Suqs		fprintf(stderr, "prefixlen not supported in this af\n");
130554263Sshin		exit(1);
130654263Sshin	}
130754263Sshin
130854263Sshin	if (len < 0 || max < len) {
1309204406Suqs		fprintf(stderr, "%s: bad value\n", str);
131054263Sshin		exit(1);
131154263Sshin	}
131254263Sshin
131354263Sshin	q = len >> 3;
131454263Sshin	r = len & 7;
131554263Sshin	so_mask.sa.sa_family = af;
131654263Sshin	so_mask.sa.sa_len = aflen;
131754263Sshin	memset((void *)p, 0, max / 8);
131854263Sshin	if (q > 0)
131954263Sshin		memset((void *)p, 0xff, q);
132054263Sshin	if (r > 0)
132154263Sshin		*((u_char *)p + q) = (0xff00 >> r) & 0xff;
132254263Sshin	if (len == max)
1323204406Suqs		return (-1);
132454263Sshin	else
1325204406Suqs		return (len);
132654263Sshin}
132754263Sshin
1328204406Suqsstatic void
1329196527Scharnierinterfaces(void)
13301558Srgrimes{
13311558Srgrimes	size_t needed;
13321558Srgrimes	int mib[6];
1333128782Sambrisko	char *buf, *lim, *next, count = 0;
133492806Sobrien	struct rt_msghdr *rtm;
13351558Srgrimes
1336128782Sambriskoretry2:
13371558Srgrimes	mib[0] = CTL_NET;
13381558Srgrimes	mib[1] = PF_ROUTE;
13391558Srgrimes	mib[2] = 0;		/* protocol */
13401558Srgrimes	mib[3] = 0;		/* wildcard address family */
13411558Srgrimes	mib[4] = NET_RT_IFLIST;
13421558Srgrimes	mib[5] = 0;		/* no flags */
13431558Srgrimes	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
134413171Swollman		err(EX_OSERR, "route-sysctl-estimate");
13451558Srgrimes	if ((buf = malloc(needed)) == NULL)
134637907Scharnier		errx(EX_OSERR, "malloc failed");
1347128782Sambrisko	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
1348128782Sambrisko		if (errno == ENOMEM && count++ < 10) {
1349128782Sambrisko			warnx("Routing table grew, retrying");
1350128782Sambrisko			sleep(1);
1351128782Sambrisko			free(buf);
1352128782Sambrisko			goto retry2;
1353128782Sambrisko		}
135413171Swollman		err(EX_OSERR, "actual retrieval of interface table");
1355128782Sambrisko	}
13561558Srgrimes	lim = buf + needed;
13571558Srgrimes	for (next = buf; next < lim; next += rtm->rtm_msglen) {
13581558Srgrimes		rtm = (struct rt_msghdr *)next;
13591558Srgrimes		print_rtmsg(rtm, rtm->rtm_msglen);
13601558Srgrimes	}
13611558Srgrimes}
13621558Srgrimes
1363204406Suqsstatic void
1364243185Shrsmonitor(int argc, char *argv[])
13651558Srgrimes{
1366243185Shrs	int n, fib, error;
1367243185Shrs	char msg[2048], *endptr;
13681558Srgrimes
1369243185Shrs	fib = defaultfib;
1370243185Shrs	while (argc > 1) {
1371243185Shrs		argc--;
1372243185Shrs		argv++;
1373243185Shrs		if (**argv != '-')
1374243185Shrs			usage(*argv);
1375243185Shrs		switch (keyword(*argv + 1)) {
1376243185Shrs		case K_FIB:
1377243185Shrs			if (!--argc)
1378243185Shrs				usage(*argv);
1379243185Shrs			fib = strtol(*++argv, &endptr, 0);
1380243185Shrs			if (*endptr != '\0' || (fib == 0 &&
1381243185Shrs			    (errno == EINVAL || errno == ERANGE)))
1382243185Shrs				usage(*argv);
1383243185Shrs			break;
1384243185Shrs		default:
1385243185Shrs			usage(*argv);
1386243185Shrs		}
1387243185Shrs	}
1388243185Shrs	error = set_sofib(fib);
1389243185Shrs	if (error)
1390243185Shrs		errx(EX_USAGE, "invalid fib number: %d", fib);
1391243185Shrs
13921558Srgrimes	verbose = 1;
13931558Srgrimes	if (debugonly) {
13941558Srgrimes		interfaces();
13951558Srgrimes		exit(0);
13961558Srgrimes	}
1397204406Suqs	for (;;) {
139854263Sshin		time_t now;
13991558Srgrimes		n = read(s, msg, 2048);
140054263Sshin		now = time(NULL);
140171061Sphk		(void) printf("\ngot message of size %d on %s", n, ctime(&now));
14021558Srgrimes		print_rtmsg((struct rt_msghdr *)msg, n);
14031558Srgrimes	}
14041558Srgrimes}
14051558Srgrimes
14061558Srgrimesstruct {
14071558Srgrimes	struct	rt_msghdr m_rtm;
14081558Srgrimes	char	m_space[512];
14091558Srgrimes} m_rtmsg;
14101558Srgrimes
1411204406Suqsstatic int
1412243185Shrsrtmsg(int cmd, int flags, int fib)
14131558Srgrimes{
14141558Srgrimes	static int seq;
14151558Srgrimes	int rlen;
141692806Sobrien	char *cp = m_rtmsg.m_space;
141792806Sobrien	int l;
14181558Srgrimes
14191558Srgrimes#define NEXTADDR(w, u) \
14201558Srgrimes	if (rtm_addrs & (w)) {\
1421128186Sluigi	    l = SA_SIZE(&(u.sa)); memmove(cp, &(u), l); cp += l;\
1422178065Sru	    if (verbose) sodump(&(u),#u);\
14231558Srgrimes	}
14241558Srgrimes
14251558Srgrimes	errno = 0;
142685048Sru	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
14271558Srgrimes	if (cmd == 'a')
14281558Srgrimes		cmd = RTM_ADD;
14291558Srgrimes	else if (cmd == 'c')
14301558Srgrimes		cmd = RTM_CHANGE;
1431191080Skmacy	else if (cmd == 'g' || cmd == 's') {
14321558Srgrimes		cmd = RTM_GET;
14331558Srgrimes		if (so_ifp.sa.sa_family == 0) {
143413171Swollman			so_ifp.sa.sa_family = AF_LINK;
143513171Swollman			so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
14361558Srgrimes			rtm_addrs |= RTA_IFP;
14371558Srgrimes		}
14381558Srgrimes	} else
14391558Srgrimes		cmd = RTM_DELETE;
14401558Srgrimes#define rtm m_rtmsg.m_rtm
14411558Srgrimes	rtm.rtm_type = cmd;
14421558Srgrimes	rtm.rtm_flags = flags;
14431558Srgrimes	rtm.rtm_version = RTM_VERSION;
14441558Srgrimes	rtm.rtm_seq = ++seq;
14451558Srgrimes	rtm.rtm_addrs = rtm_addrs;
14461558Srgrimes	rtm.rtm_rmx = rt_metrics;
14471558Srgrimes	rtm.rtm_inits = rtm_inits;
14481558Srgrimes
14491558Srgrimes	if (rtm_addrs & RTA_NETMASK)
14501558Srgrimes		mask_addr();
14511558Srgrimes	NEXTADDR(RTA_DST, so_dst);
14521558Srgrimes	NEXTADDR(RTA_GATEWAY, so_gate);
14531558Srgrimes	NEXTADDR(RTA_NETMASK, so_mask);
14541558Srgrimes	NEXTADDR(RTA_GENMASK, so_genmask);
14551558Srgrimes	NEXTADDR(RTA_IFP, so_ifp);
14561558Srgrimes	NEXTADDR(RTA_IFA, so_ifa);
14571558Srgrimes	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
14581558Srgrimes	if (verbose)
14591558Srgrimes		print_rtmsg(&rtm, l);
14601558Srgrimes	if (debugonly)
14611558Srgrimes		return (0);
14621558Srgrimes	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
1463129034Scsjp		if (errno == EPERM)
1464129034Scsjp			err(1, "writing to routing socket");
146537907Scharnier		warn("writing to routing socket");
14661558Srgrimes		return (-1);
14671558Srgrimes	}
14681558Srgrimes	if (cmd == RTM_GET) {
14691558Srgrimes		do {
14701558Srgrimes			l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
14711558Srgrimes		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
14721558Srgrimes		if (l < 0)
147313171Swollman			warn("read from routing socket");
14741558Srgrimes		else
1475243185Shrs			print_getmsg(&rtm, l, fib);
14761558Srgrimes	}
14771558Srgrimes#undef rtm
14781558Srgrimes	return (0);
14791558Srgrimes}
14801558Srgrimes
1481204406Suqsstatic void
1482196527Scharniermask_addr(void)
14831558Srgrimes{
14841558Srgrimes	int olen = so_mask.sa.sa_len;
148592806Sobrien	char *cp1 = olen + (char *)&so_mask, *cp2;
14861558Srgrimes
14871558Srgrimes	for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; )
14881558Srgrimes		if (*--cp1 != 0) {
14891558Srgrimes			so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask;
14901558Srgrimes			break;
14911558Srgrimes		}
14921558Srgrimes	if ((rtm_addrs & RTA_DST) == 0)
14931558Srgrimes		return;
14941558Srgrimes	switch (so_dst.sa.sa_family) {
14951558Srgrimes	case AF_INET:
149654263Sshin#ifdef INET6
149754263Sshin	case AF_INET6:
149854263Sshin#endif
149917046Sjulian	case AF_APPLETALK:
15001558Srgrimes	case 0:
15011558Srgrimes		return;
15021558Srgrimes	}
15031558Srgrimes	cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst;
15041558Srgrimes	cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst;
15051558Srgrimes	while (cp2 > cp1)
15061558Srgrimes		*--cp2 = 0;
15071558Srgrimes	cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask;
15081558Srgrimes	while (cp1 > so_dst.sa.sa_data)
15091558Srgrimes		*--cp1 &= *--cp2;
15101558Srgrimes}
15111558Srgrimes
1512204406Suqsconst char *msgtypes[] = {
15131558Srgrimes	"",
15141558Srgrimes	"RTM_ADD: Add Route",
15151558Srgrimes	"RTM_DELETE: Delete Route",
15161558Srgrimes	"RTM_CHANGE: Change Metrics or flags",
15171558Srgrimes	"RTM_GET: Report Metrics",
15181558Srgrimes	"RTM_LOSING: Kernel Suspects Partitioning",
15191558Srgrimes	"RTM_REDIRECT: Told to use different route",
15201558Srgrimes	"RTM_MISS: Lookup failed on this address",
15211558Srgrimes	"RTM_LOCK: fix specified metrics",
15221558Srgrimes	"RTM_OLDADD: caused by SIOCADDRT",
15231558Srgrimes	"RTM_OLDDEL: caused by SIOCDELRT",
15241558Srgrimes	"RTM_RESOLVE: Route created by cloning",
15251558Srgrimes	"RTM_NEWADDR: address being added to iface",
15261558Srgrimes	"RTM_DELADDR: address being removed from iface",
15271558Srgrimes	"RTM_IFINFO: iface status change",
152821465Swollman	"RTM_NEWMADDR: new multicast group membership on iface",
152921465Swollman	"RTM_DELMADDR: multicast group membership removed from iface",
153089498Sru	"RTM_IFANNOUNCE: interface arrival/departure",
1531216296Sglebius	"RTM_IEEE80211: IEEE 802.11 wireless event",
15321558Srgrimes};
15331558Srgrimes
15341558Srgrimeschar metricnames[] =
1535191080Skmacy"\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire"
153615690Swollman"\1mtu";
15371558Srgrimeschar routeflags[] =
1538191080Skmacy"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE"
1539191080Skmacy"\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
1540191080Skmacy"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3"
1541191080Skmacy"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY";
15421558Srgrimeschar ifnetflags[] =
154315690Swollman"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
154415690Swollman"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
154515690Swollman"\017LINK2\020MULTICAST";
15461558Srgrimeschar addrnames[] =
15471558Srgrimes"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
15481558Srgrimes
1549216297Sglebiusstatic const char errfmt[] =
1550216297Sglebius"\n%s: truncated route message, only %zu bytes left\n";
1551216297Sglebius
1552204406Suqsstatic void
1553216297Sglebiusprint_rtmsg(struct rt_msghdr *rtm, size_t msglen)
15541558Srgrimes{
15551558Srgrimes	struct if_msghdr *ifm;
15561558Srgrimes	struct ifa_msghdr *ifam;
155721465Swollman#ifdef RTM_NEWMADDR
155821465Swollman	struct ifma_msghdr *ifmam;
155921465Swollman#endif
156089498Sru	struct if_announcemsghdr *ifan;
1561204406Suqs	const char *state;
15621558Srgrimes
15631558Srgrimes	if (verbose == 0)
15641558Srgrimes		return;
15651558Srgrimes	if (rtm->rtm_version != RTM_VERSION) {
15661558Srgrimes		(void) printf("routing message version %d not understood\n",
15671558Srgrimes		    rtm->rtm_version);
15681558Srgrimes		return;
15691558Srgrimes	}
1570216297Sglebius	if (rtm->rtm_type < sizeof(msgtypes) / sizeof(msgtypes[0]))
157189498Sru		(void)printf("%s: ", msgtypes[rtm->rtm_type]);
157289498Sru	else
1573216297Sglebius		(void)printf("unknown type %d: ", rtm->rtm_type);
157489498Sru	(void)printf("len %d, ", rtm->rtm_msglen);
1575216297Sglebius
1576216297Sglebius#define	REQUIRE(x)	do {		\
1577216297Sglebius	if (msglen < sizeof(x))		\
1578216297Sglebius		goto badlen;		\
1579216297Sglebius	else				\
1580216297Sglebius		msglen -= sizeof(x);	\
1581216297Sglebius	} while (0)
1582216297Sglebius
15831558Srgrimes	switch (rtm->rtm_type) {
15841558Srgrimes	case RTM_IFINFO:
1585216297Sglebius		REQUIRE(struct if_msghdr);
15861558Srgrimes		ifm = (struct if_msghdr *)rtm;
1587128878Sandre		(void) printf("if# %d, ", ifm->ifm_index);
1588128878Sandre		switch (ifm->ifm_data.ifi_link_state) {
1589128878Sandre		case LINK_STATE_DOWN:
1590128878Sandre			state = "down";
1591128878Sandre			break;
1592128878Sandre		case LINK_STATE_UP:
1593128878Sandre			state = "up";
1594128878Sandre			break;
1595128878Sandre		default:
1596128878Sandre			state = "unknown";
1597128878Sandre			break;
1598128878Sandre		}
1599128878Sandre		(void) printf("link: %s, flags:", state);
16001558Srgrimes		bprintf(stdout, ifm->ifm_flags, ifnetflags);
1601216297Sglebius		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs, msglen);
16021558Srgrimes		break;
16031558Srgrimes	case RTM_NEWADDR:
16041558Srgrimes	case RTM_DELADDR:
1605216297Sglebius		REQUIRE(struct ifa_msghdr);
16061558Srgrimes		ifam = (struct ifa_msghdr *)rtm;
16071558Srgrimes		(void) printf("metric %d, flags:", ifam->ifam_metric);
16081558Srgrimes		bprintf(stdout, ifam->ifam_flags, routeflags);
1609216297Sglebius		pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs, msglen);
16101558Srgrimes		break;
161121465Swollman#ifdef RTM_NEWMADDR
161221465Swollman	case RTM_NEWMADDR:
161321465Swollman	case RTM_DELMADDR:
1614216297Sglebius		REQUIRE(struct ifma_msghdr);
161521465Swollman		ifmam = (struct ifma_msghdr *)rtm;
1616216297Sglebius		pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs, msglen);
161721465Swollman		break;
161821465Swollman#endif
161989498Sru	case RTM_IFANNOUNCE:
1620216297Sglebius		REQUIRE(struct if_announcemsghdr);
162189498Sru		ifan = (struct if_announcemsghdr *)rtm;
162289498Sru		(void) printf("if# %d, what: ", ifan->ifan_index);
162389498Sru		switch (ifan->ifan_what) {
162489498Sru		case IFAN_ARRIVAL:
162589498Sru			printf("arrival");
162689498Sru			break;
162789498Sru		case IFAN_DEPARTURE:
162889498Sru			printf("departure");
162989498Sru			break;
163089498Sru		default:
163189498Sru			printf("#%d", ifan->ifan_what);
163289498Sru			break;
163389498Sru		}
163489498Sru		printf("\n");
1635243860Sglebius		fflush(stdout);
163689498Sru		break;
163789498Sru
16381558Srgrimes	default:
163913171Swollman		(void) printf("pid: %ld, seq %d, errno %d, flags:",
164013171Swollman			(long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
16411558Srgrimes		bprintf(stdout, rtm->rtm_flags, routeflags);
1642216297Sglebius		pmsg_common(rtm, msglen);
16431558Srgrimes	}
1644216297Sglebius
1645216297Sglebius	return;
1646216297Sglebius
1647216297Sglebiusbadlen:
1648216297Sglebius	(void)printf(errfmt, __func__, msglen);
1649216297Sglebius#undef	REQUIRE
16501558Srgrimes}
16511558Srgrimes
1652204406Suqsstatic void
1653243185Shrsprint_getmsg(struct rt_msghdr *rtm, int msglen, int fib)
16541558Srgrimes{
16551558Srgrimes	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
16561558Srgrimes	struct sockaddr_dl *ifp = NULL;
165792806Sobrien	struct sockaddr *sa;
165892806Sobrien	char *cp;
165992806Sobrien	int i;
16601558Srgrimes
1661196527Scharnier	(void) printf("   route to: %s\n",
1662196527Scharnier	    routename((struct sockaddr *)&so_dst));
16631558Srgrimes	if (rtm->rtm_version != RTM_VERSION) {
166413171Swollman		warnx("routing message version %d not understood",
166513171Swollman		     rtm->rtm_version);
16661558Srgrimes		return;
16671558Srgrimes	}
16681558Srgrimes	if (rtm->rtm_msglen > msglen) {
166937907Scharnier		warnx("message length mismatch, in packet %d, returned %d",
167013171Swollman		      rtm->rtm_msglen, msglen);
16711558Srgrimes	}
16721558Srgrimes	if (rtm->rtm_errno)  {
167313171Swollman		errno = rtm->rtm_errno;
167413171Swollman		warn("message indicates error %d", errno);
16751558Srgrimes		return;
16761558Srgrimes	}
16771558Srgrimes	cp = ((char *)(rtm + 1));
16781558Srgrimes	if (rtm->rtm_addrs)
16791558Srgrimes		for (i = 1; i; i <<= 1)
16801558Srgrimes			if (i & rtm->rtm_addrs) {
16811558Srgrimes				sa = (struct sockaddr *)cp;
16821558Srgrimes				switch (i) {
16831558Srgrimes				case RTA_DST:
16841558Srgrimes					dst = sa;
16851558Srgrimes					break;
16861558Srgrimes				case RTA_GATEWAY:
16871558Srgrimes					gate = sa;
16881558Srgrimes					break;
16891558Srgrimes				case RTA_NETMASK:
16901558Srgrimes					mask = sa;
16911558Srgrimes					break;
16921558Srgrimes				case RTA_IFP:
16931558Srgrimes					if (sa->sa_family == AF_LINK &&
16941558Srgrimes					   ((struct sockaddr_dl *)sa)->sdl_nlen)
16951558Srgrimes						ifp = (struct sockaddr_dl *)sa;
16961558Srgrimes					break;
16971558Srgrimes				}
1698128186Sluigi				cp += SA_SIZE(sa);
16991558Srgrimes			}
17001558Srgrimes	if (dst && mask)
17011558Srgrimes		mask->sa_family = dst->sa_family;	/* XXX */
17021558Srgrimes	if (dst)
17031558Srgrimes		(void)printf("destination: %s\n", routename(dst));
17041558Srgrimes	if (mask) {
17051558Srgrimes		int savenflag = nflag;
17061558Srgrimes
17071558Srgrimes		nflag = 1;
17081558Srgrimes		(void)printf("       mask: %s\n", routename(mask));
17091558Srgrimes		nflag = savenflag;
17101558Srgrimes	}
17111558Srgrimes	if (gate && rtm->rtm_flags & RTF_GATEWAY)
17121558Srgrimes		(void)printf("    gateway: %s\n", routename(gate));
1713243185Shrs	if (fib >= 0)
1714243185Shrs		(void)printf("        fib: %u\n", (unsigned int)fib);
17151558Srgrimes	if (ifp)
17161558Srgrimes		(void)printf("  interface: %.*s\n",
17171558Srgrimes		    ifp->sdl_nlen, ifp->sdl_data);
17181558Srgrimes	(void)printf("      flags: ");
17191558Srgrimes	bprintf(stdout, rtm->rtm_flags, routeflags);
17201558Srgrimes
17211558Srgrimes#define lock(f)	((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ')
17221558Srgrimes#define msec(u)	(((u) + 500) / 1000)		/* usec to msec */
17231558Srgrimes
17241558Srgrimes	(void) printf("\n%s\n", "\
1725191080Skmacy recvpipe  sendpipe  ssthresh  rtt,msec    mtu        weight    expire");
172613171Swollman	printf("%8ld%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
172713171Swollman	printf("%8ld%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
172813171Swollman	printf("%8ld%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
172913171Swollman	printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
173013171Swollman	printf("%8ld%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
1731191080Skmacy	printf("%8ld%c ", rtm->rtm_rmx.rmx_weight, lock(WEIGHT));
17321558Srgrimes	if (rtm->rtm_rmx.rmx_expire)
17331558Srgrimes		rtm->rtm_rmx.rmx_expire -= time(0);
173413171Swollman	printf("%8ld%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
17351558Srgrimes#undef lock
17361558Srgrimes#undef msec
17371558Srgrimes#define	RTA_IGN	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD)
17381558Srgrimes	if (verbose)
1739216297Sglebius		pmsg_common(rtm, msglen);
17401558Srgrimes	else if (rtm->rtm_addrs &~ RTA_IGN) {
17411558Srgrimes		(void) printf("sockaddrs: ");
17421558Srgrimes		bprintf(stdout, rtm->rtm_addrs, addrnames);
17431558Srgrimes		putchar('\n');
17441558Srgrimes	}
17451558Srgrimes#undef	RTA_IGN
17461558Srgrimes}
17471558Srgrimes
1748204406Suqsstatic void
1749216297Sglebiuspmsg_common(struct rt_msghdr *rtm, size_t msglen)
17501558Srgrimes{
17511558Srgrimes	(void) printf("\nlocks: ");
17521558Srgrimes	bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames);
17531558Srgrimes	(void) printf(" inits: ");
17541558Srgrimes	bprintf(stdout, rtm->rtm_inits, metricnames);
1755216297Sglebius	if (msglen > sizeof(struct rt_msghdr))
1756216297Sglebius		pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs,
1757216297Sglebius		    msglen - sizeof(struct rt_msghdr));
1758216297Sglebius	else
1759216297Sglebius		(void) fflush(stdout);
17601558Srgrimes}
17611558Srgrimes
1762204406Suqsstatic void
1763216297Sglebiuspmsg_addrs(char *cp, int addrs, size_t len)
17641558Srgrimes{
176592806Sobrien	struct sockaddr *sa;
17661558Srgrimes	int i;
17671558Srgrimes
176871061Sphk	if (addrs == 0) {
176971061Sphk		(void) putchar('\n');
17701558Srgrimes		return;
177171061Sphk	}
17721558Srgrimes	(void) printf("\nsockaddrs: ");
17731558Srgrimes	bprintf(stdout, addrs, addrnames);
17741558Srgrimes	(void) putchar('\n');
1775204406Suqs	for (i = 1; i != 0; i <<= 1)
17761558Srgrimes		if (i & addrs) {
17771558Srgrimes			sa = (struct sockaddr *)cp;
1778216297Sglebius			if (len == 0 || len < SA_SIZE(sa)) {
1779216297Sglebius				(void) printf(errfmt, __func__, len);
1780216297Sglebius				break;
1781216297Sglebius			}
17821558Srgrimes			(void) printf(" %s", routename(sa));
1783216297Sglebius			len -= SA_SIZE(sa);
1784128186Sluigi			cp += SA_SIZE(sa);
17851558Srgrimes		}
17861558Srgrimes	(void) putchar('\n');
17871558Srgrimes	(void) fflush(stdout);
17881558Srgrimes}
17891558Srgrimes
1790204406Suqsstatic void
1791204406Suqsbprintf(FILE *fp, int b, u_char *str)
17921558Srgrimes{
179392806Sobrien	int i;
17941558Srgrimes	int gotsome = 0;
17951558Srgrimes
17961558Srgrimes	if (b == 0)
17971558Srgrimes		return;
1798204406Suqs	while ((i = *str++) != 0) {
17991558Srgrimes		if (b & (1 << (i-1))) {
18001558Srgrimes			if (gotsome == 0)
18011558Srgrimes				i = '<';
18021558Srgrimes			else
18031558Srgrimes				i = ',';
18041558Srgrimes			(void) putc(i, fp);
18051558Srgrimes			gotsome = 1;
1806204406Suqs			for (; (i = *str) > 32; str++)
18071558Srgrimes				(void) putc(i, fp);
18081558Srgrimes		} else
1809204406Suqs			while (*str > 32)
1810204406Suqs				str++;
18111558Srgrimes	}
18121558Srgrimes	if (gotsome)
18131558Srgrimes		(void) putc('>', fp);
18141558Srgrimes}
18151558Srgrimes
18161558Srgrimesint
1817204406Suqskeyword(const char *cp)
18181558Srgrimes{
181992806Sobrien	struct keytab *kt = keywords;
18201558Srgrimes
1821204406Suqs	while (kt->kt_cp != NULL && strcmp(kt->kt_cp, cp) != 0)
18221558Srgrimes		kt++;
1823204406Suqs	return (kt->kt_i);
18241558Srgrimes}
18251558Srgrimes
1826204406Suqsstatic void
1827204406Suqssodump(sup su, const char *which)
18281558Srgrimes{
18291558Srgrimes	switch (su->sa.sa_family) {
18301558Srgrimes	case AF_LINK:
18311558Srgrimes		(void) printf("%s: link %s; ",
18321558Srgrimes		    which, link_ntoa(&su->sdl));
18331558Srgrimes		break;
18341558Srgrimes	case AF_INET:
18351558Srgrimes		(void) printf("%s: inet %s; ",
18361558Srgrimes		    which, inet_ntoa(su->sin.sin_addr));
18371558Srgrimes		break;
183817046Sjulian	case AF_APPLETALK:
183917046Sjulian		(void) printf("%s: atalk %s; ",
184017046Sjulian		    which, atalk_ntoa(su->sat.sat_addr));
184117046Sjulian		break;
18421558Srgrimes	}
18431558Srgrimes	(void) fflush(stdout);
18441558Srgrimes}
18451558Srgrimes
18461558Srgrimes/* States*/
18471558Srgrimes#define VIRGIN	0
18481558Srgrimes#define GOTONE	1
18491558Srgrimes#define GOTTWO	2
18501558Srgrimes/* Inputs */
18511558Srgrimes#define	DIGIT	(4*0)
18521558Srgrimes#define	END	(4*1)
18531558Srgrimes#define DELIM	(4*2)
18541558Srgrimes
1855204406Suqsstatic void
1856196527Scharniersockaddr(char *addr, struct sockaddr *sa)
18571558Srgrimes{
185892806Sobrien	char *cp = (char *)sa;
18591558Srgrimes	int size = sa->sa_len;
18601558Srgrimes	char *cplim = cp + size;
186192806Sobrien	int byte = 0, state = VIRGIN, new = 0 /* foil gcc */;
18621558Srgrimes
186385048Sru	memset(cp, 0, size);
18641558Srgrimes	cp++;
18651558Srgrimes	do {
18661558Srgrimes		if ((*addr >= '0') && (*addr <= '9')) {
18671558Srgrimes			new = *addr - '0';
18681558Srgrimes		} else if ((*addr >= 'a') && (*addr <= 'f')) {
18691558Srgrimes			new = *addr - 'a' + 10;
18701558Srgrimes		} else if ((*addr >= 'A') && (*addr <= 'F')) {
18711558Srgrimes			new = *addr - 'A' + 10;
1872204406Suqs		} else if (*addr == '\0')
18731558Srgrimes			state |= END;
18741558Srgrimes		else
18751558Srgrimes			state |= DELIM;
18761558Srgrimes		addr++;
18771558Srgrimes		switch (state /* | INPUT */) {
18781558Srgrimes		case GOTTWO | DIGIT:
18791558Srgrimes			*cp++ = byte; /*FALLTHROUGH*/
18801558Srgrimes		case VIRGIN | DIGIT:
18811558Srgrimes			state = GOTONE; byte = new; continue;
18821558Srgrimes		case GOTONE | DIGIT:
18831558Srgrimes			state = GOTTWO; byte = new + (byte << 4); continue;
18841558Srgrimes		default: /* | DELIM */
18851558Srgrimes			state = VIRGIN; *cp++ = byte; byte = 0; continue;
18861558Srgrimes		case GOTONE | END:
18871558Srgrimes		case GOTTWO | END:
18881558Srgrimes			*cp++ = byte; /* FALLTHROUGH */
18891558Srgrimes		case VIRGIN | END:
18901558Srgrimes			break;
18911558Srgrimes		}
18921558Srgrimes		break;
18931558Srgrimes	} while (cp < cplim);
18941558Srgrimes	sa->sa_len = cp - (char *)sa;
18951558Srgrimes}
189617046Sjulian
1897204406Suqsstatic int
189817046Sjulianatalk_aton(const char *text, struct at_addr *addr)
189917046Sjulian{
190017046Sjulian	u_int net, node;
190117046Sjulian
190217046Sjulian	if (sscanf(text, "%u.%u", &net, &node) != 2
190317046Sjulian	    || net > 0xffff || node > 0xff)
190417046Sjulian		return(0);
190517254Sjulian	addr->s_net = htons(net);
190617046Sjulian	addr->s_node = node;
190717046Sjulian	return(1);
190817046Sjulian}
190917046Sjulian
1910204406Suqsstatic char *
191117046Sjulianatalk_ntoa(struct at_addr at)
191217046Sjulian{
191317046Sjulian	static char buf[20];
191417046Sjulian
191517254Sjulian	(void) snprintf(buf, sizeof(buf), "%u.%u", ntohs(at.s_net), at.s_node);
191617046Sjulian	return(buf);
191717046Sjulian}
1918