1/*
2 * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Copyright (c) 1983, 1989, 1991, 1993
30 *	The Regents of the University of California.  All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 *    must display the following acknowledgement:
42 *	This product includes software developed by the University of
43 *	California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61#include <sys/cdefs.h>
62
63#ifndef lint
64__unused static const char copyright[] =
65"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\
66	The Regents of the University of California.  All rights reserved.\n";
67#endif /* not lint */
68
69#include <sys/param.h>
70#include <sys/file.h>
71#include <sys/socket.h>
72#include <sys/ioctl.h>
73#include <sys/sysctl.h>
74#include <sys/types.h>
75
76#include <net/if.h>
77#include <net/route.h>
78#include <net/if_dl.h>
79#include <netinet/in.h>
80#include <arpa/inet.h>
81#include <netdb.h>
82
83#include <ctype.h>
84#include <err.h>
85#include <errno.h>
86#include <paths.h>
87#include <stdio.h>
88#include <stdlib.h>
89#include <string.h>
90#include <sysexits.h>
91#include <unistd.h>
92#include <ifaddrs.h>
93
94struct keytab {
95	char	*kt_cp;
96	int	kt_i;
97} keywords[] = {
98#include "keywords.h"
99	{0, 0}
100};
101
102union	sockunion {
103	struct	sockaddr sa;
104	struct	sockaddr_in sin;
105#ifdef INET6
106	struct	sockaddr_in6 sin6;
107#endif
108	struct	sockaddr_dl sdl;
109	struct	sockaddr_storage ss; /* added to avoid memory overrun */
110} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp;
111
112typedef union sockunion *sup;
113int	pid, rtm_addrs, uid;
114int	s;
115int	forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword();
116int	iflag, verbose, aflen = sizeof (struct sockaddr_in);
117int	locking, lockrest, debugonly;
118struct	rt_metrics rt_metrics;
119u_long  rtm_inits;
120unsigned int ifscope;
121
122static const char *route_strerror(int);
123const char	*routename(), *netname();
124void	flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf();
125void	print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr();
126int	getaddr(), rtmsg(), x25_makemask();
127int	prefixlen();
128extern	char *iso_ntoa();
129
130void usage __P((const char *)) __dead2;
131
132void
133usage(cp)
134	const char *cp;
135{
136	if (cp)
137		warnx("bad keyword: %s", cp);
138	(void) fprintf(stderr,
139	    "usage: route [-dnqtv] command [[modifiers] args]\n");
140	exit(EX_USAGE);
141	/* NOTREACHED */
142}
143
144#define ROUNDUP(a) \
145	((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
146#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
147
148int
149main(argc, argv)
150	int argc;
151	char **argv;
152{
153	int ch;
154
155	if (argc < 2)
156		usage((char *)NULL);
157
158	while ((ch = getopt(argc, argv, "nqdtv")) != -1)
159		switch(ch) {
160		case 'n':
161			nflag = 1;
162			break;
163		case 'q':
164			qflag = 1;
165			break;
166		case 'v':
167			verbose = 1;
168			break;
169		case 't':
170			tflag = 1;
171			break;
172		case 'd':
173			debugonly = 1;
174			break;
175		case '?':
176		default:
177			usage((char *)NULL);
178		}
179	argc -= optind;
180	argv += optind;
181
182	pid = getpid();
183	uid = geteuid();
184	if (tflag)
185		s = open(_PATH_DEVNULL, O_WRONLY, 0);
186	else
187		s = socket(PF_ROUTE, SOCK_RAW, 0);
188	if (s < 0)
189		err(EX_OSERR, "socket");
190	setuid(uid);
191	if (*argv)
192		switch (keyword(*argv)) {
193		case K_GET:
194			uid = 0;
195			/* FALLTHROUGH */
196
197		case K_CHANGE:
198		case K_ADD:
199		case K_DELETE:
200			newroute(argc, argv);
201			exit(0);
202			/* NOTREACHED */
203
204		case K_MONITOR:
205			monitor();
206			/* NOTREACHED */
207
208		case K_FLUSH:
209			flushroutes(argc, argv);
210			exit(0);
211			/* NOTREACHED */
212		}
213	usage(*argv);
214	/* NOTREACHED */
215}
216
217/*
218 * Purge all entries in the routing tables not
219 * associated with network interfaces.
220 */
221void
222flushroutes(argc, argv)
223	int argc;
224	char *argv[];
225{
226	size_t needed;
227	int mib[6], rlen, seqno;
228	char *buf, *next, *lim;
229	register struct rt_msghdr *rtm;
230
231	if (uid) {
232		errx(EX_NOPERM, "must be root to alter routing table");
233	}
234	shutdown(s, 0); /* Don't want to read back our messages */
235	if (argc > 1) {
236		argv++;
237		if (argc == 2 && **argv == '-')
238		    switch (keyword(*argv + 1)) {
239			case K_INET:
240				af = AF_INET;
241				break;
242#ifdef INET6
243			case K_INET6:
244				af = AF_INET6;
245				break;
246#endif
247			case K_LINK:
248				af = AF_LINK;
249				break;
250			default:
251				goto bad;
252		} else
253bad:			usage(*argv);
254	}
255	mib[0] = CTL_NET;
256	mib[1] = PF_ROUTE;
257	mib[2] = 0;		/* protocol */
258	mib[3] = 0;		/* wildcard address family */
259	mib[4] = NET_RT_DUMP;
260	mib[5] = 0;		/* no flags */
261	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
262		err(EX_OSERR, "route-sysctl-estimate");
263	if ((buf = malloc(needed)) == NULL)
264		errx(EX_OSERR, "malloc failed");
265	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
266		err(EX_OSERR, "route-sysctl-get");
267	lim = buf + needed;
268	if (verbose)
269		(void) printf("Examining routing table from sysctl\n");
270	seqno = 0;		/* ??? */
271	for (next = buf; next < lim; next += rtm->rtm_msglen) {
272		rtm = (struct rt_msghdr *)next;
273		if (verbose)
274			print_rtmsg(rtm, rtm->rtm_msglen);
275		if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
276			continue;
277		if (af) {
278			struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
279
280			if (sa->sa_family != af)
281				continue;
282		}
283		if (debugonly)
284			continue;
285		rtm->rtm_type = RTM_DELETE;
286		rtm->rtm_seq = seqno;
287		rlen = write(s, next, rtm->rtm_msglen);
288		if (rlen < (int)rtm->rtm_msglen) {
289			warn("write to routing socket");
290			(void) printf("got only %d for rlen\n", rlen);
291			break;
292		}
293		seqno++;
294		if (qflag)
295			continue;
296		if (verbose)
297			print_rtmsg(rtm, rlen);
298		else {
299			struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
300			(void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ?
301			    routename(sa) : netname(sa));
302			sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
303			(void) printf("%-20.20s ", routename(sa));
304			(void) printf("done\n");
305		}
306	}
307}
308
309const char *
310routename(sa)
311	struct sockaddr *sa;
312{
313	register char *cp;
314	static char line[MAXHOSTNAMELEN + 1];
315	struct hostent *hp;
316	static char domain[MAXHOSTNAMELEN + 1];
317	static int first = 1;
318
319	if (first) {
320		first = 0;
321		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
322		    (cp = index(domain, '.'))) {
323			domain[MAXHOSTNAMELEN] = '\0';
324			(void) memmove(domain, cp + 1, strlen(cp + 1) + 1);
325		} else
326			domain[0] = 0;
327	}
328
329	if (sa->sa_len == 0)
330		strlcpy(line, "default", sizeof(line));
331	else switch (sa->sa_family) {
332
333	case AF_INET:
334	    {	struct in_addr in;
335		in = ((struct sockaddr_in *)sa)->sin_addr;
336
337		cp = 0;
338		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
339			cp = "default";
340		if (cp == 0 && !nflag) {
341			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
342				AF_INET);
343			if (hp) {
344				if ((cp = index(hp->h_name, '.')) &&
345				    !strcmp(cp + 1, domain))
346					*cp = 0;
347				cp = hp->h_name;
348			}
349		}
350		if (cp) {
351			strncpy(line, cp, sizeof(line) - 1);
352			line[sizeof(line) - 1] = '\0';
353		} else {
354			/* XXX - why not inet_ntoa()? */
355#define C(x)	(unsigned)((x) & 0xff)
356			in.s_addr = ntohl(in.s_addr);
357			(void) snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
358			   C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
359		}
360		break;
361	    }
362
363#ifdef INET6
364	case AF_INET6:
365	{
366		struct sockaddr_in6 sin6; /* use static var for safety */
367		int niflags = 0;
368#ifdef NI_WITHSCOPEID
369		niflags = NI_WITHSCOPEID;
370#endif
371
372		memset(&sin6, 0, sizeof(sin6));
373		memcpy(&sin6, sa, sa->sa_len);
374		sin6.sin6_len = sizeof(struct sockaddr_in6);
375		sin6.sin6_family = AF_INET6;
376#ifdef __KAME__
377		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
378		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
379		     IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) ||
380		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
381		    sin6.sin6_scope_id == 0) {
382			sin6.sin6_scope_id =
383			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
384			sin6.sin6_addr.s6_addr[2] = 0;
385			sin6.sin6_addr.s6_addr[3] = 0;
386		}
387#endif
388		if (nflag)
389			niflags |= NI_NUMERICHOST;
390		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
391		    line, sizeof(line), NULL, 0, niflags) != 0)
392			strncpy(line, "invalid", sizeof(line));
393
394		return(line);
395	}
396#endif
397
398	case AF_LINK:
399		return (link_ntoa((struct sockaddr_dl *)sa));
400
401	default:
402	    {	u_short *s = (u_short *)sa;
403		u_short *slim = s + ((sa->sa_len + 1) >> 1);
404		char *cp = line + snprintf(line, sizeof(line), "(%d)", sa->sa_family);
405		char *cpe = line + sizeof(line);
406
407		while (++s < slim && cp < cpe) /* start with sa->sa_data */
408			cp += snprintf(cp, cpe - cp, " %x", *s);
409		break;
410	    }
411	}
412	return (line);
413}
414
415/*
416 * Return the name of the network whose address is given.
417 * The address is assumed to be that of a net or subnet, not a host.
418 */
419const char *
420netname(sa)
421	struct sockaddr *sa;
422{
423	char *cp = 0;
424	static char line[MAXHOSTNAMELEN + 1];
425	struct netent *np = 0;
426	in_addr_t net, mask;
427	register in_addr_t i;
428	int subnetshift;
429
430	switch (sa->sa_family) {
431
432	case AF_INET:
433	    {	struct in_addr in;
434		in = ((struct sockaddr_in *)sa)->sin_addr;
435
436		i = in.s_addr = ntohl(in.s_addr);
437		if (in.s_addr == 0)
438			cp = "default";
439		else if (!nflag) {
440			if (IN_CLASSA(i)) {
441				mask = IN_CLASSA_NET;
442				subnetshift = 8;
443			} else if (IN_CLASSB(i)) {
444				mask = IN_CLASSB_NET;
445				subnetshift = 8;
446			} else {
447				mask = IN_CLASSC_NET;
448				subnetshift = 4;
449			}
450			/*
451			 * If there are more bits than the standard mask
452			 * would suggest, subnets must be in use.
453			 * Guess at the subnet mask, assuming reasonable
454			 * width subnet fields.
455			 */
456			while (in.s_addr &~ mask)
457				mask = mask >> subnetshift;
458			net = in.s_addr & mask;
459			while ((mask & 1) == 0)
460				mask >>= 1, net >>= 1;
461			np = getnetbyaddr(net, AF_INET);
462			if (np)
463				cp = np->n_name;
464		}
465		if (cp)
466			strncpy(line, cp, sizeof(line));
467		else if ((in.s_addr & 0xffffff) == 0)
468			(void) snprintf(line, sizeof(line), "%u", C(in.s_addr >> 24));
469		else if ((in.s_addr & 0xffff) == 0)
470			(void) snprintf(line, sizeof(line), "%u.%u", C(in.s_addr >> 24),
471			    C(in.s_addr >> 16));
472		else if ((in.s_addr & 0xff) == 0)
473			(void) snprintf(line, sizeof(line), "%u.%u.%u", C(in.s_addr >> 24),
474			    C(in.s_addr >> 16), C(in.s_addr >> 8));
475		else
476			(void) snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
477			    C(in.s_addr >> 16), C(in.s_addr >> 8),
478			    C(in.s_addr));
479		break;
480	    }
481
482#ifdef INET6
483	case AF_INET6:
484	{
485		struct sockaddr_in6 sin6; /* use static var for safety */
486		int niflags = 0;
487#ifdef NI_WITHSCOPEID
488		niflags = NI_WITHSCOPEID;
489#endif
490
491		memset(&sin6, 0, sizeof(sin6));
492		memcpy(&sin6, sa, sa->sa_len);
493		sin6.sin6_len = sizeof(struct sockaddr_in6);
494		sin6.sin6_family = AF_INET6;
495#ifdef __KAME__
496		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
497		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
498		     IN6_IS_ADDR_MC_NODELOCAL(&sin6.sin6_addr) ||
499		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
500		    sin6.sin6_scope_id == 0) {
501			sin6.sin6_scope_id =
502			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
503			sin6.sin6_addr.s6_addr[2] = 0;
504			sin6.sin6_addr.s6_addr[3] = 0;
505		}
506#endif
507		if (nflag)
508			niflags |= NI_NUMERICHOST;
509		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
510		    line, sizeof(line), NULL, 0, niflags) != 0)
511			strncpy(line, "invalid", sizeof(line));
512
513		return(line);
514	}
515#endif
516
517	case AF_LINK:
518		return (link_ntoa((struct sockaddr_dl *)sa));
519
520
521	default:
522	    {	u_short *s = (u_short *)sa->sa_data;
523		u_short *slim = s + ((sa->sa_len + 1)>>1);
524		char *cp = line + snprintf(line, sizeof(line), "af %d:", sa->sa_family);
525		char *cpe = line + sizeof(line);
526
527		while (s < slim && cp < cpe)
528			cp += snprintf(cp, cpe - cp, " %x", *s++);
529		break;
530	    }
531	}
532	return (line);
533}
534
535static const char *
536route_strerror(int error)
537{
538
539	switch (error) {
540	case ESRCH:
541		return "not in table";
542	case EBUSY:
543		return "entry in use";
544	case ENOBUFS:
545		return "routing table overflow";
546	default:
547		return (strerror(error));
548	}
549}
550
551void
552set_metric(value, key)
553	char *value;
554	int key;
555{
556	int flag = 0;
557	u_int noval, *valp = &noval;
558
559	switch (key) {
560#define caseof(x, y, z)	case x: valp = (u_int *)&rt_metrics.z; flag = y; break
561	caseof(K_MTU, RTV_MTU, rmx_mtu);
562	caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
563	caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
564	caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
565	caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
566	caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
567	caseof(K_RTT, RTV_RTT, rmx_rtt);
568	caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
569	}
570	rtm_inits |= flag;
571	if (lockrest || locking)
572		rt_metrics.rmx_locks |= flag;
573	if (locking)
574		locking = 0;
575	*valp = atoi(value);
576}
577
578void
579newroute(argc, argv)
580	int argc;
581	register char **argv;
582{
583	char *cmd, *dest = "", *gateway = "";
584	int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC;
585	int key;
586	struct hostent *hp = 0;
587
588	if (uid) {
589		errx(EX_NOPERM, "must be root to alter routing table");
590	}
591	cmd = argv[0];
592	if (*cmd != 'g')
593		shutdown(s, 0); /* Don't want to read back our messages */
594	while (--argc > 0) {
595		if (**(++argv)== '-') {
596			switch (key = keyword(1 + *argv)) {
597			case K_LINK:
598				af = AF_LINK;
599				aflen = sizeof(struct sockaddr_dl);
600				break;
601			case K_INET:
602				af = AF_INET;
603				aflen = sizeof(struct sockaddr_in);
604				break;
605#ifdef INET6
606			case K_INET6:
607				af = AF_INET6;
608				aflen = sizeof(struct sockaddr_in6);
609				break;
610#endif
611			case K_SA:
612				af = PF_ROUTE;
613				aflen = sizeof(union sockunion);
614				break;
615			case K_IFACE:
616			case K_INTERFACE:
617				iflag++;
618				break;
619			case K_NOSTATIC:
620				flags &= ~RTF_STATIC;
621				break;
622			case K_LLINFO:
623				flags |= RTF_LLINFO;
624				break;
625			case K_LOCK:
626				locking = 1;
627				break;
628			case K_LOCKREST:
629				lockrest = 1;
630				break;
631			case K_HOST:
632				forcehost++;
633				break;
634			case K_REJECT:
635				flags |= RTF_REJECT;
636				break;
637			case K_BLACKHOLE:
638				flags |= RTF_BLACKHOLE;
639				break;
640			case K_PROTO1:
641				flags |= RTF_PROTO1;
642				break;
643			case K_PROTO2:
644				flags |= RTF_PROTO2;
645				break;
646			case K_CLONING:
647				flags |= RTF_CLONING;
648				break;
649			case K_XRESOLVE:
650				flags |= RTF_XRESOLVE;
651				break;
652			case K_STATIC:
653				flags |= RTF_STATIC;
654				break;
655			case K_IFA:
656				if (!--argc)
657					usage((char *)NULL);
658				(void) getaddr(RTA_IFA, *++argv, 0);
659				break;
660			case K_IFP:
661				if (!--argc)
662					usage((char *)NULL);
663				(void) getaddr(RTA_IFP, *++argv, 0);
664				break;
665			case K_GENMASK:
666				if (!--argc)
667					usage((char *)NULL);
668				(void) getaddr(RTA_GENMASK, *++argv, 0);
669				break;
670			case K_GATEWAY:
671				if (!--argc)
672					usage((char *)NULL);
673				(void) getaddr(RTA_GATEWAY, *++argv, 0);
674				break;
675			case K_DST:
676				if (!--argc)
677					usage((char *)NULL);
678				ishost = getaddr(RTA_DST, *++argv, &hp);
679				dest = *argv;
680				break;
681			case K_NETMASK:
682				if (!--argc)
683					usage((char *)NULL);
684				(void) getaddr(RTA_NETMASK, *++argv, 0);
685				/* FALLTHROUGH */
686			case K_NET:
687				forcenet++;
688				break;
689			case K_PREFIXLEN:
690				if (!--argc)
691					usage((char *)NULL);
692				if (prefixlen(*++argv) == -1) {
693					forcenet = 0;
694					ishost = 1;
695				} else {
696					forcenet = 1;
697					ishost = 0;
698				}
699				break;
700			case K_MTU:
701			case K_HOPCOUNT:
702			case K_EXPIRE:
703			case K_RECVPIPE:
704			case K_SENDPIPE:
705			case K_SSTHRESH:
706			case K_RTT:
707			case K_RTTVAR:
708				if (!--argc)
709					usage((char *)NULL);
710				set_metric(*++argv, key);
711				break;
712			case K_IFSCOPE:
713				if (!--argc)
714					usage((char *)NULL);
715				if ((ifscope = if_nametoindex(*++argv)) != 0)
716					flags |= RTF_IFSCOPE;
717				else
718					errx(1, "bad interface name");
719				break;
720			default:
721				usage(1+*argv);
722			}
723		} else {
724			if ((rtm_addrs & RTA_DST) == 0) {
725				dest = *argv;
726				ishost = getaddr(RTA_DST, *argv, &hp);
727			} else if ((rtm_addrs & RTA_GATEWAY) == 0) {
728				gateway = *argv;
729				(void) getaddr(RTA_GATEWAY, *argv, &hp);
730			} else {
731				(void) getaddr(RTA_NETMASK, *argv, 0);
732			}
733		}
734	}
735	if (forcehost) {
736		ishost = 1;
737#ifdef INET6
738		if (af == AF_INET6) {
739			rtm_addrs &= ~RTA_NETMASK;
740			memset((void *)&so_mask, 0, sizeof(so_mask));
741		}
742#endif
743	}
744	if (forcenet)
745		ishost = 0;
746	flags |= RTF_UP;
747	if (ishost)
748		flags |= RTF_HOST;
749	if (iflag == 0)
750		flags |= RTF_GATEWAY;
751	if (so_mask.sin.sin_family == AF_INET) {
752		// make sure the mask is contiguous
753		long i;
754		for (i = 0; i < 32; i++)
755			if (((so_mask.sin.sin_addr.s_addr) & ntohl((1 << i))) != 0)
756				break;
757		for (; i < 32; i++)
758			if (((so_mask.sin.sin_addr.s_addr) & ntohl((1 << i))) == 0)
759				errx(EX_NOHOST, "invalid mask: %s", inet_ntoa(so_mask.sin.sin_addr));
760	}
761	for (attempts = 1; ; attempts++) {
762		errno = 0;
763		if ((ret = rtmsg(*cmd, flags)) == 0)
764			break;
765		if (errno != ENETUNREACH && errno != ESRCH)
766			break;
767		if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) {
768			hp->h_addr_list++;
769			bcopy(hp->h_addr_list[0], &so_gate.sin.sin_addr,
770			    MIN(hp->h_length, sizeof(so_gate.sin.sin_addr)));
771		} else
772			break;
773	}
774	if (*cmd == 'g')
775		exit(0);
776	oerrno = errno;
777	(void) printf("%s %s %s", cmd, ishost? "host" : "net", dest);
778	if (*gateway) {
779		(void) printf(": gateway %s", gateway);
780		if (attempts > 1 && ret == 0 && af == AF_INET)
781		    (void) printf(" (%s)", inet_ntoa(so_gate.sin.sin_addr));
782	}
783	if (ret == 0)
784		(void) printf("\n");
785	else {
786		(void)printf(": %s\n", route_strerror(oerrno));
787	}
788}
789
790void
791inet_makenetandmask(net, sin, bits)
792	in_addr_t net, bits;
793	register struct sockaddr_in *sin;
794{
795	in_addr_t addr, mask = 0;
796	register char *cp;
797
798	rtm_addrs |= RTA_NETMASK;
799	if (bits) {
800		addr = net;
801		mask = 0xffffffff << (32 - bits);
802	} else if (net == 0)
803		mask = addr = 0;
804	else if (net < 128) {
805		addr = net << IN_CLASSA_NSHIFT;
806		mask = IN_CLASSA_NET;
807	} else if (net < 65536) {
808		addr = net << IN_CLASSB_NSHIFT;
809		mask = IN_CLASSB_NET;
810	} else if (net < 16777216L) {
811		addr = net << IN_CLASSC_NSHIFT;
812		mask = IN_CLASSC_NET;
813	} else {
814		addr = net;
815		if ((addr & IN_CLASSA_HOST) == 0)
816			mask =  IN_CLASSA_NET;
817		else if ((addr & IN_CLASSB_HOST) == 0)
818			mask =  IN_CLASSB_NET;
819		else if ((addr & IN_CLASSC_HOST) == 0)
820			mask =  IN_CLASSC_NET;
821		else
822			mask = -1;
823	}
824	sin->sin_addr.s_addr = htonl(addr);
825	sin = &so_mask.sin;
826	sin->sin_addr.s_addr = htonl(mask);
827	sin->sin_len = 0;
828	sin->sin_family = 0;
829	cp = (char *)(&sin->sin_addr + 1);
830	while (*--cp == 0 && cp > (char *)sin)
831		;
832	sin->sin_len = 1 + cp - (char *)sin;
833}
834
835#ifdef INET6
836/*
837 * XXX the function may need more improvement...
838 */
839static int
840inet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen)
841{
842	struct in6_addr in6;
843
844	if (plen == NULL) {
845		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
846		    sin6->sin6_scope_id == 0) {
847			plen = "0";
848		} else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) {
849			/* aggregatable global unicast - RFC2374 */
850			memset(&in6, 0, sizeof(in6));
851			if (!memcmp(&sin6->sin6_addr.s6_addr[8],
852				    &in6.s6_addr[8], 8))
853				plen = "64";
854		}
855	}
856
857	if (plen == NULL || strcmp(plen, "128") == 0)
858		return (1);
859	rtm_addrs |= RTA_NETMASK;
860	prefixlen(plen);
861	return (0);
862}
863#endif
864
865/*
866 * Interpret an argument as a network address of some kind,
867 * returning 1 if a host address, 0 if a network address.
868 */
869int
870getaddr(which, s, hpp)
871	int which;
872	char *s;
873	struct hostent **hpp;
874{
875	register sup su = NULL;
876	struct hostent *hp;
877	struct netent *np;
878	in_addr_t val;
879	char *q;
880	int afamily;  /* local copy of af so we can change it */
881
882	if (af == 0) {
883		af = AF_INET;
884		aflen = sizeof(struct sockaddr_in);
885	}
886	afamily = af;
887	rtm_addrs |= which;
888	switch (which) {
889	case RTA_DST:
890		su = &so_dst;
891		break;
892	case RTA_GATEWAY:
893		su = &so_gate;
894		if (iflag) {
895			struct ifaddrs *ifap, *ifa;
896			struct sockaddr_dl *sdl = NULL;
897
898			if (getifaddrs(&ifap))
899				err(1, "getifaddrs");
900
901			for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
902				if (ifa->ifa_addr->sa_family != AF_LINK)
903					continue;
904
905				if (strcmp(s, ifa->ifa_name))
906					continue;
907
908				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
909			}
910			/* If we found it, then use it */
911			if (sdl) {
912				/*
913				 * Copy is safe since we have a
914				 * sockaddr_storage member in sockunion{}.
915				 * Note that we need to copy before calling
916				 * freeifaddrs().
917				 */
918				memcpy(&su->sdl, sdl, sdl->sdl_len);
919			}
920			freeifaddrs(ifap);
921			if (sdl)
922				return(1);
923		}
924		break;
925	case RTA_NETMASK:
926		su = &so_mask;
927		break;
928	case RTA_GENMASK:
929		su = &so_genmask;
930		break;
931	case RTA_IFP:
932		su = &so_ifp;
933		afamily = AF_LINK;
934		break;
935	case RTA_IFA:
936		su = &so_ifa;
937		break;
938	default:
939		usage("internal error");
940		/*NOTREACHED*/
941	}
942	su->sa.sa_len = aflen;
943	su->sa.sa_family = afamily; /* cases that don't want it have left already */
944	if (strcmp(s, "default") == 0) {
945		/*
946		 * Default is net 0.0.0.0/0
947		 */
948		switch (which) {
949		case RTA_DST:
950			forcenet++;
951			/* bzero(su, sizeof(*su)); *//* for readability */
952			(void) getaddr(RTA_NETMASK, s, 0);
953			break;
954		case RTA_NETMASK:
955		case RTA_GENMASK:
956			/* bzero(su, sizeof(*su)); *//* for readability */
957			su->sa.sa_len = 0;
958			break;
959		}
960		return (0);
961	}
962	switch (afamily) {
963#ifdef INET6
964	case AF_INET6:
965	{
966		struct addrinfo hints, *res;
967		int ecode;
968
969		q = NULL;
970		if (which == RTA_DST && (q = strchr(s, '/')) != NULL)
971			*q = '\0';
972		memset(&hints, 0, sizeof(hints));
973		hints.ai_family = afamily;	/*AF_INET6*/
974		hints.ai_flags = AI_NUMERICHOST;
975		hints.ai_socktype = SOCK_DGRAM;		/*dummy*/
976		ecode = getaddrinfo(s, NULL, &hints, &res);
977		if (ecode != 0 || res->ai_family != AF_INET6 ||
978		    res->ai_addrlen != sizeof(su->sin6)) {
979			(void) fprintf(stderr, "%s: %s\n", s,
980			    gai_strerror(ecode));
981			exit(1);
982		}
983		memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6));
984#ifdef __KAME__
985		if ((IN6_IS_ADDR_LINKLOCAL(&su->sin6.sin6_addr) ||
986		     IN6_IS_ADDR_MC_NODELOCAL(&su->sin6.sin6_addr) ||
987		     IN6_IS_ADDR_MC_LINKLOCAL(&su->sin6.sin6_addr)) &&
988		    su->sin6.sin6_scope_id) {
989			*(u_int16_t *)&su->sin6.sin6_addr.s6_addr[2] =
990				htons(su->sin6.sin6_scope_id);
991			su->sin6.sin6_scope_id = 0;
992		}
993#endif
994		freeaddrinfo(res);
995		if (hints.ai_flags == AI_NUMERICHOST) {
996			if (q != NULL)
997				*q++ = '/';
998			if (which == RTA_DST)
999				return (inet6_makenetandmask(&su->sin6, q));
1000			return (0);
1001		} else {
1002			return (1);
1003		}
1004	}
1005#endif /* INET6 */
1006
1007	case AF_LINK:
1008		link_addr(s, &su->sdl);
1009		return (1);
1010
1011
1012	case PF_ROUTE:
1013		su->sa.sa_len = sizeof(*su);
1014		sockaddr(s, &su->sa);
1015		return (1);
1016
1017	case AF_INET:
1018	default:
1019		break;
1020	}
1021
1022	if (hpp == NULL)
1023		hpp = &hp;
1024	*hpp = NULL;
1025
1026	q = strchr(s,'/');
1027	if (q && which == RTA_DST) {
1028		*q = '\0';
1029		if ((val = inet_addr(s)) != INADDR_NONE) {
1030			inet_makenetandmask(
1031				ntohl(val), &su->sin, strtoul(q+1, 0, 0));
1032			return (0);
1033		}
1034		*q = '/';
1035	}
1036	if ((which != RTA_DST || forcenet == 0) &&
1037	    (val = inet_addr(s)) != INADDR_NONE) {
1038		su->sin.sin_addr.s_addr = val;
1039		if (which != RTA_DST ||
1040		    inet_lnaof(su->sin.sin_addr) != INADDR_ANY)
1041			return (1);
1042		else {
1043			val = ntohl(val);
1044			goto netdone;
1045		}
1046	}
1047	if (which == RTA_DST && forcehost == 0 &&
1048	    ((val = inet_network(s)) != INADDR_NONE ||
1049	    ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0))) {
1050netdone:
1051		inet_makenetandmask(val, &su->sin, 0);
1052		return (0);
1053	}
1054	hp = gethostbyname(s);
1055	if (hp) {
1056		*hpp = hp;
1057		su->sin.sin_family = hp->h_addrtype;
1058		bcopy(hp->h_addr, (char *)&su->sin.sin_addr,
1059		    MIN(hp->h_length, sizeof(su->sin.sin_addr)));
1060		return (1);
1061	}
1062	errx(EX_NOHOST, "bad address: %s", s);
1063}
1064
1065int
1066prefixlen(s)
1067	char *s;
1068{
1069	int len = atoi(s), q, r;
1070	int max;
1071	char *p;
1072
1073	rtm_addrs |= RTA_NETMASK;
1074	switch (af) {
1075#ifdef INET6
1076	case AF_INET6:
1077		max = 128;
1078		p = (char *)&so_mask.sin6.sin6_addr;
1079		break;
1080#endif
1081	case AF_INET:
1082		max = 32;
1083		p = (char *)&so_mask.sin.sin_addr;
1084		break;
1085	default:
1086		(void) fprintf(stderr, "prefixlen not supported in this af\n");
1087		exit(1);
1088		/*NOTREACHED*/
1089	}
1090
1091	if (len < 0 || max < len) {
1092		(void) fprintf(stderr, "%s: bad value\n", s);
1093		exit(1);
1094	}
1095
1096	q = len >> 3;
1097	r = len & 7;
1098	so_mask.sa.sa_family = af;
1099	so_mask.sa.sa_len = aflen;
1100	memset((void *)p, 0, max / 8);
1101	if (q > 0)
1102		memset((void *)p, 0xff, q);
1103	if (r > 0)
1104		*((u_char *)p + q) = (0xff00 >> r) & 0xff;
1105	if (len == max)
1106		return -1;
1107	else
1108		return len;
1109}
1110
1111void
1112interfaces()
1113{
1114	size_t needed;
1115	int mib[6];
1116	char *buf, *lim, *next;
1117	register struct rt_msghdr *rtm;
1118
1119	mib[0] = CTL_NET;
1120	mib[1] = PF_ROUTE;
1121	mib[2] = 0;		/* protocol */
1122	mib[3] = 0;		/* wildcard address family */
1123	mib[4] = NET_RT_IFLIST;
1124	mib[5] = 0;		/* no flags */
1125	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
1126		err(EX_OSERR, "route-sysctl-estimate");
1127	if ((buf = malloc(needed)) == NULL)
1128		errx(EX_OSERR, "malloc failed");
1129	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
1130		err(EX_OSERR, "actual retrieval of interface table");
1131	lim = buf + needed;
1132	for (next = buf; next < lim; next += rtm->rtm_msglen) {
1133		rtm = (struct rt_msghdr *)next;
1134		print_rtmsg(rtm, rtm->rtm_msglen);
1135	}
1136}
1137
1138void
1139monitor()
1140{
1141	int n;
1142	char msg[2048];
1143
1144	verbose = 1;
1145	if (debugonly) {
1146		interfaces();
1147		exit(0);
1148	}
1149	for(;;) {
1150		time_t now;
1151		n = read(s, msg, 2048);
1152		now = time(NULL);
1153		(void) printf("\ngot message of size %d on %s", n, ctime(&now));
1154		print_rtmsg((struct rt_msghdr *)msg, n);
1155	}
1156}
1157
1158struct {
1159	struct	rt_msghdr m_rtm;
1160	char	m_space[512];
1161} m_rtmsg;
1162
1163int
1164rtmsg(cmd, flags)
1165	int cmd, flags;
1166{
1167	static int seq;
1168	int rlen;
1169	register char *cp = m_rtmsg.m_space;
1170	register int l;
1171
1172#define NEXTADDR(w, u) \
1173	if (rtm_addrs & (w)) {\
1174	    l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\
1175	    if (verbose) sodump(&(u),"u");\
1176	}
1177
1178	errno = 0;
1179	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
1180	if (cmd == 'a')
1181		cmd = RTM_ADD;
1182	else if (cmd == 'c')
1183		cmd = RTM_CHANGE;
1184	else if (cmd == 'g') {
1185		cmd = RTM_GET;
1186		if (so_ifp.sa.sa_family == 0) {
1187			so_ifp.sa.sa_family = AF_LINK;
1188			so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
1189			rtm_addrs |= RTA_IFP;
1190		}
1191	} else
1192		cmd = RTM_DELETE;
1193#define rtm m_rtmsg.m_rtm
1194	rtm.rtm_type = cmd;
1195	rtm.rtm_flags = flags;
1196	rtm.rtm_version = RTM_VERSION;
1197	rtm.rtm_seq = ++seq;
1198	rtm.rtm_addrs = rtm_addrs;
1199	rtm.rtm_rmx = rt_metrics;
1200	rtm.rtm_inits = rtm_inits;
1201	rtm.rtm_index = ifscope;
1202
1203	if (rtm_addrs & RTA_NETMASK)
1204		mask_addr();
1205	NEXTADDR(RTA_DST, so_dst);
1206	NEXTADDR(RTA_GATEWAY, so_gate);
1207	NEXTADDR(RTA_NETMASK, so_mask);
1208	NEXTADDR(RTA_GENMASK, so_genmask);
1209	NEXTADDR(RTA_IFP, so_ifp);
1210	NEXTADDR(RTA_IFA, so_ifa);
1211	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
1212	if (verbose)
1213		print_rtmsg(&rtm, l);
1214	if (debugonly)
1215		return (0);
1216	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
1217		warnx("writing to routing socket: %s", route_strerror(errno));
1218		return (-1);
1219	}
1220	if (cmd == RTM_GET) {
1221		do {
1222			l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
1223		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
1224		if (l < 0)
1225			warn("read from routing socket");
1226		else
1227			print_getmsg(&rtm, l);
1228	}
1229#undef rtm
1230	return (0);
1231}
1232
1233void
1234mask_addr()
1235{
1236	int olen = so_mask.sa.sa_len;
1237	register char *cp1 = olen + (char *)&so_mask, *cp2;
1238
1239	for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; )
1240		if (*--cp1 != 0) {
1241			so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask;
1242			break;
1243		}
1244	if ((rtm_addrs & RTA_DST) == 0)
1245		return;
1246	switch (so_dst.sa.sa_family) {
1247	case AF_INET:
1248#ifdef INET6
1249	case AF_INET6:
1250#endif
1251	case AF_APPLETALK:
1252	case 0:
1253		return;
1254	}
1255	cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst;
1256	cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst;
1257	while (cp2 > cp1)
1258		*--cp2 = 0;
1259	cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask;
1260	while (cp1 > so_dst.sa.sa_data)
1261		*--cp1 &= *--cp2;
1262}
1263
1264char *msgtypes[] = {
1265	"",
1266	"RTM_ADD: Add Route",
1267	"RTM_DELETE: Delete Route",
1268	"RTM_CHANGE: Change Metrics or flags",
1269	"RTM_GET: Report Metrics",
1270	"RTM_LOSING: Kernel Suspects Partitioning",
1271	"RTM_REDIRECT: Told to use different route",
1272	"RTM_MISS: Lookup failed on this address",
1273	"RTM_LOCK: fix specified metrics",
1274	"RTM_OLDADD: caused by SIOCADDRT",
1275	"RTM_OLDDEL: caused by SIOCDELRT",
1276	"RTM_RESOLVE: Route created by cloning",
1277	"RTM_NEWADDR: address being added to iface",
1278	"RTM_DELADDR: address being removed from iface",
1279	"RTM_IFINFO: iface status change",
1280	"RTM_NEWMADDR: new multicast group membership on iface",
1281	"RTM_DELMADDR: multicast group membership removed from iface",
1282	0,
1283};
1284
1285char metricnames[] =
1286"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
1287"\1mtu";
1288char routeflags[] =
1289"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010DELCLONE"
1290"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
1291"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024b024"
1292"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\031IFSCOPE\032CONDEMNED"
1293"\033IFREF\034PROXY\035ROUTER";
1294char ifnetflags[] =
1295"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
1296"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
1297"\017LINK2\020MULTICAST";
1298char addrnames[] =
1299"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
1300
1301void
1302print_rtmsg(rtm, msglen)
1303	register struct rt_msghdr *rtm;
1304	int msglen;
1305{
1306	struct if_msghdr *ifm;
1307	struct ifa_msghdr *ifam;
1308#ifdef RTM_NEWMADDR
1309	struct ifma_msghdr *ifmam;
1310#endif
1311
1312	if (verbose == 0)
1313		return;
1314	if (rtm->rtm_version != RTM_VERSION) {
1315		(void) printf("routing message version %d not understood\n",
1316		    rtm->rtm_version);
1317		return;
1318	}
1319	(void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen);
1320	switch (rtm->rtm_type) {
1321	case RTM_IFINFO:
1322		ifm = (struct if_msghdr *)rtm;
1323		(void) printf("if# %d, flags:", ifm->ifm_index);
1324		bprintf(stdout, ifm->ifm_flags, ifnetflags);
1325		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
1326		break;
1327	case RTM_NEWADDR:
1328	case RTM_DELADDR:
1329		ifam = (struct ifa_msghdr *)rtm;
1330		(void) printf("metric %d, flags:", ifam->ifam_metric);
1331		bprintf(stdout, ifam->ifam_flags, routeflags);
1332		pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs);
1333		break;
1334#ifdef RTM_NEWMADDR
1335	case RTM_NEWMADDR:
1336	case RTM_DELMADDR:
1337		ifmam = (struct ifma_msghdr *)rtm;
1338		pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs);
1339		break;
1340#endif
1341	default:
1342		(void) printf("pid: %ld, seq %d, errno %d, ",
1343			(long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
1344		if (rtm->rtm_flags & RTF_IFSCOPE)
1345			(void) printf("ifscope %d, ", rtm->rtm_index);
1346			if (rtm->rtm_flags & RTF_IFREF)
1347			(void) printf("ifref, ");
1348			(void) printf("flags:");
1349		bprintf(stdout, rtm->rtm_flags, routeflags);
1350		pmsg_common(rtm);
1351	}
1352}
1353
1354void
1355print_getmsg(rtm, msglen)
1356	register struct rt_msghdr *rtm;
1357	int msglen;
1358{
1359	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
1360	struct sockaddr_dl *ifp = NULL;
1361	register struct sockaddr *sa;
1362	register char *cp;
1363	register int i;
1364
1365	(void) printf("   route to: %s\n", routename(&so_dst.sa));
1366	if (rtm->rtm_version != RTM_VERSION) {
1367		warnx("routing message version %d not understood",
1368		     rtm->rtm_version);
1369		return;
1370	}
1371	if (rtm->rtm_msglen > msglen) {
1372		warnx("message length mismatch, in packet %d, returned %d",
1373		      rtm->rtm_msglen, msglen);
1374	}
1375	if (rtm->rtm_errno)  {
1376		errno = rtm->rtm_errno;
1377		warn("message indicates error %d", errno);
1378		return;
1379	}
1380	cp = ((char *)(rtm + 1));
1381	if (rtm->rtm_addrs)
1382		for (i = 1; i; i <<= 1)
1383			if (i & rtm->rtm_addrs) {
1384				sa = (struct sockaddr *)cp;
1385				switch (i) {
1386				case RTA_DST:
1387					dst = sa;
1388					break;
1389				case RTA_GATEWAY:
1390					gate = sa;
1391					break;
1392				case RTA_NETMASK:
1393					mask = sa;
1394					break;
1395				case RTA_IFP:
1396					if (sa->sa_family == AF_LINK &&
1397					   ((struct sockaddr_dl *)sa)->sdl_nlen)
1398						ifp = (struct sockaddr_dl *)sa;
1399					break;
1400				}
1401				ADVANCE(cp, sa);
1402			}
1403	if (dst && mask)
1404		mask->sa_family = dst->sa_family;	/* XXX */
1405	if (dst)
1406		(void)printf("destination: %s\n", routename(dst));
1407	if (mask) {
1408		int savenflag = nflag;
1409
1410		nflag = 1;
1411		(void)printf("       mask: %s\n", routename(mask));
1412		nflag = savenflag;
1413	}
1414	if (gate && rtm->rtm_flags & RTF_GATEWAY)
1415		(void)printf("    gateway: %s\n", routename(gate));
1416	if (ifp)
1417		(void)printf("  interface: %.*s\n",
1418		    ifp->sdl_nlen, ifp->sdl_data);
1419	(void)printf("      flags: ");
1420	bprintf(stdout, rtm->rtm_flags, routeflags);
1421
1422#define lock(f)	((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ')
1423#define msec(u)	(((u) + 500) / 1000)		/* usec to msec */
1424
1425	(void) printf("\n%s\n", "\
1426 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire");
1427	printf("%8u%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
1428	printf("%8u%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
1429	printf("%8u%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
1430	printf("%8u%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
1431	printf("%8u%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
1432	printf("%8u%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
1433	printf("%8u%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
1434	if (rtm->rtm_rmx.rmx_expire)
1435		rtm->rtm_rmx.rmx_expire -= time(0);
1436	printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
1437#undef lock
1438#undef msec
1439#define	RTA_IGN	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD)
1440	if (verbose)
1441		pmsg_common(rtm);
1442	else if (rtm->rtm_addrs &~ RTA_IGN) {
1443		(void) printf("sockaddrs: ");
1444		bprintf(stdout, rtm->rtm_addrs, addrnames);
1445		putchar('\n');
1446	}
1447#undef	RTA_IGN
1448}
1449
1450void
1451pmsg_common(rtm)
1452	register struct rt_msghdr *rtm;
1453{
1454	(void) printf("\nlocks: ");
1455	bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames);
1456	(void) printf(" inits: ");
1457	bprintf(stdout, rtm->rtm_inits, metricnames);
1458	pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs);
1459}
1460
1461void
1462pmsg_addrs(cp, addrs)
1463	char	*cp;
1464	int	addrs;
1465{
1466	register struct sockaddr *sa;
1467	int i;
1468
1469	if (addrs == 0) {
1470		(void) putchar('\n');
1471		return;
1472	}
1473	(void) printf("\nsockaddrs: ");
1474	bprintf(stdout, addrs, addrnames);
1475	(void) putchar('\n');
1476	for (i = 1; i; i <<= 1)
1477		if (i & addrs) {
1478			sa = (struct sockaddr *)cp;
1479			(void) printf(" %s", routename(sa));
1480			ADVANCE(cp, sa);
1481		}
1482	(void) putchar('\n');
1483	(void) fflush(stdout);
1484}
1485
1486void
1487bprintf(fp, b, s)
1488	register FILE *fp;
1489	register int b;
1490	register u_char *s;
1491{
1492	register int i;
1493	int gotsome = 0;
1494
1495	if (b == 0)
1496		return;
1497	while ((i = *s++) != 0) {
1498		if (b & (1 << (i-1))) {
1499			if (gotsome == 0)
1500				i = '<';
1501			else
1502				i = ',';
1503			(void) putc(i, fp);
1504			gotsome = 1;
1505			for (; (i = *s) > 32; s++)
1506				(void) putc(i, fp);
1507		} else
1508			while (*s > 32)
1509				s++;
1510	}
1511	if (gotsome)
1512		(void) putc('>', fp);
1513}
1514
1515int
1516keyword(cp)
1517	char *cp;
1518{
1519	register struct keytab *kt = keywords;
1520
1521	while (kt->kt_cp && strcmp(kt->kt_cp, cp))
1522		kt++;
1523	return kt->kt_i;
1524}
1525
1526void
1527sodump(su, which)
1528	register sup su;
1529	char *which;
1530{
1531	switch (su->sa.sa_family) {
1532	case AF_LINK:
1533		(void) printf("%s: link %s; ",
1534		    which, link_ntoa(&su->sdl));
1535		break;
1536	case AF_INET:
1537		(void) printf("%s: inet %s; ",
1538		    which, inet_ntoa(su->sin.sin_addr));
1539		break;
1540	}
1541	(void) fflush(stdout);
1542}
1543
1544/* States*/
1545#define VIRGIN	0
1546#define GOTONE	1
1547#define GOTTWO	2
1548/* Inputs */
1549#define	DIGIT	(4*0)
1550#define	END	(4*1)
1551#define DELIM	(4*2)
1552
1553void
1554sockaddr(addr, sa)
1555	register char *addr;
1556	register struct sockaddr *sa;
1557{
1558	register char *cp = (char *)sa;
1559	int size = sa->sa_len;
1560	char *cplim = cp + size;
1561	register int byte = 0, state = VIRGIN, new = 0 /* foil gcc */;
1562
1563	bzero(cp, size);
1564	cp++;
1565	do {
1566		if ((*addr >= '0') && (*addr <= '9')) {
1567			new = *addr - '0';
1568		} else if ((*addr >= 'a') && (*addr <= 'f')) {
1569			new = *addr - 'a' + 10;
1570		} else if ((*addr >= 'A') && (*addr <= 'F')) {
1571			new = *addr - 'A' + 10;
1572		} else if (*addr == 0)
1573			state |= END;
1574		else
1575			state |= DELIM;
1576		addr++;
1577		switch (state /* | INPUT */) {
1578		case GOTTWO | DIGIT:
1579			*cp++ = byte; /*FALLTHROUGH*/
1580		case VIRGIN | DIGIT:
1581			state = GOTONE; byte = new; continue;
1582		case GOTONE | DIGIT:
1583			state = GOTTWO; byte = new + (byte << 4); continue;
1584		default: /* | DELIM */
1585			state = VIRGIN; *cp++ = byte; byte = 0; continue;
1586		case GOTONE | END:
1587		case GOTTWO | END:
1588			*cp++ = byte; /* FALLTHROUGH */
1589		case VIRGIN | END:
1590			break;
1591		}
1592		break;
1593	} while (cp < cplim);
1594	sa->sa_len = cp - (char *)sa;
1595}
1596