if.c revision 1.94
1/*	$NetBSD: if.c,v 1.94 2017/02/23 07:57:10 ozaki-r Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1988, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "from: @(#)if.c	8.2 (Berkeley) 2/21/94";
36#else
37__RCSID("$NetBSD: if.c,v 1.94 2017/02/23 07:57:10 ozaki-r Exp $");
38#endif
39#endif /* not lint */
40
41#include <sys/param.h>
42#include <sys/types.h>
43#include <sys/protosw.h>
44#include <sys/socket.h>
45#include <sys/time.h>
46#include <sys/sysctl.h>
47#include <sys/ioctl.h>
48
49#include <net/if.h>
50#include <net/if_dl.h>
51#include <net/if_types.h>
52#include <net/route.h>
53#include <netinet/in.h>
54#include <netinet/in_var.h>
55#include <arpa/inet.h>
56
57#include <kvm.h>
58#include <signal.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63#include <netdb.h>
64#include <err.h>
65
66#include "netstat.h"
67#include "rtutil.h"
68#include "prog_ops.h"
69
70#define	MAXIF	100
71
72#define HUMBUF_SIZE 7
73
74struct	iftot {
75	char ift_name[IFNAMSIZ];	/* interface name */
76	u_quad_t ift_ip;		/* input packets */
77	u_quad_t ift_ib;		/* input bytes */
78	u_quad_t ift_ie;		/* input errors */
79	u_quad_t ift_op;		/* output packets */
80	u_quad_t ift_ob;		/* output bytes */
81	u_quad_t ift_oe;		/* output errors */
82	u_quad_t ift_co;		/* collisions */
83	u_quad_t ift_dr;		/* drops */
84};
85
86static void set_lines(void);
87static void print_addr(const int, struct sockaddr *, struct sockaddr **,
88    struct if_data *, struct ifnet *);
89static void sidewaysintpr(u_int, u_long);
90
91static void iftot_banner(struct iftot *);
92static void iftot_print_sum(struct iftot *, struct iftot *);
93static void iftot_print(struct iftot *, struct iftot *);
94
95static void catchalarm(int);
96static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
97static void fetchifs(void);
98
99static void intpr_sysctl(void);
100static void intpr_kvm(u_long, void (*)(const char *));
101
102struct iftot iftot[MAXIF], ip_cur, ip_old, sum_cur, sum_old;
103static sig_atomic_t signalled;		/* set when alarm goes off */
104
105static unsigned redraw_lines = 21;
106
107static void
108set_lines(void)
109{
110	static bool first = true;
111	struct ttysize ts;
112
113	if (!first)
114		return;
115	first = false;
116	if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1 && ts.ts_lines)
117		redraw_lines = ts.ts_lines - 3;
118}
119
120
121/*
122 * Print a description of the network interfaces.
123 * NOTE: ifnetaddr is the location of the kernel global "ifnet",
124 * which is a TAILQ_HEAD.
125 */
126void
127intpr(int interval, u_long ifnetaddr, void (*pfunc)(const char *))
128{
129
130	if (interval) {
131		sidewaysintpr((unsigned)interval, ifnetaddr);
132		return;
133	}
134
135	if (use_sysctl) {
136		intpr_sysctl();
137	} else {
138		intpr_kvm(ifnetaddr, pfunc);
139	}
140
141}
142
143static void
144intpr_header(void)
145{
146
147	if (!sflag && !pflag) {
148		if (bflag) {
149			printf("%-5.5s %-5.5s %-13.13s %-17.17s "
150			       "%10.10s %10.10s",
151			       "Name", "Mtu", "Network", "Address",
152			       "Ibytes", "Obytes");
153		} else {
154			printf("%-5.5s %-5.5s %-13.13s %-17.17s "
155			       "%8.8s %5.5s %8.8s %5.5s %5.5s",
156			       "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs",
157			       "Opkts", "Oerrs", "Colls");
158		}
159		if (tflag)
160			printf(" %4.4s", "Time");
161		if (dflag)
162			printf(" %5.5s", "Drops");
163		putchar('\n');
164	}
165}
166
167static void
168intpr_sysctl(void)
169{
170	struct if_msghdr *ifm;
171	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
172	static char *buf = NULL;
173	static size_t olen;
174	char *next, *lim, *cp;
175	struct rt_msghdr *rtm;
176	struct ifa_msghdr *ifam;
177	struct if_data *ifd = NULL;
178	struct sockaddr *sa, *rti_info[RTAX_MAX];
179	struct sockaddr_dl *sdl;
180	uint64_t total = 0;
181	size_t len;
182	int did = 1, rtax = 0, n;
183	char name[IFNAMSIZ + 1];	/* + 1 for `*' */
184	int ifindex = 0;
185
186	if (prog_sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
187		err(1, "sysctl");
188	if (len > olen) {
189		free(buf);
190		if ((buf = malloc(len)) == NULL)
191			err(1, NULL);
192		olen = len;
193	}
194	if (prog_sysctl(mib, 6, buf, &len, NULL, 0) == -1)
195		err(1, "sysctl");
196
197	intpr_header();
198
199	lim = buf + len;
200	for (next = buf; next < lim; next += rtm->rtm_msglen) {
201		rtm = (struct rt_msghdr *)next;
202		if (rtm->rtm_version != RTM_VERSION)
203			continue;
204		switch (rtm->rtm_type) {
205		case RTM_IFINFO:
206			total = 0;
207			ifm = (struct if_msghdr *)next;
208			ifd = &ifm->ifm_data;
209
210			sa = (struct sockaddr *)(ifm + 1);
211			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
212
213			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
214			if (sdl == NULL || sdl->sdl_family != AF_LINK) {
215				continue;
216			}
217			bzero(name, sizeof(name));
218			if (sdl->sdl_nlen >= IFNAMSIZ)
219				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
220			else if (sdl->sdl_nlen > 0)
221				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
222
223			if (interface != 0 && strcmp(name, interface) != 0)
224				continue;
225
226			ifindex = sdl->sdl_index;
227
228			/* mark inactive interfaces with a '*' */
229			cp = strchr(name, '\0');
230			if ((ifm->ifm_flags & IFF_UP) == 0)
231				*cp++ = '*';
232			*cp = '\0';
233
234			if (qflag) {
235				total = ifd->ifi_ibytes + ifd->ifi_obytes +
236				    ifd->ifi_ipackets + ifd->ifi_ierrors +
237				    ifd->ifi_opackets + ifd->ifi_oerrors +
238				    ifd->ifi_collisions;
239				if (dflag)
240					total += ifd->ifi_iqdrops;
241				if (total == 0)
242					continue;
243			}
244			/* Skip the first one */
245			if (did) {
246				did = 0;
247				continue;
248			}
249			rtax = RTAX_IFP;
250			break;
251		case RTM_NEWADDR:
252			if (qflag && total == 0)
253				continue;
254			if (interface != 0 && strcmp(name, interface) != 0)
255				continue;
256			ifam = (struct ifa_msghdr *)next;
257			if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
258			    RTA_BRD)) == 0)
259				break;
260
261			sa = (struct sockaddr *)(ifam + 1);
262
263			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
264			rtax = RTAX_IFA;
265			did = 1;
266			break;
267		default:
268			continue;
269		}
270		if (vflag)
271			n = strlen(name) < 5 ? 5 : strlen(name);
272		else
273			n = 5;
274
275		printf("%-*.*s %-5" PRIu64 " ", n, n, name, ifd->ifi_mtu);
276		print_addr(ifindex, rti_info[rtax], rti_info, ifd, NULL);
277	}
278}
279
280union ifaddr_u {
281	struct ifaddr ifa;
282	struct in_ifaddr in;
283#ifdef INET6
284	struct in6_ifaddr in6;
285#endif /* INET6 */
286};
287
288static void
289intpr_kvm(u_long ifnetaddr, void (*pfunc)(const char *))
290{
291	struct ifnet ifnet;
292	union ifaddr_u ifaddr;
293	u_long ifaddraddr;
294	struct ifnet_head ifhead;	/* TAILQ_HEAD */
295	char name[IFNAMSIZ + 1];	/* + 1 for `*' */
296
297	if (ifnetaddr == 0) {
298		printf("ifnet: symbol not defined\n");
299		return;
300	}
301
302	/*
303	 * Find the pointer to the first ifnet structure.  Replace
304	 * the pointer to the TAILQ_HEAD with the actual pointer
305	 * to the first list element.
306	 */
307	if (kread(ifnetaddr, (char *)&ifhead, sizeof ifhead))
308		return;
309	ifnetaddr = (u_long)ifhead.tqh_first;
310
311	intpr_header();
312
313	ifaddraddr = 0;
314	while (ifnetaddr || ifaddraddr) {
315		char *cp;
316		int n;
317
318		if (ifaddraddr == 0) {
319			if (kread(ifnetaddr, (char *)&ifnet, sizeof ifnet))
320				return;
321			memmove(name, ifnet.if_xname, IFNAMSIZ);
322			name[IFNAMSIZ - 1] = '\0';	/* sanity */
323			ifnetaddr = (u_long)ifnet.if_list.tqe_next;
324			if (interface != 0 && strcmp(name, interface) != 0)
325				continue;
326			cp = strchr(name, '\0');
327
328			if (pfunc) {
329				(*pfunc)(name);
330				continue;
331			}
332
333			if ((ifnet.if_flags & IFF_UP) == 0)
334				*cp++ = '*';
335			*cp = '\0';
336			ifaddraddr = (u_long)ifnet.if_addrlist.tqh_first;
337		}
338		if (vflag)
339			n = strlen(name) < 5 ? 5 : strlen(name);
340		else
341			n = 5;
342		printf("%-*.*s %-5llu ", n, n, name,
343		    (unsigned long long)ifnet.if_mtu);
344		if (ifaddraddr == 0) {
345			printf("%-13.13s ", "none");
346			printf("%-17.17s ", "none");
347		} else {
348			struct sockaddr *sa;
349
350			if (kread(ifaddraddr, (char *)&ifaddr, sizeof ifaddr)) {
351				ifaddraddr = 0;
352				continue;
353			}
354#define CP(x) ((char *)(x))
355			cp = (CP(ifaddr.ifa.ifa_addr) - CP(ifaddraddr)) +
356			    CP(&ifaddr);
357			sa = (struct sockaddr *)cp;
358			print_addr(ifnet.if_index, sa, (void *)&ifaddr,
359			    &ifnet.if_data, &ifnet);
360		}
361		ifaddraddr = (u_long)ifaddr.ifa.ifa_list.tqe_next;
362	}
363
364}
365
366static void
367mc_print(const int ifindex, const size_t ias, const char *oid, int *mcast_oids,
368    void (*pr)(const void *))
369{
370	uint8_t *mcast_addrs, *p;
371	const size_t incr = 2 * ias + sizeof(uint32_t);
372	size_t len;
373
374	if (mcast_oids[0] == 0) {
375		size_t oidlen = 4;
376		if (sysctlnametomib(oid, mcast_oids, &oidlen) == -1) {
377			warnx("'%s' not found", oid);
378			return;
379		}
380		if (oidlen != 3) {
381			warnx("Wrong OID path for '%s'", oid);
382			return;
383		}
384	}
385
386	if (mcast_oids[3] == ifindex)
387		return;
388	mcast_oids[3] = ifindex;
389
390	mcast_addrs = asysctl(mcast_oids, 4, &len);
391	if (mcast_addrs == NULL && len != 0) {
392		warn("failed to read '%s'", oid);
393		return;
394	}
395	if (len) {
396		p = mcast_addrs;
397		while (len >= incr) {
398			(*pr)((p + ias));
399			p += incr;
400			len -= incr;
401		}
402	}
403	free(mcast_addrs);
404}
405
406#ifdef INET6
407static void
408ia6_print(const struct in6_addr *ia)
409{
410	struct sockaddr_in6 as6;
411	char hbuf[NI_MAXHOST];		/* for getnameinfo() */
412	int n;
413
414	memset(&as6, 0, sizeof(as6));
415	as6.sin6_len = sizeof(struct sockaddr_in6);
416	as6.sin6_family = AF_INET6;
417	as6.sin6_addr = *ia;
418	inet6_getscopeid(&as6, INET6_IS_ADDR_MC_LINKLOCAL);
419	if (getnameinfo((struct sockaddr *)&as6, as6.sin6_len, hbuf,
420	    sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
421		strlcpy(hbuf, "??", sizeof(hbuf));
422	}
423	if (vflag)
424		n = strlen(hbuf) < 17 ? 17 : strlen(hbuf);
425	else
426		n = 17;
427	printf("\n%25s %-*.*s ", "", n, n, hbuf);
428}
429
430static void
431mc6_print(const int ifindex)
432{
433	static int mcast_oids[4];
434
435	mc_print(ifindex, sizeof(struct in6_addr), "net.inet6.multicast",
436	    mcast_oids, (void (*)(const void *))ia6_print);
437}
438#endif
439
440static void
441ia4_print(const struct in_addr *ia)
442{
443	printf("\n%25s %-17.17s ", "", routename4(ia->s_addr, nflag));
444}
445
446static void
447mc4_print(const int ifindex)
448{
449	static int mcast_oids[4];
450
451	mc_print(ifindex, sizeof(struct in_addr), "net.inet.multicast",
452	    mcast_oids, (void (*)(const void *))ia4_print);
453}
454
455static void
456print_addr(const int ifindex, struct sockaddr *sa,
457    struct sockaddr **rtinfo, struct if_data *ifd, struct ifnet *ifnet)
458{
459	char hexsep = '.';		/* for hexprint */
460	static const char hexfmt[] = "%02x%c";	/* for hexprint */
461	char hbuf[NI_MAXHOST];		/* for getnameinfo() */
462#ifdef INET6
463	const int niflag = NI_NUMERICHOST;
464	struct sockaddr_in6 *sin6, *netmask6;
465#endif
466	struct sockaddr_in netmask;
467	struct sockaddr_in *sin;
468	char *cp;
469	int n, m;
470
471	switch (sa->sa_family) {
472	case AF_UNSPEC:
473		printf("%-13.13s ", "none");
474		printf("%-17.17s ", "none");
475		break;
476	case AF_INET:
477		sin = (struct sockaddr_in *)sa;
478		if (use_sysctl) {
479			netmask = *((struct sockaddr_in *)rtinfo[RTAX_NETMASK]);
480		} else {
481			struct in_ifaddr *ifaddr_in = (void *)rtinfo;
482			netmask.sin_addr.s_addr = ifaddr_in->ia_subnetmask;
483		}
484		cp = netname4(sin, &netmask, nflag);
485		if (vflag)
486			n = strlen(cp) < 13 ? 13 : strlen(cp);
487		else
488			n = 13;
489		printf("%-*.*s ", n, n, cp);
490		cp = routename4(sin->sin_addr.s_addr, nflag);
491		if (vflag)
492			n = strlen(cp) < 17 ? 17 : strlen(cp);
493		else
494			n = 17;
495		printf("%-*.*s ", n, n, cp);
496
497		if (!aflag)
498			break;
499		if (ifnet) {
500			u_long multiaddr;
501			struct in_multi inm;
502			union ifaddr_u *ifaddr = (union ifaddr_u *)rtinfo;
503
504			multiaddr = (u_long)ifaddr->in.ia_multiaddrs.lh_first;
505			while (multiaddr != 0) {
506				kread(multiaddr, (char *)&inm, sizeof inm);
507				ia4_print(&inm.inm_addr);
508				multiaddr = (u_long)inm.inm_list.le_next;
509			}
510		} else {
511			mc4_print(ifindex);
512		}
513		break;
514#ifdef INET6
515	case AF_INET6:
516		sin6 = (struct sockaddr_in6 *)sa;
517		inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL);
518#ifdef __KAME__
519		if (!vflag)
520			sin6->sin6_scope_id = 0;
521#endif
522
523		if (use_sysctl) {
524			netmask6 = (struct sockaddr_in6 *)rtinfo[RTAX_NETMASK];
525		} else {
526			struct in6_ifaddr *ifaddr_in6 = (void *)rtinfo;
527			netmask6 = &ifaddr_in6->ia_prefixmask;
528		}
529
530		cp = netname6(sin6, netmask6, nflag);
531		if (vflag)
532			n = strlen(cp) < 13 ? 13 : strlen(cp);
533		else
534			n = 13;
535		printf("%-*.*s ", n, n, cp);
536		if (getnameinfo((struct sockaddr *)sin6,
537				sin6->sin6_len,
538				hbuf, sizeof(hbuf), NULL, 0,
539				niflag) != 0) {
540			strlcpy(hbuf, "?", sizeof(hbuf));
541		}
542		cp = hbuf;
543		if (vflag)
544			n = strlen(cp) < 17 ? 17 : strlen(cp);
545		else
546			n = 17;
547		printf("%-*.*s ", n, n, cp);
548
549		if (!aflag)
550			break;
551		if (ifnet) {
552			u_long multiaddr;
553			struct in6_multi inm;
554			union ifaddr_u *ifaddr = (union ifaddr_u *)rtinfo;
555
556			multiaddr = (u_long)ifaddr->in6._ia6_multiaddrs.lh_first;
557			while (multiaddr != 0) {
558				kread(multiaddr, (char *)&inm, sizeof inm);
559				ia6_print(&inm.in6m_addr);
560				multiaddr = (u_long)inm.in6m_entry.le_next;
561			}
562		} else {
563			mc6_print(ifindex);
564		}
565		break;
566#endif /*INET6*/
567#ifndef SMALL
568	case AF_APPLETALK:
569		printf("atalk:%-7.7s ",
570		       atalk_print(sa,0x10));
571		printf("%-17.17s ", atalk_print(sa,0x0b));
572		break;
573#endif
574	case AF_LINK:
575		printf("%-13.13s ", "<Link>");
576		if (getnameinfo(sa, sa->sa_len,
577		    hbuf, sizeof(hbuf), NULL, 0,
578		    NI_NUMERICHOST) != 0) {
579			strlcpy(hbuf, "?", sizeof(hbuf));
580		}
581		cp = hbuf;
582		if (vflag)
583			n = strlen(cp) < 17 ? 17 : strlen(cp);
584		else
585			n = 17;
586		printf("%-*.*s ", n, n, cp);
587		break;
588
589	default:
590		m = printf("(%d)", sa->sa_family);
591		for (cp = sa->sa_len + (char *)sa;
592			--cp > sa->sa_data && (*cp == 0);) {}
593		n = cp - sa->sa_data + 1;
594		cp = sa->sa_data;
595
596		while (--n >= 0)
597			m += printf(hexfmt, *cp++ & 0xff,
598				    n > 0 ? hexsep : ' ');
599		m = 32 - m;
600		while (m-- > 0)
601			putchar(' ');
602		break;
603	}
604
605	if (bflag) {
606		char humbuf[HUMBUF_SIZE];
607
608		if (hflag && humanize_number(humbuf, sizeof(humbuf),
609		    ifd->ifi_ibytes, "", HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
610			printf("%10s ", humbuf);
611		else
612			printf("%10llu ", (unsigned long long)ifd->ifi_ibytes);
613
614		if (hflag && humanize_number(humbuf, sizeof(humbuf),
615		    ifd->ifi_obytes, "", HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
616			printf("%10s", humbuf);
617		else
618			printf("%10llu", (unsigned long long)ifd->ifi_obytes);
619	} else {
620		printf("%8llu %5llu %8llu %5llu %5llu",
621			(unsigned long long)ifd->ifi_ipackets,
622			(unsigned long long)ifd->ifi_ierrors,
623			(unsigned long long)ifd->ifi_opackets,
624			(unsigned long long)ifd->ifi_oerrors,
625			(unsigned long long)ifd->ifi_collisions);
626	}
627	if (tflag)
628		printf(" %4d", ifnet ? ifnet->if_timer : 0);
629	if (dflag)
630		printf(" %5lld", ifnet ?
631		    (unsigned long long)ifnet->if_snd.ifq_drops :
632		    ifd->ifi_iqdrops);
633	putchar('\n');
634}
635
636static void
637iftot_banner(struct iftot *ift)
638{
639	if (bflag)
640		printf("%7.7s in %8.8s %6.6s out %5.5s",
641		    ift->ift_name, " ",
642		    ift->ift_name, " ");
643	else
644		printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s",
645		    ift->ift_name, " ",
646		    ift->ift_name, " ", " ");
647	if (dflag)
648		printf(" %5.5s", " ");
649
650	if (bflag)
651		printf("  %7.7s in %8.8s %6.6s out %5.5s",
652		    "total", " ", "total", " ");
653	else
654		printf("  %5.5s in %5.5s%5.5s out %5.5s %5.5s",
655		    "total", " ", "total", " ", " ");
656	if (dflag)
657		printf(" %5.5s", " ");
658	putchar('\n');
659	if (bflag)
660		printf("%10.10s %8.8s %10.10s %5.5s",
661		    "bytes", " ", "bytes", " ");
662	else
663		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
664		    "packets", "errs", "packets", "errs", "colls");
665	if (dflag)
666		printf(" %5.5s", "drops");
667
668	if (bflag)
669		printf("  %10.10s %8.8s %10.10s %5.5s",
670		    "bytes", " ", "bytes", " ");
671	else
672		printf("  %8.8s %5.5s %8.8s %5.5s %5.5s",
673		    "packets", "errs", "packets", "errs", "colls");
674	if (dflag)
675		printf(" %5.5s", "drops");
676	putchar('\n');
677	fflush(stdout);
678}
679
680static void
681iftot_print(struct iftot *cur, struct iftot *old)
682{
683	if (bflag)
684		printf("%10" PRIu64 " %8.8s %10" PRIu64 " %5.5s",
685		    cur->ift_ib - old->ift_ib, " ",
686		    cur->ift_ob - old->ift_ob, " ");
687	else
688		printf("%8" PRIu64 " %5" PRIu64 " %8" PRIu64 " %5" PRIu64 " %5" PRIu64,
689		    cur->ift_ip - old->ift_ip,
690		    cur->ift_ie - old->ift_ie,
691		    cur->ift_op - old->ift_op,
692		    cur->ift_oe - old->ift_oe,
693		    cur->ift_co - old->ift_co);
694	if (dflag)
695		printf(" %5" PRIu64, cur->ift_dr - old->ift_dr);
696}
697
698static void
699iftot_print_sum(struct iftot *cur, struct iftot *old)
700{
701	if (bflag)
702		printf("  %10" PRIu64 " %8.8s %10" PRIu64 " %5.5s",
703		    cur->ift_ib - old->ift_ib, " ",
704		    cur->ift_ob - old->ift_ob, " ");
705	else
706		printf("  %8" PRIu64 " %5" PRIu64 " %8" PRIu64 " %5" PRIu64 " %5" PRIu64,
707		    cur->ift_ip - old->ift_ip,
708		    cur->ift_ie - old->ift_ie,
709		    cur->ift_op - old->ift_op,
710		    cur->ift_oe - old->ift_oe,
711		    cur->ift_co - old->ift_co);
712
713	if (dflag)
714		printf(" %5" PRIu64, cur->ift_dr - old->ift_dr);
715}
716
717__dead static void
718sidewaysintpr_sysctl(unsigned interval)
719{
720	struct itimerval it;
721	sigset_t emptyset;
722	sigset_t noalrm;
723	unsigned line;
724
725	set_lines();
726
727	fetchifs();
728	if (ip_cur.ift_name[0] == '\0') {
729		fprintf(stderr, "%s: %s: unknown interface\n",
730		    getprogname(), interface);
731		exit(1);
732	}
733
734	sigemptyset(&emptyset);
735	sigemptyset(&noalrm);
736	sigaddset(&noalrm, SIGALRM);
737	sigprocmask(SIG_SETMASK, &noalrm, NULL);
738
739	signalled = 0;
740	(void)signal(SIGALRM, catchalarm);
741
742	it.it_interval.tv_sec = it.it_value.tv_sec = interval;
743	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
744	setitimer(ITIMER_REAL, &it, NULL);
745
746banner:
747	iftot_banner(&ip_cur);
748
749	line = 0;
750	bzero(&ip_old, sizeof(ip_old));
751	bzero(&sum_old, sizeof(sum_old));
752loop:
753	bzero(&sum_cur, sizeof(sum_cur));
754
755	fetchifs();
756
757	iftot_print(&ip_cur, &ip_old);
758
759	ip_old = ip_cur;
760
761	iftot_print_sum(&sum_cur, &sum_old);
762
763	sum_old = sum_cur;
764
765	putchar('\n');
766	fflush(stdout);
767	line++;
768	if (signalled == 0) {
769		sigsuspend(&emptyset);
770	}
771	signalled = 0;
772	if (line == redraw_lines)
773		goto banner;
774	goto loop;
775	/*NOTREACHED*/
776}
777
778static void
779sidewaysintpr_kvm(unsigned interval, u_long off)
780{
781	struct itimerval it;
782	sigset_t emptyset;
783	sigset_t noalrm;
784	struct ifnet ifnet;
785	u_long firstifnet;
786	struct iftot *ip, *total;
787	unsigned line;
788	struct iftot *lastif, *sum, *interesting;
789	struct ifnet_head ifhead;	/* TAILQ_HEAD */
790
791	set_lines();
792
793	/*
794	 * Find the pointer to the first ifnet structure.  Replace
795	 * the pointer to the TAILQ_HEAD with the actual pointer
796	 * to the first list element.
797	 */
798	if (kread(off, (char *)&ifhead, sizeof ifhead))
799		return;
800	firstifnet = (u_long)ifhead.tqh_first;
801
802	lastif = iftot;
803	sum = iftot + MAXIF - 1;
804	total = sum - 1;
805	interesting = (interface == NULL) ? iftot : NULL;
806	for (off = firstifnet, ip = iftot; off;) {
807		if (kread(off, (char *)&ifnet, sizeof ifnet))
808			break;
809		memset(ip->ift_name, 0, sizeof(ip->ift_name));
810		snprintf(ip->ift_name, IFNAMSIZ, "%s", ifnet.if_xname);
811		if (interface && strcmp(ifnet.if_xname, interface) == 0)
812			interesting = ip;
813		ip++;
814		if (ip >= iftot + MAXIF - 2)
815			break;
816		off = (u_long)ifnet.if_list.tqe_next;
817	}
818	if (interesting == NULL) {
819		fprintf(stderr, "%s: %s: unknown interface\n",
820		    getprogname(), interface);
821		exit(1);
822	}
823	lastif = ip;
824
825	sigemptyset(&emptyset);
826	sigemptyset(&noalrm);
827	sigaddset(&noalrm, SIGALRM);
828	sigprocmask(SIG_SETMASK, &noalrm, NULL);
829
830	signalled = 0;
831	(void)signal(SIGALRM, catchalarm);
832
833	it.it_interval.tv_sec = it.it_value.tv_sec = interval;
834	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
835	setitimer(ITIMER_REAL, &it, NULL);
836
837banner:
838	if (bflag)
839		printf("%7.7s in %8.8s %6.6s out %5.5s",
840		    interesting->ift_name, " ",
841		    interesting->ift_name, " ");
842	else
843		printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s",
844		    interesting->ift_name, " ",
845		    interesting->ift_name, " ", " ");
846	if (dflag)
847		printf(" %5.5s", " ");
848	if (lastif - iftot > 0) {
849		if (bflag)
850			printf("  %7.7s in %8.8s %6.6s out %5.5s",
851			    "total", " ", "total", " ");
852		else
853			printf("  %5.5s in %5.5s%5.5s out %5.5s %5.5s",
854			    "total", " ", "total", " ", " ");
855		if (dflag)
856			printf(" %5.5s", " ");
857	}
858	for (ip = iftot; ip < iftot + MAXIF; ip++) {
859		ip->ift_ip = 0;
860		ip->ift_ib = 0;
861		ip->ift_ie = 0;
862		ip->ift_op = 0;
863		ip->ift_ob = 0;
864		ip->ift_oe = 0;
865		ip->ift_co = 0;
866		ip->ift_dr = 0;
867	}
868	putchar('\n');
869	if (bflag)
870		printf("%10.10s %8.8s %10.10s %5.5s",
871		    "bytes", " ", "bytes", " ");
872	else
873		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
874		    "packets", "errs", "packets", "errs", "colls");
875	if (dflag)
876		printf(" %5.5s", "drops");
877	if (lastif - iftot > 0) {
878		if (bflag)
879			printf("  %10.10s %8.8s %10.10s %5.5s",
880			    "bytes", " ", "bytes", " ");
881		else
882			printf("  %8.8s %5.5s %8.8s %5.5s %5.5s",
883			    "packets", "errs", "packets", "errs", "colls");
884		if (dflag)
885			printf(" %5.5s", "drops");
886	}
887	putchar('\n');
888	fflush(stdout);
889	line = 0;
890loop:
891	sum->ift_ip = 0;
892	sum->ift_ib = 0;
893	sum->ift_ie = 0;
894	sum->ift_op = 0;
895	sum->ift_ob = 0;
896	sum->ift_oe = 0;
897	sum->ift_co = 0;
898	sum->ift_dr = 0;
899	for (off = firstifnet, ip = iftot; off && ip < lastif; ip++) {
900		if (kread(off, (char *)&ifnet, sizeof ifnet)) {
901			off = 0;
902			continue;
903		}
904		if (ip == interesting) {
905			if (bflag) {
906				char humbuf[HUMBUF_SIZE];
907
908				if (hflag && humanize_number(humbuf,
909				    sizeof(humbuf),
910				    ifnet.if_ibytes - ip->ift_ib, "",
911				    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
912					printf("%10s %8.8s ", humbuf, " ");
913				else
914					printf("%10llu %8.8s ",
915					    (unsigned long long)
916					    (ifnet.if_ibytes-ip->ift_ib), " ");
917
918				if (hflag && humanize_number(humbuf,
919				    sizeof(humbuf),
920				    ifnet.if_obytes - ip->ift_ob, "",
921				    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
922					printf("%10s %5.5s", humbuf, " ");
923				else
924					printf("%10llu %5.5s",
925					    (unsigned long long)
926					    (ifnet.if_obytes-ip->ift_ob), " ");
927			} else {
928				printf("%8llu %5llu %8llu %5llu %5llu",
929				    (unsigned long long)
930					(ifnet.if_ipackets - ip->ift_ip),
931				    (unsigned long long)
932					(ifnet.if_ierrors - ip->ift_ie),
933				    (unsigned long long)
934					(ifnet.if_opackets - ip->ift_op),
935				    (unsigned long long)
936					(ifnet.if_oerrors - ip->ift_oe),
937				    (unsigned long long)
938					(ifnet.if_collisions - ip->ift_co));
939			}
940			if (dflag)
941				printf(" %5" PRIu64,
942					ifnet.if_snd.ifq_drops - ip->ift_dr);
943		}
944		ip->ift_ip = ifnet.if_ipackets;
945		ip->ift_ib = ifnet.if_ibytes;
946		ip->ift_ie = ifnet.if_ierrors;
947		ip->ift_op = ifnet.if_opackets;
948		ip->ift_ob = ifnet.if_obytes;
949		ip->ift_oe = ifnet.if_oerrors;
950		ip->ift_co = ifnet.if_collisions;
951		ip->ift_dr = ifnet.if_snd.ifq_drops;
952		sum->ift_ip += ip->ift_ip;
953		sum->ift_ib += ip->ift_ib;
954		sum->ift_ie += ip->ift_ie;
955		sum->ift_op += ip->ift_op;
956		sum->ift_ob += ip->ift_ob;
957		sum->ift_oe += ip->ift_oe;
958		sum->ift_co += ip->ift_co;
959		sum->ift_dr += ip->ift_dr;
960		off = (u_long)ifnet.if_list.tqe_next;
961	}
962	if (lastif - iftot > 0) {
963		if (bflag) {
964			char humbuf[HUMBUF_SIZE];
965
966			if (hflag && humanize_number(humbuf,
967			    sizeof(humbuf), sum->ift_ib - total->ift_ib, "",
968			    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
969				printf("  %10s %8.8s ", humbuf, " ");
970			else
971				printf("  %10llu %8.8s ",
972				    (unsigned long long)
973				    (sum->ift_ib - total->ift_ib), " ");
974
975			if (hflag && humanize_number(humbuf,
976			    sizeof(humbuf), sum->ift_ob -  total->ift_ob, "",
977			    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
978				printf("%10s %5.5s", humbuf, " ");
979			else
980				printf("%10llu %5.5s",
981				    (unsigned long long)
982				    (sum->ift_ob - total->ift_ob), " ");
983		} else {
984			printf("  %8llu %5llu %8llu %5llu %5llu",
985			    (unsigned long long)
986				(sum->ift_ip - total->ift_ip),
987			    (unsigned long long)
988				(sum->ift_ie - total->ift_ie),
989			    (unsigned long long)
990				(sum->ift_op - total->ift_op),
991			    (unsigned long long)
992				(sum->ift_oe - total->ift_oe),
993			    (unsigned long long)
994				(sum->ift_co - total->ift_co));
995		}
996		if (dflag)
997			printf(" %5llu",
998			    (unsigned long long)(sum->ift_dr - total->ift_dr));
999	}
1000	*total = *sum;
1001	putchar('\n');
1002	fflush(stdout);
1003	line++;
1004	if (signalled == 0) {
1005		sigsuspend(&emptyset);
1006	}
1007	signalled = 0;
1008	if (line == redraw_lines)
1009		goto banner;
1010	goto loop;
1011	/*NOTREACHED*/
1012}
1013
1014/*
1015 * Print a running summary of interface statistics.
1016 * Repeat display every interval seconds, showing statistics
1017 * collected over that interval.  Assumes that interval is non-zero.
1018 * First line printed at top of screen is always cumulative.
1019 */
1020static void
1021sidewaysintpr(unsigned int interval, u_long off)
1022{
1023
1024	if (use_sysctl) {
1025		sidewaysintpr_sysctl(interval);
1026	} else {
1027		sidewaysintpr_kvm(interval, off);
1028	}
1029}
1030
1031/*
1032 * Called if an interval expires before sidewaysintpr has completed a loop.
1033 * Sets a flag to not wait for the alarm.
1034 */
1035static void
1036catchalarm(int signo)
1037{
1038
1039	signalled = true;
1040}
1041
1042static void
1043get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
1044{
1045	int i;
1046
1047	for (i = 0; i < RTAX_MAX; i++) {
1048		if (addrs & (1 << i)) {
1049			rti_info[i] = sa;
1050			sa = (struct sockaddr *)((char *)(sa) +
1051			    RT_ROUNDUP(sa->sa_len));
1052		} else
1053			rti_info[i] = NULL;
1054	}
1055}
1056
1057static void
1058fetchifs(void)
1059{
1060	struct if_msghdr *ifm;
1061	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
1062	struct rt_msghdr *rtm;
1063	struct if_data *ifd = NULL;
1064	struct sockaddr *sa, *rti_info[RTAX_MAX];
1065	struct sockaddr_dl *sdl;
1066	static char *buf = NULL;
1067	static size_t olen;
1068	char *next, *lim;
1069	char name[IFNAMSIZ];
1070	size_t len;
1071
1072	if (prog_sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
1073		err(1, "sysctl");
1074	if (len > olen) {
1075		free(buf);
1076		if ((buf = malloc(len)) == NULL)
1077			err(1, NULL);
1078		olen = len;
1079	}
1080	if (prog_sysctl(mib, 6, buf, &len, NULL, 0) == -1)
1081		err(1, "sysctl");
1082
1083	lim = buf + len;
1084	for (next = buf; next < lim; next += rtm->rtm_msglen) {
1085		rtm = (struct rt_msghdr *)next;
1086		if (rtm->rtm_version != RTM_VERSION)
1087			continue;
1088		switch (rtm->rtm_type) {
1089		case RTM_IFINFO:
1090			ifm = (struct if_msghdr *)next;
1091			ifd = &ifm->ifm_data;
1092
1093			sa = (struct sockaddr *)(ifm + 1);
1094			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
1095
1096			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
1097			if (sdl == NULL || sdl->sdl_family != AF_LINK)
1098				continue;
1099			bzero(name, sizeof(name));
1100			if (sdl->sdl_nlen >= IFNAMSIZ)
1101				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
1102			else if (sdl->sdl_nlen > 0)
1103				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
1104
1105			if (interface != 0 && !strcmp(name, interface)) {
1106				strlcpy(ip_cur.ift_name, name,
1107				    sizeof(ip_cur.ift_name));
1108				ip_cur.ift_ip = ifd->ifi_ipackets;
1109				ip_cur.ift_ib = ifd->ifi_ibytes;
1110				ip_cur.ift_ie = ifd->ifi_ierrors;
1111				ip_cur.ift_op = ifd->ifi_opackets;
1112				ip_cur.ift_ob = ifd->ifi_obytes;
1113				ip_cur.ift_oe = ifd->ifi_oerrors;
1114				ip_cur.ift_co = ifd->ifi_collisions;
1115				ip_cur.ift_dr = ifd->ifi_iqdrops;
1116			}
1117
1118			sum_cur.ift_ip += ifd->ifi_ipackets;
1119			sum_cur.ift_ib += ifd->ifi_ibytes;
1120			sum_cur.ift_ie += ifd->ifi_ierrors;
1121			sum_cur.ift_op += ifd->ifi_opackets;
1122			sum_cur.ift_ob += ifd->ifi_obytes;
1123			sum_cur.ift_oe += ifd->ifi_oerrors;
1124			sum_cur.ift_co += ifd->ifi_collisions;
1125			sum_cur.ift_dr += ifd->ifi_iqdrops;
1126			break;
1127		}
1128	}
1129	if (interface == NULL) {
1130		strlcpy(ip_cur.ift_name, name,
1131		    sizeof(ip_cur.ift_name));
1132		ip_cur.ift_ip = ifd->ifi_ipackets;
1133		ip_cur.ift_ib = ifd->ifi_ibytes;
1134		ip_cur.ift_ie = ifd->ifi_ierrors;
1135		ip_cur.ift_op = ifd->ifi_opackets;
1136		ip_cur.ift_ob = ifd->ifi_obytes;
1137		ip_cur.ift_oe = ifd->ifi_oerrors;
1138		ip_cur.ift_co = ifd->ifi_collisions;
1139		ip_cur.ift_dr = ifd->ifi_iqdrops;
1140	}
1141}
1142