xref: /freebsd/usr.bin/netstat/inet.c (revision 17ee9d00bc1ae1e598c38f25826f861e4bc6c3ce)
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 #if defined(TF_NEEDSYN) && defined(TF_NEEDSYN)
148                               /* Show T/TCP `hidden state' */
149                               if (tcpcb.t_flags & (TF_NEEDSYN|TF_NEEDFIN))
150                                       putchar('*');
151 #endif /* defined(TF_NEEDSYN) && defined(TF_NEEDSYN) */
152                       }
153 		}
154 		putchar('\n');
155 		prev = next;
156 	}
157 }
158 
159 /*
160  * Dump TCP statistics structure.
161  */
162 void
163 tcp_stats(off, name)
164 	u_long off;
165 	char *name;
166 {
167 	struct tcpstat tcpstat;
168 
169 	if (off == 0)
170 		return;
171 	printf ("%s:\n", name);
172 	kread(off, (char *)&tcpstat, sizeof (tcpstat));
173 
174 #define	p(f, m) if (tcpstat.f || sflag <= 1) \
175     printf(m, tcpstat.f, plural(tcpstat.f))
176 #define	p2(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \
177     printf(m, tcpstat.f1, plural(tcpstat.f1), tcpstat.f2, plural(tcpstat.f2))
178 #define	p3(f, m) if (tcpstat.f || sflag <= 1) \
179     printf(m, tcpstat.f, plurales(tcpstat.f))
180 
181 	p(tcps_sndtotal, "\t%d packet%s sent\n");
182 	p2(tcps_sndpack,tcps_sndbyte,
183 		"\t\t%d data packet%s (%d byte%s)\n");
184 	p2(tcps_sndrexmitpack, tcps_sndrexmitbyte,
185 		"\t\t%d data packet%s (%d byte%s) retransmitted\n");
186 	p2(tcps_sndacks, tcps_delack,
187 		"\t\t%d ack-only packet%s (%d delayed)\n");
188 	p(tcps_sndurg, "\t\t%d URG only packet%s\n");
189 	p(tcps_sndprobe, "\t\t%d window probe packet%s\n");
190 	p(tcps_sndwinup, "\t\t%d window update packet%s\n");
191 	p(tcps_sndctrl, "\t\t%d control packet%s\n");
192 	p(tcps_rcvtotal, "\t%d packet%s received\n");
193 	p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%d ack%s (for %d byte%s)\n");
194 	p(tcps_rcvdupack, "\t\t%d duplicate ack%s\n");
195 	p(tcps_rcvacktoomuch, "\t\t%d ack%s for unsent data\n");
196 	p2(tcps_rcvpack, tcps_rcvbyte,
197 		"\t\t%d packet%s (%d byte%s) received in-sequence\n");
198 	p2(tcps_rcvduppack, tcps_rcvdupbyte,
199 		"\t\t%d completely duplicate packet%s (%d byte%s)\n");
200 	p(tcps_pawsdrop, "\t\t%d old duplicate packet%s\n");
201 	p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
202 		"\t\t%d packet%s with some dup. data (%d byte%s duped)\n");
203 	p2(tcps_rcvoopack, tcps_rcvoobyte,
204 		"\t\t%d out-of-order packet%s (%d byte%s)\n");
205 	p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
206 		"\t\t%d packet%s (%d byte%s) of data after window\n");
207 	p(tcps_rcvwinprobe, "\t\t%d window probe%s\n");
208 	p(tcps_rcvwinupd, "\t\t%d window update packet%s\n");
209 	p(tcps_rcvafterclose, "\t\t%d packet%s received after close\n");
210 	p(tcps_rcvbadsum, "\t\t%d discarded for bad checksum%s\n");
211 	p(tcps_rcvbadoff, "\t\t%d discarded for bad header offset field%s\n");
212 	p(tcps_rcvshort, "\t\t%d discarded because packet too short\n");
213 	p(tcps_connattempt, "\t%d connection request%s\n");
214 	p(tcps_accepts, "\t%d connection accept%s\n");
215 	p(tcps_connects, "\t%d connection%s established (including accepts)\n");
216 	p2(tcps_closed, tcps_drops,
217 		"\t%d connection%s closed (including %d drop%s)\n");
218 	p(tcps_conndrops, "\t%d embryonic connection%s dropped\n");
219 	p2(tcps_rttupdated, tcps_segstimed,
220 		"\t%d segment%s updated rtt (of %d attempt%s)\n");
221 	p(tcps_rexmttimeo, "\t%d retransmit timeout%s\n");
222 	p(tcps_timeoutdrop, "\t\t%d connection%s dropped by rexmit timeout\n");
223 	p(tcps_persisttimeo, "\t%d persist timeout%s\n");
224 	p(tcps_keeptimeo, "\t%d keepalive timeout%s\n");
225 	p(tcps_keepprobe, "\t\t%d keepalive probe%s sent\n");
226 	p(tcps_keepdrops, "\t\t%d connection%s dropped by keepalive\n");
227 	p(tcps_predack, "\t%d correct ACK header prediction%s\n");
228 	p(tcps_preddat, "\t%d correct data packet header prediction%s\n");
229 	p3(tcps_pcbcachemiss, "\t%d PCB cache miss%s\n");
230 #undef p
231 #undef p2
232 #undef p3
233 }
234 
235 /*
236  * Dump UDP statistics structure.
237  */
238 void
239 udp_stats(off, name)
240 	u_long off;
241 	char *name;
242 {
243 	struct udpstat udpstat;
244 	u_long delivered;
245 
246 	if (off == 0)
247 		return;
248 	kread(off, (char *)&udpstat, sizeof (udpstat));
249 	printf("%s:\n", name);
250 #define	p(f, m) if (udpstat.f || sflag <= 1) \
251     printf(m, udpstat.f, plural(udpstat.f))
252 	p(udps_ipackets, "\t%u datagram%s received\n");
253 	p(udps_hdrops, "\t%u with incomplete header\n");
254 	p(udps_badlen, "\t%u with bad data length field\n");
255 	p(udps_badsum, "\t%u with bad checksum\n");
256 	p(udps_noport, "\t%u dropped due to no socket\n");
257 	p(udps_noportbcast, "\t%u broadcast/multicast datagram%s dropped due to no socket\n");
258 	p(udps_fullsock, "\t%u dropped due to full socket buffers\n");
259 	delivered = udpstat.udps_ipackets -
260 		    udpstat.udps_hdrops -
261 		    udpstat.udps_badlen -
262 		    udpstat.udps_badsum -
263 		    udpstat.udps_noport -
264 		    udpstat.udps_noportbcast -
265 		    udpstat.udps_fullsock;
266 	if (delivered || sflag <= 1)
267 		printf("\t%u delivered\n", delivered);
268 	p(udps_opackets, "\t%u datagram%s output\n");
269 #undef p
270 }
271 
272 /*
273  * Dump IP statistics structure.
274  */
275 void
276 ip_stats(off, name)
277 	u_long off;
278 	char *name;
279 {
280 	struct ipstat ipstat;
281 
282 	if (off == 0)
283 		return;
284 	kread(off, (char *)&ipstat, sizeof (ipstat));
285 	printf("%s:\n", name);
286 
287 #define	p(f, m) if (ipstat.f || sflag <= 1) \
288     printf(m, ipstat.f, plural(ipstat.f))
289 
290 	p(ips_total, "\t%u total packet%s received\n");
291 	p(ips_badsum, "\t%u bad header checksum%s\n");
292 	p(ips_toosmall, "\t%u with size smaller than minimum\n");
293 	p(ips_tooshort, "\t%u with data size < data length\n");
294 	p(ips_badhlen, "\t%u with header length < data size\n");
295 	p(ips_badlen, "\t%u with data length < header length\n");
296 	p(ips_badoptions, "\t%u with bad options\n");
297 	p(ips_badvers, "\t%u with incorrect version number\n");
298 	p(ips_fragments, "\t%u fragment%s received\n");
299 	p(ips_fragdropped, "\t%u fragment%s dropped (dup or out of space)\n");
300 	p(ips_fragtimeout, "\t%u fragment%s dropped after timeout\n");
301 	p(ips_reassembled, "\t%u packet%s reassembled ok\n");
302 	p(ips_delivered, "\t%u packet%s for this host\n");
303 	p(ips_noproto, "\t%u packet%s for unknown/unsupported protocol\n");
304 	p(ips_forward, "\t%u packet%s forwarded\n");
305 	p(ips_cantforward, "\t%u packet%s not forwardable\n");
306 	p(ips_redirectsent, "\t%u redirect%s sent\n");
307 	p(ips_localout, "\t%u packet%s sent from this host\n");
308 	p(ips_rawout, "\t%u packet%s sent with fabricated ip header\n");
309 	p(ips_odropped, "\t%u output packet%s dropped due to no bufs, etc.\n");
310 	p(ips_noroute, "\t%u output packet%s discarded due to no route\n");
311 	p(ips_fragmented, "\t%u output datagram%s fragmented\n");
312 	p(ips_ofragments, "\t%u fragment%s created\n");
313 	p(ips_cantfrag, "\t%u datagram%s that can't be fragmented\n");
314 #undef p
315 }
316 
317 static	char *icmpnames[] = {
318 	"echo reply",
319 	"#1",
320 	"#2",
321 	"destination unreachable",
322 	"source quench",
323 	"routing redirect",
324 	"#6",
325 	"#7",
326 	"echo",
327 	"router advertisement",
328 	"router solicitation",
329 	"time exceeded",
330 	"parameter problem",
331 	"time stamp",
332 	"time stamp reply",
333 	"information request",
334 	"information request reply",
335 	"address mask request",
336 	"address mask reply",
337 };
338 
339 /*
340  * Dump ICMP statistics.
341  */
342 void
343 icmp_stats(off, name)
344 	u_long off;
345 	char *name;
346 {
347 	struct icmpstat icmpstat;
348 	register int i, first;
349 
350 	if (off == 0)
351 		return;
352 	kread(off, (char *)&icmpstat, sizeof (icmpstat));
353 	printf("%s:\n", name);
354 
355 #define	p(f, m) if (icmpstat.f || sflag <= 1) \
356     printf(m, icmpstat.f, plural(icmpstat.f))
357 
358 	p(icps_error, "\t%u call%s to icmp_error\n");
359 	p(icps_oldicmp,
360 	    "\t%u error%s not generated 'cuz old message was icmp\n");
361 	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
362 		if (icmpstat.icps_outhist[i] != 0) {
363 			if (first) {
364 				printf("\tOutput histogram:\n");
365 				first = 0;
366 			}
367 			printf("\t\t%s: %u\n", icmpnames[i],
368 				icmpstat.icps_outhist[i]);
369 		}
370 	p(icps_badcode, "\t%u message%s with bad code fields\n");
371 	p(icps_tooshort, "\t%u message%s < minimum length\n");
372 	p(icps_checksum, "\t%u bad checksum%s\n");
373 	p(icps_badlen, "\t%u message%s with bad length\n");
374 	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
375 		if (icmpstat.icps_inhist[i] != 0) {
376 			if (first) {
377 				printf("\tInput histogram:\n");
378 				first = 0;
379 			}
380 			printf("\t\t%s: %u\n", icmpnames[i],
381 				icmpstat.icps_inhist[i]);
382 		}
383 	p(icps_reflect, "\t%u message response%s generated\n");
384 #undef p
385 }
386 
387 /*
388  * Dump IGMP statistics structure.
389  */
390 void
391 igmp_stats(off, name)
392 	u_long off;
393 	char *name;
394 {
395 	struct igmpstat igmpstat;
396 
397 	if (off == 0)
398 		return;
399 	kread(off, (char *)&igmpstat, sizeof (igmpstat));
400 	printf("%s:\n", name);
401 
402 #define	p(f, m) if (igmpstat.f || sflag <= 1) \
403     printf(m, igmpstat.f, plural(igmpstat.f))
404 #define	py(f, m) if (igmpstat.f || sflag <= 1) \
405     printf(m, igmpstat.f, igmpstat.f != 1 ? "ies" : "y")
406 	p(igps_rcv_total, "\t%u message%s received\n");
407         p(igps_rcv_tooshort, "\t%u message%s received with too few bytes\n");
408         p(igps_rcv_badsum, "\t%u message%s received with bad checksum\n");
409         py(igps_rcv_queries, "\t%u membership quer%s received\n");
410         py(igps_rcv_badqueries, "\t%u membership quer%s received with invalid field(s)\n");
411         p(igps_rcv_reports, "\t%u membership report%s received\n");
412         p(igps_rcv_badreports, "\t%u membership report%s received with invalid field(s)\n");
413         p(igps_rcv_ourreports, "\t%u membership report%s received for groups to which we belong\n");
414         p(igps_snd_reports, "\t%u membership report%s sent\n");
415 #undef p
416 #undef py
417 }
418 
419 /*
420  * Pretty print an Internet address (net address + port).
421  * If the nflag was specified, use numbers instead of names.
422  */
423 void
424 inetprint(in, port, proto)
425 	register struct in_addr *in;
426 	int port;
427 	char *proto;
428 {
429 	struct servent *sp = 0;
430 	char line[80], *cp;
431 	int width;
432 
433 	sprintf(line, "%.*s.", (Aflag && !nflag) ? 12 : 16, inetname(in));
434 	cp = index(line, '\0');
435 	if (!nflag && port)
436 		sp = getservbyport((int)port, proto);
437 	if (sp || port == 0)
438 		sprintf(cp, "%.8s", sp ? sp->s_name : "*");
439 	else
440 		sprintf(cp, "%d", ntohs((u_short)port));
441 	width = Aflag ? 18 : 22;
442 	printf(" %-*.*s", width, width, line);
443 }
444 
445 /*
446  * Construct an Internet address representation.
447  * If the nflag has been supplied, give
448  * numeric value, otherwise try for symbolic name.
449  */
450 char *
451 inetname(inp)
452 	struct in_addr *inp;
453 {
454 	register char *cp;
455 	static char line[50];
456 	struct hostent *hp;
457 	struct netent *np;
458 	static char domain[MAXHOSTNAMELEN + 1];
459 	static int first = 1;
460 
461 	if (first && !nflag) {
462 		first = 0;
463 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
464 		    (cp = index(domain, '.')))
465 			(void) strcpy(domain, cp + 1);
466 		else
467 			domain[0] = 0;
468 	}
469 	cp = 0;
470 	if (!nflag && inp->s_addr != INADDR_ANY) {
471 		int net = inet_netof(*inp);
472 		int lna = inet_lnaof(*inp);
473 
474 		if (lna == INADDR_ANY) {
475 			np = getnetbyaddr(net, AF_INET);
476 			if (np)
477 				cp = np->n_name;
478 		}
479 		if (cp == 0) {
480 			hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET);
481 			if (hp) {
482 				if ((cp = index(hp->h_name, '.')) &&
483 				    !strcmp(cp + 1, domain))
484 					*cp = 0;
485 				cp = hp->h_name;
486 			}
487 		}
488 	}
489 	if (inp->s_addr == INADDR_ANY)
490 		strcpy(line, "*");
491 	else if (cp)
492 		strcpy(line, cp);
493 	else {
494 		inp->s_addr = ntohl(inp->s_addr);
495 #define C(x)	((x) & 0xff)
496 		sprintf(line, "%u.%u.%u.%u", C(inp->s_addr >> 24),
497 		    C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr));
498 	}
499 	return (line);
500 }
501