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