xref: /freebsd/usr.bin/netstat/route.c (revision 98e0ffaefb0f241cda3a72395d3be04192ae0d47)
1 /*-
2  * Copyright (c) 1983, 1988, 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 #if 0
31 #ifndef lint
32 static char sccsid[] = "From: @(#)route.c	8.6 (Berkeley) 4/28/95";
33 #endif /* not lint */
34 #endif
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/param.h>
40 #include <sys/protosw.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43 #include <sys/sysctl.h>
44 #include <sys/time.h>
45 
46 #include <net/ethernet.h>
47 #include <net/if.h>
48 #include <net/if_dl.h>
49 #include <net/if_types.h>
50 #include <net/route.h>
51 
52 #include <netinet/in.h>
53 #include <netgraph/ng_socket.h>
54 
55 #include <arpa/inet.h>
56 #include <ifaddrs.h>
57 #include <libutil.h>
58 #include <netdb.h>
59 #include <nlist.h>
60 #include <stdint.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <stdbool.h>
64 #include <string.h>
65 #include <sysexits.h>
66 #include <unistd.h>
67 #include <err.h>
68 #include <libxo/xo.h>
69 #include "netstat.h"
70 
71 /*
72  * Definitions for showing gateway flags.
73  */
74 struct bits {
75 	u_long	b_mask;
76 	char	b_val;
77 	const char *b_name;
78 } bits[] = {
79 	{ RTF_UP,	'U', "up" },
80 	{ RTF_GATEWAY,	'G', "gateway" },
81 	{ RTF_HOST,	'H', "host" },
82 	{ RTF_REJECT,	'R', "reject" },
83 	{ RTF_DYNAMIC,	'D', "dynamic" },
84 	{ RTF_MODIFIED,	'M', "modified" },
85 	{ RTF_DONE,	'd', "done" }, /* Completed -- for routing msgs only */
86 	{ RTF_XRESOLVE,	'X', "xresolve" },
87 	{ RTF_STATIC,	'S', "static" },
88 	{ RTF_PROTO1,	'1', "proto1" },
89 	{ RTF_PROTO2,	'2', "proto2" },
90 	{ RTF_PROTO3,	'3', "proto3" },
91 	{ RTF_BLACKHOLE,'B', "blackhole" },
92 	{ RTF_BROADCAST,'b', "broadcast" },
93 #ifdef RTF_LLINFO
94 	{ RTF_LLINFO,	'L', "llinfo" },
95 #endif
96 	{ 0 , 0, NULL }
97 };
98 
99 /*
100  * kvm(3) bindings for every needed symbol
101  */
102 static struct nlist rl[] = {
103 #define	N_RTSTAT	0
104 	{ .n_name = "_rtstat" },
105 #define	N_RTTRASH	1
106 	{ .n_name = "_rttrash" },
107 	{ .n_name = NULL },
108 };
109 
110 typedef union {
111 	long	dummy;		/* Helps align structure. */
112 	struct	sockaddr u_sa;
113 	u_short	u_data[128];
114 } sa_u;
115 
116 struct ifmap_entry {
117 	char ifname[IFNAMSIZ];
118 };
119 static struct ifmap_entry *ifmap;
120 static int ifmap_size;
121 struct	timespec uptime;
122 
123 static void p_rtable_sysctl(int, int);
124 static void p_rtentry_sysctl(const char *name, struct rt_msghdr *);
125 static void p_sockaddr(const char *name, struct sockaddr *, struct sockaddr *,
126     int, int);
127 static const char *fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask,
128     int flags);
129 static void p_flags(int, const char *);
130 static const char *fmt_flags(int f);
131 static void domask(char *, in_addr_t, u_long);
132 
133 /*
134  * Print routing tables.
135  */
136 void
137 routepr(int fibnum, int af)
138 {
139 	size_t intsize;
140 	int numfibs;
141 
142 	if (live == 0)
143 		return;
144 
145 	intsize = sizeof(int);
146 	if (fibnum == -1 &&
147 	    sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1)
148 		fibnum = 0;
149 	if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
150 		numfibs = 1;
151 	if (fibnum < 0 || fibnum > numfibs - 1)
152 		errx(EX_USAGE, "%d: invalid fib", fibnum);
153 	/*
154 	 * Since kernel & userland use different timebase
155 	 * (time_uptime vs time_second) and we are reading kernel memory
156 	 * directly we should do rt_expire --> expire_time conversion.
157 	 */
158 	if (clock_gettime(CLOCK_UPTIME, &uptime) < 0)
159 		err(EX_OSERR, "clock_gettime() failed");
160 
161 	xo_open_container("route-information");
162 	xo_emit("{T:Routing tables}");
163 	if (fibnum)
164 		xo_emit(" ({L:fib}: {:fib/%d})", fibnum);
165 	xo_emit("\n");
166 	p_rtable_sysctl(fibnum, af);
167 	xo_close_container("route-information");
168 }
169 
170 
171 /*
172  * Print address family header before a section of the routing table.
173  */
174 void
175 pr_family(int af1)
176 {
177 	const char *afname;
178 
179 	switch (af1) {
180 	case AF_INET:
181 		afname = "Internet";
182 		break;
183 #ifdef INET6
184 	case AF_INET6:
185 		afname = "Internet6";
186 		break;
187 #endif /*INET6*/
188 	case AF_ISO:
189 		afname = "ISO";
190 		break;
191 	case AF_CCITT:
192 		afname = "X.25";
193 		break;
194 	case AF_NETGRAPH:
195 		afname = "Netgraph";
196 		break;
197 	default:
198 		afname = NULL;
199 		break;
200 	}
201 	if (afname)
202 		xo_emit("\n{k:address-family/%s}:\n", afname);
203 	else
204 		xo_emit("\n{L:Protocol Family} {k:address-family/%d}:\n", af1);
205 }
206 
207 /* column widths; each followed by one space */
208 #ifndef INET6
209 #define	WID_DST_DEFAULT(af) 	18	/* width of destination column */
210 #define	WID_GW_DEFAULT(af)	18	/* width of gateway column */
211 #define	WID_IF_DEFAULT(af)	(Wflag ? 10 : 8) /* width of netif column */
212 #else
213 #define	WID_DST_DEFAULT(af) \
214 	((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18)
215 #define	WID_GW_DEFAULT(af) \
216 	((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18)
217 #define	WID_IF_DEFAULT(af)	((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8))
218 #endif /*INET6*/
219 
220 static int wid_dst;
221 static int wid_gw;
222 static int wid_flags;
223 static int wid_pksent;
224 static int wid_mtu;
225 static int wid_if;
226 static int wid_expire;
227 
228 /*
229  * Print header for routing table columns.
230  */
231 void
232 pr_rthdr(int af1)
233 {
234 
235 	if (Aflag)
236 		xo_emit("{T:/%-8.8s} ","Address");
237 	if (Wflag) {
238 		xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
239 		    "{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n",
240 			wid_dst,	wid_dst,	"Destination",
241 			wid_gw,		wid_gw,		"Gateway",
242 			wid_flags,	wid_flags,	"Flags",
243 			wid_pksent,	wid_pksent,	"Use",
244 			wid_mtu,	wid_mtu,	"Mtu",
245 			wid_if,		wid_if,		"Netif",
246 			wid_expire,			"Expire");
247 	} else {
248 		xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
249 		    "{T:/%*s}\n",
250 			wid_dst,	wid_dst,	"Destination",
251 			wid_gw,		wid_gw,		"Gateway",
252 			wid_flags,	wid_flags,	"Flags",
253 			wid_if,		wid_if,		"Netif",
254 			wid_expire,			"Expire");
255 	}
256 }
257 
258 static void
259 p_rtable_sysctl(int fibnum, int af)
260 {
261 	size_t needed;
262 	int mib[7];
263 	char *buf, *next, *lim;
264 	struct rt_msghdr *rtm;
265 	struct sockaddr *sa;
266 	int fam = AF_UNSPEC, ifindex = 0, size;
267 	int need_table_close = false;
268 
269 	struct ifaddrs *ifap, *ifa;
270 	struct sockaddr_dl *sdl;
271 
272 	/*
273 	 * Retrieve interface list at first
274 	 * since we need #ifindex -> if_xname match
275 	 */
276 	if (getifaddrs(&ifap) != 0)
277 		err(EX_OSERR, "getifaddrs");
278 
279 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
280 
281 		if (ifa->ifa_addr->sa_family != AF_LINK)
282 			continue;
283 
284 		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
285 		ifindex = sdl->sdl_index;
286 
287 		if (ifindex >= ifmap_size) {
288 			size = roundup(ifindex + 1, 32) *
289 			    sizeof(struct ifmap_entry);
290 			if ((ifmap = realloc(ifmap, size)) == NULL)
291 				errx(2, "realloc(%d) failed", size);
292 			memset(&ifmap[ifmap_size], 0,
293 			    size - ifmap_size *
294 			    sizeof(struct ifmap_entry));
295 
296 			ifmap_size = roundup(ifindex + 1, 32);
297 		}
298 
299 		if (*ifmap[ifindex].ifname != '\0')
300 			continue;
301 
302 		strlcpy(ifmap[ifindex].ifname, ifa->ifa_name, IFNAMSIZ);
303 	}
304 
305 	freeifaddrs(ifap);
306 
307 	mib[0] = CTL_NET;
308 	mib[1] = PF_ROUTE;
309 	mib[2] = 0;
310 	mib[3] = af;
311 	mib[4] = NET_RT_DUMP;
312 	mib[5] = 0;
313 	mib[6] = fibnum;
314 	if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0)
315 		err(EX_OSERR, "sysctl: net.route.0.%d.dump.%d estimate", af,
316 		    fibnum);
317 	if ((buf = malloc(needed)) == NULL)
318 		errx(2, "malloc(%lu)", (unsigned long)needed);
319 	if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0)
320 		err(1, "sysctl: net.route.0.%d.dump.%d", af, fibnum);
321 	lim  = buf + needed;
322 	xo_open_container("route-table");
323 	xo_open_list("rt-family");
324 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
325 		rtm = (struct rt_msghdr *)next;
326 		if (rtm->rtm_version != RTM_VERSION)
327 			continue;
328 		/*
329 		 * Peek inside header to determine AF
330 		 */
331 		sa = (struct sockaddr *)(rtm + 1);
332 		/* Only print family first time. */
333 		if (fam != sa->sa_family) {
334 			if (need_table_close) {
335 				xo_close_list("rt-entry");
336 				xo_close_instance("rt-family");
337 			}
338 			need_table_close = true;
339 
340 			fam = sa->sa_family;
341 			wid_dst = WID_DST_DEFAULT(fam);
342 			wid_gw = WID_GW_DEFAULT(fam);
343 			wid_flags = 6;
344 			wid_pksent = 8;
345 			wid_mtu = 6;
346 			wid_if = WID_IF_DEFAULT(fam);
347 			wid_expire = 6;
348 			xo_open_instance("rt-family");
349 			pr_family(fam);
350 			xo_open_list("rt-entry");
351 
352 			pr_rthdr(fam);
353 		}
354 		p_rtentry_sysctl("rt-entry", rtm);
355 	}
356 	if (need_table_close) {
357 		xo_close_list("rt-entry");
358 		xo_close_instance("rt-family");
359 	}
360 	xo_close_list("rt-family");
361 	xo_close_container("route-table");
362 	free(buf);
363 }
364 
365 static void
366 p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm)
367 {
368 	struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
369 	char buffer[128];
370 	char prettyname[128];
371 	sa_u addr, mask, gw;
372 	unsigned int l;
373 
374 	xo_open_instance(name);
375 
376 #define	GETSA(_s, _f)	{ \
377 	bzero(&(_s), sizeof(_s)); \
378 	if (rtm->rtm_addrs & _f) { \
379 		l = roundup(sa->sa_len, sizeof(long)); \
380 		memcpy(&(_s), sa, (l > sizeof(_s)) ? sizeof(_s) : l); \
381 		sa = (struct sockaddr *)((char *)sa + l); \
382 	} \
383 }
384 
385 	GETSA(addr, RTA_DST);
386 	GETSA(gw, RTA_GATEWAY);
387 	GETSA(mask, RTA_NETMASK);
388 
389 	p_sockaddr("destination", &addr.u_sa, &mask.u_sa, rtm->rtm_flags,
390 	    wid_dst);
391 	p_sockaddr("gateway", &gw.u_sa, NULL, RTF_HOST, wid_gw);
392 	snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ",
393 	    wid_flags);
394 	p_flags(rtm->rtm_flags, buffer);
395 	if (Wflag) {
396 		xo_emit("{t:use/%*lu} ", wid_pksent, rtm->rtm_rmx.rmx_pksent);
397 
398 		if (rtm->rtm_rmx.rmx_mtu != 0)
399 			xo_emit("{t:mtu/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_mtu);
400 		else
401 			xo_emit("{P:/%*s} ", wid_mtu, "");
402 	}
403 
404 	memset(prettyname, 0, sizeof(prettyname));
405 	if (rtm->rtm_index < ifmap_size) {
406 		strlcpy(prettyname, ifmap[rtm->rtm_index].ifname,
407 		    sizeof(prettyname));
408 		if (*prettyname == '\0')
409 			strlcpy(prettyname, "---", sizeof(prettyname));
410 	}
411 
412 	xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if, prettyname);
413 	if (rtm->rtm_rmx.rmx_expire) {
414 		time_t expire_time;
415 
416 		if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0)
417 			xo_emit(" {:expire-time/%*d}", wid_expire,
418 			    (int)expire_time);
419 	}
420 
421 	xo_emit("\n");
422 	xo_close_instance(name);
423 }
424 
425 static void
426 p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask,
427     int flags, int width)
428 {
429 	const char *cp;
430 	char buf[128];
431 
432 	cp = fmt_sockaddr(sa, mask, flags);
433 
434 	if (width < 0) {
435 		snprintf(buf, sizeof(buf), "{:%s/%%s} ", name);
436 		xo_emit(buf, cp);
437 	} else {
438 		if (numeric_addr) {
439 			snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ",
440 			    -width, name);
441 			xo_emit(buf, cp);
442 		} else {
443 			snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ",
444 			    -width, name);
445 			xo_emit(buf, width, cp);
446 		}
447 	}
448 }
449 
450 static const char *
451 fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags)
452 {
453 	static char workbuf[128];
454 	const char *cp;
455 
456 	if (sa == NULL)
457 		return ("null");
458 
459 	switch(sa->sa_family) {
460 	case AF_INET:
461 	    {
462 		struct sockaddr_in *sockin = (struct sockaddr_in *)sa;
463 
464 		if ((sockin->sin_addr.s_addr == INADDR_ANY) &&
465 			mask &&
466 			ntohl(((struct sockaddr_in *)mask)->sin_addr.s_addr)
467 				==0L)
468 				cp = "default" ;
469 		else if (flags & RTF_HOST)
470 			cp = routename(sockin->sin_addr.s_addr);
471 		else if (mask)
472 			cp = netname(sockin->sin_addr.s_addr,
473 			    ((struct sockaddr_in *)mask)->sin_addr.s_addr);
474 		else
475 			cp = netname(sockin->sin_addr.s_addr, INADDR_ANY);
476 		break;
477 	    }
478 
479 #ifdef INET6
480 	case AF_INET6:
481 	    {
482 		struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
483 
484 		/*
485 		 * The sa6->sin6_scope_id must be filled here because
486 		 * this sockaddr is extracted from kmem(4) directly
487 		 * and has KAME-specific embedded scope id in
488 		 * sa6->sin6_addr.s6_addr[2].
489 		 */
490 		in6_fillscopeid(sa6);
491 
492 		if (flags & RTF_HOST)
493 		    cp = routename6(sa6);
494 		else if (mask)
495 		    cp = netname6(sa6,
496 				  &((struct sockaddr_in6 *)mask)->sin6_addr);
497 		else {
498 		    cp = netname6(sa6, NULL);
499 		}
500 		break;
501 	    }
502 #endif /*INET6*/
503 
504 	case AF_NETGRAPH:
505 	    {
506 		strlcpy(workbuf, ((struct sockaddr_ng *)sa)->sg_data,
507 		    sizeof(workbuf));
508 		cp = workbuf;
509 		break;
510 	    }
511 
512 	case AF_LINK:
513 	    {
514 		struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
515 
516 		if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
517 		    sdl->sdl_slen == 0) {
518 			(void) sprintf(workbuf, "link#%d", sdl->sdl_index);
519 			cp = workbuf;
520 		} else
521 			switch (sdl->sdl_type) {
522 
523 			case IFT_ETHER:
524 			case IFT_L2VLAN:
525 			case IFT_BRIDGE:
526 				if (sdl->sdl_alen == ETHER_ADDR_LEN) {
527 					cp = ether_ntoa((struct ether_addr *)
528 					    (sdl->sdl_data + sdl->sdl_nlen));
529 					break;
530 				}
531 				/* FALLTHROUGH */
532 			default:
533 				cp = link_ntoa(sdl);
534 				break;
535 			}
536 		break;
537 	    }
538 
539 	default:
540 	    {
541 		u_char *s = (u_char *)sa->sa_data, *slim;
542 		char *cq, *cqlim;
543 
544 		cq = workbuf;
545 		slim =  sa->sa_len + (u_char *) sa;
546 		cqlim = cq + sizeof(workbuf) - 6;
547 		cq += sprintf(cq, "(%d)", sa->sa_family);
548 		while (s < slim && cq < cqlim) {
549 			cq += sprintf(cq, " %02x", *s++);
550 			if (s < slim)
551 			    cq += sprintf(cq, "%02x", *s++);
552 		}
553 		cp = workbuf;
554 	    }
555 	}
556 
557 	return (cp);
558 }
559 
560 static void
561 p_flags(int f, const char *format)
562 {
563 	struct bits *p;
564 
565 	xo_emit(format, fmt_flags(f));
566 
567 	xo_open_list("flags_pretty");
568 	for (p = bits; p->b_mask; p++)
569 		if (p->b_mask & f)
570 			xo_emit("{le:flags_pretty/%s}", p->b_name);
571 	xo_close_list("flags_pretty");
572 }
573 
574 static const char *
575 fmt_flags(int f)
576 {
577 	static char name[33];
578 	char *flags;
579 	struct bits *p = bits;
580 
581 	for (flags = name; p->b_mask; p++)
582 		if (p->b_mask & f)
583 			*flags++ = p->b_val;
584 	*flags = '\0';
585 	return (name);
586 }
587 
588 char *
589 routename(in_addr_t in)
590 {
591 	char *cp;
592 	static char line[MAXHOSTNAMELEN];
593 	struct hostent *hp;
594 
595 	cp = 0;
596 	if (!numeric_addr) {
597 		hp = gethostbyaddr(&in, sizeof (struct in_addr), AF_INET);
598 		if (hp) {
599 			cp = hp->h_name;
600 			trimdomain(cp, strlen(cp));
601 		}
602 	}
603 	if (cp) {
604 		strlcpy(line, cp, sizeof(line));
605 	} else {
606 #define	C(x)	((x) & 0xff)
607 		in = ntohl(in);
608 		sprintf(line, "%u.%u.%u.%u",
609 		    C(in >> 24), C(in >> 16), C(in >> 8), C(in));
610 	}
611 	return (line);
612 }
613 
614 #define	NSHIFT(m) (							\
615 	(m) == IN_CLASSA_NET ? IN_CLASSA_NSHIFT :			\
616 	(m) == IN_CLASSB_NET ? IN_CLASSB_NSHIFT :			\
617 	(m) == IN_CLASSC_NET ? IN_CLASSC_NSHIFT :			\
618 	0)
619 
620 static void
621 domask(char *dst, in_addr_t addr __unused, u_long mask)
622 {
623 	int b, i;
624 
625 	if (mask == 0 || (!numeric_addr && NSHIFT(mask) != 0)) {
626 		*dst = '\0';
627 		return;
628 	}
629 	i = 0;
630 	for (b = 0; b < 32; b++)
631 		if (mask & (1 << b)) {
632 			int bb;
633 
634 			i = b;
635 			for (bb = b+1; bb < 32; bb++)
636 				if (!(mask & (1 << bb))) {
637 					i = -1;	/* noncontig */
638 					break;
639 				}
640 			break;
641 		}
642 	if (i == -1)
643 		sprintf(dst, "&0x%lx", mask);
644 	else
645 		sprintf(dst, "/%d", 32-i);
646 }
647 
648 /*
649  * Return the name of the network whose address is given.
650  */
651 char *
652 netname(in_addr_t in, in_addr_t mask)
653 {
654 	char *cp = 0;
655 	static char line[MAXHOSTNAMELEN];
656 	struct netent *np = 0;
657 	in_addr_t i;
658 
659 	/* It is ok to supply host address. */
660 	in &= mask;
661 
662 	i = ntohl(in);
663 	if (!numeric_addr && i) {
664 		np = getnetbyaddr(i >> NSHIFT(ntohl(mask)), AF_INET);
665 		if (np != NULL) {
666 			cp = np->n_name;
667 			trimdomain(cp, strlen(cp));
668 		}
669 	}
670 	if (cp != NULL) {
671 		strlcpy(line, cp, sizeof(line));
672 	} else {
673 		inet_ntop(AF_INET, &in, line, sizeof(line) - 1);
674 	}
675 	domask(line + strlen(line), i, ntohl(mask));
676 	return (line);
677 }
678 
679 #undef NSHIFT
680 
681 #ifdef INET6
682 void
683 in6_fillscopeid(struct sockaddr_in6 *sa6)
684 {
685 #if defined(__KAME__)
686 	/*
687 	 * XXX: This is a special workaround for KAME kernels.
688 	 * sin6_scope_id field of SA should be set in the future.
689 	 */
690 	if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) ||
691 	    IN6_IS_ADDR_MC_NODELOCAL(&sa6->sin6_addr) ||
692 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) {
693 		if (sa6->sin6_scope_id == 0)
694 			sa6->sin6_scope_id =
695 			    ntohs(*(u_int16_t *)&sa6->sin6_addr.s6_addr[2]);
696 		sa6->sin6_addr.s6_addr[2] = sa6->sin6_addr.s6_addr[3] = 0;
697 	}
698 #endif
699 }
700 
701 const char *
702 netname6(struct sockaddr_in6 *sa6, struct in6_addr *mask)
703 {
704 	static char line[MAXHOSTNAMELEN];
705 	u_char *p = (u_char *)mask;
706 	u_char *lim;
707 	int masklen, illegal = 0, flag = 0;
708 
709 	if (mask) {
710 		for (masklen = 0, lim = p + 16; p < lim; p++) {
711 			switch (*p) {
712 			 case 0xff:
713 				 masklen += 8;
714 				 break;
715 			 case 0xfe:
716 				 masklen += 7;
717 				 break;
718 			 case 0xfc:
719 				 masklen += 6;
720 				 break;
721 			 case 0xf8:
722 				 masklen += 5;
723 				 break;
724 			 case 0xf0:
725 				 masklen += 4;
726 				 break;
727 			 case 0xe0:
728 				 masklen += 3;
729 				 break;
730 			 case 0xc0:
731 				 masklen += 2;
732 				 break;
733 			 case 0x80:
734 				 masklen += 1;
735 				 break;
736 			 case 0x00:
737 				 break;
738 			 default:
739 				 illegal ++;
740 				 break;
741 			}
742 		}
743 		if (illegal)
744 			xo_error("illegal prefixlen\n");
745 	}
746 	else
747 		masklen = 128;
748 
749 	if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr))
750 		return("default");
751 
752 	if (numeric_addr)
753 		flag |= NI_NUMERICHOST;
754 	getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line),
755 		    NULL, 0, flag);
756 
757 	if (numeric_addr)
758 		sprintf(&line[strlen(line)], "/%d", masklen);
759 
760 	return line;
761 }
762 
763 char *
764 routename6(struct sockaddr_in6 *sa6)
765 {
766 	static char line[MAXHOSTNAMELEN];
767 	int flag = 0;
768 	/* use local variable for safety */
769 	struct sockaddr_in6 sa6_local;
770 
771 	sa6_local.sin6_family = AF_INET6;
772 	sa6_local.sin6_len = sizeof(sa6_local);
773 	sa6_local.sin6_addr = sa6->sin6_addr;
774 	sa6_local.sin6_scope_id = sa6->sin6_scope_id;
775 
776 	if (numeric_addr)
777 		flag |= NI_NUMERICHOST;
778 
779 	getnameinfo((struct sockaddr *)&sa6_local, sa6_local.sin6_len,
780 		    line, sizeof(line), NULL, 0, flag);
781 
782 	return line;
783 }
784 #endif /*INET6*/
785 
786 /*
787  * Print routing statistics
788  */
789 void
790 rt_stats(void)
791 {
792 	struct rtstat rtstat;
793 	u_long rtsaddr, rttaddr;
794 	int rttrash;
795 
796 	kresolve_list(rl);
797 
798 	if ((rtsaddr = rl[N_RTSTAT].n_value) == 0) {
799 		xo_emit("{W:rtstat: symbol not in namelist}\n");
800 		return;
801 	}
802 	if ((rttaddr = rl[N_RTTRASH].n_value) == 0) {
803 		xo_emit("{W:rttrash: symbol not in namelist}\n");
804 		return;
805 	}
806 	kread(rtsaddr, (char *)&rtstat, sizeof (rtstat));
807 	kread(rttaddr, (char *)&rttrash, sizeof (rttrash));
808 	xo_emit("{T:routing}:\n");
809 
810 #define	p(f, m) if (rtstat.f || sflag <= 1) \
811 	xo_emit(m, rtstat.f, plural(rtstat.f))
812 
813 	p(rts_badredirect, "\t{:bad-redirects/%hu} "
814 	    "{N:/bad routing redirect%s}\n");
815 	p(rts_dynamic, "\t{:dynamically-created/%hu} "
816 	    "{N:/dynamically created route%s}\n");
817 	p(rts_newgateway, "\t{:new-gateways/%hu} "
818 	    "{N:/new gateway%s due to redirects}\n");
819 	p(rts_unreach, "\t{:unreachable-destination/%hu} "
820 	    "{N:/destination%s found unreachable}\n");
821 	p(rts_wildcard, "\t{:wildcard-uses/%hu} "
822 	    "{N:/use%s of a wildcard route}\n");
823 #undef p
824 
825 	if (rttrash || sflag <= 1)
826 		xo_emit("\t{:unused-but-not-freed/%u} "
827 		    "{N:/route%s not in table but not freed}\n",
828 		    rttrash, plural(rttrash));
829 }
830