xref: /freebsd/usr.bin/netstat/inet.c (revision a316b26e50bbed7cf655fbba726ab87d8ab7599d)
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 static char sccsid[] = "@(#)inet.c	8.4 (Berkeley) 4/20/94";
36 #endif /* not lint */
37 
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/socketvar.h>
41 #include <sys/mbuf.h>
42 #include <sys/protosw.h>
43 
44 #include <net/route.h>
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/ip.h>
48 #include <netinet/in_pcb.h>
49 #include <netinet/ip_icmp.h>
50 #include <netinet/icmp_var.h>
51 #include <netinet/igmp_var.h>
52 #include <netinet/ip_var.h>
53 #include <netinet/tcp.h>
54 #include <netinet/tcpip.h>
55 #include <netinet/tcp_seq.h>
56 #define TCPSTATES
57 #include <netinet/tcp_fsm.h>
58 #include <netinet/tcp_timer.h>
59 #include <netinet/tcp_var.h>
60 #include <netinet/tcp_debug.h>
61 #include <netinet/udp.h>
62 #include <netinet/udp_var.h>
63 
64 #include <arpa/inet.h>
65 #include <netdb.h>
66 #include <stdio.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include "netstat.h"
70 
71 struct	inpcb inpcb;
72 struct	tcpcb tcpcb;
73 struct	socket sockb;
74 
75 char	*inetname __P((struct in_addr *));
76 void	inetprint __P((struct in_addr *, int, char *));
77 
78 /*
79  * Print a summary of connections related to an Internet
80  * protocol.  For TCP, also give state of connection.
81  * Listening processes (aflag) are suppressed unless the
82  * -a (all) flag is specified.
83  */
84 void
85 protopr(off, name)
86 	u_long off;
87 	char *name;
88 {
89 	struct inpcb cb;
90 	register struct inpcb *prev, *next;
91 	int istcp;
92 	static int first = 1;
93 
94 	if (off == 0)
95 		return;
96 	istcp = strcmp(name, "tcp") == 0;
97 	kread(off, (char *)&cb, sizeof (struct inpcb));
98 	inpcb = cb;
99 	prev = (struct inpcb *)off;
100 	if (inpcb.inp_next == (struct inpcb *)off)
101 		return;
102 	while (inpcb.inp_next != (struct inpcb *)off) {
103 		next = inpcb.inp_next;
104 		kread((u_long)next, (char *)&inpcb, sizeof (inpcb));
105 		if (inpcb.inp_prev != prev) {
106 			printf("???\n");
107 			break;
108 		}
109 		if (!aflag &&
110 		  inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) {
111 			prev = next;
112 			continue;
113 		}
114 		kread((u_long)inpcb.inp_socket, (char *)&sockb, sizeof (sockb));
115 		if (istcp) {
116 			kread((u_long)inpcb.inp_ppcb,
117 			    (char *)&tcpcb, sizeof (tcpcb));
118 		}
119 		if (first) {
120 			printf("Active Internet connections");
121 			if (aflag)
122 				printf(" (including servers)");
123 			putchar('\n');
124 			if (Aflag)
125 				printf("%-8.8s ", "PCB");
126 			printf(Aflag ?
127 				"%-5.5s %-6.6s %-6.6s  %-18.18s %-18.18s %s\n" :
128 				"%-5.5s %-6.6s %-6.6s  %-22.22s %-22.22s %s\n",
129 				"Proto", "Recv-Q", "Send-Q",
130 				"Local Address", "Foreign Address", "(state)");
131 			first = 0;
132 		}
133 		if (Aflag)
134 			if (istcp)
135 				printf("%8x ", inpcb.inp_ppcb);
136 			else
137 				printf("%8x ", next);
138 		printf("%-5.5s %6d %6d ", name, sockb.so_rcv.sb_cc,
139 			sockb.so_snd.sb_cc);
140 		inetprint(&inpcb.inp_laddr, (int)inpcb.inp_lport, name);
141 		inetprint(&inpcb.inp_faddr, (int)inpcb.inp_fport, name);
142 		if (istcp) {
143 			if (tcpcb.t_state < 0 || tcpcb.t_state >= TCP_NSTATES)
144 				printf(" %d", tcpcb.t_state);
145 			else
146 				printf(" %s", tcpstates[tcpcb.t_state]);
147 		}
148 		putchar('\n');
149 		prev = next;
150 	}
151 }
152 
153 /*
154  * Dump TCP statistics structure.
155  */
156 void
157 tcp_stats(off, name)
158 	u_long off;
159 	char *name;
160 {
161 	struct tcpstat tcpstat;
162 
163 	if (off == 0)
164 		return;
165 	printf ("%s:\n", name);
166 	kread(off, (char *)&tcpstat, sizeof (tcpstat));
167 
168 #define	p(f, m) if (tcpstat.f || sflag <= 1) \
169     printf(m, tcpstat.f, plural(tcpstat.f))
170 #define	p2(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \
171     printf(m, tcpstat.f1, plural(tcpstat.f1), tcpstat.f2, plural(tcpstat.f2))
172 #define	p3(f, m) if (tcpstat.f || sflag <= 1) \
173     printf(m, tcpstat.f, plurales(tcpstat.f))
174 
175 	p(tcps_sndtotal, "\t%d packet%s sent\n");
176 	p2(tcps_sndpack,tcps_sndbyte,
177 		"\t\t%d data packet%s (%d byte%s)\n");
178 	p2(tcps_sndrexmitpack, tcps_sndrexmitbyte,
179 		"\t\t%d data packet%s (%d byte%s) retransmitted\n");
180 	p2(tcps_sndacks, tcps_delack,
181 		"\t\t%d ack-only packet%s (%d delayed)\n");
182 	p(tcps_sndurg, "\t\t%d URG only packet%s\n");
183 	p(tcps_sndprobe, "\t\t%d window probe packet%s\n");
184 	p(tcps_sndwinup, "\t\t%d window update packet%s\n");
185 	p(tcps_sndctrl, "\t\t%d control packet%s\n");
186 	p(tcps_rcvtotal, "\t%d packet%s received\n");
187 	p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%d ack%s (for %d byte%s)\n");
188 	p(tcps_rcvdupack, "\t\t%d duplicate ack%s\n");
189 	p(tcps_rcvacktoomuch, "\t\t%d ack%s for unsent data\n");
190 	p2(tcps_rcvpack, tcps_rcvbyte,
191 		"\t\t%d packet%s (%d byte%s) received in-sequence\n");
192 	p2(tcps_rcvduppack, tcps_rcvdupbyte,
193 		"\t\t%d completely duplicate packet%s (%d byte%s)\n");
194 	p(tcps_pawsdrop, "\t\t%d old duplicate packet%s\n");
195 	p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
196 		"\t\t%d packet%s with some dup. data (%d byte%s duped)\n");
197 	p2(tcps_rcvoopack, tcps_rcvoobyte,
198 		"\t\t%d out-of-order packet%s (%d byte%s)\n");
199 	p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
200 		"\t\t%d packet%s (%d byte%s) of data after window\n");
201 	p(tcps_rcvwinprobe, "\t\t%d window probe%s\n");
202 	p(tcps_rcvwinupd, "\t\t%d window update packet%s\n");
203 	p(tcps_rcvafterclose, "\t\t%d packet%s received after close\n");
204 	p(tcps_rcvbadsum, "\t\t%d discarded for bad checksum%s\n");
205 	p(tcps_rcvbadoff, "\t\t%d discarded for bad header offset field%s\n");
206 	p(tcps_rcvshort, "\t\t%d discarded because packet too short\n");
207 	p(tcps_connattempt, "\t%d connection request%s\n");
208 	p(tcps_accepts, "\t%d connection accept%s\n");
209 	p(tcps_connects, "\t%d connection%s established (including accepts)\n");
210 	p2(tcps_closed, tcps_drops,
211 		"\t%d connection%s closed (including %d drop%s)\n");
212 	p(tcps_conndrops, "\t%d embryonic connection%s dropped\n");
213 	p2(tcps_rttupdated, tcps_segstimed,
214 		"\t%d segment%s updated rtt (of %d attempt%s)\n");
215 	p(tcps_rexmttimeo, "\t%d retransmit timeout%s\n");
216 	p(tcps_timeoutdrop, "\t\t%d connection%s dropped by rexmit timeout\n");
217 	p(tcps_persisttimeo, "\t%d persist timeout%s\n");
218 	p(tcps_keeptimeo, "\t%d keepalive timeout%s\n");
219 	p(tcps_keepprobe, "\t\t%d keepalive probe%s sent\n");
220 	p(tcps_keepdrops, "\t\t%d connection%s dropped by keepalive\n");
221 	p(tcps_predack, "\t%d correct ACK header prediction%s\n");
222 	p(tcps_preddat, "\t%d correct data packet header prediction%s\n");
223 	p3(tcps_pcbcachemiss, "\t%d PCB cache miss%s\n");
224 #undef p
225 #undef p2
226 #undef p3
227 }
228 
229 /*
230  * Dump UDP statistics structure.
231  */
232 void
233 udp_stats(off, name)
234 	u_long off;
235 	char *name;
236 {
237 	struct udpstat udpstat;
238 	u_long delivered;
239 
240 	if (off == 0)
241 		return;
242 	kread(off, (char *)&udpstat, sizeof (udpstat));
243 	printf("%s:\n", name);
244 #define	p(f, m) if (udpstat.f || sflag <= 1) \
245     printf(m, udpstat.f, plural(udpstat.f))
246 	p(udps_ipackets, "\t%u datagram%s received\n");
247 	p(udps_hdrops, "\t%u with incomplete header\n");
248 	p(udps_badlen, "\t%u with bad data length field\n");
249 	p(udps_badsum, "\t%u with bad checksum\n");
250 	p(udps_noport, "\t%u dropped due to no socket\n");
251 	p(udps_noportbcast, "\t%u broadcast/multicast datagram%s dropped due to no socket\n");
252 	p(udps_fullsock, "\t%u dropped due to full socket buffers\n");
253 	delivered = udpstat.udps_ipackets -
254 		    udpstat.udps_hdrops -
255 		    udpstat.udps_badlen -
256 		    udpstat.udps_badsum -
257 		    udpstat.udps_noport -
258 		    udpstat.udps_noportbcast -
259 		    udpstat.udps_fullsock;
260 	if (delivered || sflag <= 1)
261 		printf("\t%u delivered\n", delivered);
262 	p(udps_opackets, "\t%u datagram%s output\n");
263 #undef p
264 }
265 
266 /*
267  * Dump IP statistics structure.
268  */
269 void
270 ip_stats(off, name)
271 	u_long off;
272 	char *name;
273 {
274 	struct ipstat ipstat;
275 
276 	if (off == 0)
277 		return;
278 	kread(off, (char *)&ipstat, sizeof (ipstat));
279 	printf("%s:\n", name);
280 
281 #define	p(f, m) if (ipstat.f || sflag <= 1) \
282     printf(m, ipstat.f, plural(ipstat.f))
283 
284 	p(ips_total, "\t%u total packet%s received\n");
285 	p(ips_badsum, "\t%u bad header checksum%s\n");
286 	p(ips_toosmall, "\t%u with size smaller than minimum\n");
287 	p(ips_tooshort, "\t%u with data size < data length\n");
288 	p(ips_badhlen, "\t%u with header length < data size\n");
289 	p(ips_badlen, "\t%u with data length < header length\n");
290 	p(ips_badoptions, "\t%u with bad options\n");
291 	p(ips_badvers, "\t%u with incorrect version number\n");
292 	p(ips_fragments, "\t%u fragment%s received\n");
293 	p(ips_fragdropped, "\t%u fragment%s dropped (dup or out of space)\n");
294 	p(ips_fragtimeout, "\t%u fragment%s dropped after timeout\n");
295 	p(ips_reassembled, "\t%u packet%s reassembled ok\n");
296 	p(ips_delivered, "\t%u packet%s for this host\n");
297 	p(ips_noproto, "\t%u packet%s for unknown/unsupported protocol\n");
298 	p(ips_forward, "\t%u packet%s forwarded\n");
299 	p(ips_cantforward, "\t%u packet%s not forwardable\n");
300 	p(ips_redirectsent, "\t%u redirect%s sent\n");
301 	p(ips_localout, "\t%u packet%s sent from this host\n");
302 	p(ips_rawout, "\t%u packet%s sent with fabricated ip header\n");
303 	p(ips_odropped, "\t%u output packet%s dropped due to no bufs, etc.\n");
304 	p(ips_noroute, "\t%u output packet%s discarded due to no route\n");
305 	p(ips_fragmented, "\t%u output datagram%s fragmented\n");
306 	p(ips_ofragments, "\t%u fragment%s created\n");
307 	p(ips_cantfrag, "\t%u datagram%s that can't be fragmented\n");
308 #undef p
309 }
310 
311 static	char *icmpnames[] = {
312 	"echo reply",
313 	"#1",
314 	"#2",
315 	"destination unreachable",
316 	"source quench",
317 	"routing redirect",
318 	"#6",
319 	"#7",
320 	"echo",
321 	"router advertisement",
322 	"router solicitation",
323 	"time exceeded",
324 	"parameter problem",
325 	"time stamp",
326 	"time stamp reply",
327 	"information request",
328 	"information request reply",
329 	"address mask request",
330 	"address mask reply",
331 };
332 
333 /*
334  * Dump ICMP statistics.
335  */
336 void
337 icmp_stats(off, name)
338 	u_long off;
339 	char *name;
340 {
341 	struct icmpstat icmpstat;
342 	register int i, first;
343 
344 	if (off == 0)
345 		return;
346 	kread(off, (char *)&icmpstat, sizeof (icmpstat));
347 	printf("%s:\n", name);
348 
349 #define	p(f, m) if (icmpstat.f || sflag <= 1) \
350     printf(m, icmpstat.f, plural(icmpstat.f))
351 
352 	p(icps_error, "\t%u call%s to icmp_error\n");
353 	p(icps_oldicmp,
354 	    "\t%u error%s not generated 'cuz old message was icmp\n");
355 	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
356 		if (icmpstat.icps_outhist[i] != 0) {
357 			if (first) {
358 				printf("\tOutput histogram:\n");
359 				first = 0;
360 			}
361 			printf("\t\t%s: %u\n", icmpnames[i],
362 				icmpstat.icps_outhist[i]);
363 		}
364 	p(icps_badcode, "\t%u message%s with bad code fields\n");
365 	p(icps_tooshort, "\t%u message%s < minimum length\n");
366 	p(icps_checksum, "\t%u bad checksum%s\n");
367 	p(icps_badlen, "\t%u message%s with bad length\n");
368 	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
369 		if (icmpstat.icps_inhist[i] != 0) {
370 			if (first) {
371 				printf("\tInput histogram:\n");
372 				first = 0;
373 			}
374 			printf("\t\t%s: %u\n", icmpnames[i],
375 				icmpstat.icps_inhist[i]);
376 		}
377 	p(icps_reflect, "\t%u message response%s generated\n");
378 #undef p
379 }
380 
381 /*
382  * Dump IGMP statistics structure.
383  */
384 void
385 igmp_stats(off, name)
386 	u_long off;
387 	char *name;
388 {
389 	struct igmpstat igmpstat;
390 
391 	if (off == 0)
392 		return;
393 	kread(off, (char *)&igmpstat, sizeof (igmpstat));
394 	printf("%s:\n", name);
395 
396 #define	p(f, m) if (igmpstat.f || sflag <= 1) \
397     printf(m, igmpstat.f, plural(igmpstat.f))
398 #define	py(f, m) if (igmpstat.f || sflag <= 1) \
399     printf(m, igmpstat.f, igmpstat.f != 1 ? "ies" : "y")
400 	p(igps_rcv_total, "\t%u message%s received\n");
401         p(igps_rcv_tooshort, "\t%u message%s received with too few bytes\n");
402         p(igps_rcv_badsum, "\t%u message%s received with bad checksum\n");
403         py(igps_rcv_queries, "\t%u membership quer%s received\n");
404         py(igps_rcv_badqueries, "\t%u membership quer%s received with invalid field(s)\n");
405         p(igps_rcv_reports, "\t%u membership report%s received\n");
406         p(igps_rcv_badreports, "\t%u membership report%s received with invalid field(s)\n");
407         p(igps_rcv_ourreports, "\t%u membership report%s received for groups to which we belong\n");
408         p(igps_snd_reports, "\t%u membership report%s sent\n");
409 #undef p
410 #undef py
411 }
412 
413 /*
414  * Pretty print an Internet address (net address + port).
415  * If the nflag was specified, use numbers instead of names.
416  */
417 void
418 inetprint(in, port, proto)
419 	register struct in_addr *in;
420 	int port;
421 	char *proto;
422 {
423 	struct servent *sp = 0;
424 	char line[80], *cp;
425 	int width;
426 
427 	sprintf(line, "%.*s.", (Aflag && !nflag) ? 12 : 16, inetname(in));
428 	cp = index(line, '\0');
429 	if (!nflag && port)
430 		sp = getservbyport((int)port, proto);
431 	if (sp || port == 0)
432 		sprintf(cp, "%.8s", sp ? sp->s_name : "*");
433 	else
434 		sprintf(cp, "%d", ntohs((u_short)port));
435 	width = Aflag ? 18 : 22;
436 	printf(" %-*.*s", width, width, line);
437 }
438 
439 /*
440  * Construct an Internet address representation.
441  * If the nflag has been supplied, give
442  * numeric value, otherwise try for symbolic name.
443  */
444 char *
445 inetname(inp)
446 	struct in_addr *inp;
447 {
448 	register char *cp;
449 	static char line[50];
450 	struct hostent *hp;
451 	struct netent *np;
452 	static char domain[MAXHOSTNAMELEN + 1];
453 	static int first = 1;
454 
455 	if (first && !nflag) {
456 		first = 0;
457 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
458 		    (cp = index(domain, '.')))
459 			(void) strcpy(domain, cp + 1);
460 		else
461 			domain[0] = 0;
462 	}
463 	cp = 0;
464 	if (!nflag && inp->s_addr != INADDR_ANY) {
465 		int net = inet_netof(*inp);
466 		int lna = inet_lnaof(*inp);
467 
468 		if (lna == INADDR_ANY) {
469 			np = getnetbyaddr(net, AF_INET);
470 			if (np)
471 				cp = np->n_name;
472 		}
473 		if (cp == 0) {
474 			hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET);
475 			if (hp) {
476 				if ((cp = index(hp->h_name, '.')) &&
477 				    !strcmp(cp + 1, domain))
478 					*cp = 0;
479 				cp = hp->h_name;
480 			}
481 		}
482 	}
483 	if (inp->s_addr == INADDR_ANY)
484 		strcpy(line, "*");
485 	else if (cp)
486 		strcpy(line, cp);
487 	else {
488 		inp->s_addr = ntohl(inp->s_addr);
489 #define C(x)	((x) & 0xff)
490 		sprintf(line, "%u.%u.%u.%u", C(inp->s_addr >> 24),
491 		    C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr));
492 	}
493 	return (line);
494 }
495