xref: /freebsd/usr.bin/netstat/inet.c (revision 99e8005137088aafb1350e23b113d69b01b0820f)
1 /*
2  * Copyright (c) 1983, 1988, 1993, 1995
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 /*
36 static char sccsid[] = "@(#)inet.c	8.5 (Berkeley) 5/24/95";
37 */
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41 
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/socket.h>
45 #include <sys/socketvar.h>
46 #include <sys/sysctl.h>
47 #include <sys/protosw.h>
48 
49 #include <net/route.h>
50 #include <netinet/in.h>
51 #include <netinet/in_systm.h>
52 #include <netinet/ip.h>
53 #ifdef INET6
54 #include <netinet/ip6.h>
55 #endif /* INET6 */
56 #include <netinet/in_pcb.h>
57 #include <netinet/ip_icmp.h>
58 #include <netinet/icmp_var.h>
59 #include <netinet/igmp_var.h>
60 #include <netinet/ip_var.h>
61 #include <netinet/tcp.h>
62 #include <netinet/tcpip.h>
63 #include <netinet/tcp_seq.h>
64 #define TCPSTATES
65 #include <netinet/tcp_fsm.h>
66 #include <netinet/tcp_timer.h>
67 #include <netinet/tcp_var.h>
68 #include <netinet/tcp_debug.h>
69 #include <netinet/udp.h>
70 #include <netinet/udp_var.h>
71 
72 #include <arpa/inet.h>
73 #include <err.h>
74 #include <errno.h>
75 #include <libutil.h>
76 #include <netdb.h>
77 #include <stdio.h>
78 #include <stdlib.h>
79 #include <string.h>
80 #include <unistd.h>
81 #include "netstat.h"
82 
83 char	*inetname __P((struct in_addr *));
84 void	inetprint __P((struct in_addr *, int, char *, int));
85 #ifdef INET6
86 extern void	inet6print __P((struct in6_addr *, int, char *, int));
87 static int udp_done, tcp_done;
88 #endif /* INET6 */
89 
90 /*
91  * Print a summary of connections related to an Internet
92  * protocol.  For TCP, also give state of connection.
93  * Listening processes (aflag) are suppressed unless the
94  * -a (all) flag is specified.
95  */
96 void
97 protopr(proto, name, af)
98 	u_long proto;		/* for sysctl version we pass proto # */
99 	char *name;
100 	int af;
101 {
102 	int istcp;
103 	static int first = 1;
104 	char *buf;
105 	const char *mibvar;
106 	struct tcpcb *tp = NULL;
107 	struct inpcb *inp;
108 	struct xinpgen *xig, *oxig;
109 	struct xsocket *so;
110 	size_t len;
111 
112 	istcp = 0;
113 	switch (proto) {
114 	case IPPROTO_TCP:
115 #ifdef INET6
116 		if (tcp_done != 0)
117 			return;
118 		else
119 			tcp_done = 1;
120 #endif
121 		istcp = 1;
122 		mibvar = "net.inet.tcp.pcblist";
123 		break;
124 	case IPPROTO_UDP:
125 #ifdef INET6
126 		if (udp_done != 0)
127 			return;
128 		else
129 			udp_done = 1;
130 #endif
131 		mibvar = "net.inet.udp.pcblist";
132 		break;
133 	case IPPROTO_DIVERT:
134 		mibvar = "net.inet.divert.pcblist";
135 		break;
136 	default:
137 		mibvar = "net.inet.raw.pcblist";
138 		break;
139 	}
140 	len = 0;
141 	if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) {
142 		if (errno != ENOENT)
143 			warn("sysctl: %s", mibvar);
144 		return;
145 	}
146 	if ((buf = malloc(len)) == 0) {
147 		warn("malloc %lu bytes", (u_long)len);
148 		return;
149 	}
150 	if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) {
151 		warn("sysctl: %s", mibvar);
152 		free(buf);
153 		return;
154 	}
155 
156 	oxig = xig = (struct xinpgen *)buf;
157 	for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
158 	     xig->xig_len > sizeof(struct xinpgen);
159 	     xig = (struct xinpgen *)((char *)xig + xig->xig_len)) {
160 		if (istcp) {
161 			tp = &((struct xtcpcb *)xig)->xt_tp;
162 			inp = &((struct xtcpcb *)xig)->xt_inp;
163 			so = &((struct xtcpcb *)xig)->xt_socket;
164 		} else {
165 			inp = &((struct xinpcb *)xig)->xi_inp;
166 			so = &((struct xinpcb *)xig)->xi_socket;
167 		}
168 
169 		/* Ignore sockets for protocols other than the desired one. */
170 		if (so->xso_protocol != proto)
171 			continue;
172 
173 		/* Ignore PCBs which were freed during copyout. */
174 		if (inp->inp_gencnt > oxig->xig_gen)
175 			continue;
176 
177 		if ((af == AF_INET && (inp->inp_vflag & INP_IPV4) == 0)
178 #ifdef INET6
179 		    || (af == AF_INET6 && (inp->inp_vflag & INP_IPV6) == 0)
180 #endif /* INET6 */
181 		    || (af == AF_UNSPEC && ((inp->inp_vflag & INP_IPV4) == 0
182 #ifdef INET6
183 					    && (inp->inp_vflag &
184 						INP_IPV6) == 0
185 #endif /* INET6 */
186 			))
187 		    )
188 			continue;
189 		if (!aflag &&
190 		    (
191 		     (af == AF_INET &&
192 		      inet_lnaof(inp->inp_laddr) == INADDR_ANY)
193 #ifdef INET6
194 		     || (af == AF_INET6 &&
195 			 IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
196 #endif /* INET6 */
197 		     || (af == AF_UNSPEC &&
198 			 (((inp->inp_vflag & INP_IPV4) != 0 &&
199 			   inet_lnaof(inp->inp_laddr) == INADDR_ANY)
200 #ifdef INET6
201 			  || ((inp->inp_vflag & INP_IPV6) != 0 &&
202 			      IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
203 #endif
204 			  ))
205 		     ))
206 			continue;
207 
208 		if (first) {
209 			if (!Lflag) {
210 				printf("Active Internet connections");
211 				if (aflag)
212 					printf(" (including servers)");
213 			} else
214 				printf(
215 	"Current listen queue sizes (qlen/incqlen/maxqlen)");
216 			putchar('\n');
217 			if (Aflag)
218 				printf("%-8.8s ", "Socket");
219 			if (Lflag)
220 				printf("%-14.14s %-22.22s\n",
221 					"Listen", "Local Address");
222 			else
223 				printf((Aflag && !Wflag) ?
224 		"%-5.5s %-6.6s %-6.6s  %-18.18s %-18.18s %s\n" :
225 		"%-5.5s %-6.6s %-6.6s  %-22.22s %-22.22s %s\n",
226 					"Proto", "Recv-Q", "Send-Q",
227 					"Local Address", "Foreign Address",
228 					"(state)");
229 			first = 0;
230 		}
231 		if (Aflag) {
232 			if (istcp)
233 				printf("%8lx ", (u_long)inp->inp_ppcb);
234 			else
235 				printf("%8lx ", (u_long)so->so_pcb);
236 		}
237 		if (Lflag)
238 			if (so->so_qlimit) {
239 				char buf[15];
240 
241 				snprintf(buf, 15, "%d/%d/%d", so->so_qlen,
242 					 so->so_incqlen, so->so_qlimit);
243 				printf("%-14.14s ", buf);
244 			} else
245 				continue;
246 		else {
247 			const char *vchar;
248 
249 #ifdef INET6
250 			if ((inp->inp_vflag & INP_IPV6) != 0)
251 				vchar = ((inp->inp_vflag & INP_IPV4) != 0)
252 					? "46" : "6 ";
253 			else
254 #endif
255 			vchar = ((inp->inp_vflag & INP_IPV4) != 0)
256 					? "4 " : "  ";
257 
258 			printf("%-3.3s%-2.2s %6ld %6ld  ", name, vchar,
259 			       so->so_rcv.sb_cc,
260 			       so->so_snd.sb_cc);
261 		}
262 		if (nflag) {
263 			if (inp->inp_vflag & INP_IPV4) {
264 				inetprint(&inp->inp_laddr, (int)inp->inp_lport,
265 					  name, 1);
266 				if (!Lflag)
267 					inetprint(&inp->inp_faddr,
268 						  (int)inp->inp_fport, name, 1);
269 			}
270 #ifdef INET6
271 			else if (inp->inp_vflag & INP_IPV6) {
272 				inet6print(&inp->in6p_laddr,
273 					   (int)inp->inp_lport, name, 1);
274 				if (!Lflag)
275 					inet6print(&inp->in6p_faddr,
276 						   (int)inp->inp_fport, name, 1);
277 			} /* else nothing printed now */
278 #endif /* INET6 */
279 		} else if (inp->inp_flags & INP_ANONPORT) {
280 			if (inp->inp_vflag & INP_IPV4) {
281 				inetprint(&inp->inp_laddr, (int)inp->inp_lport,
282 					  name, 1);
283 				if (!Lflag)
284 					inetprint(&inp->inp_faddr,
285 						  (int)inp->inp_fport, name, 0);
286 			}
287 #ifdef INET6
288 			else if (inp->inp_vflag & INP_IPV6) {
289 				inet6print(&inp->in6p_laddr,
290 					   (int)inp->inp_lport, name, 1);
291 				if (!Lflag)
292 					inet6print(&inp->in6p_faddr,
293 						   (int)inp->inp_fport, name, 0);
294 			} /* else nothing printed now */
295 #endif /* INET6 */
296 		} else {
297 			if (inp->inp_vflag & INP_IPV4) {
298 				inetprint(&inp->inp_laddr, (int)inp->inp_lport,
299 					  name, 0);
300 				if (!Lflag)
301 					inetprint(&inp->inp_faddr,
302 						  (int)inp->inp_fport, name,
303 						  inp->inp_lport !=
304 							inp->inp_fport);
305 			}
306 #ifdef INET6
307 			else if (inp->inp_vflag & INP_IPV6) {
308 				inet6print(&inp->in6p_laddr,
309 					   (int)inp->inp_lport, name, 0);
310 				if (!Lflag)
311 					inet6print(&inp->in6p_faddr,
312 						   (int)inp->inp_fport, name,
313 						   inp->inp_lport !=
314 							inp->inp_fport);
315 			} /* else nothing printed now */
316 #endif /* INET6 */
317 		}
318 		if (istcp && !Lflag) {
319 			if (tp->t_state < 0 || tp->t_state >= TCP_NSTATES)
320 				printf("%d", tp->t_state);
321                       else {
322 				printf("%s", tcpstates[tp->t_state]);
323 #if defined(TF_NEEDSYN) && defined(TF_NEEDFIN)
324                               /* Show T/TCP `hidden state' */
325                               if (tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN))
326                                       putchar('*');
327 #endif /* defined(TF_NEEDSYN) && defined(TF_NEEDFIN) */
328                       }
329 		}
330 		putchar('\n');
331 	}
332 	if (xig != oxig && xig->xig_gen != oxig->xig_gen) {
333 		if (oxig->xig_count > xig->xig_count) {
334 			printf("Some %s sockets may have been deleted.\n",
335 			       name);
336 		} else if (oxig->xig_count < xig->xig_count) {
337 			printf("Some %s sockets may have been created.\n",
338 			       name);
339 		} else {
340 			printf("Some %s sockets may have been created or deleted",
341 			       name);
342 		}
343 	}
344 	free(buf);
345 }
346 
347 /*
348  * Dump TCP statistics structure.
349  */
350 void
351 tcp_stats(off, name)
352 	u_long off;
353 	char *name;
354 {
355 	struct tcpstat tcpstat;
356 	size_t len = sizeof tcpstat;
357 
358 	if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, 0, 0) < 0) {
359 		warn("sysctl: net.inet.tcp.stats");
360 		return;
361 	}
362 
363 #ifdef INET6
364 	if (tcp_done != 0)
365 		return;
366 	else
367 		tcp_done = 1;
368 #endif
369 
370 	printf ("%s:\n", name);
371 
372 #define	p(f, m) if (tcpstat.f || sflag <= 1) \
373     printf(m, tcpstat.f, plural(tcpstat.f))
374 #define	p1a(f, m) if (tcpstat.f || sflag <= 1) \
375     printf(m, tcpstat.f)
376 #define	p2(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \
377     printf(m, tcpstat.f1, plural(tcpstat.f1), tcpstat.f2, plural(tcpstat.f2))
378 #define	p2a(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \
379     printf(m, tcpstat.f1, plural(tcpstat.f1), tcpstat.f2)
380 #define	p3(f, m) if (tcpstat.f || sflag <= 1) \
381     printf(m, tcpstat.f, plurales(tcpstat.f))
382 
383 	p(tcps_sndtotal, "\t%lu packet%s sent\n");
384 	p2(tcps_sndpack,tcps_sndbyte,
385 		"\t\t%lu data packet%s (%lu byte%s)\n");
386 	p2(tcps_sndrexmitpack, tcps_sndrexmitbyte,
387 		"\t\t%lu data packet%s (%lu byte%s) retransmitted\n");
388 	p(tcps_mturesent, "\t\t%lu resend%s initiated by MTU discovery\n");
389 	p2a(tcps_sndacks, tcps_delack,
390 		"\t\t%lu ack-only packet%s (%lu delayed)\n");
391 	p(tcps_sndurg, "\t\t%lu URG only packet%s\n");
392 	p(tcps_sndprobe, "\t\t%lu window probe packet%s\n");
393 	p(tcps_sndwinup, "\t\t%lu window update packet%s\n");
394 	p(tcps_sndctrl, "\t\t%lu control packet%s\n");
395 	p(tcps_rcvtotal, "\t%lu packet%s received\n");
396 	p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%lu ack%s (for %lu byte%s)\n");
397 	p(tcps_rcvdupack, "\t\t%lu duplicate ack%s\n");
398 	p(tcps_rcvacktoomuch, "\t\t%lu ack%s for unsent data\n");
399 	p2(tcps_rcvpack, tcps_rcvbyte,
400 		"\t\t%lu packet%s (%lu byte%s) received in-sequence\n");
401 	p2(tcps_rcvduppack, tcps_rcvdupbyte,
402 		"\t\t%lu completely duplicate packet%s (%lu byte%s)\n");
403 	p(tcps_pawsdrop, "\t\t%lu old duplicate packet%s\n");
404 	p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
405 		"\t\t%lu packet%s with some dup. data (%lu byte%s duped)\n");
406 	p2(tcps_rcvoopack, tcps_rcvoobyte,
407 		"\t\t%lu out-of-order packet%s (%lu byte%s)\n");
408 	p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
409 		"\t\t%lu packet%s (%lu byte%s) of data after window\n");
410 	p(tcps_rcvwinprobe, "\t\t%lu window probe%s\n");
411 	p(tcps_rcvwinupd, "\t\t%lu window update packet%s\n");
412 	p(tcps_rcvafterclose, "\t\t%lu packet%s received after close\n");
413 	p(tcps_rcvbadsum, "\t\t%lu discarded for bad checksum%s\n");
414 	p(tcps_rcvbadoff, "\t\t%lu discarded for bad header offset field%s\n");
415 	p1a(tcps_rcvshort, "\t\t%lu discarded because packet too short\n");
416 	p(tcps_connattempt, "\t%lu connection request%s\n");
417 	p(tcps_accepts, "\t%lu connection accept%s\n");
418 	p(tcps_badsyn, "\t%lu bad connection attempt%s\n");
419 	p(tcps_listendrop, "\t%lu listen queue overflow%s\n");
420 	p(tcps_connects, "\t%lu connection%s established (including accepts)\n");
421 	p2(tcps_closed, tcps_drops,
422 		"\t%lu connection%s closed (including %lu drop%s)\n");
423 	p(tcps_cachedrtt, "\t\t%lu connection%s updated cached RTT on close\n");
424 	p(tcps_cachedrttvar,
425 	  "\t\t%lu connection%s updated cached RTT variance on close\n");
426 	p(tcps_cachedssthresh,
427 	  "\t\t%lu connection%s updated cached ssthresh on close\n");
428 	p(tcps_conndrops, "\t%lu embryonic connection%s dropped\n");
429 	p2(tcps_rttupdated, tcps_segstimed,
430 		"\t%lu segment%s updated rtt (of %lu attempt%s)\n");
431 	p(tcps_rexmttimeo, "\t%lu retransmit timeout%s\n");
432 	p(tcps_timeoutdrop, "\t\t%lu connection%s dropped by rexmit timeout\n");
433 	p(tcps_persisttimeo, "\t%lu persist timeout%s\n");
434 	p(tcps_persistdrop, "\t\t%lu connection%s dropped by persist timeout\n");
435 	p(tcps_keeptimeo, "\t%lu keepalive timeout%s\n");
436 	p(tcps_keepprobe, "\t\t%lu keepalive probe%s sent\n");
437 	p(tcps_keepdrops, "\t\t%lu connection%s dropped by keepalive\n");
438 	p(tcps_predack, "\t%lu correct ACK header prediction%s\n");
439 	p(tcps_preddat, "\t%lu correct data packet header prediction%s\n");
440 #undef p
441 #undef p1a
442 #undef p2
443 #undef p2a
444 #undef p3
445 }
446 
447 /*
448  * Dump UDP statistics structure.
449  */
450 void
451 udp_stats(off, name)
452 	u_long off;
453 	char *name;
454 {
455 	struct udpstat udpstat;
456 	size_t len = sizeof udpstat;
457 	u_long delivered;
458 
459 	if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, 0, 0) < 0) {
460 		warn("sysctl: net.inet.udp.stats");
461 		return;
462 	}
463 
464 #ifdef INET6
465 	if (udp_done != 0)
466 		return;
467 	else
468 		udp_done = 1;
469 #endif
470 
471 	printf("%s:\n", name);
472 #define	p(f, m) if (udpstat.f || sflag <= 1) \
473     printf(m, udpstat.f, plural(udpstat.f))
474 #define	p1a(f, m) if (udpstat.f || sflag <= 1) \
475     printf(m, udpstat.f)
476 	p(udps_ipackets, "\t%lu datagram%s received\n");
477 	p1a(udps_hdrops, "\t%lu with incomplete header\n");
478 	p1a(udps_badlen, "\t%lu with bad data length field\n");
479 	p1a(udps_badsum, "\t%lu with bad checksum\n");
480 	p1a(udps_nosum, "\t%lu with no checksum\n");
481 	p1a(udps_noport, "\t%lu dropped due to no socket\n");
482 	p(udps_noportbcast,
483 	    "\t%lu broadcast/multicast datagram%s dropped due to no socket\n");
484 	p1a(udps_fullsock, "\t%lu dropped due to full socket buffers\n");
485 	p1a(udpps_pcbhashmiss, "\t%lu not for hashed pcb\n");
486 	delivered = udpstat.udps_ipackets -
487 		    udpstat.udps_hdrops -
488 		    udpstat.udps_badlen -
489 		    udpstat.udps_badsum -
490 		    udpstat.udps_noport -
491 		    udpstat.udps_noportbcast -
492 		    udpstat.udps_fullsock;
493 	if (delivered || sflag <= 1)
494 		printf("\t%lu delivered\n", delivered);
495 	p(udps_opackets, "\t%lu datagram%s output\n");
496 #undef p
497 #undef p1a
498 }
499 
500 /*
501  * Dump IP statistics structure.
502  */
503 void
504 ip_stats(off, name)
505 	u_long off;
506 	char *name;
507 {
508 	struct ipstat ipstat;
509 	size_t len = sizeof ipstat;
510 
511 	if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, 0, 0) < 0) {
512 		warn("sysctl: net.inet.ip.stats");
513 		return;
514 	}
515 
516 	printf("%s:\n", name);
517 
518 #define	p(f, m) if (ipstat.f || sflag <= 1) \
519     printf(m, ipstat.f, plural(ipstat.f))
520 #define	p1a(f, m) if (ipstat.f || sflag <= 1) \
521     printf(m, ipstat.f)
522 
523 	p(ips_total, "\t%lu total packet%s received\n");
524 	p(ips_badsum, "\t%lu bad header checksum%s\n");
525 	p1a(ips_toosmall, "\t%lu with size smaller than minimum\n");
526 	p1a(ips_tooshort, "\t%lu with data size < data length\n");
527 	p1a(ips_toolong, "\t%lu with ip length > max ip packet size\n");
528 	p1a(ips_badhlen, "\t%lu with header length < data size\n");
529 	p1a(ips_badlen, "\t%lu with data length < header length\n");
530 	p1a(ips_badoptions, "\t%lu with bad options\n");
531 	p1a(ips_badvers, "\t%lu with incorrect version number\n");
532 	p(ips_fragments, "\t%lu fragment%s received\n");
533 	p(ips_fragdropped, "\t%lu fragment%s dropped (dup or out of space)\n");
534 	p(ips_fragtimeout, "\t%lu fragment%s dropped after timeout\n");
535 	p(ips_reassembled, "\t%lu packet%s reassembled ok\n");
536 	p(ips_delivered, "\t%lu packet%s for this host\n");
537 	p(ips_noproto, "\t%lu packet%s for unknown/unsupported protocol\n");
538 	p(ips_forward, "\t%lu packet%s forwarded");
539 	p(ips_fastforward, " (%lu packet%s fast forwarded)");
540 	if (ipstat.ips_forward || sflag <= 1)
541 		putchar('\n');
542 	p(ips_cantforward, "\t%lu packet%s not forwardable\n");
543 	p(ips_notmember,
544 	  "\t%lu packet%s received for unknown multicast group\n");
545 	p(ips_redirectsent, "\t%lu redirect%s sent\n");
546 	p(ips_localout, "\t%lu packet%s sent from this host\n");
547 	p(ips_rawout, "\t%lu packet%s sent with fabricated ip header\n");
548 	p(ips_odropped,
549 	  "\t%lu output packet%s dropped due to no bufs, etc.\n");
550 	p(ips_noroute, "\t%lu output packet%s discarded due to no route\n");
551 	p(ips_fragmented, "\t%lu output datagram%s fragmented\n");
552 	p(ips_ofragments, "\t%lu fragment%s created\n");
553 	p(ips_cantfrag, "\t%lu datagram%s that can't be fragmented\n");
554 	p(ips_nogif, "\t%lu tunneling packet%s that can't find gif\n");
555 #undef p
556 #undef p1a
557 }
558 
559 static	char *icmpnames[] = {
560 	"echo reply",
561 	"#1",
562 	"#2",
563 	"destination unreachable",
564 	"source quench",
565 	"routing redirect",
566 	"#6",
567 	"#7",
568 	"echo",
569 	"router advertisement",
570 	"router solicitation",
571 	"time exceeded",
572 	"parameter problem",
573 	"time stamp",
574 	"time stamp reply",
575 	"information request",
576 	"information request reply",
577 	"address mask request",
578 	"address mask reply",
579 };
580 
581 /*
582  * Dump ICMP statistics.
583  */
584 void
585 icmp_stats(off, name)
586 	u_long off;
587 	char *name;
588 {
589 	struct icmpstat icmpstat;
590 	int i, first;
591 	int mib[4];		/* CTL_NET + PF_INET + IPPROTO_ICMP + req */
592 	size_t len;
593 
594 	mib[0] = CTL_NET;
595 	mib[1] = PF_INET;
596 	mib[2] = IPPROTO_ICMP;
597 	mib[3] = ICMPCTL_STATS;
598 
599 	len = sizeof icmpstat;
600 	memset(&icmpstat, 0, len);
601 	if (sysctl(mib, 4, &icmpstat, &len, (void *)0, 0) < 0)
602 		return;		/* XXX should complain, but not traditional */
603 
604 	printf("%s:\n", name);
605 
606 #define	p(f, m) if (icmpstat.f || sflag <= 1) \
607     printf(m, icmpstat.f, plural(icmpstat.f))
608 #define	p1a(f, m) if (icmpstat.f || sflag <= 1) \
609     printf(m, icmpstat.f)
610 
611 	p(icps_error, "\t%lu call%s to icmp_error\n");
612 	p(icps_oldicmp,
613 	    "\t%lu error%s not generated 'cuz old message was icmp\n");
614 	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
615 		if (icmpstat.icps_outhist[i] != 0) {
616 			if (first) {
617 				printf("\tOutput histogram:\n");
618 				first = 0;
619 			}
620 			printf("\t\t%s: %lu\n", icmpnames[i],
621 				icmpstat.icps_outhist[i]);
622 		}
623 	p(icps_badcode, "\t%lu message%s with bad code fields\n");
624 	p(icps_tooshort, "\t%lu message%s < minimum length\n");
625 	p(icps_checksum, "\t%lu bad checksum%s\n");
626 	p(icps_badlen, "\t%lu message%s with bad length\n");
627 	p1a(icps_bmcastecho, "\t%lu multicast echo requests ignored\n");
628 	p1a(icps_bmcasttstamp, "\t%lu multicast timestamp requests ignored\n");
629 	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
630 		if (icmpstat.icps_inhist[i] != 0) {
631 			if (first) {
632 				printf("\tInput histogram:\n");
633 				first = 0;
634 			}
635 			printf("\t\t%s: %lu\n", icmpnames[i],
636 				icmpstat.icps_inhist[i]);
637 		}
638 	p(icps_reflect, "\t%lu message response%s generated\n");
639 #undef p
640 #undef p1a
641 	mib[3] = ICMPCTL_MASKREPL;
642 	len = sizeof i;
643 	if (sysctl(mib, 4, &i, &len, (void *)0, 0) < 0)
644 		return;
645 	printf("\tICMP address mask responses are %sabled\n",
646 	       i ? "en" : "dis");
647 }
648 
649 /*
650  * Dump IGMP statistics structure.
651  */
652 void
653 igmp_stats(off, name)
654 	u_long off;
655 	char *name;
656 {
657 	struct igmpstat igmpstat;
658 	size_t len = sizeof igmpstat;
659 
660 	if (sysctlbyname("net.inet.igmp.stats", &igmpstat, &len, 0, 0) < 0) {
661 		warn("sysctl: net.inet.igmp.stats");
662 		return;
663 	}
664 
665 	printf("%s:\n", name);
666 
667 #define	p(f, m) if (igmpstat.f || sflag <= 1) \
668     printf(m, igmpstat.f, plural(igmpstat.f))
669 #define	py(f, m) if (igmpstat.f || sflag <= 1) \
670     printf(m, igmpstat.f, igmpstat.f != 1 ? "ies" : "y")
671 	p(igps_rcv_total, "\t%u message%s received\n");
672         p(igps_rcv_tooshort, "\t%u message%s received with too few bytes\n");
673         p(igps_rcv_badsum, "\t%u message%s received with bad checksum\n");
674         py(igps_rcv_queries, "\t%u membership quer%s received\n");
675         py(igps_rcv_badqueries, "\t%u membership quer%s received with invalid field(s)\n");
676         p(igps_rcv_reports, "\t%u membership report%s received\n");
677         p(igps_rcv_badreports, "\t%u membership report%s received with invalid field(s)\n");
678         p(igps_rcv_ourreports, "\t%u membership report%s received for groups to which we belong\n");
679         p(igps_snd_reports, "\t%u membership report%s sent\n");
680 #undef p
681 #undef py
682 }
683 
684 /*
685  * Pretty print an Internet address (net address + port).
686  */
687 void
688 inetprint(in, port, proto,numeric)
689 	register struct in_addr *in;
690 	int port;
691 	char *proto;
692 	int numeric;
693 {
694 	struct servent *sp = 0;
695 	char line[80], *cp;
696 	int width;
697 
698 	if (Wflag)
699 	    sprintf(line, "%s.", inetname(in));
700 	else
701 	    sprintf(line, "%.*s.", (Aflag && !numeric) ? 12 : 16, inetname(in));
702 	cp = index(line, '\0');
703 	if (!numeric && port)
704 		sp = getservbyport((int)port, proto);
705 	if (sp || port == 0)
706 		sprintf(cp, "%.15s ", sp ? sp->s_name : "*");
707 	else
708 		sprintf(cp, "%d ", ntohs((u_short)port));
709 	width = (Aflag && !Wflag) ? 18 : 22;
710 	if (Wflag)
711 	    printf("%-*s ", width, line);
712 	else
713 	    printf("%-*.*s ", width, width, line);
714 }
715 
716 /*
717  * Construct an Internet address representation.
718  * If the nflag has been supplied, give
719  * numeric value, otherwise try for symbolic name.
720  */
721 char *
722 inetname(inp)
723 	struct in_addr *inp;
724 {
725 	register char *cp;
726 	static char line[MAXHOSTNAMELEN];
727 	struct hostent *hp;
728 	struct netent *np;
729 
730 	cp = 0;
731 	if (!nflag && inp->s_addr != INADDR_ANY) {
732 		int net = inet_netof(*inp);
733 		int lna = inet_lnaof(*inp);
734 
735 		if (lna == INADDR_ANY) {
736 			np = getnetbyaddr(net, AF_INET);
737 			if (np)
738 				cp = np->n_name;
739 		}
740 		if (cp == 0) {
741 			hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET);
742 			if (hp) {
743 				cp = hp->h_name;
744 				trimdomain(cp, strlen(cp));
745 			}
746 		}
747 	}
748 	if (inp->s_addr == INADDR_ANY)
749 		strcpy(line, "*");
750 	else if (cp) {
751 		strncpy(line, cp, sizeof(line) - 1);
752 		line[sizeof(line) - 1] = '\0';
753 	} else {
754 		inp->s_addr = ntohl(inp->s_addr);
755 #define C(x)	((u_int)((x) & 0xff))
756 		sprintf(line, "%u.%u.%u.%u", C(inp->s_addr >> 24),
757 		    C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr));
758 	}
759 	return (line);
760 }
761