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