diff --git a/usr.bin/netstat/nhops.c b/usr.bin/netstat/nhops.c index f4c76f54a420..1db3c2d37167 100644 --- a/usr.bin/netstat/nhops.c +++ b/usr.bin/netstat/nhops.c @@ -1,479 +1,478 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" /* column widths; each followed by one space */ +#define WID_IF_DEFAULT 12 /* width of netif column */ #ifndef INET6 #define WID_DST_DEFAULT(af) 18 /* width of destination column */ #define WID_GW_DEFAULT(af) 18 /* width of gateway column */ -#define WID_IF_DEFAULT(af) (Wflag ? 10 : 8) /* width of netif column */ #else #define WID_DST_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18) #define WID_GW_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18) -#define WID_IF_DEFAULT(af) ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8)) #endif /*INET6*/ static int wid_dst; static int wid_gw; static int wid_flags; static int wid_pksent; static int wid_mtu; static int wid_if; static int wid_nhidx; static int wid_nhtype; static int wid_refcnt; static int wid_prepend; static struct bits nh_bits[] = { { NHF_REJECT, 'R', "reject" }, { NHF_BLACKHOLE,'B', "blackhole" }, { NHF_REDIRECT, 'r', "redirect" }, { NHF_GATEWAY, 'G', "gateway" }, { NHF_DEFAULT, 'd', "default" }, { NHF_BROADCAST,'b', "broadcast" }, { 0 , 0, NULL } }; static char *nh_types[] = { "empty", /* 0 */ "v4/resolve", /* 1 */ "v4/gw", "v6/resolve", "v6/gw" }; struct nhop_entry { char gw[64]; char ifname[IFNAMSIZ]; }; struct nhop_map { struct nhop_entry *ptr; size_t size; }; static struct nhop_map global_nhop_map; static struct nhop_entry *nhop_get(struct nhop_map *map, uint32_t idx); static struct ifmap_entry *ifmap; static size_t ifmap_size; static void print_sockaddr_buf(char *buf, size_t bufsize, const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, bufsize); break; case AF_INET6: inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf, bufsize); break; default: snprintf(buf, bufsize, "unknown:%d", sa->sa_family); break; } } static int print_addr(const char *name, const char *addr, int width) { char buf[128]; int protrusion; if (width < 0) { snprintf(buf, sizeof(buf), "{:%s/%%s} ", name); xo_emit(buf, addr); protrusion = 0; } else { if (Wflag != 0 || numeric_addr) { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ", -width, name); xo_emit(buf, addr); protrusion = strlen(addr) - width; if (protrusion < 0) protrusion = 0; } else { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ", -width, name); xo_emit(buf, width, addr); protrusion = 0; } } return (protrusion); } static void print_nhop_header(int af1 __unused) { if (Wflag) { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*.*s} {T:/%-*.*s} {T:/%*.*s} {T:/%*.*s} {T:/%*.*s} {T:/%*s}\n", wid_nhidx, wid_nhidx, "Idx", wid_nhtype, wid_nhtype, "Type", wid_dst, wid_dst, "IFA", wid_gw, wid_gw, "Gateway", wid_flags, wid_flags, "Flags", wid_pksent, wid_pksent, "Use", wid_mtu, wid_mtu, "Mtu", wid_if, wid_if, "Netif", wid_if, wid_if, "Addrif", wid_refcnt, wid_refcnt, "Refcnt", wid_prepend, "Prepend"); } else { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " " {T:/%*s}\n", wid_nhidx, wid_nhidx, "Idx", wid_dst, wid_dst, "IFA", wid_gw, wid_gw, "Gateway", wid_flags, wid_flags, "Flags", wid_if, wid_if, "Netif", wid_prepend, "Refcnt"); } } void nhop_map_update(struct nhop_map *map, uint32_t idx, char *gw, char *ifname) { if (idx >= map->size) { uint32_t new_size; size_t sz; if (map->size == 0) new_size = 32; else new_size = map->size * 2; if (new_size <= idx) new_size = roundup2(idx + 1, 32); sz = new_size * (sizeof(struct nhop_entry)); if ((map->ptr = realloc(map->ptr, sz)) == NULL) errx(2, "realloc(%zu) failed", sz); memset(&map->ptr[map->size], 0, (new_size - map->size) * sizeof(struct nhop_entry)); map->size = new_size; } strlcpy(map->ptr[idx].ifname, ifname, sizeof(map->ptr[idx].ifname)); strlcpy(map->ptr[idx].gw, gw, sizeof(map->ptr[idx].gw)); } static struct nhop_entry * nhop_get(struct nhop_map *map, uint32_t idx) { if (idx >= map->size) return (NULL); if (*map->ptr[idx].ifname == '\0') return (NULL); return &map->ptr[idx]; } static void print_nhop_entry_sysctl(const char *name, struct rt_msghdr *rtm, struct nhop_external *nh) { char buffer[128]; char iface_name[128]; int protrusion; char gw_addr[64]; struct nhop_addrs *na; struct sockaddr *sa_gw, *sa_ifa; xo_open_instance(name); snprintf(buffer, sizeof(buffer), "{[:-%d}{:index/%%lu}{]:} ", wid_nhidx); //xo_emit("{t:index/%-lu} ", wid_nhidx, nh->nh_idx); xo_emit(buffer, nh->nh_idx); if (Wflag) { char *cp = nh_types[nh->nh_type]; xo_emit("{t:type_str/%*s} ", wid_nhtype, cp); } memset(iface_name, 0, sizeof(iface_name)); if (nh->ifindex < (uint32_t)ifmap_size) { strlcpy(iface_name, ifmap[nh->ifindex].ifname, sizeof(iface_name)); if (*iface_name == '\0') strlcpy(iface_name, "---", sizeof(iface_name)); } na = (struct nhop_addrs *)((char *)nh + nh->nh_len); //inet_ntop(nh->nh_family, &nh->nh_src, src_addr, sizeof(src_addr)); //protrusion = p_addr("ifa", src_addr, wid_dst); sa_gw = (struct sockaddr *)((char *)na + na->gw_sa_off); sa_ifa = (struct sockaddr *)((char *)na + na->src_sa_off); protrusion = p_sockaddr("ifa", sa_ifa, NULL, RTF_HOST, wid_dst); if (nh->nh_flags & NHF_GATEWAY) { const char *cp; cp = fmt_sockaddr(sa_gw, NULL, RTF_HOST); strlcpy(gw_addr, cp, sizeof(gw_addr)); } else snprintf(gw_addr, sizeof(gw_addr), "%s/resolve", iface_name); protrusion = print_addr("gateway", gw_addr, wid_dst - protrusion); nhop_map_update(&global_nhop_map, nh->nh_idx, gw_addr, iface_name); snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ", wid_flags - protrusion); //p_nhflags(nh->nh_flags, buffer); print_flags_generic(rtm->rtm_flags, rt_bits, buffer, "rt_flags_pretty"); if (Wflag) { xo_emit("{t:use/%*lu} ", wid_pksent, nh->nh_pksent); xo_emit("{t:mtu/%*lu} ", wid_mtu, nh->nh_mtu); } //printf("IDX: %d IFACE: %s FAMILY: %d TYPE: %d FLAGS: %X GW \n"); if (Wflag) xo_emit("{t:interface-name/%*s}", wid_if, iface_name); else xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if, iface_name); memset(iface_name, 0, sizeof(iface_name)); if (nh->aifindex < (uint32_t)ifmap_size && nh->ifindex != nh->aifindex) { strlcpy(iface_name, ifmap[nh->aifindex].ifname, sizeof(iface_name)); if (*iface_name == '\0') strlcpy(iface_name, "---", sizeof(iface_name)); } if (Wflag) xo_emit("{t:address-interface-name/%*s}", wid_if, iface_name); xo_emit("{t:refcount/%*lu} ", wid_refcnt, nh->nh_refcount); if (Wflag && nh->prepend_len) { int max_bytes = MIN(nh->prepend_len, sizeof(buffer) / 2 - 1); for (int i = 0; i < max_bytes; i++) snprintf(&buffer[i * 2], 3, "%02X", nh->nh_prepend[i]); xo_emit(" {:nhop-prepend/%*s}", wid_prepend, buffer); } xo_emit("\n"); xo_close_instance(name); } static int cmp_nh_idx(const void *_a, const void *_b) { const struct nhops_map *a, *b; a = _a; b = _b; if (a->idx > b->idx) return (1); else if (a->idx < b->idx) return (-1); return (0); } void dump_nhops_sysctl(int fibnum, int af, struct nhops_dump *nd) { size_t needed; int mib[7]; char *buf, *next, *lim; struct rt_msghdr *rtm; struct nhop_external *nh; struct nhops_map *nh_map; size_t nh_count, nh_size; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = af; mib[4] = NET_RT_NHOP; mib[5] = 0; mib[6] = fibnum; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "sysctl: net.route.0.%d.nhdump.%d estimate", af, fibnum); if ((buf = malloc(needed)) == NULL) errx(2, "malloc(%lu)", (unsigned long)needed); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) err(1, "sysctl: net.route.0.%d.nhdump.%d", af, fibnum); lim = buf + needed; /* * nexhops are received unsorted. Collect everything first, sort and then display * sorted. */ nh_count = 0; nh_size = 16; nh_map = calloc(nh_size, sizeof(struct nhops_map)); for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; if (nh_count >= nh_size) { nh_size *= 2; nh_map = realloc(nh_map, nh_size * sizeof(struct nhops_map)); } nh = (struct nhop_external *)(rtm + 1); nh_map[nh_count].idx = nh->nh_idx; nh_map[nh_count].rtm = rtm; nh_count++; } if (nh_count > 0) qsort(nh_map, nh_count, sizeof(struct nhops_map), cmp_nh_idx); nd->nh_buf = buf; nd->nh_count = nh_count; nd->nh_map = nh_map; } static void print_nhops_sysctl(int fibnum, int af) { struct nhops_dump nd; struct nhop_external *nh; int fam; struct rt_msghdr *rtm; dump_nhops_sysctl(fibnum, af, &nd); xo_open_container("nhop-table"); xo_open_list("rt-family"); if (nd.nh_count > 0) { nh = (struct nhop_external *)(nd.nh_map[0].rtm + 1); fam = nh->nh_family; wid_dst = WID_GW_DEFAULT(fam); wid_gw = WID_GW_DEFAULT(fam); wid_nhidx = 5; wid_nhtype = 12; wid_refcnt = 6; wid_flags = 6; wid_pksent = 8; wid_mtu = 6; - wid_if = WID_IF_DEFAULT(fam); + wid_if = WID_IF_DEFAULT; xo_open_instance("rt-family"); pr_family(fam); xo_open_list("nh-entry"); print_nhop_header(fam); for (size_t i = 0; i < nd.nh_count; i++) { rtm = nd.nh_map[i].rtm; nh = (struct nhop_external *)(rtm + 1); print_nhop_entry_sysctl("nh-entry", rtm, nh); } xo_close_list("nh-entry"); xo_close_instance("rt-family"); } xo_close_list("rt-family"); xo_close_container("nhop-table"); free(nd.nh_buf); } static void p_nhflags(int f, const char *format) { struct bits *p; char *pretty_name = "nh_flags_pretty"; xo_emit(format, fmt_flags(nh_bits, f)); xo_open_list(pretty_name); for (p = nh_bits; p->b_mask; p++) if (p->b_mask & f) xo_emit("{le:nh_flags_pretty/%s}", p->b_name); xo_close_list(pretty_name); } void nhops_print(int fibnum, int af) { size_t intsize; int numfibs; intsize = sizeof(int); if (fibnum == -1 && sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1) fibnum = 0; if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) numfibs = 1; if (fibnum < 0 || fibnum > numfibs - 1) errx(EX_USAGE, "%d: invalid fib", fibnum); ifmap = prepare_ifmap(&ifmap_size); xo_open_container("route-nhop-information"); xo_emit("{T:Nexthop data}"); if (fibnum) xo_emit(" ({L:fib}: {:fib/%d})", fibnum); xo_emit("\n"); print_nhops_sysctl(fibnum, af); xo_close_container("route-nhop-information"); } diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c index 6152cbdc859f..93e882052de2 100644 --- a/usr.bin/netstat/route.c +++ b/usr.bin/netstat/route.c @@ -1,727 +1,726 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" #include "nl_defs.h" /* * Definitions for showing gateway flags. */ struct bits rt_bits[] = { { RTF_UP, 'U', "up" }, { RTF_GATEWAY, 'G', "gateway" }, { RTF_HOST, 'H', "host" }, { RTF_REJECT, 'R', "reject" }, { RTF_DYNAMIC, 'D', "dynamic" }, { RTF_MODIFIED, 'M', "modified" }, { RTF_DONE, 'd', "done" }, /* Completed -- for routing msgs only */ { RTF_XRESOLVE, 'X', "xresolve" }, { RTF_STATIC, 'S', "static" }, { RTF_PROTO1, '1', "proto1" }, { RTF_PROTO2, '2', "proto2" }, { RTF_PROTO3, '3', "proto3" }, { RTF_BLACKHOLE,'B', "blackhole" }, { RTF_BROADCAST,'b', "broadcast" }, #ifdef RTF_LLINFO { RTF_LLINFO, 'L', "llinfo" }, #endif { 0 , 0, NULL } }; #ifdef WITHOUT_NETLINK static struct ifmap_entry *ifmap; static size_t ifmap_size; #endif static struct timespec uptime; static const char *netname4(in_addr_t, in_addr_t); #ifdef INET6 static const char *netname6(struct sockaddr_in6 *, struct sockaddr_in6 *); #endif #ifdef WITHOUT_NETLINK static void p_rtable_sysctl(int, int); static void p_rtentry_sysctl(const char *name, struct rt_msghdr *); #endif static void domask(char *, size_t, u_long); const uint32_t rt_default_weight = RT_DEFAULT_WEIGHT; /* * Print routing tables. */ void routepr(int fibnum, int af) { size_t intsize; int numfibs; if (live == 0) return; intsize = sizeof(int); if (fibnum == -1 && sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1) fibnum = 0; if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) numfibs = 1; if (fibnum < 0 || fibnum > numfibs - 1) errx(EX_USAGE, "%d: invalid fib", fibnum); /* * Since kernel & userland use different timebase * (time_uptime vs time_second) and we are reading kernel memory * directly we should do rt_expire --> expire_time conversion. */ if (clock_gettime(CLOCK_UPTIME, &uptime) < 0) err(EX_OSERR, "clock_gettime() failed"); xo_open_container("route-information"); xo_emit("{T:Routing tables}"); if (fibnum) xo_emit(" ({L:fib}: {:fib/%d})", fibnum); xo_emit("\n"); #ifdef WITHOUT_NETLINK p_rtable_sysctl(fibnum, af); #else p_rtable_netlink(fibnum, af); #endif xo_close_container("route-information"); } /* * Print address family header before a section of the routing table. */ void pr_family(int af1) { const char *afname; switch (af1) { case AF_INET: afname = "Internet"; break; #ifdef INET6 case AF_INET6: afname = "Internet6"; break; #endif /*INET6*/ case AF_ISO: afname = "ISO"; break; case AF_CCITT: afname = "X.25"; break; case AF_NETGRAPH: afname = "Netgraph"; break; default: afname = NULL; break; } if (afname) xo_emit("\n{k:address-family/%s}:\n", afname); else xo_emit("\n{L:Protocol Family} {k:address-family/%d}:\n", af1); } /* column widths; each followed by one space */ +#define WID_IF_DEFAULT 12 /* width of netif column */ #ifndef INET6 #define WID_DST_DEFAULT(af) 18 /* width of destination column */ #define WID_GW_DEFAULT(af) 18 /* width of gateway column */ -#define WID_IF_DEFAULT(af) (Wflag ? 10 : 8) /* width of netif column */ #else #define WID_DST_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18) #define WID_GW_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18) -#define WID_IF_DEFAULT(af) ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8)) #endif /*INET6*/ struct _wid wid; /* * Print header for routing table columns. */ void pr_rthdr(int af1 __unused) { if (Wflag) { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n", wid.dst, wid.dst, "Destination", wid.gw, wid.gw, "Gateway", wid.flags, wid.flags, "Flags", wid.mtu, wid.mtu, "Nhop#", wid.mtu, wid.mtu, "Mtu", wid.iface, wid.iface, "Netif", wid.expire, "Expire"); } else { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*s}\n", wid.dst, wid.dst, "Destination", wid.gw, wid.gw, "Gateway", wid.flags, wid.flags, "Flags", wid.iface, wid.iface, "Netif", wid.expire, "Expire"); } } void set_wid(int fam) { wid.dst = WID_DST_DEFAULT(fam); wid.gw = WID_GW_DEFAULT(fam); wid.flags = 6; wid.pksent = 8; wid.mtu = 6; - wid.iface = WID_IF_DEFAULT(fam); + wid.iface = WID_IF_DEFAULT; wid.expire = 6; } #ifdef WITHOUT_NETLINK static void p_rtable_sysctl(int fibnum, int af) { size_t needed; int mib[7]; char *buf, *next, *lim; struct rt_msghdr *rtm; struct sockaddr *sa; int fam = AF_UNSPEC; int need_table_close = false; ifmap = prepare_ifmap(&ifmap_size); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = af; mib[4] = NET_RT_DUMP; mib[5] = 0; mib[6] = fibnum; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "sysctl: net.route.0.%d.dump.%d estimate", af, fibnum); if ((buf = malloc(needed)) == NULL) errx(2, "malloc(%lu)", (unsigned long)needed); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) err(1, "sysctl: net.route.0.%d.dump.%d", af, fibnum); lim = buf + needed; xo_open_container("route-table"); xo_open_list("rt-family"); for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; /* * Peek inside header to determine AF */ sa = (struct sockaddr *)(rtm + 1); /* Only print family first time. */ if (fam != sa->sa_family) { if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } need_table_close = true; fam = sa->sa_family; set_wid(fam); xo_open_instance("rt-family"); pr_family(fam); xo_open_list("rt-entry"); pr_rthdr(fam); } p_rtentry_sysctl("rt-entry", rtm); } if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } xo_close_list("rt-family"); xo_close_container("route-table"); free(buf); } static void p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm) { struct sockaddr *sa, *addr[RTAX_MAX]; char buffer[128]; char prettyname[128]; int i, protrusion; xo_open_instance(name); sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (rtm->rtm_addrs & (1 << i)) { addr[i] = sa; sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); } } protrusion = p_sockaddr("destination", addr[RTAX_DST], addr[RTAX_NETMASK], rtm->rtm_flags, wid.dst); protrusion = p_sockaddr("gateway", addr[RTAX_GATEWAY], NULL, RTF_HOST, wid.gw - protrusion); snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ", wid.flags - protrusion); p_flags(rtm->rtm_flags, buffer); /* Output path weight as non-visual property */ xo_emit("{e:weight/%u}", rtm->rtm_rmx.rmx_weight); if (Wflag) { /* XXX: use=0? */ xo_emit("{t:nhop/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_nhidx); if (rtm->rtm_rmx.rmx_mtu != 0) xo_emit("{t:mtu/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_mtu); else xo_emit("{P:/%*s} ", wid.mtu, ""); } memset(prettyname, 0, sizeof(prettyname)); if (rtm->rtm_index < ifmap_size) { strlcpy(prettyname, ifmap[rtm->rtm_index].ifname, sizeof(prettyname)); if (*prettyname == '\0') strlcpy(prettyname, "---", sizeof(prettyname)); } if (Wflag) xo_emit("{t:interface-name/%*s}", wid.iface, prettyname); else xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface, prettyname); if (rtm->rtm_rmx.rmx_expire) { time_t expire_time; if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0) xo_emit(" {:expire-time/%*d}", wid.expire, (int)expire_time); } xo_emit("\n"); xo_close_instance(name); } #endif int p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask, int flags, int width) { const char *cp; char buf[128]; int protrusion; cp = fmt_sockaddr(sa, mask, flags); if (width < 0) { snprintf(buf, sizeof(buf), "{:%s/%%s} ", name); xo_emit(buf, cp); protrusion = 0; } else { if (Wflag != 0 || numeric_addr) { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ", -width, name); xo_emit(buf, cp); protrusion = strlen(cp) - width; if (protrusion < 0) protrusion = 0; } else { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ", -width, name); xo_emit(buf, width, cp); protrusion = 0; } } return (protrusion); } const char * fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags) { static char buf[128]; const char *cp; if (sa == NULL) return ("null"); switch(sa->sa_family) { #ifdef INET6 case AF_INET6: /* * The sa6->sin6_scope_id must be filled here because * this sockaddr is extracted from kmem(4) directly * and has KAME-specific embedded scope id in * sa6->sin6_addr.s6_addr[2]. */ in6_fillscopeid(satosin6(sa)); /* FALLTHROUGH */ #endif /*INET6*/ case AF_INET: if (flags & RTF_HOST) cp = routename(sa, numeric_addr); else if (mask) cp = netname(sa, mask); else cp = netname(sa, NULL); break; case AF_NETGRAPH: { strlcpy(buf, ((struct sockaddr_ng *)sa)->sg_data, sizeof(buf)); cp = buf; break; } case AF_LINK: { #if 0 struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; /* Interface route. */ if (sdl->sdl_nlen) cp = sdl->sdl_data; else #endif cp = routename(sa, 1); break; } default: { u_char *s = (u_char *)sa->sa_data, *slim; char *cq, *cqlim; cq = buf; slim = sa->sa_len + (u_char *) sa; cqlim = cq + sizeof(buf) - sizeof(" ffff"); snprintf(cq, sizeof(buf), "(%d)", sa->sa_family); cq += strlen(cq); while (s < slim && cq < cqlim) { snprintf(cq, sizeof(" ff"), " %02x", *s++); cq += strlen(cq); if (s < slim) { snprintf(cq, sizeof("ff"), "%02x", *s++); cq += strlen(cq); } } cp = buf; } } return (cp); } void p_flags(int f, const char *format) { print_flags_generic(f, rt_bits, format, "flags_pretty"); } char * routename(struct sockaddr *sa, int flags) { static char line[NI_MAXHOST]; int error, f; f = (flags) ? NI_NUMERICHOST : 0; error = getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, f); if (error) { const void *src; switch (sa->sa_family) { #ifdef INET case AF_INET: src = &satosin(sa)->sin_addr; break; #endif /* INET */ #ifdef INET6 case AF_INET6: src = &satosin6(sa)->sin6_addr; break; #endif /* INET6 */ default: return(line); } inet_ntop(sa->sa_family, src, line, sizeof(line) - 1); return (line); } trimdomain(line, strlen(line)); return (line); } #define NSHIFT(m) ( \ (m) == IN_CLASSA_NET ? IN_CLASSA_NSHIFT : \ (m) == IN_CLASSB_NET ? IN_CLASSB_NSHIFT : \ (m) == IN_CLASSC_NET ? IN_CLASSC_NSHIFT : \ 0) static void domask(char *dst, size_t buflen, u_long mask) { int b, i; if (mask == 0) { *dst = '\0'; return; } i = 0; for (b = 0; b < 32; b++) if (mask & (1 << b)) { int bb; i = b; for (bb = b+1; bb < 32; bb++) if (!(mask & (1 << bb))) { i = -1; /* noncontig */ break; } break; } if (i == -1) snprintf(dst, buflen, "&0x%lx", mask); else snprintf(dst, buflen, "/%d", 32-i); } /* * Return the name of the network whose address is given. */ const char * netname(struct sockaddr *sa, struct sockaddr *mask) { switch (sa->sa_family) { case AF_INET: if (mask != NULL) return (netname4(satosin(sa)->sin_addr.s_addr, satosin(mask)->sin_addr.s_addr)); else return (netname4(satosin(sa)->sin_addr.s_addr, INADDR_ANY)); break; #ifdef INET6 case AF_INET6: return (netname6(satosin6(sa), satosin6(mask))); #endif /* INET6 */ default: return (NULL); } } static const char * netname4(in_addr_t in, in_addr_t mask) { char *cp = 0; static char line[MAXHOSTNAMELEN + sizeof("&0xffffffff")]; char nline[INET_ADDRSTRLEN]; struct netent *np = 0; in_addr_t i; if (in == INADDR_ANY && mask == 0) { strlcpy(line, "default", sizeof(line)); return (line); } /* It is ok to supply host address. */ in &= mask; i = ntohl(in); if (!numeric_addr && i) { np = getnetbyaddr(i >> NSHIFT(ntohl(mask)), AF_INET); if (np != NULL) { cp = np->n_name; trimdomain(cp, strlen(cp)); } } if (cp != NULL) strlcpy(line, cp, sizeof(line)); else { inet_ntop(AF_INET, &in, nline, sizeof(nline)); strlcpy(line, nline, sizeof(line)); domask(line + strlen(line), sizeof(line) - strlen(line), ntohl(mask)); } return (line); } #undef NSHIFT #ifdef INET6 void in6_fillscopeid(struct sockaddr_in6 *sa6) { #if defined(__KAME__) /* * XXX: This is a special workaround for KAME kernels. * sin6_scope_id field of SA should be set in the future. */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_NODELOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_scope_id == 0) sa6->sin6_scope_id = ntohs(*(u_int16_t *)&sa6->sin6_addr.s6_addr[2]); sa6->sin6_addr.s6_addr[2] = sa6->sin6_addr.s6_addr[3] = 0; } #endif } /* Mask to length table. To check an invalid value, (length + 1) is used. */ static const u_char masktolen[256] = { [0xff] = 8 + 1, [0xfe] = 7 + 1, [0xfc] = 6 + 1, [0xf8] = 5 + 1, [0xf0] = 4 + 1, [0xe0] = 3 + 1, [0xc0] = 2 + 1, [0x80] = 1 + 1, [0x00] = 0 + 1, }; static const char * netname6(struct sockaddr_in6 *sa6, struct sockaddr_in6 *mask) { static char line[NI_MAXHOST + sizeof("/xxx") - 1]; struct sockaddr_in6 addr; char nline[NI_MAXHOST]; char maskbuf[sizeof("/xxx")]; u_char *p, *lim; u_char masklen; int i; bool illegal = false; if (mask) { p = (u_char *)&mask->sin6_addr; for (masklen = 0, lim = p + 16; p < lim; p++) { if (masktolen[*p] > 0) { /* -1 is required. */ masklen += (masktolen[*p] - 1); } else illegal = true; } if (illegal) xo_error("illegal prefixlen\n"); memcpy(&addr, sa6, sizeof(addr)); for (i = 0; i < 16; ++i) addr.sin6_addr.s6_addr[i] &= mask->sin6_addr.s6_addr[i]; sa6 = &addr; } else masklen = 128; if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr)) return("default"); getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, nline, sizeof(nline), NULL, 0, NI_NUMERICHOST); if (numeric_addr) strlcpy(line, nline, sizeof(line)); else getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line), NULL, 0, 0); if (numeric_addr || strcmp(line, nline) == 0) { snprintf(maskbuf, sizeof(maskbuf), "/%d", masklen); strlcat(line, maskbuf, sizeof(line)); } return (line); } #endif /*INET6*/ /* * Print routing statistics */ void rt_stats(void) { struct rtstat rtstat; u_long rtsaddr; if ((rtsaddr = nl[N_RTSTAT].n_value) == 0) { xo_emit("{W:rtstat: symbol not in namelist}\n"); return; } kread_counters(rtsaddr, (char *)&rtstat, sizeof (rtstat)); xo_emit("{T:routing}:\n"); #define p(f, m) if (rtstat.f || sflag <= 1) \ xo_emit(m, rtstat.f, plural(rtstat.f)) p(rts_badredirect, "\t{:bad-redirects/%ju} " "{N:/bad routing redirect%s}\n"); p(rts_dynamic, "\t{:dynamically-created/%ju} " "{N:/dynamically created route%s}\n"); p(rts_newgateway, "\t{:new-gateways/%ju} " "{N:/new gateway%s due to redirects}\n"); p(rts_unreach, "\t{:unreachable-destination/%ju} " "{N:/destination%s found unreachable}\n"); p(rts_wildcard, "\t{:wildcard-uses/%ju} " "{N:/use%s of a wildcard route}\n"); #undef p }