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