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