if.c revision 1.97
1/*	$NetBSD: if.c,v 1.97 2022/08/18 12:25:32 msaitoh 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.97 2022/08/18 12:25:32 msaitoh 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",
156			    "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs");
157			if (dflag)
158				printf(" %6.6s", "Idrops");
159			printf(" %8.8s %5.5s %5.5s",
160			    "Opkts", "Oerrs", "Colls");
161		}
162		if (dflag)
163			printf(" %6.6s", "Odrops");
164		if (tflag)
165			printf(" %4.4s", "Time");
166		putchar('\n');
167	}
168}
169
170static void
171intpr_sysctl(void)
172{
173	struct if_msghdr *ifm;
174	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
175	static char *buf = NULL;
176	static size_t olen;
177	char *next, *lim, *cp;
178	struct rt_msghdr *rtm;
179	struct ifa_msghdr *ifam;
180	struct if_data *ifd = NULL;
181	struct sockaddr *sa, *rti_info[RTAX_MAX];
182	struct sockaddr_dl *sdl;
183	uint64_t total = 0;
184	size_t len;
185	int did = 1, rtax = 0, n;
186	char name[IFNAMSIZ + 1];	/* + 1 for `*' */
187	int ifindex = 0;
188
189	if (prog_sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
190		err(1, "sysctl");
191	if (len > olen) {
192		free(buf);
193		if ((buf = malloc(len)) == NULL)
194			err(1, NULL);
195		olen = len;
196	}
197	if (prog_sysctl(mib, 6, buf, &len, NULL, 0) == -1)
198		err(1, "sysctl");
199
200	intpr_header();
201
202	lim = buf + len;
203	for (next = buf; next < lim; next += rtm->rtm_msglen) {
204		rtm = (struct rt_msghdr *)next;
205		if (rtm->rtm_version != RTM_VERSION)
206			continue;
207		switch (rtm->rtm_type) {
208		case RTM_IFINFO:
209			total = 0;
210			ifm = (struct if_msghdr *)next;
211			ifd = &ifm->ifm_data;
212
213			sa = (struct sockaddr *)(ifm + 1);
214			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
215
216			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
217			if (sdl == NULL || sdl->sdl_family != AF_LINK) {
218				continue;
219			}
220			bzero(name, sizeof(name));
221			if (sdl->sdl_nlen >= IFNAMSIZ)
222				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
223			else if (sdl->sdl_nlen > 0)
224				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
225
226			if (interface != NULL && strcmp(name, interface) != 0)
227				continue;
228
229			ifindex = sdl->sdl_index;
230
231			/* mark inactive interfaces with a '*' */
232			cp = strchr(name, '\0');
233			if ((ifm->ifm_flags & IFF_UP) == 0)
234				*cp++ = '*';
235			*cp = '\0';
236
237			if (qflag) {
238				total = ifd->ifi_ibytes + ifd->ifi_obytes +
239				    ifd->ifi_ipackets + ifd->ifi_ierrors +
240				    ifd->ifi_opackets + ifd->ifi_oerrors +
241				    ifd->ifi_collisions;
242				if (dflag)
243					total += ifd->ifi_iqdrops;
244				if (total == 0)
245					continue;
246			}
247			/* Skip the first one */
248			if (did) {
249				did = 0;
250				continue;
251			}
252			rtax = RTAX_IFP;
253			break;
254		case RTM_NEWADDR:
255			if (qflag && total == 0)
256				continue;
257			if (interface != NULL && strcmp(name, interface) != 0)
258				continue;
259			ifam = (struct ifa_msghdr *)next;
260			if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
261			    RTA_BRD)) == 0)
262				break;
263
264			sa = (struct sockaddr *)(ifam + 1);
265
266			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
267			rtax = RTAX_IFA;
268			did = 1;
269			break;
270		default:
271			continue;
272		}
273		if (vflag)
274			n = strlen(name) < 5 ? 5 : strlen(name);
275		else
276			n = 5;
277
278		printf("%-*.*s %-5" PRIu64 " ", n, n, name, ifd->ifi_mtu);
279		print_addr(ifindex, rti_info[rtax], rti_info, ifd, NULL);
280	}
281}
282
283union ifaddr_u {
284	struct ifaddr ifa;
285	struct in_ifaddr in;
286#ifdef INET6
287	struct in6_ifaddr in6;
288#endif /* INET6 */
289};
290
291static void
292ifnet_to_ifdata_kvm(const struct ifnet * const ifp, struct if_data * const ifd)
293{
294
295	/*
296	 * Interface statistics are no longer kept in struct ifnet,
297	 * and thus an if_data is no longer embedded in struct ifnet.
298	 * We cannot read stats via kvm without chasing per-cpu data,
299	 * and maybe someday we could do that.  But for now, this is
300	 * what we have.
301	 *
302	 * Just copy the fields that do exist.
303	 */
304	memset(ifd, 0, sizeof(*ifd));
305	ifd->ifi_type = ifp->if_type;
306	ifd->ifi_addrlen = ifp->if_addrlen;
307	ifd->ifi_hdrlen = ifp->if_hdrlen;
308	ifd->ifi_link_state = ifp->if_link_state;
309	ifd->ifi_mtu = ifp->if_mtu;
310	ifd->ifi_metric = ifp->if_metric;
311	ifd->ifi_baudrate = ifp->if_baudrate;
312	ifd->ifi_lastchange = ifp->if_lastchange;
313}
314
315static void
316intpr_kvm(u_long ifnetaddr, void (*pfunc)(const char *))
317{
318	struct ifnet ifnet;
319	struct if_data ifd;
320	union ifaddr_u ifaddr;
321	u_long ifaddraddr;
322	struct ifnet_head ifhead;	/* TAILQ_HEAD */
323	char name[IFNAMSIZ + 1];	/* + 1 for `*' */
324
325	if (ifnetaddr == 0) {
326		printf("ifnet: symbol not defined\n");
327		return;
328	}
329
330	/*
331	 * Find the pointer to the first ifnet structure.  Replace
332	 * the pointer to the TAILQ_HEAD with the actual pointer
333	 * to the first list element.
334	 */
335	if (kread(ifnetaddr, (char *)&ifhead, sizeof ifhead))
336		return;
337	ifnetaddr = (u_long)ifhead.tqh_first;
338
339	intpr_header();
340
341	ifaddraddr = 0;
342	while (ifnetaddr || ifaddraddr) {
343		char *cp;
344		int n;
345
346		if (ifaddraddr == 0) {
347			if (kread(ifnetaddr, (char *)&ifnet, sizeof ifnet))
348				return;
349			memmove(name, ifnet.if_xname, IFNAMSIZ);
350			name[IFNAMSIZ - 1] = '\0';	/* sanity */
351			ifnetaddr = (u_long)ifnet.if_list.tqe_next;
352			if (interface != NULL && strcmp(name, interface) != 0)
353				continue;
354			cp = strchr(name, '\0');
355
356			if (pfunc) {
357				(*pfunc)(name);
358				continue;
359			}
360
361			if ((ifnet.if_flags & IFF_UP) == 0)
362				*cp++ = '*';
363			*cp = '\0';
364			ifaddraddr = (u_long)ifnet.if_addrlist.tqh_first;
365		}
366		if (vflag)
367			n = strlen(name) < 5 ? 5 : strlen(name);
368		else
369			n = 5;
370		printf("%-*.*s %-5llu ", n, n, name,
371		    (unsigned long long)ifnet.if_mtu);
372		if (ifaddraddr == 0) {
373			printf("%-13.13s ", "none");
374			printf("%-17.17s ", "none");
375		} else {
376			struct sockaddr *sa;
377
378			if (kread(ifaddraddr, (char *)&ifaddr, sizeof ifaddr)) {
379				ifaddraddr = 0;
380				continue;
381			}
382#define CP(x) ((char *)(x))
383			cp = (CP(ifaddr.ifa.ifa_addr) - CP(ifaddraddr)) +
384			    CP(&ifaddr);
385			sa = (struct sockaddr *)cp;
386			ifnet_to_ifdata_kvm(&ifnet, &ifd);
387			print_addr(ifnet.if_index, sa, (void *)&ifaddr,
388			    &ifd, &ifnet);
389		}
390		ifaddraddr = (u_long)ifaddr.ifa.ifa_list.tqe_next;
391	}
392
393}
394
395static void
396mc_print(const int ifindex, const size_t ias, const char *oid, int *mcast_oids,
397    void (*pr)(const void *))
398{
399	uint8_t *mcast_addrs, *p;
400	const size_t incr = 2 * ias + sizeof(uint32_t);
401	size_t len;
402
403	if (mcast_oids[0] == 0) {
404		size_t oidlen = 4;
405		if (sysctlnametomib(oid, mcast_oids, &oidlen) == -1) {
406			warnx("'%s' not found", oid);
407			return;
408		}
409		if (oidlen != 3) {
410			warnx("Wrong OID path for '%s'", oid);
411			return;
412		}
413	}
414
415	if (mcast_oids[3] == ifindex)
416		return;
417	mcast_oids[3] = ifindex;
418
419	mcast_addrs = asysctl(mcast_oids, 4, &len);
420	if (mcast_addrs == NULL && len != 0) {
421		warn("failed to read '%s'", oid);
422		return;
423	}
424	if (len) {
425		p = mcast_addrs;
426		while (len >= incr) {
427			(*pr)((p + ias));
428			p += incr;
429			len -= incr;
430		}
431	}
432	free(mcast_addrs);
433}
434
435#ifdef INET6
436static void
437ia6_print(const struct in6_addr *ia)
438{
439	struct sockaddr_in6 as6;
440	char hbuf[NI_MAXHOST];		/* for getnameinfo() */
441	int n;
442
443	memset(&as6, 0, sizeof(as6));
444	as6.sin6_len = sizeof(struct sockaddr_in6);
445	as6.sin6_family = AF_INET6;
446	as6.sin6_addr = *ia;
447	inet6_getscopeid(&as6, INET6_IS_ADDR_MC_LINKLOCAL);
448	if (getnameinfo((struct sockaddr *)&as6, as6.sin6_len, hbuf,
449	    sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
450		strlcpy(hbuf, "??", sizeof(hbuf));
451	}
452	if (vflag)
453		n = strlen(hbuf) < 17 ? 17 : strlen(hbuf);
454	else
455		n = 17;
456	printf("\n%25s %-*.*s ", "", n, n, hbuf);
457}
458
459static void
460mc6_print(const int ifindex)
461{
462	static int mcast_oids[4];
463
464	mc_print(ifindex, sizeof(struct in6_addr), "net.inet6.multicast",
465	    mcast_oids, (void (*)(const void *))ia6_print);
466}
467#endif
468
469static void
470ia4_print(const struct in_addr *ia)
471{
472	printf("\n%25s %-17.17s ", "", routename4(ia->s_addr, nflag));
473}
474
475static void
476mc4_print(const int ifindex)
477{
478	static int mcast_oids[4];
479
480	mc_print(ifindex, sizeof(struct in_addr), "net.inet.multicast",
481	    mcast_oids, (void (*)(const void *))ia4_print);
482}
483
484static void
485print_addr(const int ifindex, struct sockaddr *sa,
486    struct sockaddr **rtinfo, struct if_data *ifd, struct ifnet *ifnet)
487{
488	char hexsep = '.';		/* for hexprint */
489	static const char hexfmt[] = "%02x%c";	/* for hexprint */
490	char hbuf[NI_MAXHOST];		/* for getnameinfo() */
491#ifdef INET6
492	const int niflag = NI_NUMERICHOST;
493	struct sockaddr_in6 *sin6, *netmask6;
494#endif
495	struct sockaddr_in netmask;
496	struct sockaddr_in *sin;
497	char *cp;
498	int n, m;
499
500	switch (sa->sa_family) {
501	case AF_UNSPEC:
502		printf("%-13.13s ", "none");
503		printf("%-17.17s ", "none");
504		break;
505	case AF_INET:
506		sin = (struct sockaddr_in *)sa;
507		if (use_sysctl) {
508			netmask = *((struct sockaddr_in *)rtinfo[RTAX_NETMASK]);
509		} else {
510			struct in_ifaddr *ifaddr_in = (void *)rtinfo;
511			netmask.sin_addr.s_addr = ifaddr_in->ia_subnetmask;
512		}
513		cp = netname4(sin, &netmask, nflag);
514		if (vflag)
515			n = strlen(cp) < 13 ? 13 : strlen(cp);
516		else
517			n = 13;
518		printf("%-*.*s ", n, n, cp);
519		cp = routename4(sin->sin_addr.s_addr, nflag);
520		if (vflag)
521			n = strlen(cp) < 17 ? 17 : strlen(cp);
522		else
523			n = 17;
524		printf("%-*.*s ", n, n, cp);
525
526		if (!aflag)
527			break;
528		if (ifnet) {
529			u_long multiaddr;
530			struct in_multi inm;
531			union ifaddr_u *ifaddr = (union ifaddr_u *)rtinfo;
532
533			multiaddr = (u_long)ifaddr->in.ia_multiaddrs.lh_first;
534			while (multiaddr != 0) {
535				kread(multiaddr, (char *)&inm, sizeof inm);
536				ia4_print(&inm.inm_addr);
537				multiaddr = (u_long)inm.inm_list.le_next;
538			}
539		} else {
540			mc4_print(ifindex);
541		}
542		break;
543#ifdef INET6
544	case AF_INET6:
545		sin6 = (struct sockaddr_in6 *)sa;
546		inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL);
547#ifdef __KAME__
548		if (!vflag)
549			sin6->sin6_scope_id = 0;
550#endif
551
552		if (use_sysctl) {
553			netmask6 = (struct sockaddr_in6 *)rtinfo[RTAX_NETMASK];
554		} else {
555			struct in6_ifaddr *ifaddr_in6 = (void *)rtinfo;
556			netmask6 = &ifaddr_in6->ia_prefixmask;
557		}
558
559		cp = netname6(sin6, netmask6, nflag);
560		if (vflag)
561			n = strlen(cp) < 13 ? 13 : strlen(cp);
562		else
563			n = 13;
564		printf("%-*.*s ", n, n, cp);
565		if (getnameinfo((struct sockaddr *)sin6,
566				sin6->sin6_len,
567				hbuf, sizeof(hbuf), NULL, 0,
568				niflag) != 0) {
569			strlcpy(hbuf, "?", sizeof(hbuf));
570		}
571		cp = hbuf;
572		if (vflag)
573			n = strlen(cp) < 17 ? 17 : strlen(cp);
574		else
575			n = 17;
576		printf("%-*.*s ", n, n, cp);
577
578		if (!aflag)
579			break;
580		if (ifnet) {
581			u_long multiaddr;
582			struct in6_multi inm;
583			union ifaddr_u *ifaddr = (union ifaddr_u *)rtinfo;
584
585			multiaddr = (u_long)ifaddr->in6._ia6_multiaddrs.lh_first;
586			while (multiaddr != 0) {
587				kread(multiaddr, (char *)&inm, sizeof inm);
588				ia6_print(&inm.in6m_addr);
589				multiaddr = (u_long)inm.in6m_entry.le_next;
590			}
591		} else {
592			mc6_print(ifindex);
593		}
594		break;
595#endif /*INET6*/
596#ifndef SMALL
597	case AF_APPLETALK:
598		printf("atalk:%-7.7s ",
599		       atalk_print(sa,0x10));
600		printf("%-17.17s ", atalk_print(sa,0x0b));
601		break;
602#endif
603	case AF_LINK:
604		printf("%-13.13s ", "<Link>");
605		if (getnameinfo(sa, sa->sa_len,
606		    hbuf, sizeof(hbuf), NULL, 0,
607		    NI_NUMERICHOST) != 0) {
608			strlcpy(hbuf, "?", sizeof(hbuf));
609		}
610		cp = hbuf;
611		if (vflag)
612			n = strlen(cp) < 17 ? 17 : strlen(cp);
613		else
614			n = 17;
615		printf("%-*.*s ", n, n, cp);
616		break;
617
618	default:
619		m = printf("(%d)", sa->sa_family);
620		for (cp = sa->sa_len + (char *)sa;
621			--cp > sa->sa_data && (*cp == 0);) {}
622		n = cp - sa->sa_data + 1;
623		cp = sa->sa_data;
624
625		while (--n >= 0)
626			m += printf(hexfmt, *cp++ & 0xff,
627				    n > 0 ? hexsep : ' ');
628		m = 32 - m;
629		while (m-- > 0)
630			putchar(' ');
631		break;
632	}
633
634	if (bflag) {
635		char humbuf[HUMBUF_SIZE];
636
637		if (hflag && humanize_number(humbuf, sizeof(humbuf),
638		    ifd->ifi_ibytes, "", HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
639			printf("%10s ", humbuf);
640		else
641			printf("%10llu ", (unsigned long long)ifd->ifi_ibytes);
642
643		if (hflag && humanize_number(humbuf, sizeof(humbuf),
644		    ifd->ifi_obytes, "", HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
645			printf("%10s", humbuf);
646		else
647			printf("%10llu", (unsigned long long)ifd->ifi_obytes);
648	} else {
649		printf("%8llu %5llu",
650			(unsigned long long)ifd->ifi_ipackets,
651			(unsigned long long)ifd->ifi_ierrors);
652		if (dflag)
653			printf(" %6" PRIu64, ifd->ifi_iqdrops);
654		printf(" %8llu %5llu %5llu",
655			(unsigned long long)ifd->ifi_opackets,
656			(unsigned long long)ifd->ifi_oerrors,
657			(unsigned long long)ifd->ifi_collisions);
658	}
659	if (dflag)
660		printf(" %6lld", ifnet ?
661		    (unsigned long long)ifnet->if_snd.ifq_drops : 0);
662	if (tflag)
663		printf(" %4d", ifnet ? ifnet->if_timer : 0);
664	putchar('\n');
665}
666
667static void
668iftot_banner(struct iftot *ift)
669{
670	if (bflag)
671		printf("%7.7s in %8.8s %6.6s out %5.5s",
672		    ift->ift_name, " ",
673		    ift->ift_name, " ");
674	else
675		printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s",
676		    ift->ift_name, " ",
677		    ift->ift_name, " ", " ");
678	if (dflag)
679		printf(" %5.5s", " ");
680
681	if (bflag)
682		printf("  %7.7s in %8.8s %6.6s out %5.5s",
683		    "total", " ", "total", " ");
684	else
685		printf("  %5.5s in %5.5s%5.5s out %5.5s %5.5s",
686		    "total", " ", "total", " ", " ");
687	if (dflag)
688		printf(" %5.5s", " ");
689	putchar('\n');
690	if (bflag)
691		printf("%10.10s %8.8s %10.10s %5.5s",
692		    "bytes", " ", "bytes", " ");
693	else
694		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
695		    "packets", "errs", "packets", "errs", "colls");
696	if (dflag)
697		printf(" %5.5s", "drops");
698
699	if (bflag)
700		printf("  %10.10s %8.8s %10.10s %5.5s",
701		    "bytes", " ", "bytes", " ");
702	else
703		printf("  %8.8s %5.5s %8.8s %5.5s %5.5s",
704		    "packets", "errs", "packets", "errs", "colls");
705	if (dflag)
706		printf(" %5.5s", "drops");
707	putchar('\n');
708	fflush(stdout);
709}
710
711static void
712iftot_print(struct iftot *cur, struct iftot *old)
713{
714	if (bflag)
715		printf("%10" PRIu64 " %8.8s %10" PRIu64 " %5.5s",
716		    cur->ift_ib - old->ift_ib, " ",
717		    cur->ift_ob - old->ift_ob, " ");
718	else
719		printf("%8" PRIu64 " %5" PRIu64 " %8" PRIu64 " %5" PRIu64 " %5" PRIu64,
720		    cur->ift_ip - old->ift_ip,
721		    cur->ift_ie - old->ift_ie,
722		    cur->ift_op - old->ift_op,
723		    cur->ift_oe - old->ift_oe,
724		    cur->ift_co - old->ift_co);
725	if (dflag)
726		printf(" %5" PRIu64, cur->ift_dr - old->ift_dr);
727}
728
729static void
730iftot_print_sum(struct iftot *cur, struct iftot *old)
731{
732	if (bflag)
733		printf("  %10" PRIu64 " %8.8s %10" PRIu64 " %5.5s",
734		    cur->ift_ib - old->ift_ib, " ",
735		    cur->ift_ob - old->ift_ob, " ");
736	else
737		printf("  %8" PRIu64 " %5" PRIu64 " %8" PRIu64 " %5" PRIu64 " %5" PRIu64,
738		    cur->ift_ip - old->ift_ip,
739		    cur->ift_ie - old->ift_ie,
740		    cur->ift_op - old->ift_op,
741		    cur->ift_oe - old->ift_oe,
742		    cur->ift_co - old->ift_co);
743
744	if (dflag)
745		printf(" %5" PRIu64, cur->ift_dr - old->ift_dr);
746}
747
748__dead static void
749sidewaysintpr_sysctl(unsigned interval)
750{
751	struct itimerval it;
752	sigset_t emptyset;
753	sigset_t noalrm;
754	unsigned line;
755
756	set_lines();
757
758	fetchifs();
759	if (ip_cur.ift_name[0] == '\0') {
760		fprintf(stderr, "%s: %s: unknown interface\n",
761		    getprogname(), interface);
762		exit(1);
763	}
764
765	sigemptyset(&emptyset);
766	sigemptyset(&noalrm);
767	sigaddset(&noalrm, SIGALRM);
768	sigprocmask(SIG_SETMASK, &noalrm, NULL);
769
770	signalled = 0;
771	(void)signal(SIGALRM, catchalarm);
772
773	it.it_interval.tv_sec = it.it_value.tv_sec = interval;
774	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
775	setitimer(ITIMER_REAL, &it, NULL);
776
777banner:
778	iftot_banner(&ip_cur);
779
780	line = 0;
781	bzero(&ip_old, sizeof(ip_old));
782	bzero(&sum_old, sizeof(sum_old));
783loop:
784	bzero(&sum_cur, sizeof(sum_cur));
785
786	fetchifs();
787
788	iftot_print(&ip_cur, &ip_old);
789
790	ip_old = ip_cur;
791
792	iftot_print_sum(&sum_cur, &sum_old);
793
794	sum_old = sum_cur;
795
796	putchar('\n');
797	fflush(stdout);
798	line++;
799	if (signalled == 0) {
800		sigsuspend(&emptyset);
801	}
802	signalled = 0;
803	if (line == redraw_lines)
804		goto banner;
805	goto loop;
806	/*NOTREACHED*/
807}
808
809static void
810sidewaysintpr_kvm(unsigned interval, u_long off)
811{
812	struct itimerval it;
813	sigset_t emptyset;
814	sigset_t noalrm;
815	struct ifnet ifnet;
816	struct if_data ifd;
817	u_long firstifnet;
818	struct iftot *ip, *total;
819	unsigned line;
820	struct iftot *lastif, *sum, *interesting;
821	struct ifnet_head ifhead;	/* TAILQ_HEAD */
822
823	set_lines();
824
825	/*
826	 * Find the pointer to the first ifnet structure.  Replace
827	 * the pointer to the TAILQ_HEAD with the actual pointer
828	 * to the first list element.
829	 */
830	if (kread(off, (char *)&ifhead, sizeof ifhead))
831		return;
832	firstifnet = (u_long)ifhead.tqh_first;
833
834	lastif = iftot;
835	sum = iftot + MAXIF - 1;
836	total = sum - 1;
837	interesting = (interface == NULL) ? iftot : NULL;
838	for (off = firstifnet, ip = iftot; off;) {
839		if (kread(off, (char *)&ifnet, sizeof ifnet))
840			break;
841		memset(ip->ift_name, 0, sizeof(ip->ift_name));
842		snprintf(ip->ift_name, IFNAMSIZ, "%s", ifnet.if_xname);
843		if (interface && strcmp(ifnet.if_xname, interface) == 0)
844			interesting = ip;
845		ip++;
846		if (ip >= iftot + MAXIF - 2)
847			break;
848		off = (u_long)ifnet.if_list.tqe_next;
849	}
850	if (interesting == NULL) {
851		fprintf(stderr, "%s: %s: unknown interface\n",
852		    getprogname(), interface);
853		exit(1);
854	}
855	lastif = ip;
856
857	sigemptyset(&emptyset);
858	sigemptyset(&noalrm);
859	sigaddset(&noalrm, SIGALRM);
860	sigprocmask(SIG_SETMASK, &noalrm, NULL);
861
862	signalled = 0;
863	(void)signal(SIGALRM, catchalarm);
864
865	it.it_interval.tv_sec = it.it_value.tv_sec = interval;
866	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
867	setitimer(ITIMER_REAL, &it, NULL);
868
869banner:
870	if (bflag)
871		printf("%7.7s in %8.8s %6.6s out %5.5s",
872		    interesting->ift_name, " ",
873		    interesting->ift_name, " ");
874	else
875		printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s",
876		    interesting->ift_name, " ",
877		    interesting->ift_name, " ", " ");
878	if (dflag)
879		printf(" %5.5s", " ");
880	if (lastif - iftot > 0) {
881		if (bflag)
882			printf("  %7.7s in %8.8s %6.6s out %5.5s",
883			    "total", " ", "total", " ");
884		else
885			printf("  %5.5s in %5.5s%5.5s out %5.5s %5.5s",
886			    "total", " ", "total", " ", " ");
887		if (dflag)
888			printf(" %5.5s", " ");
889	}
890	for (ip = iftot; ip < iftot + MAXIF; ip++) {
891		ip->ift_ip = 0;
892		ip->ift_ib = 0;
893		ip->ift_ie = 0;
894		ip->ift_op = 0;
895		ip->ift_ob = 0;
896		ip->ift_oe = 0;
897		ip->ift_co = 0;
898		ip->ift_dr = 0;
899	}
900	putchar('\n');
901	if (bflag)
902		printf("%10.10s %8.8s %10.10s %5.5s",
903		    "bytes", " ", "bytes", " ");
904	else
905		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
906		    "packets", "errs", "packets", "errs", "colls");
907	if (dflag)
908		printf(" %5.5s", "drops");
909	if (lastif - iftot > 0) {
910		if (bflag)
911			printf("  %10.10s %8.8s %10.10s %5.5s",
912			    "bytes", " ", "bytes", " ");
913		else
914			printf("  %8.8s %5.5s %8.8s %5.5s %5.5s",
915			    "packets", "errs", "packets", "errs", "colls");
916		if (dflag)
917			printf(" %5.5s", "drops");
918	}
919	putchar('\n');
920	fflush(stdout);
921	line = 0;
922loop:
923	sum->ift_ip = 0;
924	sum->ift_ib = 0;
925	sum->ift_ie = 0;
926	sum->ift_op = 0;
927	sum->ift_ob = 0;
928	sum->ift_oe = 0;
929	sum->ift_co = 0;
930	sum->ift_dr = 0;
931	for (off = firstifnet, ip = iftot; off && ip < lastif; ip++) {
932		if (kread(off, (char *)&ifnet, sizeof ifnet)) {
933			off = 0;
934			continue;
935		}
936		ifnet_to_ifdata_kvm(&ifnet, &ifd);
937		if (ip == interesting) {
938			if (bflag) {
939				char humbuf[HUMBUF_SIZE];
940
941				if (hflag && humanize_number(humbuf,
942				    sizeof(humbuf),
943				    ifd.ifi_ibytes - ip->ift_ib, "",
944				    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
945					printf("%10s %8.8s ", humbuf, " ");
946				else
947					printf("%10llu %8.8s ",
948					    (unsigned long long)
949					    (ifd.ifi_ibytes-ip->ift_ib), " ");
950
951				if (hflag && humanize_number(humbuf,
952				    sizeof(humbuf),
953				    ifd.ifi_obytes - ip->ift_ob, "",
954				    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
955					printf("%10s %5.5s", humbuf, " ");
956				else
957					printf("%10llu %5.5s",
958					    (unsigned long long)
959					    (ifd.ifi_obytes-ip->ift_ob), " ");
960			} else {
961				printf("%8llu %5llu %8llu %5llu %5llu",
962				    (unsigned long long)
963					(ifd.ifi_ipackets - ip->ift_ip),
964				    (unsigned long long)
965					(ifd.ifi_ierrors - ip->ift_ie),
966				    (unsigned long long)
967					(ifd.ifi_opackets - ip->ift_op),
968				    (unsigned long long)
969					(ifd.ifi_oerrors - ip->ift_oe),
970				    (unsigned long long)
971					(ifd.ifi_collisions - ip->ift_co));
972			}
973			if (dflag)
974				printf(" %5" PRIu64,
975					ifnet.if_snd.ifq_drops - ip->ift_dr);
976		}
977		ip->ift_ip = ifd.ifi_ipackets;
978		ip->ift_ib = ifd.ifi_ibytes;
979		ip->ift_ie = ifd.ifi_ierrors;
980		ip->ift_op = ifd.ifi_opackets;
981		ip->ift_ob = ifd.ifi_obytes;
982		ip->ift_oe = ifd.ifi_oerrors;
983		ip->ift_co = ifd.ifi_collisions;
984		ip->ift_dr = ifnet.if_snd.ifq_drops;
985		sum->ift_ip += ip->ift_ip;
986		sum->ift_ib += ip->ift_ib;
987		sum->ift_ie += ip->ift_ie;
988		sum->ift_op += ip->ift_op;
989		sum->ift_ob += ip->ift_ob;
990		sum->ift_oe += ip->ift_oe;
991		sum->ift_co += ip->ift_co;
992		sum->ift_dr += ip->ift_dr;
993		off = (u_long)ifnet.if_list.tqe_next;
994	}
995	if (lastif - iftot > 0) {
996		if (bflag) {
997			char humbuf[HUMBUF_SIZE];
998
999			if (hflag && humanize_number(humbuf,
1000			    sizeof(humbuf), sum->ift_ib - total->ift_ib, "",
1001			    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
1002				printf("  %10s %8.8s ", humbuf, " ");
1003			else
1004				printf("  %10llu %8.8s ",
1005				    (unsigned long long)
1006				    (sum->ift_ib - total->ift_ib), " ");
1007
1008			if (hflag && humanize_number(humbuf,
1009			    sizeof(humbuf), sum->ift_ob -  total->ift_ob, "",
1010			    HN_AUTOSCALE, HN_NOSPACE | HN_B) > 0)
1011				printf("%10s %5.5s", humbuf, " ");
1012			else
1013				printf("%10llu %5.5s",
1014				    (unsigned long long)
1015				    (sum->ift_ob - total->ift_ob), " ");
1016		} else {
1017			printf("  %8llu %5llu %8llu %5llu %5llu",
1018			    (unsigned long long)
1019				(sum->ift_ip - total->ift_ip),
1020			    (unsigned long long)
1021				(sum->ift_ie - total->ift_ie),
1022			    (unsigned long long)
1023				(sum->ift_op - total->ift_op),
1024			    (unsigned long long)
1025				(sum->ift_oe - total->ift_oe),
1026			    (unsigned long long)
1027				(sum->ift_co - total->ift_co));
1028		}
1029		if (dflag)
1030			printf(" %5llu",
1031			    (unsigned long long)(sum->ift_dr - total->ift_dr));
1032	}
1033	*total = *sum;
1034	putchar('\n');
1035	fflush(stdout);
1036	line++;
1037	if (signalled == 0) {
1038		sigsuspend(&emptyset);
1039	}
1040	signalled = 0;
1041	if (line == redraw_lines)
1042		goto banner;
1043	goto loop;
1044	/*NOTREACHED*/
1045}
1046
1047/*
1048 * Print a running summary of interface statistics.
1049 * Repeat display every interval seconds, showing statistics
1050 * collected over that interval.  Assumes that interval is non-zero.
1051 * First line printed at top of screen is always cumulative.
1052 */
1053static void
1054sidewaysintpr(unsigned int interval, u_long off)
1055{
1056
1057	if (use_sysctl) {
1058		sidewaysintpr_sysctl(interval);
1059	} else {
1060		sidewaysintpr_kvm(interval, off);
1061	}
1062}
1063
1064/*
1065 * Called if an interval expires before sidewaysintpr has completed a loop.
1066 * Sets a flag to not wait for the alarm.
1067 */
1068static void
1069catchalarm(int signo)
1070{
1071
1072	signalled = true;
1073}
1074
1075static void
1076get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
1077{
1078	int i;
1079
1080	for (i = 0; i < RTAX_MAX; i++) {
1081		if (addrs & (1 << i)) {
1082			rti_info[i] = sa;
1083			sa = (struct sockaddr *)((char *)(sa) +
1084			    RT_ROUNDUP(sa->sa_len));
1085		} else
1086			rti_info[i] = NULL;
1087	}
1088}
1089
1090static void
1091fetchifs(void)
1092{
1093	struct if_msghdr *ifm;
1094	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
1095	struct rt_msghdr *rtm;
1096	struct if_data *ifd = NULL;
1097	struct sockaddr *sa, *rti_info[RTAX_MAX];
1098	struct sockaddr_dl *sdl;
1099	static char *buf = NULL;
1100	static size_t olen;
1101	char *next, *lim;
1102	char name[IFNAMSIZ];
1103	size_t len;
1104
1105	if (prog_sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
1106		err(1, "sysctl");
1107	if (len > olen) {
1108		free(buf);
1109		if ((buf = malloc(len)) == NULL)
1110			err(1, NULL);
1111		olen = len;
1112	}
1113	if (prog_sysctl(mib, 6, buf, &len, NULL, 0) == -1)
1114		err(1, "sysctl");
1115
1116	lim = buf + len;
1117	for (next = buf; next < lim; next += rtm->rtm_msglen) {
1118		rtm = (struct rt_msghdr *)next;
1119		if (rtm->rtm_version != RTM_VERSION)
1120			continue;
1121		switch (rtm->rtm_type) {
1122		case RTM_IFINFO:
1123			ifm = (struct if_msghdr *)next;
1124			ifd = &ifm->ifm_data;
1125
1126			sa = (struct sockaddr *)(ifm + 1);
1127			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
1128
1129			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
1130			if (sdl == NULL || sdl->sdl_family != AF_LINK)
1131				continue;
1132			bzero(name, sizeof(name));
1133			if (sdl->sdl_nlen >= IFNAMSIZ)
1134				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
1135			else if (sdl->sdl_nlen > 0)
1136				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
1137
1138			if (interface != NULL && !strcmp(name, interface)) {
1139				strlcpy(ip_cur.ift_name, name,
1140				    sizeof(ip_cur.ift_name));
1141				ip_cur.ift_ip = ifd->ifi_ipackets;
1142				ip_cur.ift_ib = ifd->ifi_ibytes;
1143				ip_cur.ift_ie = ifd->ifi_ierrors;
1144				ip_cur.ift_op = ifd->ifi_opackets;
1145				ip_cur.ift_ob = ifd->ifi_obytes;
1146				ip_cur.ift_oe = ifd->ifi_oerrors;
1147				ip_cur.ift_co = ifd->ifi_collisions;
1148				ip_cur.ift_dr = ifd->ifi_iqdrops;
1149			}
1150
1151			sum_cur.ift_ip += ifd->ifi_ipackets;
1152			sum_cur.ift_ib += ifd->ifi_ibytes;
1153			sum_cur.ift_ie += ifd->ifi_ierrors;
1154			sum_cur.ift_op += ifd->ifi_opackets;
1155			sum_cur.ift_ob += ifd->ifi_obytes;
1156			sum_cur.ift_oe += ifd->ifi_oerrors;
1157			sum_cur.ift_co += ifd->ifi_collisions;
1158			sum_cur.ift_dr += ifd->ifi_iqdrops;
1159			break;
1160		}
1161	}
1162	if (interface == NULL) {
1163		strlcpy(ip_cur.ift_name, name,
1164		    sizeof(ip_cur.ift_name));
1165		ip_cur.ift_ip = ifd->ifi_ipackets;
1166		ip_cur.ift_ib = ifd->ifi_ibytes;
1167		ip_cur.ift_ie = ifd->ifi_ierrors;
1168		ip_cur.ift_op = ifd->ifi_opackets;
1169		ip_cur.ift_ob = ifd->ifi_obytes;
1170		ip_cur.ift_oe = ifd->ifi_oerrors;
1171		ip_cur.ift_co = ifd->ifi_collisions;
1172		ip_cur.ift_dr = ifd->ifi_iqdrops;
1173	}
1174}
1175