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