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