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