Annotation of /trunk/mkinitrd-magellan/busybox/networking/interface.c
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 29903 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months 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 | niro | 532 | /* 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 | } |