xref: /netbsd-src/usr.bin/netstat/route.c (revision 5f7096188587a2c7c95fa3c69b78e1ec9c7923d0)
1 /*
2  * Copyright (c) 1983, 1988 Regents of the University of California.
3  * 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 /*static char sccsid[] = "from: @(#)route.c	5.20 (Berkeley) 11/29/90";*/
36 static char rcsid[] = "$Id: route.c,v 1.5 1993/08/01 18:10:48 mycroft Exp $";
37 #endif /* not lint */
38 
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/mbuf.h>
42 
43 #include <net/if.h>
44 #define  KERNEL
45 #include <net/route.h>
46 #undef KERNEL
47 #include <netinet/in.h>
48 
49 #ifdef NS
50 #include <netns/ns.h>
51 #endif
52 
53 #ifdef ISO
54 #include <netiso/iso.h>
55 #include <net/if_dl.h>
56 #include <netiso/iso_snpac.h>
57 #endif
58 
59 #include <netdb.h>
60 #include <sys/kinfo.h>
61 
62 #include <stdio.h>
63 #include <string.h>
64 
65 extern	int nflag, aflag, Aflag, af;
66 int do_rtent;
67 extern	char *routename(), *netname(), *plural();
68 #ifdef NS
69 extern	char *ns_print();
70 #endif
71 extern	char *malloc();
72 #define kget(p, d) \
73 	(kvm_read((off_t)(p), (char *)&(d), sizeof (d)))
74 
75 /*
76  * Definitions for showing gateway flags.
77  */
78 struct bits {
79 	short	b_mask;
80 	char	b_val;
81 } bits[] = {
82 	{ RTF_UP,	'U' },
83 	{ RTF_GATEWAY,	'G' },
84 	{ RTF_HOST,	'H' },
85 	{ RTF_DYNAMIC,	'D' },
86 	{ RTF_MODIFIED,	'M' },
87 	{ RTF_CLONING,	'C' },
88 	{ RTF_XRESOLVE,	'X' },
89 	{ RTF_LLINFO,	'L' },
90 	{ RTF_REJECT,	'R' },
91 	{ 0 }
92 };
93 
94 #ifdef ISO
95 struct bits2 {
96 	short	b_mask;
97 	char	b_val;
98 } bits2[] = {
99 	{ SNPA_ES,	'E' },
100 	{ SNPA_IS,	'I' },
101 	{ SNPA_PERM,	'P' },
102 	{ 0 }
103 };
104 #endif
105 
106 /*
107  * Print routing tables.
108  */
109 routepr(hostaddr, netaddr, hashsizeaddr, treeaddr)
110 	off_t hostaddr, netaddr, hashsizeaddr, treeaddr;
111 {
112 	struct mbuf mb;
113 	register struct ortentry *rt;
114 	register struct mbuf *m;
115 	char name[16], *flags;
116 	struct mbuf **routehash;
117 	int hashsize;
118 	int i, doinghost = 1;
119 
120 	printf("Routing tables\n");
121 	if (treeaddr)
122 		return treestuff(treeaddr);
123 	if (hostaddr == 0) {
124 		printf("rthost: symbol not in namelist\n");
125 		return;
126 	}
127 	if (netaddr == 0) {
128 		printf("rtnet: symbol not in namelist\n");
129 		return;
130 	}
131 	if (hashsizeaddr == 0) {
132 		printf("rthashsize: symbol not in namelist\n");
133 		return;
134 	}
135 	kget(hashsizeaddr, hashsize);
136 	routehash = (struct mbuf **)malloc( hashsize*sizeof (struct mbuf *) );
137 	kvm_read(hostaddr, (char *)routehash, hashsize*sizeof (struct mbuf *));
138 again:
139 	for (i = 0; i < hashsize; i++) {
140 		if (routehash[i] == 0)
141 			continue;
142 		m = routehash[i];
143 		while (m) {
144 			kget(m, mb);
145 			if (Aflag)
146 				printf("%8.8x ", m);
147 			p_ortentry((struct ortentry *)(mb.m_dat));
148 			m = mb.m_next;
149 		}
150 	}
151 	if (doinghost) {
152 		kvm_read(netaddr, (char *)routehash,
153 			hashsize*sizeof (struct mbuf *));
154 		doinghost = 0;
155 		goto again;
156 	}
157 	free((char *)routehash);
158 	return;
159 }
160 
161 
162 char *
163 af_name(af)
164 {
165 	static char buf[10];
166 
167 	switch(af) {
168 	case AF_INET:
169 		return "inet";
170 	case AF_UNIX:
171 		return "unix";
172 	case AF_NS:
173 		return "ns";
174 	case AF_ISO:
175 		return "iso";
176 	default:
177 		sprintf(buf, "%d", af);
178 	}
179 	return buf;
180 }
181 
182 void
183 p_heading(af)
184 {
185 	if (Aflag)
186 		printf("%-8.8s ","Address");
187 	switch(af) {
188 	case AF_INET:
189 		printf("%-16.16s %-18.18s %-6.6s %6.6s %8.8s  %s\n",
190 			"Destination", "Gateway",
191 			"Flags", "Refs", "Use", "Interface");
192 		break;
193 	case AF_ISO:
194 		if (nflag) {
195 			printf("%-50.50s %-17.17s %-5.5s %s\n",
196 				"Destination", "Media addr", "Flags", "Intf");
197 		} else {
198 			printf("%-12.12s %-19.19s %-17.17s %-6.6s %6s %8s %s\n",
199 				"NSAP-prefix", "Area/Id", "Media addr",
200 				"Flags", "Refs", "Use", "Intf");
201 		}
202 		break;
203 	default:
204 		printf("%-16.16s %-18.18s %-6.6s  %6.6s%8.8s  %s\n",
205 			"Destination", "Gateway",
206 			"Flags", "Refs", "Use", "Interface");
207 	}
208 }
209 
210 
211 static union {
212 	struct	sockaddr u_sa;
213 	u_short	u_data[128];
214 } pt_u;
215 int do_rtent = 0;
216 struct rtentry rtentry;
217 struct radix_node rnode;
218 struct radix_mask rmask;
219 
220 int NewTree = 0;
221 treestuff(rtree)
222 off_t rtree;
223 {
224 	struct radix_node_head *rnh, head;
225 
226 	if (Aflag == 0 && NewTree)
227 		return(ntreestuff());
228 	for (kget(rtree, rnh); rnh; rnh = head.rnh_next) {
229 		kget(rnh, head);
230 		if (head.rnh_af == 0) {
231 			if (Aflag || af == AF_UNSPEC) {
232 				printf("Netmasks:\n");
233 				p_tree(head.rnh_treetop);
234 			}
235 		} else if (af == AF_UNSPEC || af == head.rnh_af) {
236 			printf("\nRoute Tree for Protocol Family %s:\n",
237 					af_name(head.rnh_af));
238 			p_heading(head.rnh_af);
239 			do_rtent = 1;
240 			p_tree(head.rnh_treetop);
241 		}
242 	}
243 }
244 
245 struct sockaddr *
246 kgetsa(dst)
247 register struct sockaddr *dst;
248 {
249 	kget(dst, pt_u.u_sa);
250 	if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa)) {
251 		kvm_read((off_t)dst, pt_u.u_data, pt_u.u_sa.sa_len);
252 	}
253 	return (&pt_u.u_sa);
254 }
255 
256 p_tree(rn)
257 struct radix_node *rn;
258 {
259 
260 again:
261 	kget(rn, rnode);
262 	if (rnode.rn_b < 0) {
263 		if (Aflag)
264 			printf("%-8.8x ", rn);
265 		if (rnode.rn_flags & RNF_ROOT)
266 			printf("(root node)%s",
267 				    rnode.rn_dupedkey ? " =>\n" : "\n");
268 		else if (do_rtent) {
269 			kget(rn, rtentry);
270 			p_rtentry(&rtentry);
271 			if (Aflag)
272 				p_rtnode();
273 		} else {
274 			p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key),
275 				    0, 44);
276 			putchar('\n');
277 		}
278 		if (rn = rnode.rn_dupedkey)
279 			goto again;
280 	} else {
281 		if (Aflag && do_rtent) {
282 			printf("%-8.8x ", rn);
283 			p_rtnode();
284 		}
285 		rn = rnode.rn_r;
286 		p_tree(rnode.rn_l);
287 		p_tree(rn);
288 	}
289 }
290 char nbuf[20];
291 
292 p_rtnode()
293 {
294 
295 	struct radix_mask *rm = rnode.rn_mklist;
296 	if (rnode.rn_b < 0) {
297 		if (rnode.rn_mask) {
298 			printf("\t  mask ");
299 			p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask),
300 				    0, -1);
301 		} else if (rm == 0)
302 			return;
303 	} else {
304 		sprintf(nbuf, "(%d)", rnode.rn_b);
305 		printf("%6.6s %8.8x : %8.8x", nbuf, rnode.rn_l, rnode.rn_r);
306 	}
307 	while (rm) {
308 		kget(rm, rmask);
309 		sprintf(nbuf, " %d refs, ", rmask.rm_refs);
310 		printf(" mk = %8.8x {(%d),%s",
311 			rm, -1 - rmask.rm_b, rmask.rm_refs ? nbuf : " ");
312 		p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask), 0, -1);
313 		putchar('}');
314 		if (rm = rmask.rm_mklist)
315 			printf(" ->");
316 	}
317 	putchar('\n');
318 }
319 
320 ntreestuff()
321 {
322 	int needed;
323 	char *buf, *next, *lim;
324 	register struct rt_msghdr *rtm;
325 
326 	if ((needed = getkerninfo(KINFO_RT_DUMP, 0, 0, 0)) < 0)
327 		{ perror("route-getkerninfo-estimate"); exit(1);}
328 	if ((buf = malloc(needed)) == 0)
329 		{ printf("out of space\n"); exit(1);}
330 	if (getkerninfo(KINFO_RT_DUMP, buf, &needed, 0) < 0)
331 		{ perror("actual retrieval of routing table"); exit(1);}
332 	lim  = buf + needed;
333 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
334 		rtm = (struct rt_msghdr *)next;
335 		np_rtentry(rtm);
336 	}
337 }
338 
339 np_rtentry(rtm)
340 register struct rt_msghdr *rtm;
341 {
342 	register struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
343 	static int masks_done, old_af, banner_printed;
344 	int af = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
345 
346 #ifdef notdef
347 	/* for the moment, netmasks are skipped over */
348 	if (!banner_printed) {
349 		printf("Netmasks:\n");
350 		banner_printed = 1;
351 	}
352 	if (masks_done == 0) {
353 		if (rtm->rtm_addrs != RTA_DST ) {
354 			masks_done = 1;
355 			af = sa->sa_family;
356 		}
357 	} else
358 #endif
359 		af = sa->sa_family;
360 	if (af != old_af) {
361 		printf("\nRoute Tree for Protocol Family %d:\n", af);
362 		old_af = af;
363 	}
364 	if (rtm->rtm_addrs == RTA_DST)
365 		p_sockaddr(sa, 0, 36);
366 	else {
367 		p_sockaddr(sa, rtm->rtm_flags, 16);
368 		if (sa->sa_len == 0)
369 			sa->sa_len = sizeof(long);
370 		sa = (struct sockaddr *)(sa->sa_len + (char *)sa);
371 		p_sockaddr(sa, 0, 18);
372 	}
373 	p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
374 	putchar('\n');
375 }
376 
377 #ifdef ISO
378 extern char* dl_print();
379 #endif
380 
381 p_sockaddr(sa, flags, width)
382 struct sockaddr *sa;
383 int flags, width;
384 {
385 	char format[20], workbuf[128], *cp, *cplim;
386 	register char *cpout;
387 
388 	switch(sa->sa_family) {
389 	case AF_INET:
390 	    {
391 		register struct sockaddr_in *sin = (struct sockaddr_in *)sa;
392 
393 		cp = (sin->sin_addr.s_addr == 0) ? "default" :
394 		      ((flags & RTF_HOST) ?
395 			routename(sin->sin_addr) : netname(sin->sin_addr, 0L));
396 	    }
397 		break;
398 
399 #ifdef NS
400 	case AF_NS:
401 		cp = ns_print((struct sockaddr_ns *)sa);
402 		break;
403 #endif
404 #ifdef ISO
405 	case AF_ISO:
406 		cp = iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr);
407 		break;
408 
409 	case AF_LINK:
410 		cp = dl_print((struct sockaddr_dl *)sa);
411 		break;
412 #endif
413 
414 	default:
415 	    {
416 		register u_short *s = ((u_short *)sa->sa_data), *slim;
417 
418 		slim = (u_short *) sa + ((sa->sa_len + sizeof(u_short) - 1) /
419 		    sizeof(u_short));
420 		cp = workbuf;
421 		cplim = cp + sizeof(workbuf) - 6;
422 		cp += sprintf(cp, "(%d)", sa->sa_family);
423 		while (s < slim && cp < cplim)
424 			cp += sprintf(cp, " %x", *s++);
425 		cp = workbuf;
426 	    }
427 	}
428 	if (width < 0 )
429 		printf("%s ", cp);
430 	else {
431 		if (nflag)
432 			printf("%-*s ", width, cp);
433 		else
434 			printf("%-*.*s ", width, width, cp);
435 	}
436 }
437 
438 p_flags(f, format)
439 register int f;
440 char *format;
441 {
442 	char name[33], *flags;
443 	register struct bits *p = bits;
444 	for (flags = name; p->b_mask; p++)
445 		if (p->b_mask & f)
446 			*flags++ = p->b_val;
447 	*flags = '\0';
448 	printf(format, name);
449 }
450 
451 
452 #ifdef ISO
453 
454 p_iso_flags(f, lli, format)
455 	register int f;
456 	char *format;
457 	caddr_t lli;
458 {
459 	struct llinfo_llc ls;
460 	char name[33], *flags;
461 	register struct bits *p = bits;
462 	register struct bits2 *p2 = bits2;
463 
464 	for (flags = name; p->b_mask; p++)
465 		if (p->b_mask & f)
466 			*flags++ = p->b_val;
467 	if (lli) {
468 		kget(lli, ls);
469 		for (; p2->b_mask; p2++)
470 			if (p2->b_mask & ls.lc_flags)
471 				*flags++ = p2->b_val;
472 	}
473 	*flags = '\0';
474 	printf(format, name);
475 }
476 
477 static char *hexlist = "0123456789abcdef";
478 
479 char *
480 iso_areatoa(isoa)
481         const struct iso_addr *isoa;
482 {
483 	static char obuf[16];
484 	register char *out = obuf;
485 	register int i;
486 	/* Assumption: ISO address always with 2 byte area, 1 byte NSEL */
487 	/* and 6 bytes ID */
488 	register u_char *in = (u_char*)isoa->isoa_genaddr + isoa->isoa_len - 9;
489 	u_char *inlim = in + 2;
490 
491 	if (isoa->isoa_len < 10) return "";
492 	while (in < inlim) {
493 		i = *in++;
494 		out[1] = hexlist[i & 0xf];
495 		i >>= 4;
496 		out[0] = hexlist[i];
497 		out += 2;
498 	}
499 	*out = 0;
500 	return(obuf);
501 }
502 
503 char *
504 iso_idtoa(isoa)
505         const struct iso_addr *isoa;
506 {
507 	static char obuf[16];
508 	register char *out = obuf;
509 	register int i;
510 	/* Assumption: ISO address always with 1 byte NSEL and 6 bytes ID */
511 	register u_char *in = (u_char*)isoa->isoa_genaddr + isoa->isoa_len - 7;
512 	u_char *inlim = in + 6;
513 
514 	if (isoa->isoa_len < 10) return "";
515 	out[1] = 0;
516 	while (in < inlim) {
517 		i = *in++;
518 		if ((inlim - in) % 2 || out == obuf)
519 			*out++ = '.';
520 		out[1] = hexlist[i & 0xf];
521 		i >>= 4;
522 		out[0] = hexlist[i];
523 		out += 2;
524 	}
525 	*out = 0;
526 	return(obuf + 1);
527 }
528 
529 p_iso_route(rt, sa)
530 	struct rtentry *rt;
531 	struct sockaddr *sa;
532 {
533 	struct sockaddr_iso *siso = (struct sockaddr_iso *)sa;
534 
535 	if (nflag) {
536 		p_sockaddr(sa, rt->rt_flags, 50);
537 		p_sockaddr(kgetsa(rt->rt_gateway), 0, 17);
538 		p_iso_flags(rt->rt_flags, rt->rt_llinfo, "%-6.6s");
539 		p_interface_nl(rt);
540 	} else {
541 		p_sockaddr(sa, rt->rt_flags, 12);
542 		printf("%4.4s/%14.14s ",
543 			iso_areatoa(&siso->siso_addr),
544 			iso_idtoa(&siso->siso_addr));
545 		p_sockaddr(kgetsa(rt->rt_gateway), 0, 17);
546 		p_iso_flags(rt->rt_flags, rt->rt_llinfo, "%-6.6s ");
547 		printf("%6d %8d", rt->rt_refcnt, rt->rt_use);
548 		p_interface_nl(rt);
549 	}
550 }
551 #endif /* ISO */
552 
553 p_interface_nl(rt)
554 	struct rtentry *rt;
555 {
556 	struct ifnet ifnet;
557 	char name[16];
558 
559 	if (rt->rt_ifp == 0) {
560 		putchar('\n');
561 		return;
562 	}
563 	kget(rt->rt_ifp, ifnet);
564 	kvm_read((off_t)ifnet.if_name, name, 16);
565 	printf(" %.15s%d%s", name, ifnet.if_unit,
566 		rt->rt_nodes[0].rn_dupedkey ? " =>\n" : "\n");
567 }
568 
569 p_rtentry(rt)
570 register struct rtentry *rt;
571 {
572 	struct sockaddr *sa;
573 
574 	sa = kgetsa(rt_key(rt));
575 	if (sa->sa_family == AF_ISO) {
576 		p_iso_route(rt, sa);
577 		return;
578 	}
579 	p_sockaddr(sa, rt->rt_flags, 16);
580 	p_sockaddr(kgetsa(rt->rt_gateway), RTF_HOST, 18);
581 	p_flags(rt->rt_flags, "%-6.6s ");
582 	printf("%6d %8d ", rt->rt_refcnt, rt->rt_use);
583 	p_interface_nl(rt);
584 }
585 
586 p_ortentry(rt)
587 register struct ortentry *rt;
588 {
589 	char name[16], *flags;
590 	register struct bits *p;
591 	register struct sockaddr_in *sin;
592 	struct ifnet ifnet;
593 
594 	p_sockaddr(&rt->rt_dst, rt->rt_flags, 16);
595 	p_sockaddr(&rt->rt_gateway, 0, 18);
596 	p_flags(rt->rt_flags, "%-6.6s ");
597 	printf("%6d %8d ", rt->rt_refcnt, rt->rt_use);
598 	if (rt->rt_ifp == 0) {
599 		putchar('\n');
600 		return;
601 	}
602 	kget(rt->rt_ifp, ifnet);
603 	kvm_read((off_t)ifnet.if_name, name, 16);
604 	printf(" %.15s%d\n", name, ifnet.if_unit);
605 }
606 
607 char *
608 routename(in)
609 	struct in_addr in;
610 {
611 	register char *cp;
612 	static char line[MAXHOSTNAMELEN + 1];
613 	struct hostent *hp;
614 	static char domain[MAXHOSTNAMELEN + 1];
615 	static int first = 1;
616 	char *index();
617 
618 	if (first) {
619 		first = 0;
620 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
621 		    (cp = index(domain, '.')))
622 			(void) strcpy(domain, cp + 1);
623 		else
624 			domain[0] = 0;
625 	}
626 	cp = 0;
627 	if (!nflag) {
628 		hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
629 			AF_INET);
630 		if (hp) {
631 			if ((cp = index(hp->h_name, '.')) &&
632 			    !strcmp(cp + 1, domain))
633 				*cp = 0;
634 			cp = hp->h_name;
635 		}
636 	}
637 	if (cp)
638 		strncpy(line, cp, sizeof(line) - 1);
639 	else {
640 #define C(x)	((x) & 0xff)
641 		in.s_addr = ntohl(in.s_addr);
642 		sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
643 			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
644 	}
645 	return (line);
646 }
647 
648 /*
649  * Return the name of the network whose address is given.
650  * The address is assumed to be that of a net or subnet, not a host.
651  */
652 char *
653 netname(in, mask)
654 	struct in_addr in;
655 	u_long mask;
656 {
657 	char *cp = 0;
658 	static char line[MAXHOSTNAMELEN + 1];
659 	struct netent *np = 0;
660 	u_long net;
661 	register i;
662 	int subnetshift;
663 
664 	i = ntohl(in.s_addr);
665 	if (!nflag && i) {
666 		if (mask == 0) {
667 			if (IN_CLASSA(i)) {
668 				mask = IN_CLASSA_NET;
669 				subnetshift = 8;
670 			} else if (IN_CLASSB(i)) {
671 				mask = IN_CLASSB_NET;
672 				subnetshift = 8;
673 			} else {
674 				mask = IN_CLASSC_NET;
675 				subnetshift = 4;
676 			}
677 			/*
678 			 * If there are more bits than the standard mask
679 			 * would suggest, subnets must be in use.
680 			 * Guess at the subnet mask, assuming reasonable
681 			 * width subnet fields.
682 			 */
683 			while (i &~ mask)
684 				mask = (long)mask >> subnetshift;
685 		}
686 		net = i & mask;
687 		while ((mask & 1) == 0)
688 			mask >>= 1, net >>= 1;
689 		np = getnetbyaddr(net, AF_INET);
690 		if (np)
691 			cp = np->n_name;
692 	}
693 	if (cp)
694 		strncpy(line, cp, sizeof(line) - 1);
695 	else if ((i & 0xffffff) == 0)
696 		sprintf(line, "%u", C(i >> 24));
697 	else if ((i & 0xffff) == 0)
698 		sprintf(line, "%u.%u", C(i >> 24) , C(i >> 16));
699 	else if ((i & 0xff) == 0)
700 		sprintf(line, "%u.%u.%u", C(i >> 24), C(i >> 16), C(i >> 8));
701 	else
702 		sprintf(line, "%u.%u.%u.%u", C(i >> 24),
703 			C(i >> 16), C(i >> 8), C(i));
704 	return (line);
705 }
706 
707 /*
708  * Print routing statistics
709  */
710 rt_stats(off)
711 	off_t off;
712 {
713 	struct rtstat rtstat;
714 
715 	if (off == 0) {
716 		printf("rtstat: symbol not in namelist\n");
717 		return;
718 	}
719 	kvm_read(off, (char *)&rtstat, sizeof (rtstat));
720 	printf("routing:\n");
721 	printf("\t%u bad routing redirect%s\n",
722 		rtstat.rts_badredirect, plural(rtstat.rts_badredirect));
723 	printf("\t%u dynamically created route%s\n",
724 		rtstat.rts_dynamic, plural(rtstat.rts_dynamic));
725 	printf("\t%u new gateway%s due to redirects\n",
726 		rtstat.rts_newgateway, plural(rtstat.rts_newgateway));
727 	printf("\t%u destination%s found unreachable\n",
728 		rtstat.rts_unreach, plural(rtstat.rts_unreach));
729 	printf("\t%u use%s of a wildcard route\n",
730 		rtstat.rts_wildcard, plural(rtstat.rts_wildcard));
731 }
732 #ifdef NS
733 short ns_nullh[] = {0,0,0};
734 short ns_bh[] = {-1,-1,-1};
735 
736 char *
737 ns_print(sns)
738 struct sockaddr_ns *sns;
739 {
740 	struct ns_addr work;
741 	union { union ns_net net_e; u_long long_e; } net;
742 	u_short port;
743 	static char mybuf[50], cport[10], chost[25];
744 	char *host = "";
745 	register char *p; register u_char *q;
746 
747 	work = sns->sns_addr;
748 	port = ntohs(work.x_port);
749 	work.x_port = 0;
750 	net.net_e  = work.x_net;
751 	if (ns_nullhost(work) && net.long_e == 0) {
752 		if (port ) {
753 			sprintf(mybuf, "*.%xH", port);
754 			upHex(mybuf);
755 		} else
756 			sprintf(mybuf, "*.*");
757 		return (mybuf);
758 	}
759 
760 	if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) {
761 		host = "any";
762 	} else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) {
763 		host = "*";
764 	} else {
765 		q = work.x_host.c_host;
766 		sprintf(chost, "%02x%02x%02x%02x%02x%02xH",
767 			q[0], q[1], q[2], q[3], q[4], q[5]);
768 		for (p = chost; *p == '0' && p < chost + 12; p++);
769 		host = p;
770 	}
771 	if (port)
772 		sprintf(cport, ".%xH", htons(port));
773 	else
774 		*cport = 0;
775 
776 	sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport);
777 	upHex(mybuf);
778 	return(mybuf);
779 }
780 
781 char *
782 ns_phost(sns)
783 struct sockaddr_ns *sns;
784 {
785 	struct sockaddr_ns work;
786 	static union ns_net ns_zeronet;
787 	char *p;
788 
789 	work = *sns;
790 	work.sns_addr.x_port = 0;
791 	work.sns_addr.x_net = ns_zeronet;
792 
793 	p = ns_print(&work);
794 	if (strncmp("0H.", p, 3) == 0) p += 3;
795 	return(p);
796 }
797 upHex(p0)
798 char *p0;
799 {
800 	register char *p = p0;
801 	for (; *p; p++) switch (*p) {
802 
803 	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
804 		*p += ('A' - 'a');
805 	}
806 }
807 #endif /* NS */
808 #ifdef ISO
809 
810 char *
811 dl_print(sdl)
812 	struct sockaddr_dl *sdl;
813 {
814 	static char buf[20];
815 	char *cp = buf, *dp;
816 	int i;
817 
818 	dp = sdl->sdl_data;
819 	for (i = 0; i < sdl->sdl_nlen; i++)
820 		*cp++ = *dp++;
821 	if (sdl->sdl_nlen != 0 && sdl->sdl_alen != 0)
822 		*cp++ = ':';
823 	for (; i < sdl->sdl_nlen + sdl->sdl_alen; i++)
824 		cp += sprintf(cp, "%x%c", *dp++ & 0xff,
825 			i + 1 == sdl->sdl_nlen + sdl->sdl_alen ? ' ' : '.');
826 	*cp = 0;
827 	return buf;
828 }
829 #endif /* ISO */
830