xref: /openbsd-src/usr.bin/netstat/if.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /*	$OpenBSD: if.c,v 1.61 2009/08/04 03:45:47 tedu Exp $	*/
2 /*	$NetBSD: if.c,v 1.16.4.2 1996/06/07 21:46:46 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/protosw.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
38 
39 #include <net/if.h>
40 #include <net/if_dl.h>
41 #include <net/if_types.h>
42 #include <net/route.h>
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 #include <netinet/if_ether.h>
46 #include <arpa/inet.h>
47 
48 #include <err.h>
49 #include <limits.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 
56 #include "netstat.h"
57 
58 static void print_addr(struct sockaddr *, struct sockaddr **, struct if_data *);
59 static void sidewaysintpr(u_int);
60 static void catchalarm(int);
61 static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
62 static void fetchifs(void);
63 
64 /*
65  * Print a description of the network interfaces.
66  * NOTE: ifnetaddr is the location of the kernel global "ifnet",
67  * which is a TAILQ_HEAD.
68  */
69 void
70 intpr(int interval)
71 {
72 	struct if_msghdr ifm;
73 	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
74 	char name[IFNAMSIZ + 1];	/* + 1 for the '*' */
75 	char *buf, *next, *lim, *cp;
76 	struct rt_msghdr *rtm;
77 	struct ifa_msghdr *ifam;
78 	struct if_data *ifd;
79 	struct sockaddr *sa, *rti_info[RTAX_MAX];
80 	struct sockaddr_dl *sdl;
81 	u_int64_t total = 0;
82 	size_t len;
83 
84 	if (interval) {
85 		sidewaysintpr((unsigned)interval);
86 		return;
87 	}
88 
89 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
90 		err(1, "sysctl");
91 	if ((buf = malloc(len)) == NULL)
92 		err(1, NULL);
93 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1)
94 		err(1, "sysctl");
95 
96 	printf("%-7.7s %-5.5s %-11.11s %-17.17s ",
97 	    "Name", "Mtu", "Network", "Address");
98 	if (bflag)
99 		printf("%10.10s %10.10s", "Ibytes", "Obytes");
100 	else
101 		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
102 		    "Ipkts", "Ierrs", "Opkts", "Oerrs", "Colls");
103 	if (tflag)
104 		printf(" %s", "Time");
105 	if (dflag)
106 		printf(" %s", "Drop");
107 	putchar('\n');
108 
109 	lim = buf + len;
110 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
111 		rtm = (struct rt_msghdr *)next;
112 		if (rtm->rtm_version != RTM_VERSION)
113 			continue;
114 		switch (rtm->rtm_type) {
115 		case RTM_IFINFO:
116 			total = 0;
117 			bcopy(next, &ifm, sizeof ifm);
118 			ifd = &ifm.ifm_data;
119 
120 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
121 			get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
122 
123 			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
124 			if (sdl == NULL || sdl->sdl_family != AF_LINK)
125 				continue;
126 			bzero(name, sizeof(name));
127 			if (sdl->sdl_nlen >= IFNAMSIZ)
128 				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
129 			else if (sdl->sdl_nlen > 0)
130 				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
131 
132 			if (interface != 0 && strcmp(name, interface) != 0)
133 				continue;
134 
135 			/* mark inactive interfaces with a '*' */
136 			cp = strchr(name, '\0');
137 			if ((ifm.ifm_flags & IFF_UP) == 0)
138 				*cp++ = '*';
139 			*cp = '\0';
140 
141 			if (qflag) {
142 				total = ifd->ifi_ibytes + ifd->ifi_obytes +
143 				    ifd->ifi_ipackets + ifd->ifi_ierrors +
144 				    ifd->ifi_opackets + ifd->ifi_oerrors +
145 				    ifd->ifi_collisions;
146 				if (tflag)
147 					total += 0; // XXX ifnet.if_timer;
148 				if (dflag)
149 					total += 0; // XXX ifnet.if_snd.ifq_drops;
150 				if (total == 0)
151 					continue;
152 			}
153 
154 			printf("%-7s %-5d ", name, ifd->ifi_mtu);
155 			print_addr(rti_info[RTAX_IFP], rti_info, ifd);
156 			break;
157 		case RTM_NEWADDR:
158 			if (qflag && total == 0)
159 				continue;
160 			if (interface != 0 && strcmp(name, interface) != 0)
161 				continue;
162 
163 			ifam = (struct ifa_msghdr *)next;
164 			if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
165 			    RTA_BRD)) == 0)
166 				break;
167 
168 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
169 			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
170 
171 			printf("%-7s %-5d ", name, ifd->ifi_mtu);
172 			print_addr(rti_info[RTAX_IFA], rti_info, ifd);
173 			break;
174 		}
175 	}
176 	free(buf);
177 }
178 
179 static void
180 print_addr(struct sockaddr *sa, struct sockaddr **rtinfo, struct if_data *ifd)
181 {
182 	struct sockaddr_dl *sdl;
183 	struct sockaddr_in *sin;
184 	struct sockaddr_in6 *sin6;
185 	char *cp;
186 	int m, n;
187 
188 	switch (sa->sa_family) {
189 	case AF_UNSPEC:
190 		printf("%-11.11s ", "none");
191 		printf("%-17.17s ", "none");
192 		break;
193 	case AF_INET:
194 		sin = (struct sockaddr_in *)sa;
195 		cp = netname4(sin->sin_addr.s_addr,
196 		    ((struct sockaddr_in *)rtinfo[RTAX_NETMASK])->sin_addr.s_addr);
197 		if (vflag)
198 			n = strlen(cp) < 11 ? 11 : strlen(cp);
199 		else
200 			n = 11;
201 		printf("%-*.*s ", n, n, cp);
202 		cp = routename4(sin->sin_addr.s_addr);
203 		if (vflag)
204 			n = strlen(cp) < 17 ? 17 : strlen(cp);
205 		else
206 			n = 17;
207 		printf("%-*.*s ", n, n, cp);
208 
209 #if 0
210 		if (aflag) {
211 			u_long multiaddr;
212 			struct in_multi inm;
213 
214 			multiaddr = (u_long)LIST_FIRST(&ifaddr.in.ia_multiaddrs);
215 			while (multiaddr != 0) {
216 				kread(multiaddr, &inm, sizeof inm);
217 				printf("\n%25s %-17.17s ", "",
218 				    routename4(inm.inm_addr.s_addr));
219 				multiaddr = (u_long)LIST_NEXT(&inm, inm_list);
220 			}
221 		}
222 #endif
223 		break;
224 	case AF_INET6:
225 		sin6 = (struct sockaddr_in6 *)sa;
226 #ifdef __KAME__
227 		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
228 			sin6->sin6_scope_id =
229 			    ntohs(*(u_int16_t *)
230 			    &sin6->sin6_addr.s6_addr[2]);
231 			sin6->sin6_addr.s6_addr[2] = 0;
232 			sin6->sin6_addr.s6_addr[3] = 0;
233 		}
234 #endif
235 		cp = netname6(sin6,
236 		    (struct sockaddr_in6 *)rtinfo[RTAX_NETMASK]);
237 		if (vflag)
238 			n = strlen(cp) < 11 ? 11 : strlen(cp);
239 		else
240 			n = 11;
241 		printf("%-*.*s ", n, n, cp);
242 		cp = routename6(sin6);
243 		if (vflag)
244 			n = strlen(cp) < 17 ? 17 : strlen(cp);
245 		else
246 			n = 17;
247 		printf("%-*.*s ", n, n, cp);
248 #if 0
249 		if (aflag) {
250 			u_long multiaddr;
251 			struct in6_multi inm;
252 			struct sockaddr_in6 m6;
253 
254 			multiaddr = (u_long)LIST_FIRST(&ifaddr.in6.ia6_multiaddrs);
255 			while (multiaddr != 0) {
256 				kread(multiaddr, &inm, sizeof inm);
257 				memset(&m6, 0, sizeof(m6));
258 				m6.sin6_len = sizeof(struct sockaddr_in6);
259 				m6.sin6_family = AF_INET6;
260 				m6.sin6_addr = inm.in6m_addr;
261 #ifdef __KAME__
262 				if (IN6_IS_ADDR_MC_LINKLOCAL(&m6.sin6_addr) ||
263 				    IN6_IS_ADDR_MC_INTFACELOCAL(&m6.sin6_addr)) {
264 					m6.sin6_scope_id =
265 					    ntohs(*(u_int16_t *)
266 					    &m6.sin6_addr.s6_addr[2]);
267 					m6.sin6_addr.s6_addr[2] = 0;
268 					m6.sin6_addr.s6_addr[3] = 0;
269 				}
270 #endif
271 				cp = routename6(&m6);
272 				if (vflag)
273 					n = strlen(cp) < 17 ? 17 : strlen(cp);
274 				else
275 					n = 17;
276 				printf("\n%25s %-*.*s ", "",
277 				    n, n, cp);
278 				multiaddr = (u_long)LIST_NEXT(&inm, in6m_entry);
279 			}
280 		}
281 #endif
282 		break;
283 	case AF_APPLETALK:
284 		printf("atlk:%-12s",atalk_print(sa,0x10) );
285 		printf("%-12s ",atalk_print(sa,0x0b) );
286 		break;
287 	case AF_LINK:
288 		sdl = (struct sockaddr_dl *)sa;
289 		m = printf("%-11.11s ", "<Link>");
290 		if (sdl->sdl_type == IFT_ETHER ||
291 		    sdl->sdl_type == IFT_CARP ||
292 		    sdl->sdl_type == IFT_FDDI ||
293 		    sdl->sdl_type == IFT_ISO88025)
294 			printf("%-17.17s ",
295 			    ether_ntoa((struct ether_addr *)LLADDR(sdl)));
296 		else {
297 			cp = (char *)LLADDR(sdl);
298 			n = sdl->sdl_alen;
299 			goto hexprint;
300 		}
301 		break;
302 	default:
303 		m = printf("(%d)", sa->sa_family);
304 		for (cp = sa->sa_len + (char *)sa;
305 			--cp > sa->sa_data && (*cp == 0);) {}
306 		n = cp - sa->sa_data + 1;
307 		cp = sa->sa_data;
308 hexprint:
309 		while (--n >= 0)
310 			m += printf("%x%c", *cp++ & 0xff,
311 				    n > 0 ? '.' : ' ');
312 		m = 30 - m;
313 		while (m-- > 0)
314 			putchar(' ');
315 		break;
316 	}
317 	if (bflag)
318 		printf("%10llu %10llu",
319 		    ifd->ifi_ibytes, ifd->ifi_obytes);
320 	else
321 		printf("%8llu %5llu %8llu %5llu %5llu",
322 		    ifd->ifi_ipackets, ifd->ifi_ierrors,
323 		    ifd->ifi_opackets, ifd->ifi_oerrors,
324 		    ifd->ifi_collisions);
325 	if (tflag)
326 		printf(" %4d", 0 /* XXX ifnet.if_timer */);
327 	if (dflag)
328 		printf(" %4d", 0 /* XXX ifnet.if_snd.ifq_drops */);
329 	putchar('\n');
330 }
331 
332 struct	iftot {
333 	char	ift_name[IFNAMSIZ];	/* interface name */
334 	u_int64_t ift_ip;		/* input packets */
335 	u_int64_t ift_ib;		/* input bytes */
336 	u_int64_t ift_ie;		/* input errors */
337 	u_int64_t ift_op;		/* output packets */
338 	u_int64_t ift_ob;		/* output bytes */
339 	u_int64_t ift_oe;		/* output errors */
340 	u_int64_t ift_co;		/* collisions */
341 	u_int64_t ift_dr;		/* drops */
342 } ip_cur, ip_old, sum_cur, sum_old;
343 
344 volatile sig_atomic_t signalled;	/* set if alarm goes off "early" */
345 
346 /*
347  * Print a running summary of interface statistics.
348  * Repeat display every interval seconds, showing statistics
349  * collected over that interval.  Assumes that interval is non-zero.
350  * First line printed at top of screen is always cumulative.
351  */
352 static void
353 sidewaysintpr(unsigned int interval)
354 {
355 	sigset_t emptyset;
356 	int line;
357 
358 	fetchifs();
359 	if (ip_cur.ift_name[0] == '\0') {
360 		fprintf(stderr, "%s: %s: unknown interface\n",
361 		    __progname, interface);
362 		exit(1);
363 	}
364 
365 	(void)signal(SIGALRM, catchalarm);
366 	signalled = 0;
367 	(void)alarm(interval);
368 banner:
369 	if (bflag)
370 		printf("%7.7s in %8.8s %6.6s out %5.5s",
371 		    ip_cur.ift_name, " ",
372 		    ip_cur.ift_name, " ");
373 	else
374 		printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s",
375 		    ip_cur.ift_name, " ",
376 		    ip_cur.ift_name, " ", " ");
377 	if (dflag)
378 		printf(" %5.5s", " ");
379 
380 	if (bflag)
381 		printf("  %7.7s in %8.8s %6.6s out %5.5s",
382 		    "total", " ", "total", " ");
383 	else
384 		printf("  %5.5s in %5.5s%5.5s out %5.5s %5.5s",
385 		    "total", " ", "total", " ", " ");
386 	if (dflag)
387 		printf(" %5.5s", " ");
388 	putchar('\n');
389 	if (bflag)
390 		printf("%10.10s %8.8s %10.10s %5.5s",
391 		    "bytes", " ", "bytes", " ");
392 	else
393 		printf("%8.8s %5.5s %8.8s %5.5s %5.5s",
394 		    "packets", "errs", "packets", "errs", "colls");
395 	if (dflag)
396 		printf(" %5.5s", "drops");
397 
398 	if (bflag)
399 		printf("  %10.10s %8.8s %10.10s %5.5s",
400 		    "bytes", " ", "bytes", " ");
401 	else
402 		printf("  %8.8s %5.5s %8.8s %5.5s %5.5s",
403 		    "packets", "errs", "packets", "errs", "colls");
404 	if (dflag)
405 		printf(" %5.5s", "drops");
406 	putchar('\n');
407 	fflush(stdout);
408 	line = 0;
409 	bzero(&ip_old, sizeof(ip_old));
410 	bzero(&sum_old, sizeof(sum_old));
411 loop:
412 	bzero(&sum_cur, sizeof(sum_cur));
413 
414 	fetchifs();
415 
416 	if (bflag)
417 		printf("%10llu %8.8s %10llu %5.5s",
418 		    ip_cur.ift_ib - ip_old.ift_ib, " ",
419 		    ip_cur.ift_ob - ip_old.ift_ob, " ");
420 	else
421 		printf("%8llu %5llu %8llu %5llu %5llu",
422 		    ip_cur.ift_ip - ip_old.ift_ip,
423 		    ip_cur.ift_ie - ip_old.ift_ie,
424 		    ip_cur.ift_op - ip_old.ift_op,
425 		    ip_cur.ift_oe - ip_old.ift_oe,
426 		    ip_cur.ift_co - ip_old.ift_co);
427 	if (dflag)
428 		printf(" %5llu",
429 		    /* XXX ifnet.if_snd.ifq_drops - ip->ift_dr); */
430 		    0LL);
431 
432 	ip_old = ip_cur;
433 
434 	if (bflag)
435 		printf("  %10llu %8.8s %10llu %5.5s",
436 		    sum_cur.ift_ib - sum_old.ift_ib, " ",
437 		    sum_cur.ift_ob - sum_old.ift_ob, " ");
438 	else
439 		printf("  %8llu %5llu %8llu %5llu %5llu",
440 		    sum_cur.ift_ip - sum_old.ift_ip,
441 		    sum_cur.ift_ie - sum_old.ift_ie,
442 		    sum_cur.ift_op - sum_old.ift_op,
443 		    sum_cur.ift_oe - sum_old.ift_oe,
444 		    sum_cur.ift_co - sum_old.ift_co);
445 	if (dflag)
446 		printf(" %5llu", sum_cur.ift_dr - sum_old.ift_dr);
447 
448 	sum_old = sum_cur;
449 
450 	putchar('\n');
451 	fflush(stdout);
452 	line++;
453 	sigemptyset(&emptyset);
454 	if (!signalled)
455 		sigsuspend(&emptyset);
456 	signalled = 0;
457 	(void)alarm(interval);
458 	if (line == 21)
459 		goto banner;
460 	goto loop;
461 	/*NOTREACHED*/
462 }
463 
464 /*
465  * Called if an interval expires before sidewaysintpr has completed a loop.
466  * Sets a flag to not wait for the alarm.
467  */
468 /* ARGSUSED */
469 static void
470 catchalarm(int signo)
471 {
472 	signalled = 1;
473 }
474 
475 static void
476 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
477 {
478 	int i;
479 
480 	for (i = 0; i < RTAX_MAX; i++) {
481 		if (addrs & (1 << i)) {
482 			rti_info[i] = sa;
483 			sa = (struct sockaddr *)((char *)(sa) +
484 			    roundup(sa->sa_len, sizeof(long)));
485 		} else
486 			rti_info[i] = NULL;
487 	}
488 }
489 
490 static void
491 fetchifs(void)
492 {
493 	struct if_msghdr ifm;
494 	int mib[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
495 	struct rt_msghdr *rtm;
496 	struct if_data *ifd;
497 	struct sockaddr *sa, *rti_info[RTAX_MAX];
498 	struct sockaddr_dl *sdl;
499 	char *buf, *next, *lim;
500 	char name[IFNAMSIZ];
501 	size_t len;
502 
503 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
504 		err(1, "sysctl");
505 	if ((buf = malloc(len)) == NULL)
506 		err(1, NULL);
507 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1)
508 		err(1, "sysctl");
509 
510 	lim = buf + len;
511 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
512 		rtm = (struct rt_msghdr *)next;
513 		if (rtm->rtm_version != RTM_VERSION)
514 			continue;
515 		switch (rtm->rtm_type) {
516 		case RTM_IFINFO:
517 			bcopy(next, &ifm, sizeof ifm);
518 			ifd = &ifm.ifm_data;
519 
520 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
521 			get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
522 
523 			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
524 			if (sdl == NULL || sdl->sdl_family != AF_LINK)
525 				continue;
526 			bzero(name, sizeof(name));
527 			if (sdl->sdl_nlen >= IFNAMSIZ)
528 				memcpy(name, sdl->sdl_data, IFNAMSIZ - 1);
529 			else if (sdl->sdl_nlen > 0)
530 				memcpy(name, sdl->sdl_data, sdl->sdl_nlen);
531 
532 			if (interface != NULL && !strcmp(name, interface)) {
533 				strlcpy(ip_cur.ift_name, name,
534 				    sizeof(ip_cur.ift_name));
535 				ip_cur.ift_ip = ifd->ifi_ipackets;
536 				ip_cur.ift_ib = ifd->ifi_ibytes;
537 				ip_cur.ift_ie = ifd->ifi_ierrors;
538 				ip_cur.ift_op = ifd->ifi_opackets;
539 				ip_cur.ift_ob = ifd->ifi_obytes;
540 				ip_cur.ift_oe = ifd->ifi_oerrors;
541 				ip_cur.ift_co = ifd->ifi_collisions;
542 				ip_cur.ift_dr = 0;
543 				    /* XXX ifnet.if_snd.ifq_drops */
544 			}
545 
546 			sum_cur.ift_ip += ifd->ifi_ipackets;
547 			sum_cur.ift_ib += ifd->ifi_ibytes;
548 			sum_cur.ift_ie += ifd->ifi_ierrors;
549 			sum_cur.ift_op += ifd->ifi_opackets;
550 			sum_cur.ift_ob += ifd->ifi_obytes;
551 			sum_cur.ift_oe += ifd->ifi_oerrors;
552 			sum_cur.ift_co += ifd->ifi_collisions;
553 			sum_cur.ift_dr += 0; /* XXX ifnet.if_snd.ifq_drops */
554 			break;
555 		}
556 	}
557 	if (interface == NULL) {
558 		strlcpy(ip_cur.ift_name, name,
559 		    sizeof(ip_cur.ift_name));
560 		ip_cur.ift_ip = ifd->ifi_ipackets;
561 		ip_cur.ift_ib = ifd->ifi_ibytes;
562 		ip_cur.ift_ie = ifd->ifi_ierrors;
563 		ip_cur.ift_op = ifd->ifi_opackets;
564 		ip_cur.ift_ob = ifd->ifi_obytes;
565 		ip_cur.ift_oe = ifd->ifi_oerrors;
566 		ip_cur.ift_co = ifd->ifi_collisions;
567 		ip_cur.ift_dr = 0;
568 		    /* XXX ifnet.if_snd.ifq_drops */
569 	}
570 	free(buf);
571 }
572