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