1 /* vi: set sw=4 ts=4: */
2 /*
3  * stolen from net-tools-1.59 and stripped down for busybox by
4  *			Erik Andersen <andersen@codepoet.org>
5  *
6  * Heavily modified by Manuel Novoa III       Mar 12, 2001
7  *
8  * Added print_bytes_scaled function to reduce code size.
9  * Added some (potentially) missing defines.
10  * Improved display support for -a and for a named interface.
11  *
12  * -----------------------------------------------------------
13  *
14  * ifconfig   This file contains an implementation of the command
15  *              that either displays or sets the characteristics of
16  *              one or more of the system's networking interfaces.
17  *
18  *
19  * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
20  *              and others.  Copyright 1993 MicroWalt Corporation
21  *
22  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
23  *
24  * Patched to support 'add' and 'del' keywords for INET(4) addresses
25  * by Mrs. Brisby <mrs.brisby@nimh.org>
26  *
27  * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
28  *                     - gettext instead of catgets for i18n
29  *          10/1998  - Andi Kleen. Use interface list primitives.
30  *          20001008 - Bernd Eckenfels, Patch from RH for setting mtu
31  *			(default AF was wrong)
32  */
33 
34 #include "libbb.h"
35 #include "inet_common.h"
36 #include <net/if.h>
37 #include <net/if_arp.h>
38 #ifdef HAVE_NET_ETHERNET_H
39 # include <net/ethernet.h>
40 #endif
41 
42 #if ENABLE_FEATURE_HWIB
43 /* #include <linux/if_infiniband.h> */
44 # undef INFINIBAND_ALEN
45 # define INFINIBAND_ALEN 20
46 #endif
47 
48 #if ENABLE_FEATURE_IPV6
49 # define HAVE_AFINET6 1
50 #else
51 # undef HAVE_AFINET6
52 #endif
53 
54 #define _PATH_PROCNET_DEV               "/proc/net/dev"
55 #define _PATH_PROCNET_IFINET6           "/proc/net/if_inet6"
56 
57 #ifdef HAVE_AFINET6
58 # ifndef _LINUX_IN6_H
59 /*
60  * This is from linux/include/net/ipv6.h
61  */
62 struct in6_ifreq {
63 	struct in6_addr ifr6_addr;
64 	uint32_t ifr6_prefixlen;
65 	unsigned int ifr6_ifindex;
66 };
67 # endif
68 #endif /* HAVE_AFINET6 */
69 
70 /* Defines for glibc2.0 users. */
71 #ifndef SIOCSIFTXQLEN
72 # define SIOCSIFTXQLEN      0x8943
73 # define SIOCGIFTXQLEN      0x8942
74 #endif
75 
76 /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
77 #ifndef ifr_qlen
78 # define ifr_qlen        ifr_ifru.ifru_mtu
79 #endif
80 
81 #ifndef HAVE_TXQUEUELEN
82 # define HAVE_TXQUEUELEN 1
83 #endif
84 
85 #ifndef IFF_DYNAMIC
86 # define IFF_DYNAMIC     0x8000 /* dialup device with changing addresses */
87 #endif
88 
89 /* Display an Internet socket address. */
INET_sprint(struct sockaddr * sap,int numeric)90 static const char* FAST_FUNC INET_sprint(struct sockaddr *sap, int numeric)
91 {
92 	if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
93 		return "[NONE SET]";
94 	return auto_string(INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00));
95 }
96 
97 #ifdef UNUSED_AND_BUGGY
INET_getsock(char * bufp,struct sockaddr * sap)98 static int INET_getsock(char *bufp, struct sockaddr *sap)
99 {
100 	char *sp = bufp, *bp;
101 	unsigned int i;
102 	unsigned val;
103 	struct sockaddr_in *sock_in;
104 
105 	sock_in = (struct sockaddr_in *) sap;
106 	sock_in->sin_family = AF_INET;
107 	sock_in->sin_port = 0;
108 
109 	val = 0;
110 	bp = (char *) &val;
111 	for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
112 		*sp = toupper(*sp);
113 
114 		if ((unsigned)(*sp - 'A') <= 5)
115 			bp[i] |= (int) (*sp - ('A' - 10));
116 		else if (isdigit(*sp))
117 			bp[i] |= (int) (*sp - '0');
118 		else
119 			return -1;
120 
121 		bp[i] <<= 4;
122 		sp++;
123 		*sp = toupper(*sp);
124 
125 		if ((unsigned)(*sp - 'A') <= 5)
126 			bp[i] |= (int) (*sp - ('A' - 10));
127 		else if (isdigit(*sp))
128 			bp[i] |= (int) (*sp - '0');
129 		else
130 			return -1;
131 
132 		sp++;
133 	}
134 	sock_in->sin_addr.s_addr = htonl(val);
135 
136 	return (sp - bufp);
137 }
138 #endif
139 
INET_input(const char * bufp,struct sockaddr * sap)140 static int FAST_FUNC INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
141 {
142 	return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
143 /*
144 	switch (type) {
145 	case 1:
146 		return (INET_getsock(bufp, sap));
147 	case 256:
148 		return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
149 	default:
150 		return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
151 	}
152 */
153 }
154 
155 static const struct aftype inet_aftype = {
156 	.name   = "inet",
157 	.title  = "DARPA Internet",
158 	.af     = AF_INET,
159 	.alen   = 4,
160 	.sprint = INET_sprint,
161 	.input  = INET_input,
162 };
163 
164 #ifdef HAVE_AFINET6
165 
166 /* Display an Internet socket address. */
167 /* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
INET6_sprint(struct sockaddr * sap,int numeric)168 static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric)
169 {
170 	if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
171 		return "[NONE SET]";
172 	return auto_string(INET6_rresolve((struct sockaddr_in6 *) sap, numeric));
173 }
174 
175 #ifdef UNUSED
INET6_getsock(char * bufp,struct sockaddr * sap)176 static int INET6_getsock(char *bufp, struct sockaddr *sap)
177 {
178 	struct sockaddr_in6 *sin6;
179 
180 	sin6 = (struct sockaddr_in6 *) sap;
181 	sin6->sin6_family = AF_INET6;
182 	sin6->sin6_port = 0;
183 
184 	if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
185 		return -1;
186 
187 	return 16;			/* ?;) */
188 }
189 #endif
190 
INET6_input(const char * bufp,struct sockaddr * sap)191 static int FAST_FUNC INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
192 {
193 	return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
194 /*
195 	switch (type) {
196 	case 1:
197 		return (INET6_getsock(bufp, sap));
198 	default:
199 		return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
200 	}
201 */
202 }
203 
204 static const struct aftype inet6_aftype = {
205 	.name   = "inet6",
206 	.title  = "IPv6",
207 	.af     = AF_INET6,
208 	.alen   = sizeof(struct in6_addr),
209 	.sprint = INET6_sprint,
210 	.input  = INET6_input,
211 };
212 
213 #endif /* HAVE_AFINET6 */
214 
215 /* Display an UNSPEC address. */
UNSPEC_print(unsigned char * ptr)216 static char* FAST_FUNC UNSPEC_print(unsigned char *ptr)
217 {
218 	char *buff;
219 	char *pos;
220 	unsigned int i;
221 
222 	buff = auto_string(xmalloc(sizeof(struct sockaddr) * 3 + 1));
223 	pos = buff;
224 	for (i = 0; i < sizeof(struct sockaddr); i++) {
225 		/* careful -- not every libc's sprintf returns # bytes written */
226 		sprintf(pos, "%02X-", (*ptr++ & 0377));
227 		pos += 3;
228 	}
229 	/* Erase trailing "-".  Works as long as sizeof(struct sockaddr) != 0 */
230 	*--pos = '\0';
231 	return buff;
232 }
233 
234 /* Display an UNSPEC socket address. */
UNSPEC_sprint(struct sockaddr * sap,int numeric UNUSED_PARAM)235 static const char* FAST_FUNC UNSPEC_sprint(struct sockaddr *sap, int numeric UNUSED_PARAM)
236 {
237 	if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
238 		return "[NONE SET]";
239 	return UNSPEC_print((unsigned char *)sap->sa_data);
240 }
241 
242 static const struct aftype unspec_aftype = {
243 	.name   = "unspec",
244 	.title  = "UNSPEC",
245 	.af     = AF_UNSPEC,
246 	.alen   = 0,
247 	.print  = UNSPEC_print,
248 	.sprint = UNSPEC_sprint,
249 };
250 
251 static const struct aftype *const aftypes[] = {
252 	&inet_aftype,
253 #ifdef HAVE_AFINET6
254 	&inet6_aftype,
255 #endif
256 	&unspec_aftype,
257 	NULL
258 };
259 
260 /* Check our protocol family table for this family. */
get_aftype(const char * name)261 const struct aftype* FAST_FUNC get_aftype(const char *name)
262 {
263 	const struct aftype *const *afp;
264 
265 	afp = aftypes;
266 	while (*afp != NULL) {
267 		if (strcmp((*afp)->name, name) == 0)
268 			return (*afp);
269 		afp++;
270 	}
271 	return NULL;
272 }
273 
274 /* Check our protocol family table for this family. */
get_afntype(int af)275 static const struct aftype *get_afntype(int af)
276 {
277 	const struct aftype *const *afp;
278 
279 	afp = aftypes;
280 	while (*afp != NULL) {
281 		if ((*afp)->af == af)
282 			return *afp;
283 		afp++;
284 	}
285 	return NULL;
286 }
287 
288 struct user_net_device_stats {
289 	unsigned long long rx_packets;	/* total packets received       */
290 	unsigned long long tx_packets;	/* total packets transmitted    */
291 	unsigned long long rx_bytes;	/* total bytes received         */
292 	unsigned long long tx_bytes;	/* total bytes transmitted      */
293 	unsigned long rx_errors;	/* bad packets received         */
294 	unsigned long tx_errors;	/* packet transmit problems     */
295 	unsigned long rx_dropped;	/* no space in linux buffers    */
296 	unsigned long tx_dropped;	/* no space available in linux  */
297 	unsigned long rx_multicast;	/* multicast packets received   */
298 	unsigned long rx_compressed;
299 	unsigned long tx_compressed;
300 	unsigned long collisions;
301 
302 	/* detailed rx_errors: */
303 	unsigned long rx_length_errors;
304 	unsigned long rx_over_errors;	/* receiver ring buff overflow  */
305 	unsigned long rx_crc_errors;	/* recved pkt with crc error    */
306 	unsigned long rx_frame_errors;	/* recv'd frame alignment error */
307 	unsigned long rx_fifo_errors;	/* recv'r fifo overrun          */
308 	unsigned long rx_missed_errors;	/* receiver missed packet     */
309 	/* detailed tx_errors */
310 	unsigned long tx_aborted_errors;
311 	unsigned long tx_carrier_errors;
312 	unsigned long tx_fifo_errors;
313 	unsigned long tx_heartbeat_errors;
314 	unsigned long tx_window_errors;
315 };
316 
317 struct interface {
318 	struct interface *next, *prev;
319 	char name[IFNAMSIZ];                    /* interface name        */
320 	short type;                             /* if type               */
321 	short flags;                            /* various flags         */
322 	int metric;                             /* routing metric        */
323 	int mtu;                                /* MTU value             */
324 	int tx_queue_len;                       /* transmit queue length */
325 	struct ifmap map;                       /* hardware setup        */
326 	struct sockaddr addr;                   /* IP address            */
327 	struct sockaddr dstaddr;                /* P-P IP address        */
328 	struct sockaddr broadaddr;              /* IP broadcast address  */
329 	struct sockaddr netmask;                /* IP network mask       */
330 	int has_ip;
331 	char hwaddr[32];                        /* HW address            */
332 	int statistics_valid;
333 	struct user_net_device_stats stats;     /* statistics            */
334 	int keepalive;                          /* keepalive value for SLIP */
335 	int outfill;                            /* outfill value for SLIP */
336 };
337 
338 
339 smallint interface_opt_a;	/* show all interfaces */
340 
341 static struct interface *int_list, *int_last;
342 
343 
344 #if 0
345 /* like strcmp(), but knows about numbers */
346 except that the freshly added calls to xatoul() brf on ethernet aliases with
347 uClibc with e.g.: ife->name='lo'  name='eth0:1'
348 static int nstrcmp(const char *a, const char *b)
349 {
350 	const char *a_ptr = a;
351 	const char *b_ptr = b;
352 
353 	while (*a == *b) {
354 		if (*a == '\0') {
355 			return 0;
356 		}
357 		if (!isdigit(*a) && isdigit(*(a+1))) {
358 			a_ptr = a+1;
359 			b_ptr = b+1;
360 		}
361 		a++;
362 		b++;
363 	}
364 
365 	if (isdigit(*a) && isdigit(*b)) {
366 		return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
367 	}
368 	return *a - *b;
369 }
370 #endif
371 
add_interface(char * name)372 static struct interface *add_interface(char *name)
373 {
374 	struct interface *ife, **nextp, *new;
375 
376 	for (ife = int_last; ife; ife = ife->prev) {
377 		int n = /*n*/strcmp(ife->name, name);
378 
379 		if (n == 0)
380 			return ife;
381 		if (n < 0)
382 			break;
383 	}
384 
385 	new = xzalloc(sizeof(*new));
386 	strncpy_IFNAMSIZ(new->name, name);
387 	nextp = ife ? &ife->next : &int_list;
388 	new->prev = ife;
389 	new->next = *nextp;
390 	if (new->next)
391 		new->next->prev = new;
392 	else
393 		int_last = new;
394 	*nextp = new;
395 	return new;
396 }
397 
get_name(char * name,char * p)398 static char *get_name(char *name, char *p)
399 {
400 	/* Extract <name> from nul-terminated p where p matches
401 	 * <name>: after leading whitespace.
402 	 * If match is not made, set name empty and return unchanged p
403 	 */
404 	char *nameend;
405 	char *namestart = skip_whitespace(p);
406 
407 	nameend = namestart;
408 	while (*nameend && *nameend != ':' && !isspace(*nameend))
409 		nameend++;
410 	if (*nameend == ':') {
411 		if ((nameend - namestart) < IFNAMSIZ) {
412 			memcpy(name, namestart, nameend - namestart);
413 			name[nameend - namestart] = '\0';
414 			p = nameend;
415 		} else {
416 			/* Interface name too large */
417 			name[0] = '\0';
418 		}
419 	} else {
420 		/* trailing ':' not found - return empty */
421 		name[0] = '\0';
422 	}
423 	return p + 1;
424 }
425 
426 /* If scanf supports size qualifiers for %n conversions, then we can
427  * use a modified fmt that simply stores the position in the fields
428  * having no associated fields in the proc string.  Of course, we need
429  * to zero them again when we're done.  But that is smaller than the
430  * old approach of multiple scanf occurrences with large numbers of
431  * args. */
432 
433 /* static const char *const ss_fmt[] = { */
434 /*	"%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
435 /*	"%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
436 /*	"%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
437 /* }; */
438 
439 	/* Lie about the size of the int pointed to for %n. */
440 #if INT_MAX == LONG_MAX
441 static const char *const ss_fmt[] = {
442 	"%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
443 	"%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
444 	"%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
445 };
446 #else
447 static const char *const ss_fmt[] = {
448 	"%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
449 	"%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
450 	"%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
451 };
452 
453 #endif
454 
get_dev_fields(char * bp,struct interface * ife,int procnetdev_vsn)455 static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
456 {
457 	memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
458 
459 	sscanf(bp, ss_fmt[procnetdev_vsn],
460 		   &ife->stats.rx_bytes, /* missing for 0 */
461 		   &ife->stats.rx_packets,
462 		   &ife->stats.rx_errors,
463 		   &ife->stats.rx_dropped,
464 		   &ife->stats.rx_fifo_errors,
465 		   &ife->stats.rx_frame_errors,
466 		   &ife->stats.rx_compressed, /* missing for <= 1 */
467 		   &ife->stats.rx_multicast, /* missing for <= 1 */
468 		   &ife->stats.tx_bytes, /* missing for 0 */
469 		   &ife->stats.tx_packets,
470 		   &ife->stats.tx_errors,
471 		   &ife->stats.tx_dropped,
472 		   &ife->stats.tx_fifo_errors,
473 		   &ife->stats.collisions,
474 		   &ife->stats.tx_carrier_errors,
475 		   &ife->stats.tx_compressed /* missing for <= 1 */
476 		   );
477 
478 	if (procnetdev_vsn <= 1) {
479 		if (procnetdev_vsn == 0) {
480 			ife->stats.rx_bytes = 0;
481 			ife->stats.tx_bytes = 0;
482 		}
483 		ife->stats.rx_multicast = 0;
484 		ife->stats.rx_compressed = 0;
485 		ife->stats.tx_compressed = 0;
486 	}
487 }
488 
procnetdev_version(char * buf)489 static int procnetdev_version(char *buf)
490 {
491 	if (strstr(buf, "compressed"))
492 		return 2;
493 	if (strstr(buf, "bytes"))
494 		return 1;
495 	return 0;
496 }
497 
if_readconf(void)498 static int if_readconf(void)
499 {
500 	int numreqs = 30;
501 	struct ifconf ifc;
502 	struct ifreq *ifr;
503 	int n, err = -1;
504 	int skfd;
505 
506 	ifc.ifc_buf = NULL;
507 
508 	/* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
509 	   (as of 2.1.128) */
510 	skfd = socket(AF_INET, SOCK_DGRAM, 0);
511 	if (skfd < 0) {
512 		bb_perror_msg("error: no inet socket available");
513 		return -1;
514 	}
515 
516 	for (;;) {
517 		ifc.ifc_len = sizeof(struct ifreq) * numreqs;
518 		ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
519 
520 		if (ioctl_or_warn(skfd, SIOCGIFCONF, &ifc) < 0) {
521 			goto out;
522 		}
523 		if (ifc.ifc_len == (int)(sizeof(struct ifreq) * numreqs)) {
524 			/* assume it overflowed and try again */
525 			numreqs += 10;
526 			continue;
527 		}
528 		break;
529 	}
530 
531 	ifr = ifc.ifc_req;
532 	for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
533 		add_interface(ifr->ifr_name);
534 		ifr++;
535 	}
536 	err = 0;
537 
538  out:
539 	close(skfd);
540 	free(ifc.ifc_buf);
541 	return err;
542 }
543 
if_readlist_proc(char * target)544 static int if_readlist_proc(char *target)
545 {
546 	static smallint proc_read;
547 
548 	FILE *fh;
549 	char buf[512];
550 	struct interface *ife;
551 	int err, procnetdev_vsn;
552 
553 	if (proc_read)
554 		return 0;
555 	if (!target)
556 		proc_read = 1;
557 
558 	fh = fopen_or_warn(_PATH_PROCNET_DEV, "r");
559 	if (!fh) {
560 		return if_readconf();
561 	}
562 	fgets(buf, sizeof buf, fh);	/* eat line */
563 	fgets(buf, sizeof buf, fh);
564 
565 	procnetdev_vsn = procnetdev_version(buf);
566 
567 	err = 0;
568 	while (fgets(buf, sizeof buf, fh)) {
569 		char *s, name[128];
570 
571 		s = get_name(name, buf);
572 		ife = add_interface(name);
573 		get_dev_fields(s, ife, procnetdev_vsn);
574 		ife->statistics_valid = 1;
575 		if (target && strcmp(target, name) == 0)
576 			break;
577 	}
578 	if (ferror(fh)) {
579 		bb_perror_msg(_PATH_PROCNET_DEV);
580 		err = -1;
581 		proc_read = 0;
582 	}
583 	fclose(fh);
584 	return err;
585 }
586 
if_readlist(void)587 static int if_readlist(void)
588 {
589 	int err = if_readlist_proc(NULL);
590 	/* Needed in order to get ethN:M aliases */
591 	if (!err)
592 		err = if_readconf();
593 	return err;
594 }
595 
596 /* Fetch the interface configuration from the kernel. */
if_fetch(struct interface * ife)597 static int if_fetch(struct interface *ife)
598 {
599 	struct ifreq ifr;
600 	char *ifname = ife->name;
601 	int skfd;
602 
603 	skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
604 
605 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
606 	if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
607 		close(skfd);
608 		return -1;
609 	}
610 	ife->flags = ifr.ifr_flags;
611 
612 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
613 	memset(ife->hwaddr, 0, 32);
614 	if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
615 		memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
616 
617 	ife->type = ifr.ifr_hwaddr.sa_family;
618 
619 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
620 	ife->metric = 0;
621 	if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
622 		ife->metric = ifr.ifr_metric;
623 
624 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
625 	ife->mtu = 0;
626 	if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
627 		ife->mtu = ifr.ifr_mtu;
628 
629 	memset(&ife->map, 0, sizeof(struct ifmap));
630 #ifdef SIOCGIFMAP
631 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
632 	if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
633 		ife->map = ifr.ifr_map;
634 #endif
635 
636 #ifdef HAVE_TXQUEUELEN
637 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
638 	ife->tx_queue_len = -1;	/* unknown value */
639 	if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
640 		ife->tx_queue_len = ifr.ifr_qlen;
641 #else
642 	ife->tx_queue_len = -1;	/* unknown value */
643 #endif
644 
645 	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
646 	ifr.ifr_addr.sa_family = AF_INET;
647 	memset(&ife->addr, 0, sizeof(struct sockaddr));
648 	if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
649 		ife->has_ip = 1;
650 		ife->addr = ifr.ifr_addr;
651 		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
652 		memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
653 		if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
654 			ife->dstaddr = ifr.ifr_dstaddr;
655 
656 		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
657 		memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
658 		if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
659 			ife->broadaddr = ifr.ifr_broadaddr;
660 
661 		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
662 		memset(&ife->netmask, 0, sizeof(struct sockaddr));
663 		if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
664 			ife->netmask = ifr.ifr_netmask;
665 	}
666 
667 	close(skfd);
668 	return 0;
669 }
670 
do_if_fetch(struct interface * ife)671 static int do_if_fetch(struct interface *ife)
672 {
673 	if (if_fetch(ife) < 0) {
674 		const char *errmsg;
675 
676 		if (errno == ENODEV) {
677 			/* Give better error message for this case. */
678 			errmsg = "Device not found";
679 		} else {
680 			errmsg = strerror(errno);
681 		}
682 		bb_error_msg("%s: error fetching interface information: %s",
683 				ife->name, errmsg);
684 		return -1;
685 	}
686 	return 0;
687 }
688 
689 static const struct hwtype unspec_hwtype = {
690 	.name =		"unspec",
691 	.title =	"UNSPEC",
692 	.type =		-1,
693 	.print =	UNSPEC_print
694 };
695 
696 static const struct hwtype loop_hwtype = {
697 	.name =		"loop",
698 	.title =	"Local Loopback",
699 	.type =		ARPHRD_LOOPBACK
700 };
701 
702 /* Display an Ethernet address in readable format. */
ether_print(unsigned char * ptr)703 static char* FAST_FUNC ether_print(unsigned char *ptr)
704 {
705 	char *buff;
706 	buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X",
707 			 (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
708 			 (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
709 		);
710 	return auto_string(buff);
711 }
712 
713 static const struct hwtype ether_hwtype = {
714 	.name  = "ether",
715 	.title = "Ethernet",
716 	.type  = ARPHRD_ETHER,
717 	.alen  = ETH_ALEN,
718 	.print = ether_print,
719 	.input = in_ether
720 };
721 
722 static const struct hwtype ppp_hwtype = {
723 	.name =		"ppp",
724 	.title =	"Point-to-Point Protocol",
725 	.type =		ARPHRD_PPP
726 };
727 
728 #if ENABLE_FEATURE_IPV6
729 static const struct hwtype sit_hwtype = {
730 	.name =			"sit",
731 	.title =		"IPv6-in-IPv4",
732 	.type =			ARPHRD_SIT,
733 	.print =		UNSPEC_print,
734 	.suppress_null_addr =	1
735 };
736 #endif
737 #if ENABLE_FEATURE_HWIB
738 static const struct hwtype ib_hwtype = {
739 	.name  = "infiniband",
740 	.title = "InfiniBand",
741 	.type  = ARPHRD_INFINIBAND,
742 	.alen  = INFINIBAND_ALEN,
743 	.print = UNSPEC_print,
744 	.input = in_ib,
745 };
746 #endif
747 
748 
749 static const struct hwtype *const hwtypes[] = {
750 	&loop_hwtype,
751 	&ether_hwtype,
752 	&ppp_hwtype,
753 	&unspec_hwtype,
754 #if ENABLE_FEATURE_IPV6
755 	&sit_hwtype,
756 #endif
757 #if ENABLE_FEATURE_HWIB
758 	&ib_hwtype,
759 #endif
760 	NULL
761 };
762 
763 #ifdef IFF_PORTSEL
764 static const char *const if_port_text[] = {
765 	/* Keep in step with <linux/netdevice.h> */
766 	"unknown",
767 	"10base2",
768 	"10baseT",
769 	"AUI",
770 	"100baseT",
771 	"100baseTX",
772 	"100baseFX",
773 	NULL
774 };
775 #endif
776 
777 /* Check our hardware type table for this type. */
get_hwtype(const char * name)778 const struct hwtype* FAST_FUNC get_hwtype(const char *name)
779 {
780 	const struct hwtype *const *hwp;
781 
782 	hwp = hwtypes;
783 	while (*hwp != NULL) {
784 		if (strcmp((*hwp)->name, name) == 0)
785 			return (*hwp);
786 		hwp++;
787 	}
788 	return NULL;
789 }
790 
791 /* Check our hardware type table for this type. */
get_hwntype(int type)792 const struct hwtype* FAST_FUNC get_hwntype(int type)
793 {
794 	const struct hwtype *const *hwp;
795 
796 	hwp = hwtypes;
797 	while (*hwp != NULL) {
798 		if ((*hwp)->type == type)
799 			return *hwp;
800 		hwp++;
801 	}
802 	return NULL;
803 }
804 
805 /* return 1 if address is all zeros */
hw_null_address(const struct hwtype * hw,void * ap)806 static int hw_null_address(const struct hwtype *hw, void *ap)
807 {
808 	int i;
809 	unsigned char *address = (unsigned char *) ap;
810 
811 	for (i = 0; i < hw->alen; i++)
812 		if (address[i])
813 			return 0;
814 	return 1;
815 }
816 
817 static const char TRext[] ALIGN1 = "\0\0\0Ki\0Mi\0Gi\0Ti";
818 
print_bytes_scaled(unsigned long long ull,const char * end)819 static void print_bytes_scaled(unsigned long long ull, const char *end)
820 {
821 	unsigned long long int_part;
822 	const char *ext;
823 	unsigned int frac_part;
824 	int i;
825 
826 	frac_part = 0;
827 	ext = TRext;
828 	int_part = ull;
829 	i = 4;
830 	do {
831 		if (int_part >= 1024) {
832 			frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
833 			int_part /= 1024;
834 			ext += 3;	/* KiB, MiB, GiB, TiB */
835 		}
836 		--i;
837 	} while (i);
838 
839 	printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
840 }
841 
842 
843 #ifdef HAVE_AFINET6
844 #define IPV6_ADDR_ANY           0x0000U
845 
846 #define IPV6_ADDR_UNICAST       0x0001U
847 #define IPV6_ADDR_MULTICAST     0x0002U
848 #define IPV6_ADDR_ANYCAST       0x0004U
849 
850 #define IPV6_ADDR_LOOPBACK      0x0010U
851 #define IPV6_ADDR_LINKLOCAL     0x0020U
852 #define IPV6_ADDR_SITELOCAL     0x0040U
853 
854 #define IPV6_ADDR_COMPATv4      0x0080U
855 
856 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
857 
858 #define IPV6_ADDR_MAPPED        0x1000U
859 #define IPV6_ADDR_RESERVED      0x2000U	/* reserved address space */
860 
861 
ife_print6(struct interface * ptr)862 static void ife_print6(struct interface *ptr)
863 {
864 	FILE *f;
865 	char addr6[40], devname[21];
866 	struct sockaddr_in6 sap;
867 	int plen, scope, dad_status, if_idx;
868 	char addr6p[8][5];
869 
870 	f = fopen_for_read(_PATH_PROCNET_IFINET6);
871 	if (f == NULL)
872 		return;
873 
874 	while (fscanf
875 		   (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
876 			addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
877 			addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
878 			&dad_status, devname) != EOF
879 	) {
880 		if (strcmp(devname, ptr->name) == 0) {
881 			sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
882 					addr6p[0], addr6p[1], addr6p[2], addr6p[3],
883 					addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
884 			memset(&sap, 0, sizeof(sap));
885 			inet_pton(AF_INET6, addr6,
886 					  (struct sockaddr *) &sap.sin6_addr);
887 			sap.sin6_family = AF_INET6;
888 			printf("          inet6 addr: %s/%d",
889 				INET6_sprint((struct sockaddr *) &sap, 1),
890 				plen);
891 			printf(" Scope:");
892 			switch (scope & IPV6_ADDR_SCOPE_MASK) {
893 			case 0:
894 				puts("Global");
895 				break;
896 			case IPV6_ADDR_LINKLOCAL:
897 				puts("Link");
898 				break;
899 			case IPV6_ADDR_SITELOCAL:
900 				puts("Site");
901 				break;
902 			case IPV6_ADDR_COMPATv4:
903 				puts("Compat");
904 				break;
905 			case IPV6_ADDR_LOOPBACK:
906 				puts("Host");
907 				break;
908 			default:
909 				puts("Unknown");
910 			}
911 		}
912 	}
913 	fclose(f);
914 }
915 #else
916 #define ife_print6(a) ((void)0)
917 #endif
918 
ife_print(struct interface * ptr)919 static void ife_print(struct interface *ptr)
920 {
921 	const struct aftype *ap;
922 	const struct hwtype *hw;
923 	int hf;
924 	int can_compress = 0;
925 
926 	ap = get_afntype(ptr->addr.sa_family);
927 	if (ap == NULL)
928 		ap = get_afntype(0);
929 
930 	hf = ptr->type;
931 
932 	if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
933 		can_compress = 1;
934 
935 	hw = get_hwntype(hf);
936 	if (hw == NULL)
937 		hw = get_hwntype(-1);
938 
939 	printf("%-9s Link encap:%s  ", ptr->name, hw->title);
940 	/* For some hardware types (eg Ash, ATM) we don't print the
941 	   hardware address if it's null.  */
942 	if (hw->print != NULL
943 	 && !(hw_null_address(hw, ptr->hwaddr) && hw->suppress_null_addr)
944 	) {
945 		printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
946 	}
947 #ifdef IFF_PORTSEL
948 	if (ptr->flags & IFF_PORTSEL) {
949 		printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
950 		if (ptr->flags & IFF_AUTOMEDIA)
951 			printf("(auto)");
952 	}
953 #endif
954 	bb_putchar('\n');
955 
956 	if (ptr->has_ip) {
957 		printf("          %s addr:%s ", ap->name,
958 			ap->sprint(&ptr->addr, 1));
959 		if (ptr->flags & IFF_POINTOPOINT) {
960 			printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
961 		}
962 		if (ptr->flags & IFF_BROADCAST) {
963 			printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
964 		}
965 		printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
966 	}
967 
968 	ife_print6(ptr);
969 
970 	printf("          ");
971 	/* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
972 
973 	if (ptr->flags == 0) {
974 		printf("[NO FLAGS] ");
975 	} else {
976 		static const char ife_print_flags_strs[] ALIGN1 =
977 			"UP\0"
978 			"BROADCAST\0"
979 			"DEBUG\0"
980 			"LOOPBACK\0"
981 			"POINTOPOINT\0"
982 			"NOTRAILERS\0"
983 			"RUNNING\0"
984 			"NOARP\0"
985 			"PROMISC\0"
986 			"ALLMULTI\0"
987 			"SLAVE\0"
988 			"MASTER\0"
989 			"MULTICAST\0"
990 #ifdef HAVE_DYNAMIC
991 			"DYNAMIC\0"
992 #endif
993 			;
994 		static const unsigned short ife_print_flags_mask[] ALIGN2 = {
995 			IFF_UP,
996 			IFF_BROADCAST,
997 			IFF_DEBUG,
998 			IFF_LOOPBACK,
999 			IFF_POINTOPOINT,
1000 			IFF_NOTRAILERS,
1001 			IFF_RUNNING,
1002 			IFF_NOARP,
1003 			IFF_PROMISC,
1004 			IFF_ALLMULTI,
1005 			IFF_SLAVE,
1006 			IFF_MASTER,
1007 			IFF_MULTICAST
1008 #ifdef HAVE_DYNAMIC
1009 			,IFF_DYNAMIC
1010 #endif
1011 		};
1012 		const unsigned short *mask = ife_print_flags_mask;
1013 		const char *str = ife_print_flags_strs;
1014 		do {
1015 			if (ptr->flags & *mask) {
1016 				printf("%s ", str);
1017 			}
1018 			mask++;
1019 			str += strlen(str) + 1;
1020 		} while (*str);
1021 	}
1022 
1023 	/* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1024 	printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
1025 #ifdef SIOCSKEEPALIVE
1026 	if (ptr->outfill || ptr->keepalive)
1027 		printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
1028 #endif
1029 	bb_putchar('\n');
1030 
1031 	/* If needed, display the interface statistics. */
1032 
1033 	if (ptr->statistics_valid) {
1034 		/* XXX: statistics are currently only printed for the primary address,
1035 		 *      not for the aliases, although strictly speaking they're shared
1036 		 *      by all addresses.
1037 		 */
1038 		printf("          ");
1039 
1040 		printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1041 			ptr->stats.rx_packets, ptr->stats.rx_errors,
1042 			ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1043 			ptr->stats.rx_frame_errors);
1044 		if (can_compress)
1045 			printf("             compressed:%lu\n",
1046 				ptr->stats.rx_compressed);
1047 		printf("          ");
1048 		printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
1049 			ptr->stats.tx_packets, ptr->stats.tx_errors,
1050 			ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1051 			ptr->stats.tx_carrier_errors);
1052 		printf("          collisions:%lu ", ptr->stats.collisions);
1053 		if (can_compress)
1054 			printf("compressed:%lu ", ptr->stats.tx_compressed);
1055 		if (ptr->tx_queue_len != -1)
1056 			printf("txqueuelen:%d ", ptr->tx_queue_len);
1057 		printf("\n          R");
1058 		print_bytes_scaled(ptr->stats.rx_bytes, "  T");
1059 		print_bytes_scaled(ptr->stats.tx_bytes, "\n");
1060 	}
1061 
1062 	if (ptr->map.irq || ptr->map.mem_start
1063 	 || ptr->map.dma || ptr->map.base_addr
1064 	) {
1065 		printf("          ");
1066 		if (ptr->map.irq)
1067 			printf("Interrupt:%d ", ptr->map.irq);
1068 		if (ptr->map.base_addr >= 0x100) /* Only print devices using it for I/O maps */
1069 			printf("Base address:0x%lx ",
1070 				(unsigned long) ptr->map.base_addr);
1071 		if (ptr->map.mem_start) {
1072 			printf("Memory:%lx-%lx ", ptr->map.mem_start,
1073 				ptr->map.mem_end);
1074 		}
1075 		if (ptr->map.dma)
1076 			printf("DMA chan:%x ", ptr->map.dma);
1077 		bb_putchar('\n');
1078 	}
1079 	bb_putchar('\n');
1080 }
1081 
do_if_print(struct interface * ife)1082 static int do_if_print(struct interface *ife) /*, int *opt_a)*/
1083 {
1084 	int res;
1085 
1086 	res = do_if_fetch(ife);
1087 	if (res >= 0) {
1088 		if ((ife->flags & IFF_UP) || interface_opt_a)
1089 			ife_print(ife);
1090 	}
1091 	return res;
1092 }
1093 
lookup_interface(char * name)1094 static struct interface *lookup_interface(char *name)
1095 {
1096 	struct interface *ife = NULL;
1097 
1098 	if (if_readlist_proc(name) < 0)
1099 		return NULL;
1100 	ife = add_interface(name);
1101 	return ife;
1102 }
1103 
1104 #ifdef UNUSED
for_all_interfaces(int (* doit)(struct interface *,void *),void * cookie)1105 static int for_all_interfaces(int (*doit) (struct interface *, void *),
1106 							void *cookie)
1107 {
1108 	struct interface *ife;
1109 
1110 	if (!int_list && (if_readlist() < 0))
1111 		return -1;
1112 	for (ife = int_list; ife; ife = ife->next) {
1113 		int err = doit(ife, cookie);
1114 		if (err)
1115 			return err;
1116 	}
1117 	return 0;
1118 }
1119 #endif
1120 
1121 /* for ipv4 add/del modes */
if_print(char * ifname)1122 static int if_print(char *ifname)
1123 {
1124 	struct interface *ife;
1125 	int res;
1126 
1127 	if (!ifname) {
1128 		/*res = for_all_interfaces(do_if_print, &interface_opt_a);*/
1129 		if (!int_list && (if_readlist() < 0))
1130 			return -1;
1131 		for (ife = int_list; ife; ife = ife->next) {
1132 			int err = do_if_print(ife); /*, &interface_opt_a);*/
1133 			if (err)
1134 				return err;
1135 		}
1136 		return 0;
1137 	}
1138 	ife = lookup_interface(ifname);
1139 	res = do_if_fetch(ife);
1140 	if (res >= 0)
1141 		ife_print(ife);
1142 	return res;
1143 }
1144 
1145 #if ENABLE_FEATURE_HWIB
1146 /* Input an Infiniband address and convert to binary. */
in_ib(const char * bufp,struct sockaddr * sap)1147 int FAST_FUNC in_ib(const char *bufp, struct sockaddr *sap)
1148 {
1149 	sap->sa_family = ib_hwtype.type;
1150 //TODO: error check?
1151 	hex2bin((char*)sap->sa_data, bufp, INFINIBAND_ALEN);
1152 # ifdef HWIB_DEBUG
1153 	fprintf(stderr, "in_ib(%s): %s\n", bufp, UNSPEC_print(sap->sa_data));
1154 # endif
1155 	return 0;
1156 }
1157 #endif
1158 
display_interfaces(char * ifname)1159 int FAST_FUNC display_interfaces(char *ifname)
1160 {
1161 	int status;
1162 
1163 	status = if_print(ifname);
1164 
1165 	return (status < 0); /* status < 0 == 1 -- error */
1166 }
1167