source: MondoRescue/branches/3.3/mindi-busybox/networking/interface.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 27.6 KB
Line 
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 */
62struct 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. */
90static 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
98static 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
140static 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
155static 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. */
168static 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
176static 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
191static 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
204static 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. */
216static 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. */
235static 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
242static 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
251static 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. */
261const 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))
268 return (*afp);
269 afp++;
270 }
271 return NULL;
272}
273
274/* Check our protocol family table for this family. */
275static 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
288struct 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
317struct 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
339smallint interface_opt_a; /* show all interfaces */
340
341static struct interface *int_list, *int_last;
342
343
344#if 0
345/* like strcmp(), but knows about numbers */
346except that the freshly added calls to xatoul() brf on ethernet aliases with
347uClibc with e.g.: ife->name='lo' name='eth0:1'
348static 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
372static 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
398static 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
441static 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
447static 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
455static 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
489static 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
498static 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
544static 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))
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
587static 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. */
597static 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
671static 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
689static const struct hwtype unspec_hwtype = {
690 .name = "unspec",
691 .title = "UNSPEC",
692 .type = -1,
693 .print = UNSPEC_print
694};
695
696static 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. */
703static 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
713static 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
722static 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
729static 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
738static 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
749static 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
764static 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. */
778const 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))
785 return (*hwp);
786 hwp++;
787 }
788 return NULL;
789}
790
791/* Check our hardware type table for this type. */
792const 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 */
806static 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
817static const char TRext[] ALIGN1 = "\0\0\0Ki\0Mi\0Gi\0Ti";
818
819static 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
862static 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)) {
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
919static 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
1082static 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
1094static 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
1105static 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 */
1122static 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. */
1147int 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
1159int 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}
Note: See TracBrowser for help on using the repository browser.