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