xref: /freebsd/usr.bin/netstat/route.c (revision b601c69bdbe8755d26570261d7fd4c02ee4eff74)
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 
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "From: @(#)route.c	8.6 (Berkeley) 4/28/95";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41 
42 #include <sys/param.h>
43 #include <sys/protosw.h>
44 #include <sys/socket.h>
45 #include <sys/time.h>
46 
47 #include <net/if.h>
48 #include <net/if_var.h>
49 #include <net/if_dl.h>
50 #include <net/if_types.h>
51 #include <net/route.h>
52 
53 #include <netinet/in.h>
54 #include <netipx/ipx.h>
55 #include <netatalk/at.h>
56 #include <netgraph/ng_socket.h>
57 
58 #ifdef NS
59 #include <netns/ns.h>
60 #endif
61 
62 #include <sys/sysctl.h>
63 
64 #include <arpa/inet.h>
65 #include <netdb.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <err.h>
71 #include <time.h>
72 #include "netstat.h"
73 
74 #define kget(p, d) (kread((u_long)(p), (char *)&(d), sizeof (d)))
75 
76 
77 /* alignment constraint for routing socket */
78 #define ROUNDUP(a) \
79        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
80 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
81 
82 /*
83  * Definitions for showing gateway flags.
84  */
85 struct bits {
86 	u_long	b_mask;
87 	char	b_val;
88 } bits[] = {
89 	{ RTF_UP,	'U' },
90 	{ RTF_GATEWAY,	'G' },
91 	{ RTF_HOST,	'H' },
92 	{ RTF_REJECT,	'R' },
93 	{ RTF_DYNAMIC,	'D' },
94 	{ RTF_MODIFIED,	'M' },
95 	{ RTF_DONE,	'd' }, /* Completed -- for routing messages only */
96 	{ RTF_CLONING,	'C' },
97 	{ RTF_XRESOLVE,	'X' },
98 	{ RTF_LLINFO,	'L' },
99 	{ RTF_STATIC,	'S' },
100 	{ RTF_PROTO1,	'1' },
101 	{ RTF_PROTO2,	'2' },
102 	{ RTF_WASCLONED,'W' },
103 	{ RTF_PRCLONING,'c' },
104 	{ RTF_PROTO3,	'3' },
105 	{ RTF_BLACKHOLE,'B' },
106 	{ RTF_BROADCAST,'b' },
107 	{ 0 }
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 static sa_u pt_u;
117 
118 int	do_rtent = 0;
119 struct	rtentry rtentry;
120 struct	radix_node rnode;
121 struct	radix_mask rmask;
122 struct	radix_node_head *rt_tables[AF_MAX+1];
123 
124 int	NewTree = 0;
125 
126 static struct sockaddr *kgetsa __P((struct sockaddr *));
127 static void p_tree __P((struct radix_node *));
128 static void p_rtnode __P((void));
129 static void ntreestuff __P((void));
130 static void np_rtentry __P((struct rt_msghdr *));
131 static void p_sockaddr __P((struct sockaddr *, struct sockaddr *, int, int));
132 static void p_flags __P((int, char *));
133 static void p_rtentry __P((struct rtentry *));
134 static u_long forgemask __P((u_long));
135 static void domask __P((char *, u_long, u_long));
136 
137 #ifdef INET6
138 char *routename6 __P((struct sockaddr_in6 *));
139 char *netname6 __P((struct sockaddr_in6 *, struct in6_addr *));
140 #endif /*INET6*/
141 
142 /*
143  * Print routing tables.
144  */
145 void
146 routepr(rtree)
147 	u_long rtree;
148 {
149 	struct radix_node_head *rnh, head;
150 	int i;
151 
152 	printf("Routing tables\n");
153 
154 	if (Aflag == 0 && NewTree)
155 		ntreestuff();
156 	else {
157 		if (rtree == 0) {
158 			printf("rt_tables: symbol not in namelist\n");
159 			return;
160 		}
161 
162 		kget(rtree, rt_tables);
163 		for (i = 0; i <= AF_MAX; i++) {
164 			if ((rnh = rt_tables[i]) == 0)
165 				continue;
166 			kget(rnh, head);
167 			if (i == AF_UNSPEC) {
168 				if (Aflag && af == 0) {
169 					printf("Netmasks:\n");
170 					p_tree(head.rnh_treetop);
171 				}
172 			} else if (af == AF_UNSPEC || af == i) {
173 				pr_family(i);
174 				do_rtent = 1;
175 				pr_rthdr(i);
176 				p_tree(head.rnh_treetop);
177 			}
178 		}
179 	}
180 }
181 
182 /*
183  * Print address family header before a section of the routing table.
184  */
185 void
186 pr_family(af)
187 	int af;
188 {
189 	char *afname;
190 
191 	switch (af) {
192 	case AF_INET:
193 		afname = "Internet";
194 		break;
195 #ifdef INET6
196 	case AF_INET6:
197 		afname = "Internet6";
198 		break;
199 #endif /*INET6*/
200 	case AF_IPX:
201 		afname = "IPX";
202 		break;
203 #ifdef NS
204 	case AF_NS:
205 		afname = "XNS";
206 		break;
207 #endif
208 	case AF_ISO:
209 		afname = "ISO";
210 		break;
211 	case AF_APPLETALK:
212 		afname = "AppleTalk";
213 		break;
214 	case AF_CCITT:
215 		afname = "X.25";
216 		break;
217 	case AF_NETGRAPH:
218 		afname = "Netgraph";
219 		break;
220 	default:
221 		afname = NULL;
222 		break;
223 	}
224 	if (afname)
225 		printf("\n%s:\n", afname);
226 	else
227 		printf("\nProtocol Family %d:\n", af);
228 }
229 
230 /* column widths; each followed by one space */
231 #ifndef INET6
232 #define	WID_DST(af) 	18	/* width of destination column */
233 #define	WID_GW(af)	18	/* width of gateway column */
234 #else
235 #define	WID_DST(af) \
236 	((af) == AF_INET6 ? (lflag ? 39 : (nflag ? 33: 18)) : 18)
237 #define	WID_GW(af) \
238 	((af) == AF_INET6 ? (lflag ? 31 : (nflag ? 29 : 18)) : 18)
239 #endif /*INET6*/
240 
241 /*
242  * Print header for routing table columns.
243  */
244 void
245 pr_rthdr(af)
246 	int af;
247 {
248 
249 	if (Aflag)
250 		printf("%-8.8s ","Address");
251 	if (lflag)
252 		printf("%-*.*s %-*.*s %-6.6s  %6.6s%8.8s  %8.8s %6s\n",
253 			WID_DST(af), WID_DST(af), "Destination",
254 			WID_GW(af), WID_GW(af), "Gateway",
255 			"Flags", "Refs", "Use", "Netif", "Expire");
256 	else
257 		printf("%-*.*s %-*.*s %-6.6s  %8.8s %6s\n",
258 			WID_DST(af), WID_DST(af), "Destination",
259 			WID_GW(af), WID_GW(af), "Gateway",
260 			"Flags", "Netif", "Expire");
261 }
262 
263 static struct sockaddr *
264 kgetsa(dst)
265 	register struct sockaddr *dst;
266 {
267 
268 	kget(dst, pt_u.u_sa);
269 	if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa))
270 		kread((u_long)dst, (char *)pt_u.u_data, pt_u.u_sa.sa_len);
271 	return (&pt_u.u_sa);
272 }
273 
274 static void
275 p_tree(rn)
276 	struct radix_node *rn;
277 {
278 
279 again:
280 	kget(rn, rnode);
281 	if (rnode.rn_bit < 0) {
282 		if (Aflag)
283 			printf("%-8.8lx ", (u_long)rn);
284 		if (rnode.rn_flags & RNF_ROOT) {
285 			if (Aflag)
286 				printf("(root node)%s",
287 				    rnode.rn_dupedkey ? " =>\n" : "\n");
288 		} else if (do_rtent) {
289 			kget(rn, rtentry);
290 			p_rtentry(&rtentry);
291 			if (Aflag)
292 				p_rtnode();
293 		} else {
294 			p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key),
295 				   NULL, 0, 44);
296 			putchar('\n');
297 		}
298 		if ((rn = rnode.rn_dupedkey))
299 			goto again;
300 	} else {
301 		if (Aflag && do_rtent) {
302 			printf("%-8.8lx ", (u_long)rn);
303 			p_rtnode();
304 		}
305 		rn = rnode.rn_right;
306 		p_tree(rnode.rn_left);
307 		p_tree(rn);
308 	}
309 }
310 
311 char	nbuf[20];
312 
313 static void
314 p_rtnode()
315 {
316 	struct radix_mask *rm = rnode.rn_mklist;
317 
318 	if (rnode.rn_bit < 0) {
319 		if (rnode.rn_mask) {
320 			printf("\t  mask ");
321 			p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask),
322 				   NULL, 0, -1);
323 		} else if (rm == 0)
324 			return;
325 	} else {
326 		sprintf(nbuf, "(%d)", rnode.rn_bit);
327 		printf("%6.6s %8.8lx : %8.8lx", nbuf, (u_long)rnode.rn_left, (u_long)rnode.rn_right);
328 	}
329 	while (rm) {
330 		kget(rm, rmask);
331 		sprintf(nbuf, " %d refs, ", rmask.rm_refs);
332 		printf(" mk = %8.8lx {(%d),%s",
333 			(u_long)rm, -1 - rmask.rm_bit, rmask.rm_refs ? nbuf : " ");
334 		if (rmask.rm_flags & RNF_NORMAL) {
335 			struct radix_node rnode_aux;
336 			printf(" <normal>, ");
337 			kget(rmask.rm_leaf, rnode_aux);
338 			p_sockaddr(kgetsa((struct sockaddr *)rnode_aux.rn_mask),
339 				    NULL, 0, -1);
340 		} else
341 		    p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask),
342 				NULL, 0, -1);
343 		putchar('}');
344 		if ((rm = rmask.rm_mklist))
345 			printf(" ->");
346 	}
347 	putchar('\n');
348 }
349 
350 static void
351 ntreestuff()
352 {
353 	size_t needed;
354 	int mib[6];
355 	char *buf, *next, *lim;
356 	register struct rt_msghdr *rtm;
357 
358 	mib[0] = CTL_NET;
359 	mib[1] = PF_ROUTE;
360 	mib[2] = 0;
361 	mib[3] = 0;
362 	mib[4] = NET_RT_DUMP;
363 	mib[5] = 0;
364 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
365 		err(1, "sysctl: net.route.0.0.dump estimate");
366 	}
367 
368 	if ((buf = malloc(needed)) == 0) {
369 		err(2, "malloc(%lu)", (unsigned long)needed);
370 	}
371 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
372 		err(1, "sysctl: net.route.0.0.dump");
373 	}
374 	lim  = buf + needed;
375 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
376 		rtm = (struct rt_msghdr *)next;
377 		np_rtentry(rtm);
378 	}
379 }
380 
381 static void
382 np_rtentry(rtm)
383 	register struct rt_msghdr *rtm;
384 {
385 	register struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
386 #ifdef notdef
387 	static int masks_done, banner_printed;
388 #endif
389 	static int old_af;
390 	int af = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
391 
392 #ifdef notdef
393 	/* for the moment, netmasks are skipped over */
394 	if (!banner_printed) {
395 		printf("Netmasks:\n");
396 		banner_printed = 1;
397 	}
398 	if (masks_done == 0) {
399 		if (rtm->rtm_addrs != RTA_DST ) {
400 			masks_done = 1;
401 			af = sa->sa_family;
402 		}
403 	} else
404 #endif
405 		af = sa->sa_family;
406 	if (af != old_af) {
407 		pr_family(af);
408 		old_af = af;
409 	}
410 	if (rtm->rtm_addrs == RTA_DST)
411 		p_sockaddr(sa, NULL, 0, 36);
412 	else {
413 		p_sockaddr(sa, NULL, rtm->rtm_flags, 16);
414 		sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
415 		p_sockaddr(sa, NULL, 0, 18);
416 	}
417 	p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
418 	putchar('\n');
419 }
420 
421 static void
422 p_sockaddr(sa, mask, flags, width)
423 	struct sockaddr *sa, *mask;
424 	int flags, width;
425 {
426 	char workbuf[128], *cplim;
427 	register char *cp = workbuf;
428 
429 	switch(sa->sa_family) {
430 	case AF_INET:
431 	    {
432 		register struct sockaddr_in *sin = (struct sockaddr_in *)sa;
433 
434 		if ((sin->sin_addr.s_addr == INADDR_ANY) &&
435 			mask &&
436 			ntohl(((struct sockaddr_in *)mask)->sin_addr.s_addr)
437 				==0L)
438 				cp = "default" ;
439 		else if (flags & RTF_HOST)
440 			cp = routename(sin->sin_addr.s_addr);
441 		else if (mask)
442 			cp = netname(sin->sin_addr.s_addr,
443 				     ntohl(((struct sockaddr_in *)mask)
444 					   ->sin_addr.s_addr));
445 		else
446 			cp = netname(sin->sin_addr.s_addr, 0L);
447 		break;
448 	    }
449 
450 #ifdef INET6
451 	case AF_INET6:
452 	    {
453 		struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
454 		struct in6_addr *in6 = &sa6->sin6_addr;
455 
456 		/*
457 		 * XXX: This is a special workaround for KAME kernels.
458 		 * sin6_scope_id field of SA should be set in the future.
459 		 */
460 		if (IN6_IS_ADDR_LINKLOCAL(in6) ||
461 		    IN6_IS_ADDR_MC_LINKLOCAL(in6)) {
462 		    /* XXX: override is ok? */
463 		    sa6->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)&in6->s6_addr[2]);
464 		    *(u_short *)&in6->s6_addr[2] = 0;
465 		}
466 
467 		if (flags & RTF_HOST)
468 		    cp = routename6(sa6);
469 		else if (mask)
470 		    cp = netname6(sa6,
471 				  &((struct sockaddr_in6 *)mask)->sin6_addr);
472 		else {
473 		    cp = netname6(sa6, NULL);
474 		}
475 		break;
476 	    }
477 #endif /*INET6*/
478 
479 	case AF_IPX:
480 	    {
481 		struct ipx_addr work = ((struct sockaddr_ipx *)sa)->sipx_addr;
482 		if (ipx_nullnet(satoipx_addr(work)))
483 			cp = "default";
484 		else
485 			cp = ipx_print(sa);
486 		break;
487 	    }
488 	case AF_APPLETALK:
489 	    {
490 		if (!(flags & RTF_HOST) && mask)
491 			cp = atalk_print2(sa,mask,9);
492 		else
493 			cp = atalk_print(sa,11);
494 		break;
495 	    }
496 	case AF_NETGRAPH:
497 	    {
498 		printf("%s", ((struct sockaddr_ng *)sa)->sg_data);
499 		break;
500 	    }
501 #ifdef NS
502 	case AF_NS:
503 		cp = ns_print(sa);
504 		break;
505 #endif
506 
507 	case AF_LINK:
508 	    {
509 		register struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
510 
511 		if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
512 		    sdl->sdl_slen == 0)
513 			(void) sprintf(workbuf, "link#%d", sdl->sdl_index);
514 		else
515 			switch (sdl->sdl_type) {
516 
517 			case IFT_ETHER:
518 			    {
519 				register int i;
520 				register u_char *lla = (u_char *)sdl->sdl_data +
521 				    sdl->sdl_nlen;
522 
523 				cplim = "";
524 				for (i = 0; i < sdl->sdl_alen; i++, lla++) {
525 					cp += sprintf(cp, "%s%x", cplim, *lla);
526 					cplim = ":";
527 				}
528 				cp = workbuf;
529 				break;
530 			    }
531 
532 			default:
533 				cp = link_ntoa(sdl);
534 				break;
535 			}
536 		break;
537 	    }
538 
539 	default:
540 	    {
541 		register u_char *s = (u_char *)sa->sa_data, *slim;
542 
543 		slim =  sa->sa_len + (u_char *) sa;
544 		cplim = cp + sizeof(workbuf) - 6;
545 		cp += sprintf(cp, "(%d)", sa->sa_family);
546 		while (s < slim && cp < cplim) {
547 			cp += sprintf(cp, " %02x", *s++);
548 			if (s < slim)
549 			    cp += sprintf(cp, "%02x", *s++);
550 		}
551 		cp = workbuf;
552 	    }
553 	}
554 	if (width < 0 )
555 		printf("%s ", cp);
556 	else {
557 		if (nflag)
558 			printf("%-*s ", width, cp);
559 		else
560 			printf("%-*.*s ", width, width, cp);
561 	}
562 }
563 
564 static void
565 p_flags(f, format)
566 	register int f;
567 	char *format;
568 {
569 	char name[33], *flags;
570 	register struct bits *p = bits;
571 
572 	for (flags = name; p->b_mask; p++)
573 		if (p->b_mask & f)
574 			*flags++ = p->b_val;
575 	*flags = '\0';
576 	printf(format, name);
577 }
578 
579 static void
580 p_rtentry(rt)
581 	register struct rtentry *rt;
582 {
583 	static struct ifnet ifnet, *lastif;
584 	static char name[16];
585 	static char prettyname[9];
586 	struct sockaddr *sa;
587 	sa_u addr, mask;
588 
589 	/*
590 	 * Don't print protocol-cloned routes unless -a.
591 	 */
592 	if (rt->rt_parent && !aflag)
593 		return;
594 
595 	bzero(&addr, sizeof(addr));
596 	if ((sa = kgetsa(rt_key(rt))))
597 		bcopy(sa, &addr, sa->sa_len);
598 	bzero(&mask, sizeof(mask));
599 	if (rt_mask(rt) && (sa = kgetsa(rt_mask(rt))))
600 		bcopy(sa, &mask, sa->sa_len);
601 	p_sockaddr(&addr.u_sa, &mask.u_sa, rt->rt_flags,
602 	    WID_DST(addr.u_sa.sa_family));
603 	p_sockaddr(kgetsa(rt->rt_gateway), NULL, RTF_HOST,
604 	    WID_GW(addr.u_sa.sa_family));
605 	p_flags(rt->rt_flags, "%-6.6s ");
606 	if (addr.u_sa.sa_family == AF_INET || lflag)
607 		printf("%6ld %8ld ", rt->rt_refcnt, rt->rt_use);
608 	if (rt->rt_ifp) {
609 		if (rt->rt_ifp != lastif) {
610 			kget(rt->rt_ifp, ifnet);
611 			kread((u_long)ifnet.if_name, name, 16);
612 			lastif = rt->rt_ifp;
613 			snprintf(prettyname, sizeof prettyname,
614 				 "%s%d", name, ifnet.if_unit);
615 		}
616 		printf("%8.8s", prettyname);
617 		if (rt->rt_rmx.rmx_expire) {
618 			time_t expire_time;
619 
620 			if ((expire_time =
621 			    rt->rt_rmx.rmx_expire - time((time_t *)0)) > 0)
622 				printf(" %6d%s", (int)expire_time,
623 				    rt->rt_nodes[0].rn_dupedkey ? " =>" : "");
624 			else
625 			    goto ifandkey;
626 		} else if (rt->rt_nodes[0].rn_dupedkey) {
627 ifandkey:;
628 			printf(" =>");
629 		}
630 
631 	}
632 	putchar('\n');
633 }
634 
635 char *
636 routename(in)
637 	u_long in;
638 {
639 	register char *cp;
640 	static char line[MAXHOSTNAMELEN + 1];
641 	struct hostent *hp;
642 
643 	cp = 0;
644 	if (!nflag) {
645 		hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
646 			AF_INET);
647 		if (hp) {
648 			cp = hp->h_name;
649 			trimdomain(cp);
650 		}
651 	}
652 	if (cp) {
653 		strncpy(line, cp, sizeof(line) - 1);
654 		line[sizeof(line) - 1] = '\0';
655 	} else {
656 #define C(x)	((x) & 0xff)
657 		in = ntohl(in);
658 		sprintf(line, "%lu.%lu.%lu.%lu",
659 		    C(in >> 24), C(in >> 16), C(in >> 8), C(in));
660 	}
661 	return (line);
662 }
663 
664 static u_long
665 forgemask(a)
666 	u_long a;
667 {
668 	u_long m;
669 
670 	if (IN_CLASSA(a))
671 		m = IN_CLASSA_NET;
672 	else if (IN_CLASSB(a))
673 		m = IN_CLASSB_NET;
674 	else
675 		m = IN_CLASSC_NET;
676 	return (m);
677 }
678 
679 static void
680 domask(dst, addr, mask)
681 	char *dst;
682 	u_long addr, mask;
683 {
684 	register int b, i;
685 
686 	if (!mask || (forgemask(addr) == mask)) {
687 		*dst = '\0';
688 		return;
689 	}
690 	i = 0;
691 	for (b = 0; b < 32; b++)
692 		if (mask & (1 << b)) {
693 			register int bb;
694 
695 			i = b;
696 			for (bb = b+1; bb < 32; bb++)
697 				if (!(mask & (1 << bb))) {
698 					i = -1;	/* noncontig */
699 					break;
700 				}
701 			break;
702 		}
703 	if (i == -1)
704 		sprintf(dst, "&0x%lx", mask);
705 	else
706 		sprintf(dst, "/%d", 32-i);
707 }
708 
709 /*
710  * Return the name of the network whose address is given.
711  * The address is assumed to be that of a net or subnet, not a host.
712  */
713 char *
714 netname(in, mask)
715 	u_long in, mask;
716 {
717 	char *cp = 0;
718 	static char line[MAXHOSTNAMELEN + 1];
719 	struct netent *np = 0;
720 	u_long net, omask, dmask;
721 	register u_long i;
722 
723 	i = ntohl(in);
724 	omask = mask;
725 	if (!nflag && i) {
726 		dmask = forgemask(i);
727 		net = i & dmask;
728 		if (!(np = getnetbyaddr(i, AF_INET)) && net != i)
729 			np = getnetbyaddr(net, AF_INET);
730 		if (np) {
731 			cp = np->n_name;
732 			trimdomain(cp);
733 		}
734 	}
735 	if (cp)
736 		strncpy(line, cp, sizeof(line) - 1);
737 	else if ((i & 0xffffff) == 0)
738 		sprintf(line, "%lu", C(i >> 24));
739 	else if ((i & 0xffff) == 0)
740 		sprintf(line, "%lu.%lu", C(i >> 24) , C(i >> 16));
741 	else if ((i & 0xff) == 0)
742 		sprintf(line, "%lu.%lu.%lu", C(i >> 24), C(i >> 16), C(i >> 8));
743 	else
744 		sprintf(line, "%lu.%lu.%lu.%lu", C(i >> 24),
745 			C(i >> 16), C(i >> 8), C(i));
746 	domask(line+strlen(line), i, omask);
747 	return (line);
748 }
749 
750 #ifdef INET6
751 char *
752 netname6(sa6, mask)
753 	struct sockaddr_in6 *sa6;
754 	struct in6_addr *mask;
755 {
756 	static char line[MAXHOSTNAMELEN + 1];
757 	u_char *p = (u_char *)mask;
758 	u_char *lim;
759 	int masklen, illegal = 0, flag = NI_WITHSCOPEID;
760 
761 	if (mask) {
762 		for (masklen = 0, lim = p + 16; p < lim; p++) {
763 			switch (*p) {
764 			 case 0xff:
765 				 masklen += 8;
766 				 break;
767 			 case 0xfe:
768 				 masklen += 7;
769 				 break;
770 			 case 0xfc:
771 				 masklen += 6;
772 				 break;
773 			 case 0xf8:
774 				 masklen += 5;
775 				 break;
776 			 case 0xf0:
777 				 masklen += 4;
778 				 break;
779 			 case 0xe0:
780 				 masklen += 3;
781 				 break;
782 			 case 0xc0:
783 				 masklen += 2;
784 				 break;
785 			 case 0x80:
786 				 masklen += 1;
787 				 break;
788 			 case 0x00:
789 				 break;
790 			 default:
791 				 illegal ++;
792 				 break;
793 			}
794 		}
795 		if (illegal)
796 			fprintf(stderr, "illegal prefixlen\n");
797 	}
798 	else
799 		masklen = 128;
800 
801 	if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr))
802 		return("default");
803 
804 	if (nflag)
805 		flag |= NI_NUMERICHOST;
806 	getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line),
807 		    NULL, 0, flag);
808 
809 	if (nflag)
810 		sprintf(&line[strlen(line)], "/%d", masklen);
811 
812 	return line;
813 }
814 
815 char *
816 routename6(sa6)
817 	struct sockaddr_in6 *sa6;
818 {
819 	static char line[MAXHOSTNAMELEN + 1];
820 	int flag = NI_WITHSCOPEID;
821 	/* use local variable for safety */
822 	struct sockaddr_in6 sa6_local = {AF_INET6, sizeof(sa6_local),};
823 
824 	sa6_local.sin6_addr = sa6->sin6_addr;
825 	sa6_local.sin6_scope_id = sa6->sin6_scope_id;
826 
827 	if (nflag)
828 		flag |= NI_NUMERICHOST;
829 
830 	getnameinfo((struct sockaddr *)&sa6_local, sa6_local.sin6_len,
831 		    line, sizeof(line), NULL, 0, flag);
832 
833 	return line;
834 }
835 #endif /*INET6*/
836 
837 /*
838  * Print routing statistics
839  */
840 void
841 rt_stats(off)
842 	u_long off;
843 {
844 	struct rtstat rtstat;
845 
846 	if (off == 0) {
847 		printf("rtstat: symbol not in namelist\n");
848 		return;
849 	}
850 	kread(off, (char *)&rtstat, sizeof (rtstat));
851 	printf("routing:\n");
852 	printf("\t%u bad routing redirect%s\n",
853 		rtstat.rts_badredirect, plural(rtstat.rts_badredirect));
854 	printf("\t%u dynamically created route%s\n",
855 		rtstat.rts_dynamic, plural(rtstat.rts_dynamic));
856 	printf("\t%u new gateway%s due to redirects\n",
857 		rtstat.rts_newgateway, plural(rtstat.rts_newgateway));
858 	printf("\t%u destination%s found unreachable\n",
859 		rtstat.rts_unreach, plural(rtstat.rts_unreach));
860 	printf("\t%u use%s of a wildcard route\n",
861 		rtstat.rts_wildcard, plural(rtstat.rts_wildcard));
862 }
863 
864 char *
865 ipx_print(sa)
866 	register struct sockaddr *sa;
867 {
868 	u_short port;
869 	struct servent *sp = 0;
870 	char *net = "", *host = "";
871 	register char *p;
872 	register u_char *q;
873 	struct ipx_addr work = ((struct sockaddr_ipx *)sa)->sipx_addr;
874 	static char mybuf[50];
875 	char cport[10], chost[15], cnet[15];
876 
877 	port = ntohs(work.x_port);
878 
879 	if (ipx_nullnet(work) && ipx_nullhost(work)) {
880 
881 		if (port) {
882 			if (sp)
883 				sprintf(mybuf, "*.%s", sp->s_name);
884 			else
885 				sprintf(mybuf, "*.%x", port);
886 		} else
887 			sprintf(mybuf, "*.*");
888 
889 		return (mybuf);
890 	}
891 
892 	if (ipx_wildnet(work))
893 		net = "any";
894 	else if (ipx_nullnet(work))
895 		net = "*";
896 	else {
897 		q = work.x_net.c_net;
898 		sprintf(cnet, "%02x%02x%02x%02x",
899 			q[0], q[1], q[2], q[3]);
900 		for (p = cnet; *p == '0' && p < cnet + 8; p++)
901 			continue;
902 		net = p;
903 	}
904 
905 	if (ipx_wildhost(work))
906 		host = "any";
907 	else if (ipx_nullhost(work))
908 		host = "*";
909 	else {
910 		q = work.x_host.c_host;
911 		sprintf(chost, "%02x%02x%02x%02x%02x%02x",
912 			q[0], q[1], q[2], q[3], q[4], q[5]);
913 		for (p = chost; *p == '0' && p < chost + 12; p++)
914 			continue;
915 		host = p;
916 	}
917 
918 	if (port) {
919 		if (strcmp(host, "*") == 0)
920 			host = "";
921 		if (sp)
922 			snprintf(cport, sizeof(cport),
923 				"%s%s", *host ? "." : "", sp->s_name);
924 		else
925 			snprintf(cport, sizeof(cport),
926 				"%s%x", *host ? "." : "", port);
927 	} else
928 		*cport = 0;
929 
930 	snprintf(mybuf, sizeof(mybuf), "%s.%s%s", net, host, cport);
931 	return(mybuf);
932 }
933 
934 char *
935 ipx_phost(sa)
936 	struct sockaddr *sa;
937 {
938 	register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)sa;
939 	struct sockaddr_ipx work;
940 	static union ipx_net ipx_zeronet;
941 	char *p;
942 	struct ipx_addr in;
943 
944 	work = *sipx;
945 	in = work.sipx_addr;
946 
947 	work.sipx_addr.x_port = 0;
948 	work.sipx_addr.x_net = ipx_zeronet;
949 	p = ipx_print((struct sockaddr *)&work);
950 	if (strncmp("*.", p, 2) == 0) p += 2;
951 
952 	return(p);
953 }
954 
955 #ifdef NS
956 short ns_nullh[] = {0,0,0};
957 short ns_bh[] = {-1,-1,-1};
958 
959 char *
960 ns_print(sa)
961 	register struct sockaddr *sa;
962 {
963 	register struct sockaddr_ns *sns = (struct sockaddr_ns*)sa;
964 	struct ns_addr work;
965 	union { union ns_net net_e; u_long long_e; } net;
966 	u_short port;
967 	static char mybuf[50], cport[10], chost[25];
968 	char *host = "";
969 	register char *p; register u_char *q;
970 
971 	work = sns->sns_addr;
972 	port = ntohs(work.x_port);
973 	work.x_port = 0;
974 	net.net_e  = work.x_net;
975 	if (ns_nullhost(work) && net.long_e == 0) {
976 		if (port ) {
977 			sprintf(mybuf, "*.%xH", port);
978 			upHex(mybuf);
979 		} else
980 			sprintf(mybuf, "*.*");
981 		return (mybuf);
982 	}
983 
984 	if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) {
985 		host = "any";
986 	} else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) {
987 		host = "*";
988 	} else {
989 		q = work.x_host.c_host;
990 		sprintf(chost, "%02x%02x%02x%02x%02x%02xH",
991 			q[0], q[1], q[2], q[3], q[4], q[5]);
992 		for (p = chost; *p == '0' && p < chost + 12; p++)
993 			continue;
994 		host = p;
995 	}
996 	if (port)
997 		sprintf(cport, ".%xH", htons(port));
998 	else
999 		*cport = 0;
1000 
1001 	sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport);
1002 	upHex(mybuf);
1003 	return(mybuf);
1004 }
1005 
1006 char *
1007 ns_phost(sa)
1008 	struct sockaddr *sa;
1009 {
1010 	register struct sockaddr_ns *sns = (struct sockaddr_ns *)sa;
1011 	struct sockaddr_ns work;
1012 	static union ns_net ns_zeronet;
1013 	char *p;
1014 
1015 	work = *sns;
1016 	work.sns_addr.x_port = 0;
1017 	work.sns_addr.x_net = ns_zeronet;
1018 
1019 	p = ns_print((struct sockaddr *)&work);
1020 	if (strncmp("0H.", p, 3) == 0)
1021 		p += 3;
1022 	return(p);
1023 }
1024 #endif
1025 
1026 void
1027 upHex(p0)
1028 	char *p0;
1029 {
1030 	register char *p = p0;
1031 
1032 	for (; *p; p++)
1033 		switch (*p) {
1034 
1035 		case 'a':
1036 		case 'b':
1037 		case 'c':
1038 		case 'd':
1039 		case 'e':
1040 		case 'f':
1041 			*p += ('A' - 'a');
1042 			break;
1043 		}
1044 }
1045