xref: /f-stack/tools/netstat/route.c (revision d4a07e70)
11eaf0ac3Slogwang /*-
222ce4affSfengbojiang  * SPDX-License-Identifier: BSD-3-Clause
322ce4affSfengbojiang  *
41eaf0ac3Slogwang  * Copyright (c) 1983, 1988, 1993
51eaf0ac3Slogwang  *	The Regents of the University of California.  All rights reserved.
61eaf0ac3Slogwang  *
71eaf0ac3Slogwang  * Redistribution and use in source and binary forms, with or without
81eaf0ac3Slogwang  * modification, are permitted provided that the following conditions
91eaf0ac3Slogwang  * are met:
101eaf0ac3Slogwang  * 1. Redistributions of source code must retain the above copyright
111eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer.
121eaf0ac3Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
131eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer in the
141eaf0ac3Slogwang  *    documentation and/or other materials provided with the distribution.
1522ce4affSfengbojiang  * 3. Neither the name of the University nor the names of its contributors
161eaf0ac3Slogwang  *    may be used to endorse or promote products derived from this software
171eaf0ac3Slogwang  *    without specific prior written permission.
181eaf0ac3Slogwang  *
191eaf0ac3Slogwang  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
201eaf0ac3Slogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
211eaf0ac3Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
221eaf0ac3Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
231eaf0ac3Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
241eaf0ac3Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
251eaf0ac3Slogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
261eaf0ac3Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
271eaf0ac3Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
281eaf0ac3Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
291eaf0ac3Slogwang  * SUCH DAMAGE.
301eaf0ac3Slogwang  */
311eaf0ac3Slogwang 
321eaf0ac3Slogwang #if 0
331eaf0ac3Slogwang #ifndef lint
341eaf0ac3Slogwang static char sccsid[] = "From: @(#)route.c	8.6 (Berkeley) 4/28/95";
351eaf0ac3Slogwang #endif /* not lint */
361eaf0ac3Slogwang #endif
371eaf0ac3Slogwang 
381eaf0ac3Slogwang #include <sys/cdefs.h>
391eaf0ac3Slogwang __FBSDID("$FreeBSD$");
401eaf0ac3Slogwang 
411eaf0ac3Slogwang #include <sys/param.h>
421eaf0ac3Slogwang #include <sys/protosw.h>
431eaf0ac3Slogwang #include <sys/socket.h>
441eaf0ac3Slogwang #include <sys/socketvar.h>
451eaf0ac3Slogwang #include <sys/sysctl.h>
461eaf0ac3Slogwang #include <sys/time.h>
471eaf0ac3Slogwang 
481eaf0ac3Slogwang #include <net/ethernet.h>
491eaf0ac3Slogwang #include <net/if.h>
501eaf0ac3Slogwang #include <net/if_dl.h>
511eaf0ac3Slogwang #include <net/if_types.h>
521eaf0ac3Slogwang #include <net/route.h>
531eaf0ac3Slogwang 
541eaf0ac3Slogwang #include <netinet/in.h>
551eaf0ac3Slogwang #include <netgraph/ng_socket.h>
561eaf0ac3Slogwang 
571eaf0ac3Slogwang #include <arpa/inet.h>
581eaf0ac3Slogwang #include <ifaddrs.h>
591eaf0ac3Slogwang #include <libutil.h>
601eaf0ac3Slogwang #include <netdb.h>
6122ce4affSfengbojiang #include <stdbool.h>
621eaf0ac3Slogwang #include <stdint.h>
631eaf0ac3Slogwang #include <stdio.h>
641eaf0ac3Slogwang #include <stdlib.h>
651eaf0ac3Slogwang #include <stdbool.h>
661eaf0ac3Slogwang #include <string.h>
671eaf0ac3Slogwang #include <sysexits.h>
681eaf0ac3Slogwang #include <unistd.h>
691eaf0ac3Slogwang #include <err.h>
701eaf0ac3Slogwang #include <libxo/xo.h>
711eaf0ac3Slogwang #include "netstat.h"
7222ce4affSfengbojiang #include "common.h"
731eaf0ac3Slogwang #include "nl_defs.h"
741eaf0ac3Slogwang 
75*d4a07e70Sfengbojiang #ifdef FSTACK
76*d4a07e70Sfengbojiang #include <time.h>
77*d4a07e70Sfengbojiang #endif
78*d4a07e70Sfengbojiang 
791eaf0ac3Slogwang /*
801eaf0ac3Slogwang  * Definitions for showing gateway flags.
811eaf0ac3Slogwang  */
8222ce4affSfengbojiang struct bits rt_bits[] = {
831eaf0ac3Slogwang 	{ RTF_UP,	'U', "up" },
841eaf0ac3Slogwang 	{ RTF_GATEWAY,	'G', "gateway" },
851eaf0ac3Slogwang 	{ RTF_HOST,	'H', "host" },
861eaf0ac3Slogwang 	{ RTF_REJECT,	'R', "reject" },
871eaf0ac3Slogwang 	{ RTF_DYNAMIC,	'D', "dynamic" },
881eaf0ac3Slogwang 	{ RTF_MODIFIED,	'M', "modified" },
891eaf0ac3Slogwang 	{ RTF_DONE,	'd', "done" }, /* Completed -- for routing msgs only */
901eaf0ac3Slogwang 	{ RTF_XRESOLVE,	'X', "xresolve" },
911eaf0ac3Slogwang 	{ RTF_STATIC,	'S', "static" },
921eaf0ac3Slogwang 	{ RTF_PROTO1,	'1', "proto1" },
931eaf0ac3Slogwang 	{ RTF_PROTO2,	'2', "proto2" },
941eaf0ac3Slogwang 	{ RTF_PROTO3,	'3', "proto3" },
951eaf0ac3Slogwang 	{ RTF_BLACKHOLE,'B', "blackhole" },
961eaf0ac3Slogwang 	{ RTF_BROADCAST,'b', "broadcast" },
971eaf0ac3Slogwang #ifdef RTF_LLINFO
981eaf0ac3Slogwang 	{ RTF_LLINFO,	'L', "llinfo" },
991eaf0ac3Slogwang #endif
1001eaf0ac3Slogwang 	{ 0 , 0, NULL }
1011eaf0ac3Slogwang };
1021eaf0ac3Slogwang 
1031eaf0ac3Slogwang static struct ifmap_entry *ifmap;
10422ce4affSfengbojiang static size_t ifmap_size;
1051eaf0ac3Slogwang static struct timespec uptime;
1061eaf0ac3Slogwang 
1071eaf0ac3Slogwang static const char *netname4(in_addr_t, in_addr_t);
1081eaf0ac3Slogwang #ifdef INET6
1091eaf0ac3Slogwang static const char *netname6(struct sockaddr_in6 *, struct sockaddr_in6 *);
1101eaf0ac3Slogwang #endif
1111eaf0ac3Slogwang static void p_rtable_sysctl(int, int);
1121eaf0ac3Slogwang static void p_rtentry_sysctl(const char *name, struct rt_msghdr *);
1131eaf0ac3Slogwang static void p_flags(int, const char *);
11422ce4affSfengbojiang static void domask(char *, size_t, u_long);
1151eaf0ac3Slogwang 
1161eaf0ac3Slogwang 
1171eaf0ac3Slogwang /*
1181eaf0ac3Slogwang  * Print routing tables.
1191eaf0ac3Slogwang  */
1201eaf0ac3Slogwang void
routepr(int fibnum,int af)1211eaf0ac3Slogwang routepr(int fibnum, int af)
1221eaf0ac3Slogwang {
1231eaf0ac3Slogwang 	size_t intsize;
1241eaf0ac3Slogwang 	int numfibs;
1251eaf0ac3Slogwang 
1261eaf0ac3Slogwang 	if (live == 0)
1271eaf0ac3Slogwang 		return;
1281eaf0ac3Slogwang 
1291eaf0ac3Slogwang 	intsize = sizeof(int);
1301eaf0ac3Slogwang 	if (fibnum == -1 &&
1311eaf0ac3Slogwang 	    sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1)
1321eaf0ac3Slogwang 		fibnum = 0;
1331eaf0ac3Slogwang 	if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
1341eaf0ac3Slogwang 		numfibs = 1;
1351eaf0ac3Slogwang 	if (fibnum < 0 || fibnum > numfibs - 1)
1361eaf0ac3Slogwang 		errx(EX_USAGE, "%d: invalid fib", fibnum);
1371eaf0ac3Slogwang 	/*
1381eaf0ac3Slogwang 	 * Since kernel & userland use different timebase
1391eaf0ac3Slogwang 	 * (time_uptime vs time_second) and we are reading kernel memory
1401eaf0ac3Slogwang 	 * directly we should do rt_expire --> expire_time conversion.
1411eaf0ac3Slogwang 	 */
142*d4a07e70Sfengbojiang #ifndef FSTACK
1431eaf0ac3Slogwang 	if (clock_gettime(CLOCK_UPTIME, &uptime) < 0)
144*d4a07e70Sfengbojiang #else
145*d4a07e70Sfengbojiang 	if (clock_gettime(CLOCK_BOOTTIME, &uptime) < 0)
146*d4a07e70Sfengbojiang #endif
1471eaf0ac3Slogwang 		err(EX_OSERR, "clock_gettime() failed");
1481eaf0ac3Slogwang 
1491eaf0ac3Slogwang 	xo_open_container("route-information");
1501eaf0ac3Slogwang 	xo_emit("{T:Routing tables}");
1511eaf0ac3Slogwang 	if (fibnum)
1521eaf0ac3Slogwang 		xo_emit(" ({L:fib}: {:fib/%d})", fibnum);
1531eaf0ac3Slogwang 	xo_emit("\n");
1541eaf0ac3Slogwang 	p_rtable_sysctl(fibnum, af);
1551eaf0ac3Slogwang 	xo_close_container("route-information");
1561eaf0ac3Slogwang }
1571eaf0ac3Slogwang 
1581eaf0ac3Slogwang 
1591eaf0ac3Slogwang /*
1601eaf0ac3Slogwang  * Print address family header before a section of the routing table.
1611eaf0ac3Slogwang  */
1621eaf0ac3Slogwang void
pr_family(int af1)1631eaf0ac3Slogwang pr_family(int af1)
1641eaf0ac3Slogwang {
1651eaf0ac3Slogwang 	const char *afname;
1661eaf0ac3Slogwang 
1671eaf0ac3Slogwang 	switch (af1) {
1681eaf0ac3Slogwang 	case AF_INET:
1691eaf0ac3Slogwang 		afname = "Internet";
1701eaf0ac3Slogwang 		break;
1711eaf0ac3Slogwang #ifdef INET6
1721eaf0ac3Slogwang 	case AF_INET6:
1731eaf0ac3Slogwang 		afname = "Internet6";
1741eaf0ac3Slogwang 		break;
1751eaf0ac3Slogwang #endif /*INET6*/
1761eaf0ac3Slogwang 	case AF_ISO:
1771eaf0ac3Slogwang 		afname = "ISO";
1781eaf0ac3Slogwang 		break;
1791eaf0ac3Slogwang 	case AF_CCITT:
1801eaf0ac3Slogwang 		afname = "X.25";
1811eaf0ac3Slogwang 		break;
1821eaf0ac3Slogwang 	case AF_NETGRAPH:
1831eaf0ac3Slogwang 		afname = "Netgraph";
1841eaf0ac3Slogwang 		break;
1851eaf0ac3Slogwang 	default:
1861eaf0ac3Slogwang 		afname = NULL;
1871eaf0ac3Slogwang 		break;
1881eaf0ac3Slogwang 	}
1891eaf0ac3Slogwang 	if (afname)
1901eaf0ac3Slogwang 		xo_emit("\n{k:address-family/%s}:\n", afname);
1911eaf0ac3Slogwang 	else
1921eaf0ac3Slogwang 		xo_emit("\n{L:Protocol Family} {k:address-family/%d}:\n", af1);
1931eaf0ac3Slogwang }
1941eaf0ac3Slogwang 
1951eaf0ac3Slogwang /* column widths; each followed by one space */
1961eaf0ac3Slogwang #ifndef INET6
1971eaf0ac3Slogwang #define	WID_DST_DEFAULT(af) 	18	/* width of destination column */
1981eaf0ac3Slogwang #define	WID_GW_DEFAULT(af)	18	/* width of gateway column */
1991eaf0ac3Slogwang #define	WID_IF_DEFAULT(af)	(Wflag ? 10 : 8) /* width of netif column */
2001eaf0ac3Slogwang #else
2011eaf0ac3Slogwang #define	WID_DST_DEFAULT(af) \
2021eaf0ac3Slogwang 	((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18)
2031eaf0ac3Slogwang #define	WID_GW_DEFAULT(af) \
2041eaf0ac3Slogwang 	((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18)
2051eaf0ac3Slogwang #define	WID_IF_DEFAULT(af)	((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8))
2061eaf0ac3Slogwang #endif /*INET6*/
2071eaf0ac3Slogwang 
2081eaf0ac3Slogwang static int wid_dst;
2091eaf0ac3Slogwang static int wid_gw;
2101eaf0ac3Slogwang static int wid_flags;
2111eaf0ac3Slogwang static int wid_pksent;
2121eaf0ac3Slogwang static int wid_mtu;
2131eaf0ac3Slogwang static int wid_if;
2141eaf0ac3Slogwang static int wid_expire;
2151eaf0ac3Slogwang 
2161eaf0ac3Slogwang /*
2171eaf0ac3Slogwang  * Print header for routing table columns.
2181eaf0ac3Slogwang  */
2191eaf0ac3Slogwang static void
pr_rthdr(int af1 __unused)2201eaf0ac3Slogwang pr_rthdr(int af1 __unused)
2211eaf0ac3Slogwang {
2221eaf0ac3Slogwang 
2231eaf0ac3Slogwang 	if (Wflag) {
2241eaf0ac3Slogwang 		xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
2251eaf0ac3Slogwang 		    "{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n",
2261eaf0ac3Slogwang 			wid_dst,	wid_dst,	"Destination",
2271eaf0ac3Slogwang 			wid_gw,		wid_gw,		"Gateway",
2281eaf0ac3Slogwang 			wid_flags,	wid_flags,	"Flags",
22922ce4affSfengbojiang 			wid_mtu,	wid_mtu,	"Nhop#",
2301eaf0ac3Slogwang 			wid_mtu,	wid_mtu,	"Mtu",
2311eaf0ac3Slogwang 			wid_if,		wid_if,		"Netif",
2321eaf0ac3Slogwang 			wid_expire,			"Expire");
2331eaf0ac3Slogwang 	} else {
2341eaf0ac3Slogwang 		xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
2351eaf0ac3Slogwang 		    "{T:/%*s}\n",
2361eaf0ac3Slogwang 			wid_dst,	wid_dst,	"Destination",
2371eaf0ac3Slogwang 			wid_gw,		wid_gw,		"Gateway",
2381eaf0ac3Slogwang 			wid_flags,	wid_flags,	"Flags",
2391eaf0ac3Slogwang 			wid_if,		wid_if,		"Netif",
2401eaf0ac3Slogwang 			wid_expire,			"Expire");
2411eaf0ac3Slogwang 	}
2421eaf0ac3Slogwang }
2431eaf0ac3Slogwang 
2441eaf0ac3Slogwang static void
p_rtable_sysctl(int fibnum,int af)2451eaf0ac3Slogwang p_rtable_sysctl(int fibnum, int af)
2461eaf0ac3Slogwang {
2471eaf0ac3Slogwang 	size_t needed;
2481eaf0ac3Slogwang 	int mib[7];
2491eaf0ac3Slogwang 	char *buf, *next, *lim;
2501eaf0ac3Slogwang 	struct rt_msghdr *rtm;
2511eaf0ac3Slogwang 	struct sockaddr *sa;
25222ce4affSfengbojiang 	int fam = AF_UNSPEC;
2531eaf0ac3Slogwang 	int need_table_close = false;
2541eaf0ac3Slogwang 
25522ce4affSfengbojiang 	ifmap = prepare_ifmap(&ifmap_size);
2561eaf0ac3Slogwang 
2571eaf0ac3Slogwang 	mib[0] = CTL_NET;
2581eaf0ac3Slogwang 	mib[1] = PF_ROUTE;
2591eaf0ac3Slogwang 	mib[2] = 0;
2601eaf0ac3Slogwang 	mib[3] = af;
2611eaf0ac3Slogwang 	mib[4] = NET_RT_DUMP;
2621eaf0ac3Slogwang 	mib[5] = 0;
2631eaf0ac3Slogwang 	mib[6] = fibnum;
2641eaf0ac3Slogwang 	if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0)
2651eaf0ac3Slogwang 		err(EX_OSERR, "sysctl: net.route.0.%d.dump.%d estimate", af,
2661eaf0ac3Slogwang 		    fibnum);
2671eaf0ac3Slogwang 	if ((buf = malloc(needed)) == NULL)
2681eaf0ac3Slogwang 		errx(2, "malloc(%lu)", (unsigned long)needed);
2691eaf0ac3Slogwang 	if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0)
2701eaf0ac3Slogwang 		err(1, "sysctl: net.route.0.%d.dump.%d", af, fibnum);
2711eaf0ac3Slogwang 	lim  = buf + needed;
2721eaf0ac3Slogwang 	xo_open_container("route-table");
2731eaf0ac3Slogwang 	xo_open_list("rt-family");
2741eaf0ac3Slogwang 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
2751eaf0ac3Slogwang 		rtm = (struct rt_msghdr *)next;
2761eaf0ac3Slogwang 		if (rtm->rtm_version != RTM_VERSION)
2771eaf0ac3Slogwang 			continue;
2781eaf0ac3Slogwang 		/*
2791eaf0ac3Slogwang 		 * Peek inside header to determine AF
2801eaf0ac3Slogwang 		 */
2811eaf0ac3Slogwang 		sa = (struct sockaddr *)(rtm + 1);
2821eaf0ac3Slogwang 		/* Only print family first time. */
2831eaf0ac3Slogwang 		if (fam != sa->sa_family) {
2841eaf0ac3Slogwang 			if (need_table_close) {
2851eaf0ac3Slogwang 				xo_close_list("rt-entry");
2861eaf0ac3Slogwang 				xo_close_instance("rt-family");
2871eaf0ac3Slogwang 			}
2881eaf0ac3Slogwang 			need_table_close = true;
2891eaf0ac3Slogwang 
2901eaf0ac3Slogwang 			fam = sa->sa_family;
2911eaf0ac3Slogwang 			wid_dst = WID_DST_DEFAULT(fam);
2921eaf0ac3Slogwang 			wid_gw = WID_GW_DEFAULT(fam);
2931eaf0ac3Slogwang 			wid_flags = 6;
2941eaf0ac3Slogwang 			wid_pksent = 8;
2951eaf0ac3Slogwang 			wid_mtu = 6;
2961eaf0ac3Slogwang 			wid_if = WID_IF_DEFAULT(fam);
2971eaf0ac3Slogwang 			wid_expire = 6;
2981eaf0ac3Slogwang 			xo_open_instance("rt-family");
2991eaf0ac3Slogwang 			pr_family(fam);
3001eaf0ac3Slogwang 			xo_open_list("rt-entry");
3011eaf0ac3Slogwang 
3021eaf0ac3Slogwang 			pr_rthdr(fam);
3031eaf0ac3Slogwang 		}
3041eaf0ac3Slogwang 		p_rtentry_sysctl("rt-entry", rtm);
3051eaf0ac3Slogwang 	}
3061eaf0ac3Slogwang 	if (need_table_close) {
3071eaf0ac3Slogwang 		xo_close_list("rt-entry");
3081eaf0ac3Slogwang 		xo_close_instance("rt-family");
3091eaf0ac3Slogwang 	}
3101eaf0ac3Slogwang 	xo_close_list("rt-family");
3111eaf0ac3Slogwang 	xo_close_container("route-table");
3121eaf0ac3Slogwang 	free(buf);
3131eaf0ac3Slogwang }
3141eaf0ac3Slogwang 
3151eaf0ac3Slogwang static void
p_rtentry_sysctl(const char * name,struct rt_msghdr * rtm)3161eaf0ac3Slogwang p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm)
3171eaf0ac3Slogwang {
3181eaf0ac3Slogwang 	struct sockaddr *sa, *addr[RTAX_MAX];
3191eaf0ac3Slogwang 	char buffer[128];
3201eaf0ac3Slogwang 	char prettyname[128];
3211eaf0ac3Slogwang 	int i, protrusion;
3221eaf0ac3Slogwang 
3231eaf0ac3Slogwang 	xo_open_instance(name);
3241eaf0ac3Slogwang 	sa = (struct sockaddr *)(rtm + 1);
3251eaf0ac3Slogwang 	for (i = 0; i < RTAX_MAX; i++) {
32622ce4affSfengbojiang 		if (rtm->rtm_addrs & (1 << i)) {
3271eaf0ac3Slogwang 			addr[i] = sa;
3281eaf0ac3Slogwang 			sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
3291eaf0ac3Slogwang 		}
33022ce4affSfengbojiang 	}
3311eaf0ac3Slogwang 
3321eaf0ac3Slogwang 	protrusion = p_sockaddr("destination", addr[RTAX_DST],
3331eaf0ac3Slogwang 	    addr[RTAX_NETMASK],
3341eaf0ac3Slogwang 	    rtm->rtm_flags, wid_dst);
3351eaf0ac3Slogwang 	protrusion = p_sockaddr("gateway", addr[RTAX_GATEWAY], NULL, RTF_HOST,
3361eaf0ac3Slogwang 	    wid_gw - protrusion);
3371eaf0ac3Slogwang 	snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ",
3381eaf0ac3Slogwang 	    wid_flags - protrusion);
3391eaf0ac3Slogwang 	p_flags(rtm->rtm_flags, buffer);
3401eaf0ac3Slogwang 	if (Wflag) {
34122ce4affSfengbojiang 		/* XXX: use=0? */
34222ce4affSfengbojiang 		xo_emit("{t:nhop/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_nhidx);
3431eaf0ac3Slogwang 
3441eaf0ac3Slogwang 		if (rtm->rtm_rmx.rmx_mtu != 0)
3451eaf0ac3Slogwang 			xo_emit("{t:mtu/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_mtu);
3461eaf0ac3Slogwang 		else
3471eaf0ac3Slogwang 			xo_emit("{P:/%*s} ", wid_mtu, "");
3481eaf0ac3Slogwang 	}
3491eaf0ac3Slogwang 
3501eaf0ac3Slogwang 	memset(prettyname, 0, sizeof(prettyname));
3511eaf0ac3Slogwang 	if (rtm->rtm_index < ifmap_size) {
3521eaf0ac3Slogwang 		strlcpy(prettyname, ifmap[rtm->rtm_index].ifname,
3531eaf0ac3Slogwang 		    sizeof(prettyname));
3541eaf0ac3Slogwang 		if (*prettyname == '\0')
3551eaf0ac3Slogwang 			strlcpy(prettyname, "---", sizeof(prettyname));
3561eaf0ac3Slogwang 	}
3571eaf0ac3Slogwang 
3581eaf0ac3Slogwang 	if (Wflag)
3591eaf0ac3Slogwang 		xo_emit("{t:interface-name/%*s}", wid_if, prettyname);
3601eaf0ac3Slogwang 	else
3611eaf0ac3Slogwang 		xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if,
3621eaf0ac3Slogwang 		    prettyname);
3631eaf0ac3Slogwang 	if (rtm->rtm_rmx.rmx_expire) {
3641eaf0ac3Slogwang 		time_t expire_time;
3651eaf0ac3Slogwang 
3661eaf0ac3Slogwang 		if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0)
3671eaf0ac3Slogwang 			xo_emit(" {:expire-time/%*d}", wid_expire,
3681eaf0ac3Slogwang 			    (int)expire_time);
3691eaf0ac3Slogwang 	}
3701eaf0ac3Slogwang 
3711eaf0ac3Slogwang 	xo_emit("\n");
3721eaf0ac3Slogwang 	xo_close_instance(name);
3731eaf0ac3Slogwang }
3741eaf0ac3Slogwang 
37522ce4affSfengbojiang int
p_sockaddr(const char * name,struct sockaddr * sa,struct sockaddr * mask,int flags,int width)3761eaf0ac3Slogwang p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask,
3771eaf0ac3Slogwang     int flags, int width)
3781eaf0ac3Slogwang {
3791eaf0ac3Slogwang 	const char *cp;
3801eaf0ac3Slogwang 	char buf[128];
3811eaf0ac3Slogwang 	int protrusion;
3821eaf0ac3Slogwang 
3831eaf0ac3Slogwang 	cp = fmt_sockaddr(sa, mask, flags);
3841eaf0ac3Slogwang 
3851eaf0ac3Slogwang 	if (width < 0) {
3861eaf0ac3Slogwang 		snprintf(buf, sizeof(buf), "{:%s/%%s} ", name);
3871eaf0ac3Slogwang 		xo_emit(buf, cp);
3881eaf0ac3Slogwang 		protrusion = 0;
3891eaf0ac3Slogwang 	} else {
3901eaf0ac3Slogwang 		if (Wflag != 0 || numeric_addr) {
3911eaf0ac3Slogwang 			snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ",
3921eaf0ac3Slogwang 			    -width, name);
3931eaf0ac3Slogwang 			xo_emit(buf, cp);
3941eaf0ac3Slogwang 			protrusion = strlen(cp) - width;
3951eaf0ac3Slogwang 			if (protrusion < 0)
3961eaf0ac3Slogwang 				protrusion = 0;
3971eaf0ac3Slogwang 		} else {
3981eaf0ac3Slogwang 			snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ",
3991eaf0ac3Slogwang 			    -width, name);
4001eaf0ac3Slogwang 			xo_emit(buf, width, cp);
4011eaf0ac3Slogwang 			protrusion = 0;
4021eaf0ac3Slogwang 		}
4031eaf0ac3Slogwang 	}
4041eaf0ac3Slogwang 	return (protrusion);
4051eaf0ac3Slogwang }
4061eaf0ac3Slogwang 
40722ce4affSfengbojiang const char *
fmt_sockaddr(struct sockaddr * sa,struct sockaddr * mask,int flags)4081eaf0ac3Slogwang fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags)
4091eaf0ac3Slogwang {
4101eaf0ac3Slogwang 	static char buf[128];
4111eaf0ac3Slogwang 	const char *cp;
4121eaf0ac3Slogwang 
4131eaf0ac3Slogwang 	if (sa == NULL)
4141eaf0ac3Slogwang 		return ("null");
4151eaf0ac3Slogwang 
4161eaf0ac3Slogwang 	switch(sa->sa_family) {
4171eaf0ac3Slogwang #ifdef INET6
4181eaf0ac3Slogwang 	case AF_INET6:
4191eaf0ac3Slogwang 		/*
4201eaf0ac3Slogwang 		 * The sa6->sin6_scope_id must be filled here because
4211eaf0ac3Slogwang 		 * this sockaddr is extracted from kmem(4) directly
4221eaf0ac3Slogwang 		 * and has KAME-specific embedded scope id in
4231eaf0ac3Slogwang 		 * sa6->sin6_addr.s6_addr[2].
4241eaf0ac3Slogwang 		 */
4251eaf0ac3Slogwang 		in6_fillscopeid(satosin6(sa));
4261eaf0ac3Slogwang 		/* FALLTHROUGH */
4271eaf0ac3Slogwang #endif /*INET6*/
4281eaf0ac3Slogwang 	case AF_INET:
4291eaf0ac3Slogwang 		if (flags & RTF_HOST)
4301eaf0ac3Slogwang 			cp = routename(sa, numeric_addr);
4311eaf0ac3Slogwang 		else if (mask)
4321eaf0ac3Slogwang 			cp = netname(sa, mask);
4331eaf0ac3Slogwang 		else
4341eaf0ac3Slogwang 			cp = netname(sa, NULL);
4351eaf0ac3Slogwang 		break;
4361eaf0ac3Slogwang 	case AF_NETGRAPH:
4371eaf0ac3Slogwang 	    {
4381eaf0ac3Slogwang 		strlcpy(buf, ((struct sockaddr_ng *)sa)->sg_data,
4391eaf0ac3Slogwang 		    sizeof(buf));
4401eaf0ac3Slogwang 		cp = buf;
4411eaf0ac3Slogwang 		break;
4421eaf0ac3Slogwang 	    }
4431eaf0ac3Slogwang 	case AF_LINK:
4441eaf0ac3Slogwang 	    {
4451eaf0ac3Slogwang #if 0
4461eaf0ac3Slogwang 		struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
4471eaf0ac3Slogwang 
4481eaf0ac3Slogwang 		/* Interface route. */
4491eaf0ac3Slogwang 		if (sdl->sdl_nlen)
4501eaf0ac3Slogwang 			cp = sdl->sdl_data;
4511eaf0ac3Slogwang 		else
4521eaf0ac3Slogwang #endif
4531eaf0ac3Slogwang 			cp = routename(sa, 1);
4541eaf0ac3Slogwang 		break;
4551eaf0ac3Slogwang 	    }
4561eaf0ac3Slogwang 	default:
4571eaf0ac3Slogwang 	    {
4581eaf0ac3Slogwang 		u_char *s = (u_char *)sa->sa_data, *slim;
4591eaf0ac3Slogwang 		char *cq, *cqlim;
4601eaf0ac3Slogwang 
4611eaf0ac3Slogwang 		cq = buf;
4621eaf0ac3Slogwang 		slim =  sa->sa_len + (u_char *) sa;
46322ce4affSfengbojiang 		cqlim = cq + sizeof(buf) - sizeof(" ffff");
46422ce4affSfengbojiang 		snprintf(cq, sizeof(buf), "(%d)", sa->sa_family);
46522ce4affSfengbojiang 		cq += strlen(cq);
4661eaf0ac3Slogwang 		while (s < slim && cq < cqlim) {
46722ce4affSfengbojiang 			snprintf(cq, sizeof(" ff"), " %02x", *s++);
46822ce4affSfengbojiang 			cq += strlen(cq);
46922ce4affSfengbojiang 			if (s < slim) {
47022ce4affSfengbojiang 			    snprintf(cq, sizeof("ff"), "%02x", *s++);
47122ce4affSfengbojiang 			    cq += strlen(cq);
47222ce4affSfengbojiang 			}
4731eaf0ac3Slogwang 		}
4741eaf0ac3Slogwang 		cp = buf;
4751eaf0ac3Slogwang 	    }
4761eaf0ac3Slogwang 	}
4771eaf0ac3Slogwang 
4781eaf0ac3Slogwang 	return (cp);
4791eaf0ac3Slogwang }
4801eaf0ac3Slogwang 
4811eaf0ac3Slogwang static void
p_flags(int f,const char * format)4821eaf0ac3Slogwang p_flags(int f, const char *format)
4831eaf0ac3Slogwang {
4841eaf0ac3Slogwang 
48522ce4affSfengbojiang 	print_flags_generic(f, rt_bits, format, "flags_pretty");
4861eaf0ac3Slogwang }
4871eaf0ac3Slogwang 
4881eaf0ac3Slogwang 
4891eaf0ac3Slogwang char *
routename(struct sockaddr * sa,int flags)4901eaf0ac3Slogwang routename(struct sockaddr *sa, int flags)
4911eaf0ac3Slogwang {
4921eaf0ac3Slogwang 	static char line[NI_MAXHOST];
4931eaf0ac3Slogwang 	int error, f;
4941eaf0ac3Slogwang 
4951eaf0ac3Slogwang 	f = (flags) ? NI_NUMERICHOST : 0;
4961eaf0ac3Slogwang 	error = getnameinfo(sa, sa->sa_len, line, sizeof(line),
4971eaf0ac3Slogwang 	    NULL, 0, f);
4981eaf0ac3Slogwang 	if (error) {
4991eaf0ac3Slogwang 		const void *src;
5001eaf0ac3Slogwang 		switch (sa->sa_family) {
5011eaf0ac3Slogwang #ifdef INET
5021eaf0ac3Slogwang 		case AF_INET:
5031eaf0ac3Slogwang 			src = &satosin(sa)->sin_addr;
5041eaf0ac3Slogwang 			break;
5051eaf0ac3Slogwang #endif /* INET */
5061eaf0ac3Slogwang #ifdef INET6
5071eaf0ac3Slogwang 		case AF_INET6:
5081eaf0ac3Slogwang 			src = &satosin6(sa)->sin6_addr;
5091eaf0ac3Slogwang 			break;
5101eaf0ac3Slogwang #endif /* INET6 */
5111eaf0ac3Slogwang 		default:
5121eaf0ac3Slogwang 			return(line);
5131eaf0ac3Slogwang 		}
5141eaf0ac3Slogwang 		inet_ntop(sa->sa_family, src, line, sizeof(line) - 1);
5151eaf0ac3Slogwang 		return (line);
5161eaf0ac3Slogwang 	}
5171eaf0ac3Slogwang 	trimdomain(line, strlen(line));
5181eaf0ac3Slogwang 
5191eaf0ac3Slogwang 	return (line);
5201eaf0ac3Slogwang }
5211eaf0ac3Slogwang 
5221eaf0ac3Slogwang #define	NSHIFT(m) (							\
5231eaf0ac3Slogwang 	(m) == IN_CLASSA_NET ? IN_CLASSA_NSHIFT :			\
5241eaf0ac3Slogwang 	(m) == IN_CLASSB_NET ? IN_CLASSB_NSHIFT :			\
5251eaf0ac3Slogwang 	(m) == IN_CLASSC_NET ? IN_CLASSC_NSHIFT :			\
5261eaf0ac3Slogwang 	0)
5271eaf0ac3Slogwang 
5281eaf0ac3Slogwang static void
domask(char * dst,size_t buflen,u_long mask)52922ce4affSfengbojiang domask(char *dst, size_t buflen, u_long mask)
5301eaf0ac3Slogwang {
5311eaf0ac3Slogwang 	int b, i;
5321eaf0ac3Slogwang 
5331eaf0ac3Slogwang 	if (mask == 0) {
5341eaf0ac3Slogwang 		*dst = '\0';
5351eaf0ac3Slogwang 		return;
5361eaf0ac3Slogwang 	}
5371eaf0ac3Slogwang 	i = 0;
5381eaf0ac3Slogwang 	for (b = 0; b < 32; b++)
5391eaf0ac3Slogwang 		if (mask & (1 << b)) {
5401eaf0ac3Slogwang 			int bb;
5411eaf0ac3Slogwang 
5421eaf0ac3Slogwang 			i = b;
5431eaf0ac3Slogwang 			for (bb = b+1; bb < 32; bb++)
5441eaf0ac3Slogwang 				if (!(mask & (1 << bb))) {
5451eaf0ac3Slogwang 					i = -1;	/* noncontig */
5461eaf0ac3Slogwang 					break;
5471eaf0ac3Slogwang 				}
5481eaf0ac3Slogwang 			break;
5491eaf0ac3Slogwang 		}
5501eaf0ac3Slogwang 	if (i == -1)
55122ce4affSfengbojiang 		snprintf(dst, buflen, "&0x%lx", mask);
5521eaf0ac3Slogwang 	else
55322ce4affSfengbojiang 		snprintf(dst, buflen, "/%d", 32-i);
5541eaf0ac3Slogwang }
5551eaf0ac3Slogwang 
5561eaf0ac3Slogwang /*
5571eaf0ac3Slogwang  * Return the name of the network whose address is given.
5581eaf0ac3Slogwang  */
5591eaf0ac3Slogwang const char *
netname(struct sockaddr * sa,struct sockaddr * mask)5601eaf0ac3Slogwang netname(struct sockaddr *sa, struct sockaddr *mask)
5611eaf0ac3Slogwang {
5621eaf0ac3Slogwang 	switch (sa->sa_family) {
5631eaf0ac3Slogwang 	case AF_INET:
5641eaf0ac3Slogwang 		if (mask != NULL)
5651eaf0ac3Slogwang 			return (netname4(satosin(sa)->sin_addr.s_addr,
5661eaf0ac3Slogwang 			    satosin(mask)->sin_addr.s_addr));
5671eaf0ac3Slogwang 		else
5681eaf0ac3Slogwang 			return (netname4(satosin(sa)->sin_addr.s_addr,
5691eaf0ac3Slogwang 			    INADDR_ANY));
5701eaf0ac3Slogwang 		break;
5711eaf0ac3Slogwang #ifdef INET6
5721eaf0ac3Slogwang 	case AF_INET6:
5731eaf0ac3Slogwang 		return (netname6(satosin6(sa), satosin6(mask)));
5741eaf0ac3Slogwang #endif /* INET6 */
5751eaf0ac3Slogwang 	default:
5761eaf0ac3Slogwang 		return (NULL);
5771eaf0ac3Slogwang 	}
5781eaf0ac3Slogwang }
5791eaf0ac3Slogwang 
5801eaf0ac3Slogwang static const char *
netname4(in_addr_t in,in_addr_t mask)5811eaf0ac3Slogwang netname4(in_addr_t in, in_addr_t mask)
5821eaf0ac3Slogwang {
5831eaf0ac3Slogwang 	char *cp = 0;
58422ce4affSfengbojiang 	static char line[MAXHOSTNAMELEN + sizeof("&0xffffffff")];
5851eaf0ac3Slogwang 	char nline[INET_ADDRSTRLEN];
5861eaf0ac3Slogwang 	struct netent *np = 0;
5871eaf0ac3Slogwang 	in_addr_t i;
5881eaf0ac3Slogwang 
5891eaf0ac3Slogwang 	if (in == INADDR_ANY && mask == 0) {
5901eaf0ac3Slogwang 		strlcpy(line, "default", sizeof(line));
5911eaf0ac3Slogwang 		return (line);
5921eaf0ac3Slogwang 	}
5931eaf0ac3Slogwang 
5941eaf0ac3Slogwang 	/* It is ok to supply host address. */
5951eaf0ac3Slogwang 	in &= mask;
5961eaf0ac3Slogwang 
5971eaf0ac3Slogwang 	i = ntohl(in);
5981eaf0ac3Slogwang 	if (!numeric_addr && i) {
5991eaf0ac3Slogwang 		np = getnetbyaddr(i >> NSHIFT(ntohl(mask)), AF_INET);
6001eaf0ac3Slogwang 		if (np != NULL) {
6011eaf0ac3Slogwang 			cp = np->n_name;
6021eaf0ac3Slogwang 			trimdomain(cp, strlen(cp));
6031eaf0ac3Slogwang 		}
6041eaf0ac3Slogwang 	}
6051eaf0ac3Slogwang 	if (cp != NULL)
6061eaf0ac3Slogwang 		strlcpy(line, cp, sizeof(line));
6071eaf0ac3Slogwang 	else {
6081eaf0ac3Slogwang 		inet_ntop(AF_INET, &in, nline, sizeof(nline));
6091eaf0ac3Slogwang 		strlcpy(line, nline, sizeof(line));
61022ce4affSfengbojiang 		domask(line + strlen(line), sizeof(line) - strlen(line), ntohl(mask));
6111eaf0ac3Slogwang 	}
6121eaf0ac3Slogwang 
6131eaf0ac3Slogwang 	return (line);
6141eaf0ac3Slogwang }
6151eaf0ac3Slogwang 
6161eaf0ac3Slogwang #undef NSHIFT
6171eaf0ac3Slogwang 
6181eaf0ac3Slogwang #ifdef INET6
6191eaf0ac3Slogwang void
in6_fillscopeid(struct sockaddr_in6 * sa6)6201eaf0ac3Slogwang in6_fillscopeid(struct sockaddr_in6 *sa6)
6211eaf0ac3Slogwang {
6221eaf0ac3Slogwang #if defined(__KAME__)
6231eaf0ac3Slogwang 	/*
6241eaf0ac3Slogwang 	 * XXX: This is a special workaround for KAME kernels.
6251eaf0ac3Slogwang 	 * sin6_scope_id field of SA should be set in the future.
6261eaf0ac3Slogwang 	 */
6271eaf0ac3Slogwang 	if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) ||
6281eaf0ac3Slogwang 	    IN6_IS_ADDR_MC_NODELOCAL(&sa6->sin6_addr) ||
6291eaf0ac3Slogwang 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) {
6301eaf0ac3Slogwang 		if (sa6->sin6_scope_id == 0)
6311eaf0ac3Slogwang 			sa6->sin6_scope_id =
6321eaf0ac3Slogwang 			    ntohs(*(u_int16_t *)&sa6->sin6_addr.s6_addr[2]);
6331eaf0ac3Slogwang 		sa6->sin6_addr.s6_addr[2] = sa6->sin6_addr.s6_addr[3] = 0;
6341eaf0ac3Slogwang 	}
6351eaf0ac3Slogwang #endif
6361eaf0ac3Slogwang }
6371eaf0ac3Slogwang 
6381eaf0ac3Slogwang /* Mask to length table.  To check an invalid value, (length + 1) is used. */
63922ce4affSfengbojiang static const u_char masktolen[256] = {
6401eaf0ac3Slogwang 	[0xff] = 8 + 1,
6411eaf0ac3Slogwang 	[0xfe] = 7 + 1,
6421eaf0ac3Slogwang 	[0xfc] = 6 + 1,
6431eaf0ac3Slogwang 	[0xf8] = 5 + 1,
6441eaf0ac3Slogwang 	[0xf0] = 4 + 1,
6451eaf0ac3Slogwang 	[0xe0] = 3 + 1,
6461eaf0ac3Slogwang 	[0xc0] = 2 + 1,
6471eaf0ac3Slogwang 	[0x80] = 1 + 1,
6481eaf0ac3Slogwang 	[0x00] = 0 + 1,
6491eaf0ac3Slogwang };
6501eaf0ac3Slogwang 
6511eaf0ac3Slogwang static const char *
netname6(struct sockaddr_in6 * sa6,struct sockaddr_in6 * mask)6521eaf0ac3Slogwang netname6(struct sockaddr_in6 *sa6, struct sockaddr_in6 *mask)
6531eaf0ac3Slogwang {
6541eaf0ac3Slogwang 	static char line[NI_MAXHOST + sizeof("/xxx") - 1];
6551eaf0ac3Slogwang 	struct sockaddr_in6 addr;
6561eaf0ac3Slogwang 	char nline[NI_MAXHOST];
65722ce4affSfengbojiang 	char maskbuf[sizeof("/xxx")];
6581eaf0ac3Slogwang 	u_char *p, *lim;
65922ce4affSfengbojiang 	u_char masklen;
66022ce4affSfengbojiang 	int i;
66122ce4affSfengbojiang 	bool illegal = false;
6621eaf0ac3Slogwang 
6631eaf0ac3Slogwang 	if (mask) {
6641eaf0ac3Slogwang 		p = (u_char *)&mask->sin6_addr;
6651eaf0ac3Slogwang 		for (masklen = 0, lim = p + 16; p < lim; p++) {
66622ce4affSfengbojiang 			if (masktolen[*p] > 0) {
6671eaf0ac3Slogwang 				/* -1 is required. */
66822ce4affSfengbojiang 				masklen += (masktolen[*p] - 1);
66922ce4affSfengbojiang 			} else
67022ce4affSfengbojiang 				illegal = true;
6711eaf0ac3Slogwang 		}
6721eaf0ac3Slogwang 		if (illegal)
6731eaf0ac3Slogwang 			xo_error("illegal prefixlen\n");
6741eaf0ac3Slogwang 
6751eaf0ac3Slogwang 		memcpy(&addr, sa6, sizeof(addr));
6761eaf0ac3Slogwang 		for (i = 0; i < 16; ++i)
6771eaf0ac3Slogwang 			addr.sin6_addr.s6_addr[i] &=
6781eaf0ac3Slogwang 			    mask->sin6_addr.s6_addr[i];
6791eaf0ac3Slogwang 		sa6 = &addr;
6801eaf0ac3Slogwang 	}
6811eaf0ac3Slogwang 	else
6821eaf0ac3Slogwang 		masklen = 128;
6831eaf0ac3Slogwang 
6841eaf0ac3Slogwang 	if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr))
6851eaf0ac3Slogwang 		return("default");
6861eaf0ac3Slogwang 
6871eaf0ac3Slogwang 	getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, nline, sizeof(nline),
6881eaf0ac3Slogwang 	    NULL, 0, NI_NUMERICHOST);
6891eaf0ac3Slogwang 	if (numeric_addr)
6901eaf0ac3Slogwang 		strlcpy(line, nline, sizeof(line));
6911eaf0ac3Slogwang 	else
6921eaf0ac3Slogwang 		getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line,
6931eaf0ac3Slogwang 		    sizeof(line), NULL, 0, 0);
69422ce4affSfengbojiang 	if (numeric_addr || strcmp(line, nline) == 0) {
69522ce4affSfengbojiang 		snprintf(maskbuf, sizeof(maskbuf), "/%d", masklen);
69622ce4affSfengbojiang 		strlcat(line, maskbuf, sizeof(line));
69722ce4affSfengbojiang 	}
6981eaf0ac3Slogwang 
6991eaf0ac3Slogwang 	return (line);
7001eaf0ac3Slogwang }
7011eaf0ac3Slogwang #endif /*INET6*/
7021eaf0ac3Slogwang 
7031eaf0ac3Slogwang /*
7041eaf0ac3Slogwang  * Print routing statistics
7051eaf0ac3Slogwang  */
7061eaf0ac3Slogwang void
rt_stats(void)7071eaf0ac3Slogwang rt_stats(void)
7081eaf0ac3Slogwang {
7091eaf0ac3Slogwang 	struct rtstat rtstat;
710*d4a07e70Sfengbojiang #ifndef FSTACK
7111eaf0ac3Slogwang 	u_long rtsaddr, rttaddr;
7121eaf0ac3Slogwang 	int rttrash;
7131eaf0ac3Slogwang 
7141eaf0ac3Slogwang 	if ((rtsaddr = nl[N_RTSTAT].n_value) == 0) {
7151eaf0ac3Slogwang 		xo_emit("{W:rtstat: symbol not in namelist}\n");
7161eaf0ac3Slogwang 		return;
7171eaf0ac3Slogwang 	}
7181eaf0ac3Slogwang 	if ((rttaddr = nl[N_RTTRASH].n_value) == 0) {
7191eaf0ac3Slogwang 		xo_emit("{W:rttrash: symbol not in namelist}\n");
7201eaf0ac3Slogwang 		return;
7211eaf0ac3Slogwang 	}
72222ce4affSfengbojiang 	kread_counters(rtsaddr, (char *)&rtstat, sizeof (rtstat));
7231eaf0ac3Slogwang 	kread(rttaddr, (char *)&rttrash, sizeof (rttrash));
724*d4a07e70Sfengbojiang #else
725*d4a07e70Sfengbojiang 	if (fetch_stats("net.rtstat", 0, &rtstat,
726*d4a07e70Sfengbojiang 	    sizeof(rtstat), kread_counters) != 0)
727*d4a07e70Sfengbojiang 		return;
728*d4a07e70Sfengbojiang #endif
7291eaf0ac3Slogwang 	xo_emit("{T:routing}:\n");
7301eaf0ac3Slogwang 
7311eaf0ac3Slogwang #define	p(f, m) if (rtstat.f || sflag <= 1) \
7321eaf0ac3Slogwang 	xo_emit(m, rtstat.f, plural(rtstat.f))
7331eaf0ac3Slogwang 
73422ce4affSfengbojiang 	p(rts_badredirect, "\t{:bad-redirects/%ju} "
7351eaf0ac3Slogwang 	    "{N:/bad routing redirect%s}\n");
73622ce4affSfengbojiang 	p(rts_dynamic, "\t{:dynamically-created/%ju} "
7371eaf0ac3Slogwang 	    "{N:/dynamically created route%s}\n");
73822ce4affSfengbojiang 	p(rts_newgateway, "\t{:new-gateways/%ju} "
7391eaf0ac3Slogwang 	    "{N:/new gateway%s due to redirects}\n");
74022ce4affSfengbojiang 	p(rts_unreach, "\t{:unreachable-destination/%ju} "
7411eaf0ac3Slogwang 	    "{N:/destination%s found unreachable}\n");
74222ce4affSfengbojiang 	p(rts_wildcard, "\t{:wildcard-uses/%ju} "
7431eaf0ac3Slogwang 	    "{N:/use%s of a wildcard route}\n");
7441eaf0ac3Slogwang #undef p
7451eaf0ac3Slogwang 
746*d4a07e70Sfengbojiang #ifndef FSTACK
7471eaf0ac3Slogwang 	if (rttrash || sflag <= 1)
7481eaf0ac3Slogwang 		xo_emit("\t{:unused-but-not-freed/%u} "
7491eaf0ac3Slogwang 		    "{N:/route%s not in table but not freed}\n",
7501eaf0ac3Slogwang 		    rttrash, plural(rttrash));
751*d4a07e70Sfengbojiang #endif
752*d4a07e70Sfengbojiang 
7531eaf0ac3Slogwang }
754