Merge branch 'vendor/FILE'
[dragonfly.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  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)route.c  8.6 (Berkeley) 4/28/95
34  * $FreeBSD: src/usr.bin/netstat/route.c,v 1.41.2.14 2002/07/17 02:22:22 kbyanc Exp $
35  * $DragonFly: src/usr.bin/netstat/route.c,v 1.13 2008/07/07 22:02:10 nant Exp $
36  */
37
38 #include <sys/kinfo.h>
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42
43 #include <net/ethernet.h>
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <net/if_dl.h>
47 #include <net/if_types.h>
48 #include <net/route.h>
49
50 #include <netinet/in.h>
51 #include <netipx/ipx.h>
52 #include <netatalk/at.h>
53 #include <netgraph/socket/ng_socket.h>
54
55 #include <netproto/mpls/mpls.h>
56
57 #include <sys/sysctl.h>
58
59 #include <arpa/inet.h>
60 #include <libutil.h>
61 #include <netdb.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <err.h>
67 #include <time.h>
68 #include <kinfo.h>
69 #include "netstat.h"
70
71 #define kget(p, d) (kread((u_long)(p), (char *)&(d), sizeof (d)))
72
73
74 /* alignment constraint for routing socket */
75 #define ROUNDUP(a) \
76        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
77 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
78
79 /*
80  * Definitions for showing gateway flags.
81  */
82 struct bits {
83         u_long  b_mask;
84         char    b_val;
85 } bits[] = {
86         { RTF_UP,       'U' },
87         { RTF_GATEWAY,  'G' },
88         { RTF_HOST,     'H' },
89         { RTF_REJECT,   'R' },
90         { RTF_DYNAMIC,  'D' },
91         { RTF_MODIFIED, 'M' },
92         { RTF_DONE,     'd' }, /* Completed -- for routing messages only */
93         { RTF_CLONING,  'C' },
94         { RTF_XRESOLVE, 'X' },
95         { RTF_LLINFO,   'L' },
96         { RTF_STATIC,   'S' },
97         { RTF_PROTO1,   '1' },
98         { RTF_PROTO2,   '2' },
99         { RTF_WASCLONED,'W' },
100         { RTF_PRCLONING,'c' },
101         { RTF_PROTO3,   '3' },
102         { RTF_BLACKHOLE,'B' },
103         { RTF_BROADCAST,'b' },
104         { RTF_MPLSOPS,  'm' },
105         { 0, 0 }
106 };
107
108 typedef union {
109         long    dummy;          /* Helps align structure. */
110         struct  sockaddr u_sa;
111         u_short u_data[128];
112 } sa_u;
113
114 static sa_u pt_u;
115
116 int     do_rtent = 0;
117 struct  rtentry rtentry;
118 struct  radix_node rnode;
119 struct  radix_mask rmask;
120 struct  radix_node_head *rt_tables[AF_MAX+1];
121
122 int     NewTree = 0;
123
124 static struct sockaddr *kgetsa (struct sockaddr *);
125 static void size_cols (int ef, struct radix_node *rn);
126 static void size_cols_tree (struct radix_node *rn);
127 static void size_cols_rtentry (struct rtentry *rt);
128 static void p_tree (struct radix_node *);
129 static void p_rtnode (void);
130 static void ntreestuff (void);
131 static void np_rtentry (struct rt_msghdr *);
132 static void p_sockaddr (struct sockaddr *, struct sockaddr *, int, int);
133 static const char *fmt_sockaddr (struct sockaddr *sa, struct sockaddr *mask,
134                                  int flags);
135 static void p_flags (int, const char *);
136 static const char *fmt_flags(int f);
137 static void p_rtentry (struct rtentry *);
138 static u_long forgemask (u_long);
139 static void domask (char *, u_long, u_long);
140 static const char *labelops(struct rtentry *);
141
142 /*
143  * Print routing tables.
144  */
145 void
146 routepr(u_long rtree)
147 {
148         struct radix_node_head *rnh, head;
149         int i;
150
151         printf("Routing tables\n");
152
153         if (Aflag == 0 && NewTree) {
154                 ntreestuff();
155         } else {
156                 if (rtree == 0) {
157                         printf("rt_tables: symbol not in namelist\n");
158                         return;
159                 }
160                 if (cpuflag >= 0) {
161                         /*
162                          * Severe hack.
163                          */
164                         rtree += cpuflag * (AF_MAX + 1) * sizeof(void *);
165                 }
166                 kget(rtree, rt_tables);
167                 for (i = 0; i <= AF_MAX; i++) {
168                         if ((rnh = rt_tables[i]) == 0)
169                                 continue;
170                         kget(rnh, head);
171                         if (i == AF_UNSPEC) {
172                                 if (Aflag && af == 0) {
173                                         printf("Netmasks:\n");
174                                         p_tree(head.rnh_treetop);
175                                 }
176                         } else if (af == AF_UNSPEC || af == i) {
177                                 size_cols(i, head.rnh_treetop);
178                                 pr_family(i);
179                                 do_rtent = 1;
180                                 pr_rthdr(i);
181                                 p_tree(head.rnh_treetop);
182                         }
183                 }
184         }
185 }
186
187 /*
188  * Print address family header before a section of the routing table.
189  */
190 void
191 pr_family(int af1)
192 {
193         const char *afname;
194
195         switch (af1) {
196         case AF_INET:
197                 afname = "Internet";
198                 break;
199 #ifdef INET6
200         case AF_INET6:
201                 afname = "Internet6";
202                 break;
203 #endif /*INET6*/
204         case AF_IPX:
205                 afname = "IPX";
206                 break;
207         case AF_ISO:
208                 afname = "ISO";
209                 break;
210         case AF_APPLETALK:
211                 afname = "AppleTalk";
212                 break;
213         case AF_CCITT:
214                 afname = "X.25";
215                 break;
216         case AF_NETGRAPH:
217                 afname = "Netgraph";
218                 break;
219         case AF_MPLS:
220                 afname = "MPLS";
221                 break;
222         default:
223                 afname = NULL;
224                 break;
225         }
226         if (afname)
227                 printf("\n%s:\n", afname);
228         else
229                 printf("\nProtocol Family %d:\n", af1);
230 }
231
232 /* column widths; each followed by one space */
233 #ifndef INET6
234 #define WID_DST_DEFAULT(af)     18      /* width of destination column */
235 #define WID_GW_DEFAULT(af)      18      /* width of gateway column */
236 #define WID_IF_DEFAULT(af)      (Wflag ? 8 : 6) /* width of netif column */
237 #else
238 #define WID_DST_DEFAULT(af) \
239         ((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18)
240 #define WID_GW_DEFAULT(af) \
241         ((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18)
242 #define WID_IF_DEFAULT(af)      ((af) == AF_INET6 ? 8 : (Wflag ? 8 :6))
243 #endif /*INET6*/
244
245 static int wid_dst;
246 static int wid_gw;
247 static int wid_flags;
248 static int wid_refs;
249 static int wid_use;
250 static int wid_mtu;
251 static int wid_if;
252 static int wid_expire;
253 static int wid_mplslops;
254
255 static void
256 size_cols(int ef, struct radix_node *rn)
257 {
258         wid_dst = WID_DST_DEFAULT(ef);
259         wid_gw = WID_GW_DEFAULT(ef);
260         wid_flags = 6;
261         wid_refs = 6;
262         wid_use = 8;
263         wid_mtu = 6;
264         wid_if = WID_IF_DEFAULT(ef);
265         wid_expire = 6;
266         wid_mplslops = 7;
267
268         if (Wflag)
269                 size_cols_tree(rn);
270 }
271
272 static void
273 size_cols_tree(struct radix_node *rn)
274 {
275 again:
276         kget(rn, rnode);
277         if (rnode.rn_bit < 0) {
278                 if ((rnode.rn_flags & RNF_ROOT) == 0) {
279                         kget(rn, rtentry);
280                         size_cols_rtentry(&rtentry);
281                 }
282                 if ((rn = rnode.rn_dupedkey))
283                         goto again;
284         } else {
285                 rn = rnode.rn_right;
286                 size_cols_tree(rnode.rn_left);
287                 size_cols_tree(rn);
288         }
289 }
290
291 static void
292 size_cols_rtentry(struct rtentry *rt)
293 {
294         static struct ifnet ifnet, *lastif;
295         struct rtentry parent;
296         static char buffer[100];
297         const char *bp;
298         struct sockaddr *sa;
299         sa_u addr, mask;
300         int len;
301
302         /*
303          * Don't print protocol-cloned routes unless -a.
304          */
305         if (rt->rt_flags & RTF_WASCLONED && !aflag) {
306                 kget(rt->rt_parent, parent);
307                 if (parent.rt_flags & RTF_PRCLONING)
308                         return;
309         }
310
311         bzero(&addr, sizeof(addr));
312         if ((sa = kgetsa(rt_key(rt))))
313                 bcopy(sa, &addr, sa->sa_len);
314         bzero(&mask, sizeof(mask));
315         if (rt_mask(rt) && (sa = kgetsa(rt_mask(rt))))
316                 bcopy(sa, &mask, sa->sa_len);
317         bp = fmt_sockaddr(&addr.u_sa, &mask.u_sa, rt->rt_flags);
318         len = strlen(bp);
319         wid_dst = MAX(len, wid_dst);
320
321         bp = fmt_sockaddr(kgetsa(rt->rt_gateway), NULL, RTF_HOST);
322         len = strlen(bp);
323         wid_gw = MAX(len, wid_gw);
324
325         bp = fmt_flags(rt->rt_flags);
326         len = strlen(bp);
327         wid_flags = MAX(len, wid_flags);
328
329         if (addr.u_sa.sa_family == AF_INET || Wflag) {
330                 len = snprintf(buffer, sizeof(buffer), "%ld", rt->rt_refcnt);
331                 wid_refs = MAX(len, wid_refs);
332                 len = snprintf(buffer, sizeof(buffer), "%lu", rt->rt_use);
333                 wid_use = MAX(len, wid_use);
334                 if (Wflag && rt->rt_rmx.rmx_mtu != 0) {
335                         len = snprintf(buffer, sizeof(buffer),
336                                        "%lu", rt->rt_rmx.rmx_mtu);
337                         wid_mtu = MAX(len, wid_mtu);
338                 }
339         }
340         if (rt->rt_ifp) {
341                 if (rt->rt_ifp != lastif) {
342                         kget(rt->rt_ifp, ifnet);
343                         lastif = rt->rt_ifp;
344                         len = strlen(ifnet.if_xname);
345                         wid_if = MAX(len, wid_if);
346                 }
347                 if (rt->rt_rmx.rmx_expire) {
348                         time_t expire_time;
349
350                         if ((expire_time =
351                             rt->rt_rmx.rmx_expire - time(NULL)) > 0) {
352                                 snprintf(buffer, sizeof(buffer), "%d",
353                                          (int)expire_time);
354                                 wid_expire = MAX(len, wid_expire);
355                         }
356                 }
357         }
358         if (rt->rt_shim[0] != NULL)
359                 len = strlen(labelops(rt));
360         wid_mplslops = MAX(len, wid_mplslops);
361 }
362
363
364 /*
365  * Print header for routing table columns.
366  */
367 void
368 pr_rthdr(int af1)
369 {
370
371         if (Aflag)
372                 printf("%-8.8s ","Address");
373         if (af1 == AF_INET || Wflag) {
374                 if (Wflag) {
375                         printf("%-*.*s %-*.*s %-*.*s %*.*s %*.*s %*.*s %*.*s %*s %-*s\n",
376                                 wid_dst,        wid_dst,        "Destination",
377                                 wid_gw,         wid_gw,         "Gateway",
378                                 wid_flags,      wid_flags,      "Flags",
379                                 wid_refs,       wid_refs,       "Refs",
380                                 wid_use,        wid_use,        "Use",
381                                 wid_mtu,        wid_mtu,        "Mtu",
382                                 wid_if,         wid_if,         "Netif",
383                                 wid_expire,                     "Expire",
384                                 wid_mplslops,                   "Labelops");
385                 } else {
386                         printf("%-*.*s %-*.*s %-*.*s %*.*s %*.*s %*.*s %*s\n",
387                                 wid_dst,        wid_dst,        "Destination",
388                                 wid_gw,         wid_gw,         "Gateway",
389                                 wid_flags,      wid_flags,      "Flags",
390                                 wid_refs,       wid_refs,       "Refs",
391                                 wid_use,        wid_use,        "Use",
392                                 wid_if,         wid_if,         "Netif",
393                                 wid_expire,                     "Expire");
394                 }
395         } else {
396                 printf("%-*.*s %-*.*s %-*.*s  %*.*s %*s\n",
397                         wid_dst,        wid_dst,        "Destination",
398                         wid_gw,         wid_gw,         "Gateway",
399                         wid_flags,      wid_flags,      "Flags",
400                         wid_if,         wid_if,         "Netif",
401                         wid_expire,                     "Expire");
402         }
403 }
404
405 static struct sockaddr *
406 kgetsa(struct sockaddr *dst)
407 {
408
409         kget(dst, pt_u.u_sa);
410         if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa))
411                 kread((u_long)dst, (char *)pt_u.u_data, pt_u.u_sa.sa_len);
412         return (&pt_u.u_sa);
413 }
414
415 static void
416 p_tree(struct radix_node *rn)
417 {
418
419 again:
420         kget(rn, rnode);
421         if (rnode.rn_bit < 0) {
422                 if (Aflag)
423                         printf("%-8.8lx ", (u_long)rn);
424                 if (rnode.rn_flags & RNF_ROOT) {
425                         if (Aflag)
426                                 printf("(root node)%s",
427                                     rnode.rn_dupedkey ? " =>\n" : "\n");
428                 } else if (do_rtent) {
429                         kget(rn, rtentry);
430                         p_rtentry(&rtentry);
431                         if (Aflag)
432                                 p_rtnode();
433                 } else {
434                         p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key),
435                                    NULL, 0, 44);
436                         putchar('\n');
437                 }
438                 if ((rn = rnode.rn_dupedkey))
439                         goto again;
440         } else {
441                 if (Aflag && do_rtent) {
442                         printf("%-8.8lx ", (u_long)rn);
443                         p_rtnode();
444                 }
445                 rn = rnode.rn_right;
446                 p_tree(rnode.rn_left);
447                 p_tree(rn);
448         }
449 }
450
451 char    nbuf[20];
452
453 static void
454 p_rtnode(void)
455 {
456         struct radix_mask *rm = rnode.rn_mklist;
457
458         if (rnode.rn_bit < 0) {
459                 if (rnode.rn_mask) {
460                         printf("\t  mask ");
461                         p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask),
462                                    NULL, 0, -1);
463                 } else if (rm == 0)
464                         return;
465         } else {
466                 sprintf(nbuf, "(%d)", rnode.rn_bit);
467                 printf("%6.6s %8.8lx : %8.8lx", nbuf, (u_long)rnode.rn_left, (u_long)rnode.rn_right);
468         }
469         while (rm) {
470                 kget(rm, rmask);
471                 sprintf(nbuf, " %d refs, ", rmask.rm_refs);
472                 printf(" mk = %8.8lx {(%d),%s",
473                         (u_long)rm, -1 - rmask.rm_bit, rmask.rm_refs ? nbuf : " ");
474                 if (rmask.rm_flags & RNF_NORMAL) {
475                         struct radix_node rnode_aux;
476                         printf(" <normal>, ");
477                         kget(rmask.rm_leaf, rnode_aux);
478                         p_sockaddr(kgetsa((struct sockaddr *)rnode_aux.rn_mask),
479                                     NULL, 0, -1);
480                 } else
481                     p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask),
482                                 NULL, 0, -1);
483                 putchar('}');
484                 if ((rm = rmask.rm_next))
485                         printf(" ->");
486         }
487         putchar('\n');
488 }
489
490 static void
491 ntreestuff(void)
492 {
493         size_t needed;
494         int mib[7];
495         int miblen;
496         char *buf, *next, *lim;
497         struct rt_msghdr *rtm;
498
499         mib[0] = CTL_NET;
500         mib[1] = PF_ROUTE;
501         mib[2] = 0;
502         mib[3] = 0;
503         mib[4] = NET_RT_DUMP;
504         mib[5] = 0;
505         if (cpuflag >= 0) {
506                 mib[6] = cpuflag;
507                 miblen = 7;
508         } else {
509                 miblen = 6;
510         }
511         if (sysctl(mib, miblen, NULL, &needed, NULL, 0) < 0) {
512                 err(1, "sysctl: net.route.0.0.dump estimate");
513         }
514
515         if ((buf = malloc(needed)) == 0) {
516                 err(2, "malloc(%lu)", (unsigned long)needed);
517         }
518         if (sysctl(mib, miblen, buf, &needed, NULL, 0) < 0) {
519                 err(1, "sysctl: net.route.0.0.dump");
520         }
521         lim  = buf + needed;
522         for (next = buf; next < lim; next += rtm->rtm_msglen) {
523                 rtm = (struct rt_msghdr *)next;
524                 np_rtentry(rtm);
525         }
526 }
527
528 static void
529 np_rtentry(struct rt_msghdr *rtm)
530 {
531         struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
532 #ifdef notdef
533         static int masks_done, banner_printed;
534 #endif
535         static int old_af;
536         int af1 = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
537
538 #ifdef notdef
539         /* for the moment, netmasks are skipped over */
540         if (!banner_printed) {
541                 printf("Netmasks:\n");
542                 banner_printed = 1;
543         }
544         if (masks_done == 0) {
545                 if (rtm->rtm_addrs != RTA_DST ) {
546                         masks_done = 1;
547                         af1 = sa->sa_family;
548                 }
549         } else
550 #endif
551                 af1 = sa->sa_family;
552         if (af1 != old_af) {
553                 pr_family(af1);
554                 old_af = af1;
555         }
556         if (rtm->rtm_addrs == RTA_DST)
557                 p_sockaddr(sa, NULL, 0, 36);
558         else {
559                 p_sockaddr(sa, NULL, rtm->rtm_flags, 16);
560                 sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
561                 p_sockaddr(sa, NULL, 0, 18);
562         }
563         p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
564         putchar('\n');
565 }
566
567 static void
568 p_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags, int width)
569 {
570         const char *cp;
571
572         cp = fmt_sockaddr(sa, mask, flags);
573
574         if (width < 0 )
575                 printf("%s ", cp);
576         else {
577                 if (numeric_addr)
578                         printf("%-*s ", width, cp);
579                 else
580                         printf("%-*.*s ", width, width, cp);
581         }
582 }
583
584 static const char *
585 fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags)
586 {
587         static char workbuf[128];
588         const char *cp = workbuf;
589
590         switch(sa->sa_family) {
591         case AF_INET:
592             {
593                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
594
595                 if ((sin->sin_addr.s_addr == INADDR_ANY) &&
596                         mask &&
597                         ntohl(((struct sockaddr_in *)mask)->sin_addr.s_addr)
598                                 ==0L)
599                                 cp = "default" ;
600                 else if (flags & RTF_HOST)
601                         cp = routename(sin->sin_addr.s_addr);
602                 else if (mask)
603                         cp = netname(sin->sin_addr.s_addr,
604                                      ntohl(((struct sockaddr_in *)mask)
605                                            ->sin_addr.s_addr));
606                 else
607                         cp = netname(sin->sin_addr.s_addr, 0L);
608                 break;
609             }
610
611 #ifdef INET6
612         case AF_INET6:
613             {
614                 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
615                 struct in6_addr *in6 = &sa6->sin6_addr;
616
617                 /*
618                  * XXX: This is a special workaround for KAME kernels.
619                  * sin6_scope_id field of SA should be set in the future.
620                  */
621                 if (IN6_IS_ADDR_LINKLOCAL(in6) ||
622                     IN6_IS_ADDR_MC_LINKLOCAL(in6)) {
623                     /* XXX: override is ok? */
624                     sa6->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)&in6->s6_addr[2]);
625                     *(u_short *)&in6->s6_addr[2] = 0;
626                 }
627
628                 if (flags & RTF_HOST)
629                     cp = routename6(sa6);
630                 else if (mask)
631                     cp = netname6(sa6,
632                                   &((struct sockaddr_in6 *)mask)->sin6_addr);
633                 else {
634                     cp = netname6(sa6, NULL);
635                 }
636                 break;
637             }
638 #endif /*INET6*/
639
640         case AF_IPX:
641             {
642                 struct ipx_addr work = ((struct sockaddr_ipx *)sa)->sipx_addr;
643                 if (ipx_nullnet(satoipx_addr(work)))
644                         cp = "default";
645                 else
646                         cp = ipx_print(sa);
647                 break;
648             }
649         case AF_APPLETALK:
650             {
651                 if (!(flags & RTF_HOST) && mask)
652                         cp = atalk_print2(sa,mask,9);
653                 else
654                         cp = atalk_print(sa,11);
655                 break;
656             }
657         case AF_NETGRAPH:
658             {
659                 printf("%s", ((struct sockaddr_ng *)sa)->sg_data);
660                 break;
661             }
662         case AF_LINK:
663             {
664                 struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
665
666                 if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
667                     sdl->sdl_slen == 0)
668                         (void) sprintf(workbuf, "link#%d", sdl->sdl_index);
669                 else
670                         switch (sdl->sdl_type) {
671
672                         case IFT_ETHER:
673                         case IFT_L2VLAN:
674                                 if (sdl->sdl_alen == ETHER_ADDR_LEN) {
675                                         cp = ether_ntoa((struct ether_addr *)
676                                             (sdl->sdl_data + sdl->sdl_nlen));
677                                         break;
678                                 }
679                                 /* FALLTHROUGH */
680                         default:
681                                 cp = link_ntoa(sdl);
682                                 break;
683                         }
684                 break;
685             }
686
687         case AF_MPLS:
688             {
689                 struct sockaddr_mpls *smpls = (struct sockaddr_mpls *)sa;
690
691                 (void) sprintf(workbuf, "%d", ntohl(smpls->smpls_label));
692                 break;
693             }
694                 
695         default:
696             {
697                 u_char *s = (u_char *)sa->sa_data, *slim;
698                 char *cq, *cqlim;
699
700                 cq = workbuf;
701                 slim =  sa->sa_len + (u_char *) sa;
702                 cqlim = cq + sizeof(workbuf) - 6;
703                 cq += sprintf(cq, "(%d)", sa->sa_family);
704                 while (s < slim && cq < cqlim) {
705                         cq += sprintf(cq, " %02x", *s++);
706                         if (s < slim)
707                             cq += sprintf(cq, "%02x", *s++);
708                 }
709                 cp = workbuf;
710             }
711         }
712
713         return (cp);
714 }
715
716 static void
717 p_flags(int f, const char *format)
718 {
719         printf(format, fmt_flags(f));
720 }
721
722 static const char *
723 fmt_flags(int f)
724 {
725         static char name[33];
726         char *flags;
727         struct bits *p = bits;
728
729         for (flags = name; p->b_mask; p++)
730                 if (p->b_mask & f)
731                         *flags++ = p->b_val;
732         *flags = '\0';
733         return (name);
734 }
735
736 static void
737 p_rtentry(struct rtentry *rt)
738 {
739         static struct ifnet ifnet, *lastif;
740         struct rtentry parent;
741         static char buffer[128];
742         static char prettyname[128];
743         struct sockaddr *sa;
744         sa_u addr, mask;
745
746         /*
747          * Don't print protocol-cloned routes unless -a.
748          */
749         if (rt->rt_flags & RTF_WASCLONED && !aflag) {
750                 kget(rt->rt_parent, parent);
751                 if (parent.rt_flags & RTF_PRCLONING)
752                         return;
753         }
754
755         bzero(&addr, sizeof(addr));
756         if ((sa = kgetsa(rt_key(rt))))
757                 bcopy(sa, &addr, sa->sa_len);
758         bzero(&mask, sizeof(mask));
759         if (rt_mask(rt) && (sa = kgetsa(rt_mask(rt))))
760                 bcopy(sa, &mask, sa->sa_len);
761         p_sockaddr(&addr.u_sa, &mask.u_sa, rt->rt_flags, wid_dst);
762         p_sockaddr(kgetsa(rt->rt_gateway), NULL, RTF_HOST, wid_gw);
763         snprintf(buffer, sizeof(buffer), "%%-%d.%ds ", wid_flags, wid_flags);
764         p_flags(rt->rt_flags, buffer);
765         if (addr.u_sa.sa_family == AF_INET || Wflag) {
766                 printf("%*ld %*lu ", wid_refs, rt->rt_refcnt,
767                                      wid_use, rt->rt_use);
768                 if (Wflag) {
769                         if (rt->rt_rmx.rmx_mtu != 0)
770                                 printf("%*lu ", wid_mtu, rt->rt_rmx.rmx_mtu);
771                         else
772                                 printf("%*s ", wid_mtu, "");
773                 }
774         }
775         if (rt->rt_ifp) {
776                 if (rt->rt_ifp != lastif) {
777                         kget(rt->rt_ifp, ifnet);
778                         lastif = rt->rt_ifp;
779                         strlcpy(prettyname, ifnet.if_xname, sizeof(prettyname));
780                 }
781                 printf("%*.*s", wid_if, wid_if, prettyname);
782                 if (rt->rt_rmx.rmx_expire) {
783                         time_t expire_time;
784
785                         if ((expire_time =
786                             rt->rt_rmx.rmx_expire - time(NULL)) > 0)
787                                 printf(" %*d", wid_expire, (int)expire_time);
788                 } else {
789                         printf("%*s ", wid_expire, "");
790                 }
791                 if (rt->rt_nodes[0].rn_dupedkey)
792                         printf(" =>");
793         }
794         if (Wflag) {
795                 if (rt->rt_shim[0] != NULL)
796                         printf(" %-*s", wid_mplslops, labelops(rt));
797         }
798         putchar('\n');
799 }
800
801 char *
802 routename(u_long in)
803 {
804         char *cp;
805         static char line[MAXHOSTNAMELEN];
806         struct hostent *hp;
807
808         cp = 0;
809         if (!numeric_addr) {
810                 hp = gethostbyaddr(&in, sizeof (struct in_addr), AF_INET);
811                 if (hp) {
812                         cp = hp->h_name;
813                         trimdomain(cp, strlen(cp));
814                 }
815         }
816         if (cp) {
817                 strncpy(line, cp, sizeof(line) - 1);
818                 line[sizeof(line) - 1] = '\0';
819         } else {
820 #define C(x)    ((x) & 0xff)
821                 in = ntohl(in);
822                 sprintf(line, "%lu.%lu.%lu.%lu",
823                     C(in >> 24), C(in >> 16), C(in >> 8), C(in));
824         }
825         return (line);
826 }
827
828 static u_long
829 forgemask(u_long a)
830 {
831         u_long m;
832
833         if (IN_CLASSA(a))
834                 m = IN_CLASSA_NET;
835         else if (IN_CLASSB(a))
836                 m = IN_CLASSB_NET;
837         else
838                 m = IN_CLASSC_NET;
839         return (m);
840 }
841
842 static void
843 domask(char *dst, u_long addr, u_long mask)
844 {
845         int b, i;
846
847         if (!mask || (forgemask(addr) == mask)) {
848                 *dst = '\0';
849                 return;
850         }
851         i = 0;
852         for (b = 0; b < 32; b++)
853                 if (mask & (1 << b)) {
854                         int bb;
855
856                         i = b;
857                         for (bb = b+1; bb < 32; bb++)
858                                 if (!(mask & (1 << bb))) {
859                                         i = -1; /* noncontig */
860                                         break;
861                                 }
862                         break;
863                 }
864         if (i == -1)
865                 sprintf(dst, "&0x%lx", mask);
866         else
867                 sprintf(dst, "/%d", 32-i);
868 }
869
870 /*
871  * Return the name of the network whose address is given.
872  * The address is assumed to be that of a net or subnet, not a host.
873  */
874 char *
875 netname(u_long in, u_long mask)
876 {
877         char *cp = 0;
878         static char line[MAXHOSTNAMELEN];
879         struct netent *np = 0;
880         u_long dmask;
881         u_long i;
882
883 #define NSHIFT(m) (                                                     \
884         (m) == IN_CLASSA_NET ? IN_CLASSA_NSHIFT :                       \
885         (m) == IN_CLASSB_NET ? IN_CLASSB_NSHIFT :                       \
886         (m) == IN_CLASSC_NET ? IN_CLASSC_NSHIFT :                       \
887         0)
888
889         i = ntohl(in);
890         dmask = forgemask(i);
891         if (!numeric_addr && i) {
892                 np = getnetbyaddr(i >> NSHIFT(mask), AF_INET);
893                 if (np == NULL && mask == 0)
894                         np = getnetbyaddr(i >> NSHIFT(dmask), AF_INET);
895                 if (np != NULL) {
896                         cp = np->n_name;
897                         trimdomain(cp, strlen(cp));
898                 }
899         }
900 #undef NSHIFT
901         if (cp != NULL) {
902                 strncpy(line, cp, sizeof(line) - 1);
903                 line[sizeof(line) - 1] = '\0';
904         } else {
905                 if (mask <= IN_CLASSA_NET &&
906                         (i & IN_CLASSA_HOST) == 0) {
907                                 sprintf(line, "%lu", C(i >> 24));
908                 } else if (mask <= IN_CLASSB_NET &&
909                         (i & IN_CLASSB_HOST) == 0) {
910                                 sprintf(line, "%lu.%lu",
911                                         C(i >> 24), C(i >> 16));
912                 } else if (mask <= IN_CLASSC_NET &&
913                         (i & IN_CLASSC_HOST) == 0) {
914                                 sprintf(line, "%lu.%lu.%lu",
915                                         C(i >> 24), C(i >> 16), C(i >> 8));
916                 } else {
917                         sprintf(line, "%lu.%lu.%lu.%lu",
918                                 C(i >> 24), C(i >> 16), C(i >> 8), C(i));
919                 }
920         }
921         domask(line + strlen(line), i, mask);
922         return (line);
923 }
924
925 #ifdef INET6
926 const char *
927 netname6(struct sockaddr_in6 *sa6, struct in6_addr *mask)
928 {
929         static char line[MAXHOSTNAMELEN];
930         u_char *p = (u_char *)mask;
931         u_char *lim;
932         int masklen, illegal = 0, flag = NI_WITHSCOPEID;
933
934         if (mask) {
935                 for (masklen = 0, lim = p + 16; p < lim; p++) {
936                         switch (*p) {
937                          case 0xff:
938                                  masklen += 8;
939                                  break;
940                          case 0xfe:
941                                  masklen += 7;
942                                  break;
943                          case 0xfc:
944                                  masklen += 6;
945                                  break;
946                          case 0xf8:
947                                  masklen += 5;
948                                  break;
949                          case 0xf0:
950                                  masklen += 4;
951                                  break;
952                          case 0xe0:
953                                  masklen += 3;
954                                  break;
955                          case 0xc0:
956                                  masklen += 2;
957                                  break;
958                          case 0x80:
959                                  masklen += 1;
960                                  break;
961                          case 0x00:
962                                  break;
963                          default:
964                                  illegal ++;
965                                  break;
966                         }
967                 }
968                 if (illegal)
969                         fprintf(stderr, "illegal prefixlen\n");
970         }
971         else
972                 masklen = 128;
973
974         if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr))
975                 return("default");
976
977         if (numeric_addr)
978                 flag |= NI_NUMERICHOST;
979         getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line),
980                     NULL, 0, flag);
981
982         if (numeric_addr)
983                 sprintf(&line[strlen(line)], "/%d", masklen);
984
985         return line;
986 }
987
988 char *
989 routename6(struct sockaddr_in6 *sa6)
990 {
991         static char line[MAXHOSTNAMELEN];
992         int flag = NI_WITHSCOPEID;
993         /* use local variable for safety */
994         struct sockaddr_in6 sa6_local;
995
996         sa6_local.sin6_family = AF_INET6;
997         sa6_local.sin6_len = sizeof(sa6_local);
998         sa6_local.sin6_addr = sa6->sin6_addr;
999         sa6_local.sin6_scope_id = sa6->sin6_scope_id;
1000
1001         if (numeric_addr)
1002                 flag |= NI_NUMERICHOST;
1003
1004         getnameinfo((struct sockaddr *)&sa6_local, sa6_local.sin6_len,
1005                     line, sizeof(line), NULL, 0, flag);
1006
1007         return line;
1008 }
1009 #endif /*INET6*/
1010
1011 /*
1012  * Print routing statistics
1013  */
1014 void
1015 rt_stats(void)
1016 {
1017         struct rtstatistics rts;
1018         int error = 0;
1019
1020         error = kinfo_get_net_rtstatistics(&rts);
1021         if (error) {
1022                 printf("routing: could not retrieve statistics\n");
1023                 return;
1024         }
1025         printf("routing:\n");
1026
1027 #define p(f, m) if (rts.f || sflag <= 1) \
1028         printf(m, rts.f, plural(rts.f))
1029
1030         p(rts_badredirect, "\t%lu bad routing redirect%s\n");
1031         p(rts_dynamic, "\t%lu dynamically created route%s\n");
1032         p(rts_newgateway, "\t%lu new gateway%s due to redirects\n");
1033         p(rts_unreach, "\t%lu destination%s found unreachable\n");
1034         p(rts_wildcard, "\t%lu use%s of a wildcard route\n");
1035 #undef p
1036 }
1037
1038 char *
1039 ipx_print(struct sockaddr *sa)
1040 {
1041         u_short port;
1042         struct servent *sp = 0;
1043         const char *net = "", *host = "";
1044         char *p;
1045         u_char *q;
1046         struct ipx_addr work = ((struct sockaddr_ipx *)sa)->sipx_addr;
1047         static char mybuf[50];
1048         char cport[10], chost[15], cnet[15];
1049
1050         port = ntohs(work.x_port);
1051
1052         if (ipx_nullnet(work) && ipx_nullhost(work)) {
1053
1054                 if (port) {
1055                         if (sp)
1056                                 sprintf(mybuf, "*.%s", sp->s_name);
1057                         else
1058                                 sprintf(mybuf, "*.%x", port);
1059                 } else
1060                         sprintf(mybuf, "*.*");
1061
1062                 return (mybuf);
1063         }
1064
1065         if (ipx_wildnet(work))
1066                 net = "any";
1067         else if (ipx_nullnet(work))
1068                 net = "*";
1069         else {
1070                 q = work.x_net.c_net;
1071                 sprintf(cnet, "%02x%02x%02x%02x",
1072                         q[0], q[1], q[2], q[3]);
1073                 for (p = cnet; *p == '0' && p < cnet + 8; p++)
1074                         continue;
1075                 net = p;
1076         }
1077
1078         if (ipx_wildhost(work))
1079                 host = "any";
1080         else if (ipx_nullhost(work))
1081                 host = "*";
1082         else {
1083                 q = work.x_host.c_host;
1084                 sprintf(chost, "%02x%02x%02x%02x%02x%02x",
1085                         q[0], q[1], q[2], q[3], q[4], q[5]);
1086                 for (p = chost; *p == '0' && p < chost + 12; p++)
1087                         continue;
1088                 host = p;
1089         }
1090
1091         if (port) {
1092                 if (strcmp(host, "*") == 0)
1093                         host = "";
1094                 if (sp) 
1095                         snprintf(cport, sizeof(cport),
1096                                 "%s%s", *host ? "." : "", sp->s_name);
1097                 else    
1098                         snprintf(cport, sizeof(cport),
1099                                 "%s%x", *host ? "." : "", port);
1100         } else
1101                 *cport = 0;
1102
1103         snprintf(mybuf, sizeof(mybuf), "%s.%s%s", net, host, cport);
1104         return(mybuf);
1105 }
1106
1107 char *
1108 ipx_phost(struct sockaddr *sa)
1109 {
1110         struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)sa;
1111         struct sockaddr_ipx work;
1112         static union ipx_net ipx_zeronet;
1113         char *p;
1114         struct ipx_addr in;
1115
1116         work = *sipx;
1117         in = work.sipx_addr;
1118
1119         work.sipx_addr.x_port = 0;
1120         work.sipx_addr.x_net = ipx_zeronet;
1121         p = ipx_print((struct sockaddr *)&work);
1122         if (strncmp("*.", p, 2) == 0) p += 2;
1123
1124         return(p);
1125 }
1126
1127 void
1128 upHex(char *p0)
1129 {
1130         char *p = p0;
1131
1132         for (; *p; p++)
1133                 switch (*p) {
1134
1135                 case 'a':
1136                 case 'b':
1137                 case 'c':
1138                 case 'd':
1139                 case 'e':
1140                 case 'f':
1141                         *p += ('A' - 'a');
1142                         break;
1143                 }
1144 }
1145
1146 static const char *
1147 labelops(struct rtentry *rt)
1148 {
1149         const char *lops[] = { "push", "pop", "swap", "pop all" };
1150         static char buffer[100];
1151         char *cp = buffer;
1152         struct sockaddr_mpls *smpls;
1153         int i;
1154
1155         for (i=0; i<MPLS_MAXLOPS; ++i) {
1156
1157                 if (rt->rt_shim[i] == NULL)
1158                         break;
1159                 if (i>0) {
1160                         cp += snprintf(cp,
1161                                        sizeof(buffer) - (cp - buffer),
1162                                        ", ");
1163                 }
1164                 smpls = (struct sockaddr_mpls *)kgetsa(rt->rt_shim[i]);
1165                 if (smpls->smpls_op != MPLSLOP_POP &&
1166                     smpls->smpls_op != MPLSLOP_POPALL){
1167                         cp += snprintf(cp,
1168                                        sizeof(buffer) - (cp - buffer),
1169                                        "%s %d",
1170                                        lops[smpls->smpls_op - 1],
1171                                        ntohl(smpls->smpls_label));
1172                 } else {
1173                         cp += snprintf(cp,
1174                                        sizeof(buffer) - (cp - buffer),
1175                                        "%s",
1176                                        lops[smpls->smpls_op - 1]);
1177                 }
1178         }
1179
1180         return (buffer);
1181 }