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