hi tech@, netstat -r with -A/-M (which uses kvm(3)) was broken since in-kernel routing table implementation was changed to ART-based.
this patch adds support for ART-based implementation in netstat. kernel and src build ok. by the way, is "netstat -r with -A on live system" really needed? it needs kern.allowkmem=1. Regards, -- FUKAUMI Naoki Index: regress/sys/net/rtable/Makefile.inc =================================================================== RCS file: /cvs/src/regress/sys/net/rtable/Makefile.inc,v retrieving revision 1.3 diff -u -p -r1.3 Makefile.inc --- regress/sys/net/rtable/Makefile.inc 27 Jul 2017 13:34:30 -0000 1.3 +++ regress/sys/net/rtable/Makefile.inc 13 Mar 2019 07:03:52 -0000 @@ -9,6 +9,6 @@ SRCS+= art.c CFLAGS+= -DART .endif -CPPFLAGS+= -I${TOPDIR} -Wall +CPPFLAGS+= -I${TOPDIR} -Wall -Wno-macro-redefined .PATH: ${TOPDIR} ${TOPDIR}/../../../../sys/net Index: sys/net/art.c =================================================================== RCS file: /cvs/src/sys/net/art.c,v retrieving revision 1.27 diff -u -p -r1.27 art.c --- sys/net/art.c 28 Feb 2017 09:50:13 -0000 1.27 +++ sys/net/art.c 13 Mar 2019 07:03:53 -0000 @@ -37,37 +37,6 @@ #include <net/art.h> -#define ISLEAF(e) (((unsigned long)(e) & 1) == 0) -#define SUBTABLE(e) ((struct art_table *)((unsigned long)(e) & ~1)) -#define ASNODE(t) ((struct art_node *)((unsigned long)(t) | 1)) - -/* - * Allotment Table. - */ -struct art_table { - struct art_table *at_parent; /* Parent table */ - uint32_t at_index; /* Index in the parent table */ - uint32_t at_minfringe; /* Index that fringe begins */ - uint32_t at_level; /* Level of the table */ - uint8_t at_bits; /* Stride length of the table */ - uint8_t at_offset; /* Sum of parents' stride len */ - - /* - * Items stored in the heap are pointers to nodes, in the leaf - * case, or tables otherwise. One exception is index 0 which - * is a route counter. - */ - union { - struct srp node; - unsigned long count; - } *at_heap; /* Array of 2^(slen+1) items */ -}; -#define at_refcnt at_heap[0].count/* Refcounter (1 per different route) */ -#define at_default at_heap[1].node /* Default route (was in parent heap) */ - -/* Heap size for an ART table of stride length ``slen''. */ -#define AT_HEAPSIZE(slen) ((1 << ((slen) + 1)) * sizeof(void *)) - int art_bindex(struct art_table *, uint8_t *, int); void art_allot(struct art_table *at, int, struct art_node *, struct art_node *); Index: sys/net/art.h =================================================================== RCS file: /cvs/src/sys/net/art.h,v retrieving revision 1.17 diff -u -p -r1.17 art.h --- sys/net/art.h 28 Feb 2017 09:50:13 -0000 1.17 +++ sys/net/art.h 13 Mar 2019 07:03:53 -0000 @@ -20,6 +20,7 @@ #define _NET_ART_H_ #include <sys/rwlock.h> +#include <sys/srp.h> #define ART_MAXLVL 32 /* We currently use 32 levels for IPv6. */ @@ -35,6 +36,37 @@ struct art_root { uint8_t ar_off; /* Offset of the key in bytes */ unsigned int ar_rtableid; /* ID of this routing table */ }; + +#define ISLEAF(e) (((unsigned long)(e) & 1) == 0) +#define SUBTABLE(e) ((struct art_table *)((unsigned long)(e) & ~1)) +#define ASNODE(t) ((struct art_node *)((unsigned long)(t) | 1)) + +/* + * Allotment Table. + */ +struct art_table { + struct art_table *at_parent; /* Parent table */ + uint32_t at_index; /* Index in the parent table */ + uint32_t at_minfringe; /* Index that fringe begins */ + uint32_t at_level; /* Level of the table */ + uint8_t at_bits; /* Stride length of the table */ + uint8_t at_offset; /* Sum of parents' stride len */ + + /* + * Items stored in the heap are pointers to nodes, in the leaf + * case, or tables otherwise. One exception is index 0 which + * is a route counter. + */ + union { + struct srp node; + unsigned long count; + } *at_heap; /* Array of 2^(slen+1) items */ +}; +#define at_refcnt at_heap[0].count/* Refcounter (1 per different route) */ +#define at_default at_heap[1].node /* Default route (was in parent heap) */ + +/* Heap size for an ART table of stride length ``slen''. */ +#define AT_HEAPSIZE(slen) ((1 << ((slen) + 1)) * sizeof(void *)) /* * Forward declaration needed for the list of mpath routes Index: sys/net/route.h =================================================================== RCS file: /cvs/src/sys/net/route.h,v retrieving revision 1.174 diff -u -p -r1.174 route.h --- sys/net/route.h 13 Feb 2019 23:47:42 -0000 1.174 +++ sys/net/route.h 13 Mar 2019 07:03:54 -0000 @@ -93,12 +93,8 @@ struct rt_metrics { */ struct rtentry { -#if !defined(_KERNEL) && !defined(ART) - struct radix_node rt_nodes[2]; /* tree glue, and other values */ -#else struct sockaddr *rt_dest; /* destination */ SRPL_ENTRY(rtentry) rt_next; /* Next multipath entry to our dst. */ -#endif struct sockaddr *rt_gateway; /* value */ struct ifaddr *rt_ifa; /* the answer: interface addr to use */ caddr_t rt_llinfo; /* pointer to link level info cache or @@ -115,9 +111,7 @@ struct rtentry { unsigned int rt_ifidx; /* the answer: interface to use */ unsigned int rt_flags; /* up/down?, host/net */ int rt_refcnt; /* # held references */ -#if defined(_KERNEL) || defined(ART) int rt_plen; /* prefix length */ -#endif uint16_t rt_labelid; /* route label ID */ uint8_t rt_priority; /* routing priority to use */ }; Index: sys/net/rtable.h =================================================================== RCS file: /cvs/src/sys/net/rtable.h,v retrieving revision 1.22 diff -u -p -r1.22 rtable.h --- sys/net/rtable.h 23 Nov 2018 16:24:11 -0000 1.22 +++ sys/net/rtable.h 13 Mar 2019 07:03:54 -0000 @@ -19,20 +19,6 @@ #ifndef _NET_RTABLE_H_ #define _NET_RTABLE_H_ -#if !defined(_KERNEL) && !defined(ART) - -/* - * Traditional BSD routing table implementation based on a radix tree. - */ -#include <net/radix.h> - -#define rt_key(rt) (((struct sockaddr *)(rt)->rt_nodes[0].rn_key)) -#define rt_mask(rt) (((struct sockaddr *)(rt)->rt_nodes[0].rn_mask)) -#define rt_plen(rt) (rtable_satoplen(rt_key(rt)->sa_family, rt_mask(rt))) -#define RT_ROOT(rt) ((rt)->rt_nodes[0].rn_flags & RNF_ROOT) - -#else /* defined(_KERNEL) || defined(ART) */ - /* * Newer routing table implementation based on ART (Allotment Routing * Table). @@ -42,8 +28,6 @@ #define rt_key(rt) ((rt)->rt_dest) #define rt_plen(rt) ((rt)->rt_plen) #define RT_ROOT(rt) (0) - -#endif /* defined(_KERNEL) || defined(ART) */ int rtable_satoplen(sa_family_t, struct sockaddr *); Index: sys/sys/srp.h =================================================================== RCS file: /cvs/src/sys/sys/srp.h,v retrieving revision 1.13 diff -u -p -r1.13 srp.h --- sys/sys/srp.h 20 Nov 2016 11:40:58 -0000 1.13 +++ sys/sys/srp.h 13 Mar 2019 07:03:54 -0000 @@ -70,6 +70,13 @@ struct srpl { struct srp sl_head; }; +#define SRPL_HEAD(name, type) struct srpl + +#define SRPL_ENTRY(type) \ +struct { \ + struct srp se_next; \ +} + #ifdef _KERNEL void srp_startup(void); @@ -102,13 +109,6 @@ void srpl_rc_init(struct srpl_rc *, voi void (*)(void *, void *), void *); #define SRPL_INIT(_sl) srp_init(&(_sl)->sl_head) - -#define SRPL_HEAD(name, type) struct srpl - -#define SRPL_ENTRY(type) \ -struct { \ - struct srp se_next; \ -} #define SRPL_FIRST(_sr, _sl) srp_enter((_sr), &(_sl)->sl_head) #define SRPL_NEXT(_sr, _e, _ENTRY) srp_enter((_sr), &(_e)->_ENTRY.se_next) Index: usr.bin/netstat/main.c =================================================================== RCS file: /cvs/src/usr.bin/netstat/main.c,v retrieving revision 1.115 diff -u -p -r1.115 main.c --- usr.bin/netstat/main.c 5 Mar 2019 06:47:12 -0000 1.115 +++ usr.bin/netstat/main.c 13 Mar 2019 07:03:54 -0000 @@ -54,14 +54,12 @@ #include "netstat.h" struct nlist nl[] = { -#define N_RTREE 0 - { "_rt_tables"}, -#define N_RTMASK 1 - { "_mask_rnhead" }, -#define N_AF2RTAFIDX 2 - { "_af2rtafidx" }, -#define N_RTBLIDMAX 3 - { "_rtbl_id_max" }, +#define N_AFMAP 0 + { "_afmap"}, +#define N_AF2IDX 1 + { "_af2idx" }, +#define N_AF2IDXMAX 2 + { "_af2idx_max" }, { "" } }; @@ -104,7 +102,7 @@ struct protox *protoprotox[] = { static void usage(void); static struct protox *name2protox(char *); static struct protox *knownname(char *); -u_int gettable(const char *); +void gettable(u_int); kvm_t *kvmd; @@ -168,8 +166,6 @@ main(int argc, char *argv[]) af = AF_UNIX; else if (strcmp(optarg, "mpls") == 0) af = AF_MPLS; - else if (strcmp(optarg, "mask") == 0) - af = 0xff; else { (void)fprintf(stderr, "%s: %s: unknown address family\n", @@ -236,8 +232,10 @@ main(int argc, char *argv[]) ++sflag; break; case 'T': + tableid = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); + if (errstr) + errx(1, "invalid table id: %s", errstr); Tflag = 1; - tableid = gettable(optarg); break; case 't': tflag = 1; @@ -341,11 +339,13 @@ main(int argc, char *argv[]) errx(1, "no namelist"); } + if (!need_nlist && Tflag) + gettable(tableid); + if (rflag) { if (Aflag || nlistf != NULL || memf != NULL) - routepr(nl[N_RTREE].n_value, nl[N_RTMASK].n_value, - nl[N_AF2RTAFIDX].n_value, nl[N_RTBLIDMAX].n_value, - tableid); + routepr(nl[N_AFMAP].n_value, nl[N_AF2IDX].n_value, + nl[N_AF2IDXMAX].n_value, tableid); else p_rttables(af, tableid); exit(0); @@ -458,18 +458,12 @@ usage(void) exit(1); } -u_int -gettable(const char *s) +void +gettable(u_int tableid) { - const char *errstr; struct rt_tableinfo info; int mib[6]; size_t len; - u_int tableid; - - tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr); - if (errstr) - errx(1, "invalid table id: %s", errstr); mib[0] = CTL_NET; mib[1] = PF_ROUTE; @@ -481,6 +475,4 @@ gettable(const char *s) len = sizeof(info); if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) err(1, "routing table %d", tableid); - - return (tableid); } Index: usr.bin/netstat/netstat.h =================================================================== RCS file: /cvs/src/usr.bin/netstat/netstat.h,v retrieving revision 1.73 diff -u -p -r1.73 netstat.h --- usr.bin/netstat/netstat.h 4 Mar 2019 21:32:26 -0000 1.73 +++ usr.bin/netstat/netstat.h 13 Mar 2019 07:03:54 -0000 @@ -138,7 +138,7 @@ char *routename4(in_addr_t); char *netname(struct sockaddr *, struct sockaddr *); char *netname4(in_addr_t, in_addr_t); char *mpls_op(u_int32_t); -void routepr(u_long, u_long, u_long, u_long, u_int); +void routepr(u_long, u_long, u_long, u_int); void nsprotopr(u_long, char *); Index: usr.bin/netstat/route.c =================================================================== RCS file: /cvs/src/usr.bin/netstat/route.c,v retrieving revision 1.101 diff -u -p -r1.101 route.c --- usr.bin/netstat/route.c 15 Sep 2016 01:01:07 -0000 1.101 +++ usr.bin/netstat/route.c 13 Mar 2019 07:03:54 -0000 @@ -55,95 +55,90 @@ #include "netstat.h" -/* alignment constraint for routing socket */ -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) -#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) - -struct radix_node_head ***rt_head; -struct radix_node_head ***rnt; -struct radix_node_head *rt_tables[AF_MAX+1]; /* provides enough space */ -u_int8_t af2rtafidx[AF_MAX+1]; - static union { struct sockaddr u_sa; u_int32_t u_data[64]; int u_dummy; /* force word-alignment */ } pt_u; -int do_rtent = 0; struct rtentry rtentry; -struct radix_node rnode; -struct radix_mask rmask; static struct sockaddr *kgetsa(struct sockaddr *); -static void p_tree(struct radix_node *); -static void p_rtnode(void); -static void p_rtflags(u_char); +static struct sockaddr *plentosa(sa_family_t, int, struct sockaddr *); +static struct art_node *getdefault(struct art_table *); +static void p_table(struct art_table *); +static void p_artnode(struct art_node *); static void p_krtentry(struct rtentry *); /* * Print routing tables. */ void -routepr(u_long rtree, u_long mtree, u_long af2idx, u_long rtbl_id_max, - u_int tableid) +routepr(u_long afmap, u_long af2idx, u_long af2idx_max, u_int tableid) { - struct radix_node_head *rnh, head; - int i, idxmax = 0; - u_int rtidxmax; + struct art_root ar; + struct art_node *node; + struct srp *afm_head, *afm; + struct { + unsigned int limit; + void **tbl; + } map; + void **tbl; + int i; + uint8_t af2i[AF_MAX+1]; + uint8_t af2i_max; printf("Routing tables\n"); - if (rtree == 0 || af2idx == 0) { - printf("rt_tables: symbol not in namelist\n"); + if (afmap == 0 || af2idx == 0 || af2idx_max == 0) { + printf("symbol not in namelist\n"); return; } - kread((u_long)rtree, &rt_head, sizeof(rt_head)); - kread((u_long)rtbl_id_max, &rtidxmax, sizeof(rtidxmax)); - kread((long)af2idx, &af2rtafidx, sizeof(af2rtafidx)); - - for (i = 0; i <= AF_MAX; i++) { - if (af2rtafidx[i] > idxmax) - idxmax = af2rtafidx[i]; - } + kread(afmap, &afm_head, sizeof(afm_head)); + kread(af2idx, af2i, sizeof(af2i)); + kread(af2idx_max, &af2i_max, sizeof(af2i_max)); - if ((rnt = calloc(rtidxmax + 1, sizeof(struct radix_node_head **))) == - NULL) + if ((afm = calloc(af2i_max + 1, sizeof(*afm))) == NULL) err(1, NULL); - kread((u_long)rt_head, rnt, (rtidxmax + 1) * - sizeof(struct radix_node_head **)); - if (tableid > rtidxmax || rnt[tableid] == NULL) { - printf("Bad table %u\n", tableid); - return; - } - kread((u_long)rnt[tableid], rt_tables, (idxmax + 1) * sizeof(rnh)); + kread((u_long)afm_head, afm, (af2i_max + 1) * sizeof(*afm)); - for (i = 0; i <= AF_MAX; i++) { - if (i == AF_UNSPEC) { - if (Aflag && (af == AF_UNSPEC || af == 0xff)) { - kread(mtree, &rnh, sizeof(rnh)); - kread((u_long)rnh, &head, sizeof(head)); - printf("Netmasks:\n"); - p_tree(head.rnh_treetop); - } + for (i = 1; i <= AF_MAX; i++) { + if (af != AF_UNSPEC && af != i) continue; - } - if (af2rtafidx[i] == 0) - /* no table for this AF */ + if (af2i[i] == 0 || afm[af2i[i]].ref == NULL) continue; - if ((rnh = rt_tables[af2rtafidx[i]]) == NULL) + + kread((u_long)afm[af2i[i]].ref, &map, sizeof(map)); + if (tableid >= map.limit) continue; - kread((u_long)rnh, &head, sizeof(head)); - if (af == AF_UNSPEC || af == i) { - pr_family(i); - do_rtent = 1; - pr_rthdr(i, Aflag); - p_tree(head.rnh_treetop); - } + + if ((tbl = calloc(map.limit, sizeof(*tbl))) == NULL) + err(1, NULL); + + kread((u_long)map.tbl, tbl, map.limit * sizeof(*tbl)); + if (tbl[tableid] == NULL) + continue; + + kread((u_long)tbl[tableid], &ar, sizeof(ar)); + + free(tbl); + + if (ar.ar_root.ref == NULL) + continue; + + pr_family(i); + pr_rthdr(i, Aflag); + + node = getdefault(ar.ar_root.ref); + if (node != NULL) + p_artnode(node); + + p_table(ar.ar_root.ref); } + + free(afm); } static struct sockaddr * @@ -156,106 +151,128 @@ kgetsa(struct sockaddr *dst) return (&pt_u.u_sa); } -static void -p_tree(struct radix_node *rn) +static struct sockaddr * +plentosa(sa_family_t af, int plen, struct sockaddr *sa_mask) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa_mask; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa_mask; + uint8_t *p; + int i; + + if (plen < 0) + return (NULL); + + memset(sa_mask, 0, sizeof(struct sockaddr_storage)); + + switch (af) { + case AF_INET: + if (plen > 32) + return (NULL); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + memset(&sin->sin_addr, 0, sizeof(sin->sin_addr)); + p = (uint8_t *)&sin->sin_addr; + break; + case AF_INET6: + if (plen > 128) + return (NULL); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + memset(&sin6->sin6_addr.s6_addr, 0, sizeof(sin6->sin6_addr.s6_addr)); + p = sin6->sin6_addr.s6_addr; + break; + default: + return (NULL); + } + + for (i = 0; i < plen / 8; i++) + p[i] = 0xff; + if (plen % 8) + p[i] = (0xff00 >> (plen % 8)) & 0xff; -again: - kread((u_long)rn, &rnode, sizeof(rnode)); - if (rnode.rn_b < 0) { - if (Aflag) - printf("%-16p ", rn); - if (rnode.rn_flags & RNF_ROOT) { - if (Aflag) - printf("(root node)%s", - rnode.rn_dupedkey ? " =>\n" : "\n"); - } else if (do_rtent) { - kread((u_long)rn, &rtentry, sizeof(rtentry)); - p_krtentry(&rtentry); - if (Aflag) - p_rtnode(); - } else { - p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key), - 0, 0, 44); - putchar('\n'); - } - if ((rn = rnode.rn_dupedkey)) - goto again; - } else { - if (Aflag && do_rtent) { - printf("%-16p ", rn); - p_rtnode(); - } - rn = rnode.rn_r; - p_tree(rnode.rn_l); - p_tree(rn); - } + return (sa_mask); } -static void -p_rtflags(u_char flags) +static struct art_node * +getdefault(struct art_table *at) { - putchar('<'); - if (flags & RNF_NORMAL) - putchar('N'); - if (flags & RNF_ROOT) - putchar('R'); - if (flags & RNF_ACTIVE) - putchar('A'); - if (flags & ~(RNF_NORMAL | RNF_ROOT | RNF_ACTIVE)) - printf("/0x%02x", flags); - putchar('>'); -} + struct art_node *node; + struct art_table table; + union { + struct srp node; + unsigned long count; + } *heap; + + kread((u_long)at, &table, sizeof(table)); + heap = calloc(1, AT_HEAPSIZE(table.at_bits)); + kread((u_long)table.at_heap, heap, AT_HEAPSIZE(table.at_bits)); + + node = heap[1].node.ref; -char nbuf[25]; + free(heap); + + return (node); +} static void -p_rtnode(void) +p_table(struct art_table *at) { - struct radix_mask *rm = rnode.rn_mklist; - - if (rnode.rn_b < 0) { - snprintf(nbuf, sizeof nbuf, " => %p", rnode.rn_dupedkey); - printf("\t (%p)%s", rnode.rn_p, rnode.rn_dupedkey ? nbuf : ""); - if (rnode.rn_mask) { - printf(" mask "); - p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask), - 0, 0, -1); - } else if (rm == NULL) { - putchar('\n'); - return; + struct art_node *next, *node; + struct art_table *nat, table; + union { + struct srp node; + unsigned long count; + } *heap; + int i, j; + + kread((u_long)at, &table, sizeof(table)); + heap = calloc(1, AT_HEAPSIZE(table.at_bits)); + kread((u_long)table.at_heap, heap, AT_HEAPSIZE(table.at_bits)); + + for (j = 1; j < table.at_minfringe; j += 2) { + for (i = (j > 2) ? j : 2; i < table.at_minfringe; i <<= 1) { + next = heap[i >> 1].node.ref; + node = heap[i].node.ref; + if (node != NULL && node != next) + p_artnode(node); } - } else { - snprintf(nbuf, sizeof nbuf, "(%d)", rnode.rn_b); - printf("%6.6s (%p) %16p : %16p", nbuf, - rnode.rn_p, rnode.rn_l, rnode.rn_r); - } - - putchar(' '); - p_rtflags(rnode.rn_flags); - - while (rm) { - kread((u_long)rm, &rmask, sizeof(rmask)); - snprintf(nbuf, sizeof nbuf, " %d refs, ", rmask.rm_refs); - printf("\n\tmk = %p {(%d),%s", rm, -1 - rmask.rm_b, - rmask.rm_refs ? nbuf : " "); - p_rtflags(rmask.rm_flags); - printf(", "); - if (rmask.rm_flags & RNF_NORMAL) { - struct radix_node rnode_aux; - - printf("leaf = %p ", rmask.rm_leaf); - kread((u_long)rmask.rm_leaf, &rnode_aux, sizeof(rnode_aux)); - p_sockaddr(kgetsa((struct sockaddr *)rnode_aux.rn_mask), - 0, 0, -1); + } + + for (i = table.at_minfringe; i < table.at_minfringe << 1; i++) { + next = heap[i >> 1].node.ref; + node = heap[i].node.ref; + if (!ISLEAF(node)) { + nat = SUBTABLE(node); + node = getdefault(nat); } else - p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask), - 0, 0, -1); - putchar('}'); - if ((rm = rmask.rm_mklist)) - printf(" ->"); + nat = NULL; + + if (node != NULL && node != next) + p_artnode(node); + + if (nat != NULL) + p_table(nat); + } + + free(heap); +} + +static void +p_artnode(struct art_node *an) +{ + struct art_node node; + struct rtentry *rt; + + kread((u_long)an, &node, sizeof(node)); + rt = node.an_rtlist.sl_head.ref; + + while (rt != NULL) { + kread((u_long)rt, &rtentry, sizeof(rtentry)); + if (Aflag) + printf("%-16p ", rt); + p_krtentry(&rtentry); + rt = rtentry.rt_next.se_next.ref; } - putchar('\n'); } static void @@ -274,28 +291,21 @@ p_krtentry(struct rtentry *rt) return; } - if (rt_mask(rt)) { - bcopy(kgetsa(rt_mask(rt)), mask, sizeof(struct sockaddr)); - if (sa->sa_len > sizeof(struct sockaddr)) - bcopy(kgetsa(rt_mask(rt)), mask, sa->sa_len); - } else - mask = 0; + mask = plentosa(sa->sa_family, rt_plen(rt), mask); p_addr(sa, mask, rt->rt_flags); p_gwaddr(kgetsa(rt->rt_gateway), sa->sa_family); p_flags(rt->rt_flags, "%-6.6s "); - printf("%5u %8lld ", rt->rt_refcnt, rt->rt_use); + printf("%5u %8lld ", rt->rt_refcnt - 1, rt->rt_use); if (rt->rt_rmx.rmx_mtu) printf("%5u ", rt->rt_rmx.rmx_mtu); else printf("%5s ", "-"); putchar((rt->rt_rmx.rmx_locks & RTV_MTU) ? 'L' : ' '); - printf(" %2d", rt->rt_priority); + printf(" %2d", rt->rt_priority & RTP_MASK); - if (rt->rt_ifidx != 0) { - printf(" if%d%s", rt->rt_ifidx, - rt->rt_nodes[0].rn_dupedkey ? " =>" : ""); - } + if (rt->rt_ifidx != 0) + printf(" if%d", rt->rt_ifidx); putchar('\n'); if (vflag) printf("\texpire %10lld%c\n",