1/*-
2 * Copyright (c) 1998 Andrzej Bialecki
3 * 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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29
30/*
31 * Small replacement for netstat. Uses only sysctl(3) to get the info.
32 */
33
34#include <sys/types.h>
35#include <sys/time.h>
36#include <sys/sysctl.h>
37#include <sys/socket.h>
38#include <sys/un.h>
39
40#include <net/if.h>
41#include <net/route.h>
42#include <net/if_dl.h>
43#include <netinet/in_systm.h>
44#include <netinet/in.h>
45#include <netinet/ip.h>
46#include <netinet/ip_icmp.h>
47#include <netinet/icmp_var.h>
48#include <netinet/ip_var.h>
49#include <netinet/tcp.h>
50#include <netinet/tcp_timer.h>
51#include <netinet/tcp_var.h>
52#include <netinet/udp.h>
53#include <netinet/udp_var.h>
54#include <arpa/inet.h>
55
56#include <err.h>
57#include <errno.h>
58#include <osreldate.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63
64char *progname;
65int iflag = 0;
66int lflag = 0;			/* print cpu load info */
67int rflag = 0;
68int sflag = 0;
69int pflag = 0;
70int wflag = 0;			/* repeat every wait seconds */
71int delta = 0 ;
72
73extern char *optarg;
74extern int optind;
75
76void print_load_stats(void);
77
78void
79usage()
80{
81	fprintf(stderr, "\n%s [-nrsil] [-p proto] [-w wait]\n", progname);
82	fprintf(stderr, "  proto: {ip|tcp|udp|icmp}\n\n");
83}
84
85
86/*
87 * The following parts related to retrieving the routing table and
88 * interface information, were borrowed from R. Stevens' code examples
89 * accompanying his excellent book. Thanks!
90 */
91char *
92sock_ntop(const struct sockaddr *sa, size_t salen)
93{
94	char	portstr[7];
95	static	char str[128];	/* Unix domain is largest */
96
97	switch (sa->sa_family) {
98	case 255: {
99		int	i = 0;
100		u_long	mask;
101		u_int	index = 1 << 31;
102		u_short	new_mask = 0;
103
104		mask = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
105
106		while (mask & index) {
107			new_mask++;
108			index >>= 1;
109		}
110		sprintf(str, "/%hu", new_mask);
111		return (str);
112	}
113	case AF_UNSPEC:
114	case AF_INET: {
115		struct	sockaddr_in *sin = (struct sockaddr_in *)sa;
116
117		if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
118		    == NULL)
119			return (NULL);
120		if (ntohs(sin->sin_port) != 0) {
121			snprintf(portstr, sizeof(portstr), ".%d",
122			    ntohs(sin->sin_port));
123			strcat(str, portstr);
124		}
125		if (strcmp(str, "0.0.0.0") == 0)
126			sprintf(str, "default");
127		return (str);
128	}
129
130	case AF_UNIX: {
131		struct	sockaddr_un *unp = (struct sockaddr_un *)sa;
132
133		/*
134		 * OK to have no pathname bound to the socket:
135		 * happens on every connect() unless client calls
136		 * bind() first.
137		 */
138		if (unp->sun_path[0] == 0)
139			strcpy(str, "(no pathname bound)");
140		else
141			snprintf(str, sizeof(str), "%s", unp->sun_path);
142		return (str);
143	}
144
145	case AF_LINK: {
146		struct	sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
147
148		if (sdl->sdl_nlen > 0) {
149			bcopy(&sdl->sdl_data[0], str, sdl->sdl_nlen);
150			str[sdl->sdl_nlen] = '\0';
151		} else
152			snprintf(str, sizeof(str), "link#%d", sdl->sdl_index);
153		return (str);
154	}
155
156	default:
157		snprintf(str, sizeof(str),
158		    "sock_ntop: unknown AF_xxx: %d, len %d", sa->sa_family,
159		    salen);
160		return (str);
161	}
162	return (NULL);
163}
164
165char *
166Sock_ntop(const struct sockaddr *sa, size_t salen)
167{
168	char	*ptr;
169
170	if ((ptr = sock_ntop(sa, salen)) == NULL)
171		err(1, "sock_ntop error");	/* inet_ntop() sets errno */
172	return (ptr);
173}
174
175
176#define ROUNDUP(a,size) (((a) & ((size)-1))?(1+((a)|((size)-1))):(a))
177
178#define NEXT_SA(ap) 							\
179	ap=(struct sockaddr *)						\
180	    ((caddr_t)ap+(ap->sa_len?ROUNDUP(ap->sa_len,sizeof(u_long)):\
181	    sizeof(u_long)))
182
183void
184get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
185{
186	int	i;
187
188	for (i = 0; i < RTAX_MAX; i++) {
189		if (addrs & (1 << i)) {
190			rti_info[i] = sa;
191			NEXT_SA(sa);
192		} else
193			rti_info[i] = NULL;
194	}
195}
196
197void
198get_flags(char *buf, int flags)
199{
200	if (flags & 0x1)
201		strcat(buf, "U");
202	if (flags & 0x2)
203		strcat(buf, "G");
204	if (flags & 0x4)
205		strcat(buf, "H");
206	if (flags & 0x8)
207		strcat(buf, "r");
208	if (flags & 0x10)
209		strcat(buf, "d");
210#ifdef NEVER
211	if (flags & 0x20)
212		strcat(buf, "mod,");
213#endif /*NEVER*/
214	if (flags & 0x100)
215		strcat(buf, "C");
216	if (flags & 0x400)
217		strcat(buf, "L");
218	if (flags & 0x800)
219		strcat(buf, "S");
220	if (flags & 0x10000)
221		strcat(buf, "c");
222	if (flags & 0x20000)
223		strcat(buf, "W");
224#ifdef NEVER
225	if (flags & 0x200000)
226		strcat(buf, ",LOC");
227#endif /*NEVER*/
228	if (flags & 0x400000)
229		strcat(buf, "b");
230#ifdef NEVER
231	if (flags & 0x800000)
232		strcat(buf, ",MCA");
233#endif /*NEVER*/
234}
235
236void
237print_routing(char *proto)
238{
239	int	mib[6];
240	int	i = 0;
241	int	rt_len;
242	int	if_len;
243	int	if_num;
244	char	*rt_buf;
245	char	*if_buf;
246	char	*next;
247	char	*lim;
248	struct	rt_msghdr *rtm;
249	struct	if_msghdr *ifm;
250	struct	if_msghdr **ifm_table;
251	struct	ifa_msghdr *ifam;
252	struct	sockaddr *sa;
253	struct	sockaddr *sa1;
254	struct	sockaddr *rti_info[RTAX_MAX];
255	struct	sockaddr **if_table;
256	struct	rt_metrics rm;
257	char	fbuf[50];
258
259	/* keep a copy of statistics here for future use */
260	static unsigned *base_stats = NULL ;
261	static unsigned base_len = 0 ;
262
263	/* Get the routing table */
264	mib[0] = CTL_NET;
265	mib[1] = PF_ROUTE;
266	mib[2] = 0;
267	mib[3] = 0;
268	mib[4] = NET_RT_DUMP;
269	mib[5] = 0;
270
271	/*Estimate the size of table */
272	if (sysctl(mib, 6, NULL, &rt_len, NULL, 0) == -1) {
273		perror("sysctl size");
274		exit(-1);
275	}
276	if ((rt_buf = (char *)malloc(rt_len)) == NULL) {
277		perror("malloc");
278		exit(-1);
279	}
280
281	/* Now get it. */
282	if (sysctl(mib, 6, rt_buf, &rt_len, NULL, 0) == -1) {
283		perror("sysctl get");
284		exit(-1);
285	}
286
287	/* Get the interfaces table */
288	mib[0] = CTL_NET;
289	mib[1] = PF_ROUTE;
290	mib[2] = 0;
291	mib[3] = 0;
292	mib[4] = NET_RT_IFLIST;
293	mib[5] = 0;
294
295	/* Estimate the size of table */
296	if (sysctl(mib, 6, NULL, &if_len, NULL, 0) == -1) {
297		perror("sysctl size");
298		exit(-1);
299	}
300	if ((if_buf = (char *)malloc(if_len)) == NULL) {
301		perror("malloc");
302		exit(-1);
303	}
304
305	/* Now get it. */
306	if (sysctl(mib, 6, if_buf, &if_len, NULL, 0) == -1) {
307		perror("sysctl get");
308		exit(-1);
309	}
310	lim = if_buf + if_len;
311	i = 0;
312	for (next = if_buf, i = 0; next < lim; next += ifm->ifm_msglen) {
313		ifm = (struct if_msghdr *)next;
314		i++;
315	}
316	if_num = i;
317	if_table = (struct sockaddr **)calloc(i, sizeof(struct sockaddr));
318	ifm_table = (struct if_msghdr **)calloc(i, sizeof(struct if_msghdr));
319	if (iflag) {
320		printf("\nInterface table:\n");
321		printf("----------------\n");
322		printf("Name  Mtu   Network       Address            "
323		    "Ipkts Ierrs    Opkts Oerrs  Coll\n");
324	}
325        /* scan the list and store base values */
326	i = 0 ;
327	for (next = if_buf; next < lim; next += ifm->ifm_msglen) {
328		ifm = (struct if_msghdr *)next;
329		i++ ;
330	}
331	if (base_stats == NULL || i != base_len) {
332		base_stats = calloc(i*5, sizeof(unsigned));
333		base_len = i ;
334	}
335	i = 0;
336	for (next = if_buf; next < lim; next += ifm->ifm_msglen) {
337		ifm = (struct if_msghdr *)next;
338		if_table[i] = (struct sockaddr *)(ifm + 1);
339		ifm_table[i] = ifm;
340
341		sa = if_table[i];
342		if (iflag && sa->sa_family == AF_LINK) {
343			struct	sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
344			unsigned *bp = &base_stats[i*5];
345
346			printf("%-4s  %-5d <Link>   ",
347			    sock_ntop(if_table[i], if_table[i]->sa_len),
348			    ifm->ifm_data.ifi_mtu);
349			if (sdl->sdl_alen == 6) {
350				unsigned char *p =
351				    sdl->sdl_data + sdl->sdl_nlen;
352				printf("%02x:%02x:%02x:%02x:%02x:%02x   ",
353				    p[0], p[1], p[2], p[3], p[4], p[5]);
354			} else
355				printf("                    ");
356			printf("%9d%6d%9d%6d%6d\n",
357			    ifm->ifm_data.ifi_ipackets - bp[0],
358			    ifm->ifm_data.ifi_ierrors - bp[1],
359			    ifm->ifm_data.ifi_opackets - bp[2],
360			    ifm->ifm_data.ifi_oerrors - bp[3],
361			    ifm->ifm_data.ifi_collisions -bp[4]);
362			if (delta > 0) {
363			    bp[0] = ifm->ifm_data.ifi_ipackets ;
364			    bp[1] = ifm->ifm_data.ifi_ierrors ;
365			    bp[2] = ifm->ifm_data.ifi_opackets ;
366			    bp[3] = ifm->ifm_data.ifi_oerrors ;
367			    bp[4] = ifm->ifm_data.ifi_collisions ;
368			}
369		}
370		i++;
371	}
372	if (!rflag) {
373		free(rt_buf);
374		free(if_buf);
375		free(if_table);
376		free(ifm_table);
377		return;
378	}
379
380	/* Now dump the routing table */
381	printf("\nRouting table:\n");
382	printf("--------------\n");
383	printf
384	    ("Destination        Gateway            Flags       Netif  Use\n");
385	lim = rt_buf + rt_len;
386	for (next = rt_buf; next < lim; next += rtm->rtm_msglen) {
387		rtm = (struct rt_msghdr *)next;
388		sa = (struct sockaddr *)(rtm + 1);
389		get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
390		if ((sa = rti_info[RTAX_DST]) != NULL) {
391			sprintf(fbuf, "%s", sock_ntop(sa, sa->sa_len));
392			if (((sa1 = rti_info[RTAX_NETMASK]) != NULL)
393			    && sa1->sa_family == 255) {
394				strcat(fbuf, sock_ntop(sa1, sa1->sa_len));
395			}
396			printf("%-19s", fbuf);
397		}
398		if ((sa = rti_info[RTAX_GATEWAY]) != NULL) {
399			printf("%-19s", sock_ntop(sa, sa->sa_len));
400		}
401		memset(fbuf, 0, sizeof(fbuf));
402		get_flags(fbuf, rtm->rtm_flags);
403		printf("%-10s", fbuf);
404		for (i = 0; i < if_num; i++) {
405			ifm = ifm_table[i];
406			if ((ifm->ifm_index == rtm->rtm_index) &&
407			    (ifm->ifm_data.ifi_type > 0)) {
408				sa = if_table[i];
409				break;
410			}
411		}
412		if (ifm->ifm_type == RTM_IFINFO) {
413			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
414			printf("  %s", Sock_ntop(sa, sa->sa_len));
415		} else if (ifm->ifm_type == RTM_NEWADDR) {
416			ifam =
417			    (struct ifa_msghdr *)ifm_table[rtm->rtm_index - 1];
418			sa = (struct sockaddr *)(ifam + 1);
419			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
420			printf("  %s", Sock_ntop(sa, sa->sa_len));
421		}
422		/* printf("    %u", rtm->rtm_use); */
423		printf("\n");
424	}
425	free(rt_buf);
426	free(if_buf);
427	free(if_table);
428	free(ifm_table);
429}
430
431void
432print_ip_stats(void)
433{
434	int	mib[4];
435	int	len;
436	struct	ipstat s;
437
438	mib[0] = CTL_NET;
439	mib[1] = PF_INET;
440	mib[2] = IPPROTO_IP;
441#ifndef IPCTL_STATS
442	printf("sorry, ip stats not available\n");
443	return -1;
444#else
445	mib[3] = IPCTL_STATS;
446	len = sizeof(struct ipstat);
447	if (sysctl(mib, 4, &s, &len, NULL, 0) < 0) {
448		perror("sysctl");
449		return;
450	}
451	printf("\nIP statistics:\n");
452	printf("--------------\n");
453	printf("  %10lu total packets received\n", s.ips_total);
454	printf("* Packets ok:\n");
455	printf("  %10lu fragments received\n", s.ips_fragments);
456	printf("  %10lu forwarded\n", s.ips_forward);
457#if __FreeBSD_version > 300001
458	printf("  %10lu fast forwarded\n", s.ips_fastforward);
459#endif
460	printf("  %10lu forwarded on same net (redirect)\n",
461	    s.ips_redirectsent);
462	printf("  %10lu delivered to upper level\n", s.ips_delivered);
463	printf("  %10lu total ip packets generated here\n", s.ips_localout);
464	printf("  %10lu total packets reassembled ok\n", s.ips_reassembled);
465	printf("  %10lu total datagrams successfully fragmented\n",
466	    s.ips_fragmented);
467	printf("  %10lu output fragments created\n", s.ips_ofragments);
468	printf("  %10lu total raw IP packets generated\n", s.ips_rawout);
469	printf("\n* Bad packets:\n");
470	printf("  %10lu bad checksum\n", s.ips_badsum);
471	printf("  %10lu too short\n", s.ips_tooshort);
472	printf("  %10lu not enough data (too small)\n", s.ips_toosmall);
473	printf("  %10lu more data than declared in header\n", s.ips_badhlen);
474	printf("  %10lu less data than declared in header\n", s.ips_badlen);
475	printf("  %10lu fragments dropped (dups, no mbuf)\n",
476	    s.ips_fragdropped);
477	printf("  %10lu fragments timed out in reassembly\n",
478	    s.ips_fragtimeout);
479	printf("  %10lu received for unreachable dest.\n", s.ips_cantforward);
480	printf("  %10lu unknown or unsupported protocol\n", s.ips_noproto);
481	printf("  %10lu lost due to no bufs etc.\n", s.ips_odropped);
482	printf("  %10lu couldn't fragment (DF set, etc.)\n", s.ips_cantfrag);
483	printf("  %10lu error in IP options processing\n", s.ips_badoptions);
484	printf("  %10lu dropped due to no route\n", s.ips_noroute);
485	printf("  %10lu bad IP version\n", s.ips_badvers);
486	printf("  %10lu too long (more than max IP size)\n", s.ips_toolong);
487#if __FreeBSD_version > 300001
488	printf("  %10lu multicast for unregistered groups\n", s.ips_notmember);
489#endif
490#endif
491}
492
493void
494print_tcp_stats(void)
495{
496	int	mib[4];
497	int	len;
498	struct	tcpstat s;
499
500	mib[0] = CTL_NET;
501	mib[1] = PF_INET;
502	mib[2] = IPPROTO_TCP;
503#ifndef TCPCTL_STATS
504	printf("sorry, tcp stats not available\n");
505	return;
506#else
507	mib[3] = TCPCTL_STATS;
508	len = sizeof(struct tcpstat);
509	if (sysctl(mib, 4, &s, &len, NULL, 0) < 0) {
510		perror("sysctl");
511		return;
512	}
513	printf("\nTCP statistics:\n");
514	printf("---------------\n");
515	printf("* Connections:\n");
516	printf("  %10lu initiated\n", s.tcps_connattempt);
517	printf("  %10lu accepted\n", s.tcps_accepts);
518	printf("  %10lu established\n", s.tcps_connects);
519	printf("  %10lu dropped\n", s.tcps_drops);
520	printf("  %10lu embryonic connections dropped\n", s.tcps_conndrops);
521	printf("  %10lu closed (includes dropped)\n", s.tcps_closed);
522	printf("  %10lu segments where we tried to get RTT\n",
523	    s.tcps_segstimed);
524	printf("  %10lu times RTT successfully updated\n", s.tcps_rttupdated);
525	printf("  %10lu delayed ACKs sent\n", s.tcps_delack);
526	printf("  %10lu dropped in rxmt timeout\n", s.tcps_timeoutdrop);
527	printf("  %10lu retrasmit timeouts\n", s.tcps_rexmttimeo);
528	printf("  %10lu persist timeouts\n", s.tcps_persisttimeo);
529	printf("  %10lu keepalive timeouts\n", s.tcps_keeptimeo);
530	printf("  %10lu keepalive probes sent\n", s.tcps_keepprobe);
531	printf("  %10lu dropped in keepalive\n", s.tcps_keepdrops);
532
533	printf("* Packets sent:\n");
534	printf("  %10lu total packets sent\n", s.tcps_sndtotal);
535	printf("  %10lu data packets sent\n", s.tcps_sndpack);
536	printf("  %10lu data bytes sent\n", s.tcps_sndbyte);
537	printf("  %10lu data packets retransmitted\n", s.tcps_sndrexmitpack);
538	printf("  %10lu data bytes retransmitted\n", s.tcps_sndrexmitbyte);
539	printf("  %10lu ACK-only packets sent\n", s.tcps_sndacks);
540	printf("  %10lu window probes sent\n", s.tcps_sndprobe);
541	printf("  %10lu URG-only packets sent\n", s.tcps_sndurg);
542	printf("  %10lu window update-only packets sent\n", s.tcps_sndwinup);
543	printf("  %10lu control (SYN,FIN,RST) packets sent\n", s.tcps_sndctrl);
544	printf("* Packets received:\n");
545	printf("  %10lu total packets received\n", s.tcps_rcvtotal);
546	printf("  %10lu packets in sequence\n", s.tcps_rcvpack);
547	printf("  %10lu bytes in sequence\n", s.tcps_rcvbyte);
548	printf("  %10lu packets with bad checksum\n", s.tcps_rcvbadsum);
549	printf("  %10lu packets with bad offset\n", s.tcps_rcvbadoff);
550	printf("  %10lu packets too short\n", s.tcps_rcvshort);
551	printf("  %10lu duplicate-only packets\n", s.tcps_rcvduppack);
552	printf("  %10lu duplicate-only bytes\n", s.tcps_rcvdupbyte);
553	printf("  %10lu packets with some duplicate data\n",
554	    s.tcps_rcvpartduppack);
555	printf("  %10lu duplicate bytes in partially dup. packets\n",
556	    s.tcps_rcvpartdupbyte);
557	printf("  %10lu out-of-order packets\n", s.tcps_rcvoopack);
558	printf("  %10lu out-of-order bytes\n", s.tcps_rcvoobyte);
559	printf("  %10lu packets with data after window\n",
560	    s.tcps_rcvpackafterwin);
561	printf("  %10lu bytes received after window\n",
562	    s.tcps_rcvbyteafterwin);
563	printf("  %10lu packets received after 'close'\n",
564	    s.tcps_rcvafterclose);
565	printf("  %10lu window probe packets\n", s.tcps_rcvwinprobe);
566	printf("  %10lu duplicate ACKs\n", s.tcps_rcvdupack);
567	printf("  %10lu ACKs for unsent data\n", s.tcps_rcvacktoomuch);
568	printf("  %10lu ACK packets\n", s.tcps_rcvackpack);
569	printf("  %10lu bytes ACKed by received ACKs\n", s.tcps_rcvackbyte);
570	printf("  %10lu window update packets\n", s.tcps_rcvwinupd);
571	printf("  %10lu segments dropped due to PAWS\n", s.tcps_pawsdrop);
572	printf("  %10lu times header predict ok for ACKs\n", s.tcps_predack);
573	printf("  %10lu times header predict ok for data packets\n",
574	    s.tcps_preddat);
575	printf("  %10lu PCB cache misses\n", s.tcps_pcbcachemiss);
576	printf("  %10lu times cached RTT in route updated\n",
577	    s.tcps_cachedrtt);
578	printf("  %10lu times cached RTTVAR updated\n", s.tcps_cachedrttvar);
579	printf("  %10lu times ssthresh updated\n", s.tcps_cachedssthresh);
580	printf("  %10lu times RTT initialized from route\n", s.tcps_usedrtt);
581	printf("  %10lu times RTTVAR initialized from route\n",
582	    s.tcps_usedrttvar);
583	printf("  %10lu times ssthresh initialized from route\n",
584	    s.tcps_usedssthresh);
585	printf("  %10lu timeout in persist state\n", s.tcps_persistdrop);
586	printf("  %10lu bogus SYN, e.g. premature ACK\n", s.tcps_badsyn);
587	printf("  %10lu resends due to MTU discovery\n", s.tcps_mturesent);
588	printf("  %10lu listen queue overflows\n", s.tcps_listendrop);
589#endif
590}
591
592void
593print_udp_stats(void)
594{
595	int	mib[4];
596	int	len;
597	struct	udpstat s;
598
599	mib[0] = CTL_NET;
600	mib[1] = PF_INET;
601	mib[2] = IPPROTO_UDP;
602	mib[3] = UDPCTL_STATS;
603	len = sizeof(struct udpstat);
604	if (sysctl(mib, 4, &s, &len, NULL, 0) < 0) {
605		perror("sysctl");
606		return;
607	}
608	printf("\nUDP statistics:\n");
609	printf("---------------\n");
610	printf("* Packets received:\n");
611	printf("  %10lu total input packets\n", s.udps_ipackets);
612	printf("  %10lu packets shorter than header (dropped)\n",
613	    s.udps_hdrops);
614	printf("  %10lu bad checksum\n", s.udps_badsum);
615	printf("  %10lu data length larger than packet\n", s.udps_badlen);
616	printf("  %10lu no socket on specified port\n", s.udps_noport);
617	printf("  %10lu of above, arrived as broadcast\n", s.udps_noportbcast);
618	printf("  %10lu not delivered, input socket full\n", s.udps_fullsock);
619	printf("  %10lu packets missing PCB cache\n", s.udpps_pcbcachemiss);
620	printf("  %10lu packets not for hashed PCBs\n", s.udpps_pcbhashmiss);
621	printf("* Packets sent:\n");
622	printf("  %10lu total output packets\n", s.udps_opackets);
623#if __FreeBSD_version > 300001
624	printf("  %10lu output packets on fast path\n", s.udps_fastout);
625#endif
626}
627
628char *icmp_names[] = {
629	"echo reply",
630	"#1",
631	"#2",
632	"destination unreachable",
633	"source quench",
634	"routing redirect",
635	"#6",
636	"#7",
637	"echo",
638	"router advertisement",
639	"router solicitation",
640	"time exceeded",
641	"parameter problem",
642	"time stamp",
643	"time stamp reply",
644	"information request",
645	"information request reply",
646	"address mask request",
647	"address mask reply",
648};
649
650print_icmp_stats()
651{
652	int	mib[4];
653	int	len;
654	int	i;
655	struct	icmpstat s;
656
657	mib[0] = CTL_NET;
658	mib[1] = PF_INET;
659	mib[2] = IPPROTO_ICMP;
660	mib[3] = ICMPCTL_STATS;
661	len = sizeof(struct icmpstat);
662	if (sysctl(mib, 4, &s, &len, NULL, 0) < 0) {
663		perror("sysctl");
664		return (-1);
665	}
666	printf("\nICMP statistics:\n");
667	printf("----------------\n");
668	printf("* Output histogram:\n");
669	for (i = 0; i < (ICMP_MAXTYPE + 1); i++) {
670		if (s.icps_outhist[i] > 0)
671			printf("\t%10lu %s\n",
672			    s.icps_outhist[i], icmp_names[i]);
673	}
674	printf("* Input histogram:\n");
675	for (i = 0; i < (ICMP_MAXTYPE + 1); i++) {
676		if (s.icps_inhist[i] > 0)
677			printf("\t%10lu %s\n",
678			    s.icps_inhist[i], icmp_names[i]);
679	}
680	printf("* Other stats:\n");
681	printf("  %10lu calls to icmp_error\n", s.icps_error);
682	printf("  %10lu no error 'cuz old ip too short\n", s.icps_oldshort);
683	printf("  %10lu no error 'cuz old was icmp\n", s.icps_oldicmp);
684
685	printf("  %10lu icmp code out of range\n", s.icps_badcode);
686	printf("  %10lu packets shorter than min length\n", s.icps_tooshort);
687	printf("  %10lu bad checksum\n", s.icps_checksum);
688	printf("  %10lu calculated bound mismatch\n", s.icps_badlen);
689	printf("  %10lu number of responses\n", s.icps_reflect);
690	printf("  %10lu broad/multi-cast echo requests dropped\n",
691	    s.icps_bmcastecho);
692	printf("  %10lu broad/multi-cast timestamp requests dropped\n",
693	    s.icps_bmcasttstamp);
694}
695
696int
697stats(char *proto)
698{
699	if (!sflag)
700		return 0;
701	if (pflag) {
702		if (proto == NULL) {
703			fprintf(stderr, "Option '-p' requires paramter.\n");
704			usage();
705			exit(-1);
706		}
707		if (strcmp(proto, "ip") == 0)
708			print_ip_stats();
709		if (strcmp(proto, "icmp") == 0)
710			print_icmp_stats();
711		if (strcmp(proto, "udp") == 0)
712			print_udp_stats();
713		if (strcmp(proto, "tcp") == 0)
714			print_tcp_stats();
715		return (0);
716	}
717	print_ip_stats();
718	print_icmp_stats();
719	print_udp_stats();
720	print_tcp_stats();
721	return (0);
722}
723
724int
725main(int argc, char *argv[])
726{
727	char	c;
728	char	*proto = NULL;
729
730	progname = argv[0];
731
732	while ((c = getopt(argc, argv, "dilnrsp:w:")) != -1) {
733		switch (c) {
734		case 'd': /* print deltas in stats every w seconds */
735			delta++ ;
736			break;
737		case 'w':
738			wflag = atoi(optarg);
739			break;
740		case 'n': /* ignored, just for compatibility with std netstat */
741			break;
742		case 'r':
743			rflag++;
744			break;
745		case 'i':
746			iflag++;
747			break;
748		case 'l':
749			lflag++;
750			break;
751		case 's':
752			sflag++;
753			rflag = 0;
754			break;
755		case 'p':
756			pflag++;
757			sflag++;
758			proto = optarg;
759			break;
760		case '?':
761		default:
762			usage();
763			exit(0);
764			break;
765		}
766	}
767	if (rflag == 0 && sflag == 0 && iflag == 0)
768		rflag = 1;
769	argc -= optind;
770
771	if (argc > 0) {
772		usage();
773		exit(-1);
774	}
775	if (wflag)
776		printf("\033[H\033[J");
777again:
778	if (wflag) {
779		struct timeval t;
780
781		gettimeofday(&t, NULL);
782		printf("\033[H%s", ctime(&t.tv_sec));
783	}
784	print_routing(proto);
785	print_load_stats();
786	stats(proto);
787	if (wflag) {
788		sleep(wflag);
789		goto again;
790	}
791	exit(0);
792}
793
794void
795print_load_stats(void)
796{
797	static u_int32_t cp_time[5];
798	u_int32_t new_cp_time[5];
799	int l;
800	int shz;
801	static int stathz ;
802
803	if (!lflag || !wflag)
804		return;
805	l = sizeof(new_cp_time) ;
806	bzero(new_cp_time, l);
807	if (sysctlbyname("kern.cp_time", new_cp_time, &l, NULL, 0) < 0) {
808		warn("sysctl: retrieving cp_time length");
809		return;
810	}
811	if (stathz == 0) {
812		struct clockinfo ci;
813
814		bzero (&ci, sizeof(ci));
815		l = sizeof(ci) ;
816		if (sysctlbyname("kern.clockrate", &ci, &l, NULL, 0) < 0) {
817			warn("sysctl: retrieving clockinfo length");
818			return;
819		}
820		stathz = ci.stathz ;
821		bcopy(new_cp_time, cp_time, sizeof(cp_time));
822	}
823	shz = stathz * wflag ;
824	if (shz == 0)
825		shz = 1;
826#define X(i)   ( (double)(new_cp_time[i] - cp_time[i])*100/shz )
827	printf("\nUSER %5.2f%% NICE %5.2f%% SYS %5.2f%% "
828			"INTR %5.2f%% IDLE %5.2f%%\n",
829		X(0), X(1), X(2), X(3), X(4) );
830	bcopy(new_cp_time, cp_time, sizeof(cp_time));
831}
832