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