diff options
Diffstat (limited to 'networking/interface.c')
-rw-r--r-- | networking/interface.c | 1152 |
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 | |||
65 | struct 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. */ | ||
95 | struct 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. */ | ||
115 | static 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 | |||
129 | static 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. */ | ||
142 | static 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 | |||
154 | static 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. */ | ||
166 | static 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. */ | ||
184 | static 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 | |||
193 | static struct aftype unspec_aftype = { | ||
194 | "unspec", "UNSPEC", AF_UNSPEC, 0, | ||
195 | UNSPEC_print, UNSPEC_sprint, NULL, NULL, | ||
196 | NULL, | ||
197 | }; | ||
198 | |||
199 | static 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. */ | ||
209 | static 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 */ | ||
223 | static 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 | |||
236 | struct 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 | |||
265 | struct 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 | |||
287 | int interface_opt_a; /* show all interfaces */ | ||
288 | |||
289 | static struct interface *int_list, *int_last; | ||
290 | static int skfd = -1; /* generic raw socket desc. */ | ||
291 | |||
292 | |||
293 | static 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 | ||
334 | static 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 */ | ||
348 | except that the freshly added calls to xatoul() brf on ethernet aliases with | ||
349 | uClibc with e.g.: ife->name='lo' name='eth0:1' | ||
350 | static 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 | |||
374 | static 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 | |||
401 | static 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 | |||
449 | static 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 | ||
491 | static 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 | ||
497 | static 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 | |||
505 | static 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 | |||
539 | static 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 | |||
548 | static 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 | |||
591 | static 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 | |||
600 | static 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. */ | ||
617 | static 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 | |||
699 | static 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. */ | ||
718 | struct 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 | |||
729 | static const struct hwtype unspec_hwtype = { | ||
730 | .name = "unspec", | ||
731 | .title = "UNSPEC", | ||
732 | .type = -1, | ||
733 | .print = UNSPEC_print | ||
734 | }; | ||
735 | |||
736 | static 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. */ | ||
751 | static 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 | |||
762 | static 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 | |||
772 | static 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 | ||
779 | static 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 | |||
788 | static const struct hwtype * const hwtypes[] = { | ||
789 | &loop_hwtype, | ||
790 | ðer_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 | ||
800 | static 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. */ | ||
814 | static 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 */ | ||
828 | static 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 | |||
839 | static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti"; | ||
840 | |||
841 | static 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 | |||
864 | static 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 | |||
883 | static 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 | |||
903 | static 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 | |||
1095 | static 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 | |||
1108 | static 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 */ | ||
1119 | static 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 | |||
1136 | int display_interfaces(char *ifname); | ||
1137 | int 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 | } | ||