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