if.c revision 293307
1/*-
2 * Copyright (c) 2013 Gleb Smirnoff <glebius@FreeBSD.org>
3 * Copyright (c) 1983, 1988, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 4. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#if 0
32#ifndef lint
33static char sccsid[] = "@(#)if.c	8.3 (Berkeley) 4/28/95";
34#endif /* not lint */
35#endif
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: stable/10/usr.bin/netstat/if.c 293307 2016-01-07 07:21:37Z markj $");
39
40#include <sys/param.h>
41#include <sys/protosw.h>
42#include <sys/socket.h>
43#include <sys/socketvar.h>
44#include <sys/time.h>
45
46#define	_IFI_OQDROPS
47#include <net/if.h>
48#include <net/if_var.h>
49#include <net/if_dl.h>
50#include <net/if_types.h>
51#include <net/ethernet.h>
52#include <netinet/in.h>
53#include <netinet/in_var.h>
54#include <netipx/ipx.h>
55#include <netipx/ipx_if.h>
56#include <arpa/inet.h>
57#ifdef PF
58#include <net/pfvar.h>
59#include <net/if_pfsync.h>
60#endif
61
62#include <err.h>
63#include <errno.h>
64#include <ifaddrs.h>
65#include <libutil.h>
66#ifdef INET6
67#include <netdb.h>
68#endif
69#include <signal.h>
70#include <stdbool.h>
71#include <stdint.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <sysexits.h>
76#include <unistd.h>
77
78#include "netstat.h"
79
80static void sidewaysintpr(int);
81
82#ifdef INET6
83static char addr_buf[NI_MAXHOST];		/* for getnameinfo() */
84#endif
85
86#ifdef PF
87static const char* pfsyncacts[] = {
88	/* PFSYNC_ACT_CLR */		"clear all request",
89	/* PFSYNC_ACT_INS */		"state insert",
90	/* PFSYNC_ACT_INS_ACK */	"state inserted ack",
91	/* PFSYNC_ACT_UPD */		"state update",
92	/* PFSYNC_ACT_UPD_C */		"compressed state update",
93	/* PFSYNC_ACT_UPD_REQ */	"uncompressed state request",
94	/* PFSYNC_ACT_DEL */		"state delete",
95	/* PFSYNC_ACT_DEL_C */		"compressed state delete",
96	/* PFSYNC_ACT_INS_F */		"fragment insert",
97	/* PFSYNC_ACT_DEL_F */		"fragment delete",
98	/* PFSYNC_ACT_BUS */		"bulk update mark",
99	/* PFSYNC_ACT_TDB */		"TDB replay counter update",
100	/* PFSYNC_ACT_EOF */		"end of frame mark",
101};
102
103static void
104pfsync_acts_stats(const char *fmt, uint64_t *a)
105{
106	int i;
107
108	for (i = 0; i < PFSYNC_ACT_MAX; i++, a++)
109		if (*a || sflag <= 1)
110			printf(fmt, *a, pfsyncacts[i], plural(*a));
111}
112
113/*
114 * Dump pfsync statistics structure.
115 */
116void
117pfsync_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
118{
119	struct pfsyncstats pfsyncstat;
120
121	if (fetch_stats("net.pfsync.stats", off, &pfsyncstat,
122	    sizeof(pfsyncstat), kread) != 0)
123		return;
124
125	printf("%s:\n", name);
126
127#define	p(f, m) if (pfsyncstat.f || sflag <= 1) \
128	printf(m, (uintmax_t)pfsyncstat.f, plural(pfsyncstat.f))
129
130	p(pfsyncs_ipackets, "\t%ju packet%s received (IPv4)\n");
131	p(pfsyncs_ipackets6, "\t%ju packet%s received (IPv6)\n");
132	pfsync_acts_stats("\t    %ju %s%s received\n",
133	    &pfsyncstat.pfsyncs_iacts[0]);
134	p(pfsyncs_badif, "\t\t%ju packet%s discarded for bad interface\n");
135	p(pfsyncs_badttl, "\t\t%ju packet%s discarded for bad ttl\n");
136	p(pfsyncs_hdrops, "\t\t%ju packet%s shorter than header\n");
137	p(pfsyncs_badver, "\t\t%ju packet%s discarded for bad version\n");
138	p(pfsyncs_badauth, "\t\t%ju packet%s discarded for bad HMAC\n");
139	p(pfsyncs_badact,"\t\t%ju packet%s discarded for bad action\n");
140	p(pfsyncs_badlen, "\t\t%ju packet%s discarded for short packet\n");
141	p(pfsyncs_badval, "\t\t%ju state%s discarded for bad values\n");
142	p(pfsyncs_stale, "\t\t%ju stale state%s\n");
143	p(pfsyncs_badstate, "\t\t%ju failed state lookup/insert%s\n");
144	p(pfsyncs_opackets, "\t%ju packet%s sent (IPv4)\n");
145	p(pfsyncs_opackets6, "\t%ju packet%s sent (IPv6)\n");
146	pfsync_acts_stats("\t    %ju %s%s sent\n",
147	    &pfsyncstat.pfsyncs_oacts[0]);
148	p(pfsyncs_onomem, "\t\t%ju failure%s due to mbuf memory error\n");
149	p(pfsyncs_oerrors, "\t\t%ju send error%s\n");
150#undef p
151}
152#endif /* PF */
153
154/*
155 * Display a formatted value, or a '-' in the same space.
156 */
157static void
158show_stat(const char *fmt, int width, u_long value, short showvalue,
159    int div1000)
160{
161	const char *lsep, *rsep;
162	char newfmt[32];
163
164	lsep = "";
165	if (strncmp(fmt, "LS", 2) == 0) {
166		lsep = " ";
167		fmt += 2;
168	}
169	rsep = " ";
170	if (strncmp(fmt, "NRS", 3) == 0) {
171		rsep = "";
172		fmt += 3;
173	}
174	if (showvalue == 0) {
175		/* Print just dash. */
176		sprintf(newfmt, "%s%%%ds%s", lsep, width, rsep);
177		printf(newfmt, "-");
178		return;
179	}
180
181	if (hflag) {
182		char buf[5];
183
184		/* Format in human readable form. */
185		humanize_number(buf, sizeof(buf), (int64_t)value, "",
186		    HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL | \
187		    ((div1000) ? HN_DIVISOR_1000 : 0));
188		sprintf(newfmt, "%s%%%ds%s", lsep, width, rsep);
189		printf(newfmt, buf);
190	} else {
191		/* Construct the format string. */
192		sprintf(newfmt, "%s%%%d%s%s", lsep, width, fmt, rsep);
193		printf(newfmt, value);
194	}
195}
196
197/*
198 * Find next multiaddr for a given interface name.
199 */
200static struct ifmaddrs *
201next_ifma(struct ifmaddrs *ifma, const char *name, const sa_family_t family)
202{
203
204	for(; ifma != NULL; ifma = ifma->ifma_next) {
205		struct sockaddr_dl *sdl;
206
207		sdl = (struct sockaddr_dl *)ifma->ifma_name;
208		if (ifma->ifma_addr->sa_family == family &&
209		    strcmp(sdl->sdl_data, name) == 0)
210			break;
211	}
212
213	return (ifma);
214}
215
216/*
217 * Print a description of the network interfaces.
218 */
219void
220intpr(int interval, void (*pfunc)(char *), int af)
221{
222	struct ifaddrs *ifap, *ifa;
223	struct ifmaddrs *ifmap, *ifma;
224	u_int ifn_len_max = 5;
225
226	if (interval)
227		return sidewaysintpr(interval);
228
229	if (getifaddrs(&ifap) != 0)
230		err(EX_OSERR, "getifaddrs");
231	if (aflag && getifmaddrs(&ifmap) != 0)
232		err(EX_OSERR, "getifmaddrs");
233
234	if (Wflag) {
235		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
236			if (interface != NULL &&
237			    strcmp(ifa->ifa_name, interface) != 0)
238				continue;
239			if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af)
240				continue;
241			ifn_len_max = MAX(ifn_len_max, strlen(ifa->ifa_name));
242		}
243	}
244
245	if (!pfunc) {
246		printf("%-*.*s", ifn_len_max, ifn_len_max, "Name");
247		printf(" %5.5s %-13.13s %-17.17s %8.8s %5.5s %5.5s",
248		    "Mtu", "Network", "Address", "Ipkts", "Ierrs", "Idrop");
249		if (bflag)
250			printf(" %10.10s","Ibytes");
251		printf(" %8.8s %5.5s", "Opkts", "Oerrs");
252		if (bflag)
253			printf(" %10.10s","Obytes");
254		printf(" %5s", "Coll");
255		if (dflag)
256			printf("  %s", "Drop");
257		putchar('\n');
258	}
259
260	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
261		bool network = false, link = false;
262
263		if (interface != NULL && strcmp(ifa->ifa_name, interface) != 0)
264			continue;
265
266		if (pfunc) {
267			char *name;
268
269			name = ifa->ifa_name;
270			(*pfunc)(name);
271
272			/*
273			 * Skip all ifaddrs belonging to same interface.
274			 */
275			while(ifa->ifa_next != NULL &&
276			    (strcmp(ifa->ifa_next->ifa_name, name) == 0)) {
277				ifa = ifa->ifa_next;
278			}
279			continue;
280		}
281
282		if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af)
283			continue;
284
285		printf("%-*.*s", ifn_len_max, ifn_len_max, ifa->ifa_name);
286
287#define IFA_MTU(ifa)	(((struct if_data *)(ifa)->ifa_data)->ifi_mtu)
288		show_stat("lu", 6, IFA_MTU(ifa), IFA_MTU(ifa), 0);
289#undef IFA_MTU
290
291		switch (ifa->ifa_addr->sa_family) {
292		case AF_UNSPEC:
293			printf("%-13.13s ", "none");
294			printf("%-15.15s ", "none");
295			break;
296		case AF_INET:
297		    {
298			struct sockaddr_in *sin, *mask;
299
300			sin = (struct sockaddr_in *)ifa->ifa_addr;
301			mask = (struct sockaddr_in *)ifa->ifa_netmask;
302			printf("%-13.13s ", netname(sin->sin_addr.s_addr,
303			    mask->sin_addr.s_addr));
304			printf("%-17.17s ",
305			    routename(sin->sin_addr.s_addr));
306
307			network = true;
308			break;
309		    }
310#ifdef INET6
311		case AF_INET6:
312		    {
313			struct sockaddr_in6 *sin6, *mask;
314
315			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
316			mask = (struct sockaddr_in6 *)ifa->ifa_netmask;
317
318			printf("%-13.13s ", netname6(sin6, &mask->sin6_addr));
319			getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len,
320			    addr_buf, sizeof(addr_buf), 0, 0, NI_NUMERICHOST);
321			printf("%-17.17s ", addr_buf);
322
323			network = 1;
324			break;
325	            }
326#endif /* INET6 */
327		case AF_IPX:
328		    {
329			struct sockaddr_ipx *sipx;
330			u_long net;
331			char netnum[10];
332
333			sipx = (struct sockaddr_ipx *)ifa->ifa_addr;
334			*(union ipx_net *) &net = sipx->sipx_addr.x_net;
335
336			sprintf(netnum, "%lx", (u_long)ntohl(net));
337			printf("ipx:%-8s  ", netnum);
338			printf("%-17s ", ipx_phost((struct sockaddr *)sipx));
339
340			network = 1;
341			break;
342		    }
343		case AF_APPLETALK:
344			printf("atalk:%-12.12s ",
345			    atalk_print(ifa->ifa_addr, 0x10));
346			printf("%-11.11s  ",
347			    atalk_print(ifa->ifa_addr, 0x0b));
348			break;
349		case AF_LINK:
350		    {
351			struct sockaddr_dl *sdl;
352			char *cp, linknum[10];
353			int n, m;
354
355			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
356			cp = (char *)LLADDR(sdl);
357			n = sdl->sdl_alen;
358			sprintf(linknum, "<Link#%d>", sdl->sdl_index);
359			m = printf("%-13.13s ", linknum);
360
361			while ((--n >= 0) && (m < 30))
362				m += printf("%02x%c", *cp++ & 0xff,
363					    n > 0 ? ':' : ' ');
364			m = 32 - m;
365			while (m-- > 0)
366				putchar(' ');
367
368			link = 1;
369			break;
370		    }
371		}
372
373#define	IFA_STAT(s)	(((struct if_data *)ifa->ifa_data)->ifi_ ## s)
374		show_stat("lu", 8, IFA_STAT(ipackets), link|network, 1);
375		show_stat("lu", 5, IFA_STAT(ierrors), link, 1);
376		show_stat("lu", 5, IFA_STAT(iqdrops), link, 1);
377		if (bflag)
378			show_stat("lu", 10, IFA_STAT(ibytes), link|network, 0);
379		show_stat("lu", 8, IFA_STAT(opackets), link|network, 1);
380		show_stat("lu", 5, IFA_STAT(oerrors), link, 1);
381		if (bflag)
382			show_stat("lu", 10, IFA_STAT(obytes), link|network, 0);
383		show_stat("NRSlu", 5, IFA_STAT(collisions), link, 1);
384		if (dflag)
385			show_stat("LSlu", 5, IFA_STAT(oqdrops), link, 1);
386		putchar('\n');
387
388		if (!aflag)
389			continue;
390
391		/*
392		 * Print family's multicast addresses.
393		 */
394		for (ifma = next_ifma(ifmap, ifa->ifa_name,
395		     ifa->ifa_addr->sa_family);
396		     ifma != NULL;
397		     ifma = next_ifma(ifma, ifa->ifa_name,
398		     ifa->ifa_addr->sa_family)) {
399			const char *fmt = NULL;
400
401			switch (ifma->ifma_addr->sa_family) {
402			case AF_INET:
403			    {
404				struct sockaddr_in *sin;
405
406				sin = (struct sockaddr_in *)ifma->ifma_addr;
407				fmt = routename(sin->sin_addr.s_addr);
408				break;
409			    }
410#ifdef INET6
411			case AF_INET6:
412
413				/* in6_fillscopeid(&msa.in6); */
414				getnameinfo(ifma->ifma_addr,
415				    ifma->ifma_addr->sa_len, addr_buf,
416				    sizeof(addr_buf), 0, 0, NI_NUMERICHOST);
417				printf("%*s %s\n",
418				    Wflag ? 27 : 25, "", addr_buf);
419				break;
420#endif /* INET6 */
421			case AF_LINK:
422			    {
423				struct sockaddr_dl *sdl;
424
425				sdl = (struct sockaddr_dl *)ifma->ifma_addr;
426				switch (sdl->sdl_type) {
427				case IFT_ETHER:
428				case IFT_FDDI:
429					fmt = ether_ntoa(
430					    (struct ether_addr *)LLADDR(sdl));
431					break;
432				}
433				break;
434			    }
435			}
436
437			if (fmt) {
438				printf("%*s %-17.17s",
439				    Wflag ? 27 : 25, "", fmt);
440				if (ifma->ifma_addr->sa_family == AF_LINK) {
441					printf(" %8lu", IFA_STAT(imcasts));
442					printf("%*s", bflag ? 17 : 6, "");
443					printf(" %8lu", IFA_STAT(omcasts));
444				}
445				putchar('\n');
446			}
447
448			ifma = ifma->ifma_next;
449		}
450	}
451
452	freeifaddrs(ifap);
453	if (aflag)
454		freeifmaddrs(ifmap);
455}
456
457struct iftot {
458	u_long	ift_ip;			/* input packets */
459	u_long	ift_ie;			/* input errors */
460	u_long	ift_id;			/* input drops */
461	u_long	ift_op;			/* output packets */
462	u_long	ift_oe;			/* output errors */
463	u_long	ift_od;			/* output drops */
464	u_long	ift_co;			/* collisions */
465	u_long	ift_ib;			/* input bytes */
466	u_long	ift_ob;			/* output bytes */
467};
468
469/*
470 * Obtain stats for interface(s).
471 */
472static void
473fill_iftot(struct iftot *st)
474{
475	struct ifaddrs *ifap, *ifa;
476	bool found = false;
477
478	if (getifaddrs(&ifap) != 0)
479		err(EX_OSERR, "getifaddrs");
480
481	bzero(st, sizeof(*st));
482
483	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
484		if (ifa->ifa_addr->sa_family != AF_LINK)
485			continue;
486		if (interface) {
487			if (strcmp(ifa->ifa_name, interface) == 0)
488				found = true;
489			else
490				continue;
491		}
492
493		st->ift_ip += IFA_STAT(ipackets);
494		st->ift_ie += IFA_STAT(ierrors);
495		st->ift_id += IFA_STAT(iqdrops);
496		st->ift_ib += IFA_STAT(ibytes);
497		st->ift_op += IFA_STAT(opackets);
498		st->ift_oe += IFA_STAT(oerrors);
499		st->ift_od += IFA_STAT(oqdrops);
500		st->ift_ob += IFA_STAT(obytes);
501 		st->ift_co += IFA_STAT(collisions);
502	}
503
504	if (interface && found == false)
505		err(EX_DATAERR, "interface %s not found", interface);
506
507	freeifaddrs(ifap);
508}
509
510/*
511 * Set a flag to indicate that a signal from the periodic itimer has been
512 * caught.
513 */
514static sig_atomic_t signalled;
515static void
516catchalarm(int signo __unused)
517{
518	signalled = true;
519}
520
521/*
522 * Print a running summary of interface statistics.
523 * Repeat display every interval seconds, showing statistics
524 * collected over that interval.  Assumes that interval is non-zero.
525 * First line printed at top of screen is always cumulative.
526 */
527static void
528sidewaysintpr(int interval)
529{
530	struct iftot ift[2], *new, *old;
531	struct itimerval interval_it;
532	int oldmask, line;
533
534	new = &ift[0];
535	old = &ift[1];
536	fill_iftot(old);
537
538	(void)signal(SIGALRM, catchalarm);
539	signalled = false;
540	interval_it.it_interval.tv_sec = interval;
541	interval_it.it_interval.tv_usec = 0;
542	interval_it.it_value = interval_it.it_interval;
543	setitimer(ITIMER_REAL, &interval_it, NULL);
544
545banner:
546	printf("%17s %14s %16s", "input",
547	    interface != NULL ? interface : "(Total)", "output");
548	putchar('\n');
549	printf("%10s %5s %5s %10s %10s %5s %10s %5s",
550	    "packets", "errs", "idrops", "bytes", "packets", "errs", "bytes",
551	    "colls");
552	if (dflag)
553		printf(" %5.5s", "drops");
554	putchar('\n');
555	fflush(stdout);
556	line = 0;
557
558loop:
559	if ((noutputs != 0) && (--noutputs == 0))
560		exit(0);
561	oldmask = sigblock(sigmask(SIGALRM));
562	while (!signalled)
563		sigpause(0);
564	signalled = false;
565	sigsetmask(oldmask);
566	line++;
567
568	fill_iftot(new);
569
570	show_stat("lu", 10, new->ift_ip - old->ift_ip, 1, 1);
571	show_stat("lu", 5, new->ift_ie - old->ift_ie, 1, 1);
572	show_stat("lu", 5, new->ift_id - old->ift_id, 1, 1);
573	show_stat("lu", 10, new->ift_ib - old->ift_ib, 1, 0);
574	show_stat("lu", 10, new->ift_op - old->ift_op, 1, 1);
575	show_stat("lu", 5, new->ift_oe - old->ift_oe, 1, 1);
576	show_stat("lu", 10, new->ift_ob - old->ift_ob, 1, 0);
577	show_stat("NRSlu", 5, new->ift_co - old->ift_co, 1, 1);
578	if (dflag)
579		show_stat("LSlu", 5, new->ift_od - old->ift_od, 1, 1);
580	putchar('\n');
581	fflush(stdout);
582
583	if (new == &ift[0]) {
584		new = &ift[1];
585		old = &ift[0];
586	} else {
587		new = &ift[0];
588		old = &ift[1];
589	}
590
591	if (line == 21)
592		goto banner;
593	else
594		goto loop;
595
596	/* NOTREACHED */
597}
598