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