- use RESERVE_CONFIG_BUFFER. For defconfig this gives:
[oweals/busybox.git] / networking / interface.c
1 /*
2  * stolen from net-tools-1.59 and stripped down for busybox by
3  *                      Erik Andersen <andersen@codepoet.org>
4  *
5  * Heavily modified by Manuel Novoa III       Mar 12, 2001
6  *
7  * Pruned unused code using KEEP_UNUSED define.
8  * Added print_bytes_scaled function to reduce code size.
9  * Added some (potentially) missing defines.
10  * Improved display support for -a and for a named interface.
11  *
12  * -----------------------------------------------------------
13  *
14  * ifconfig   This file contains an implementation of the command
15  *              that either displays or sets the characteristics of
16  *              one or more of the system's networking interfaces.
17  *
18  * Version:     $Id: interface.c,v 1.25 2004/08/26 21:45:21 andersen Exp $
19  *
20  * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
21  *              and others.  Copyright 1993 MicroWalt Corporation
22  *
23  *              This program is free software; you can redistribute it
24  *              and/or  modify it under  the terms of  the GNU General
25  *              Public  License as  published  by  the  Free  Software
26  *              Foundation;  either  version 2 of the License, or  (at
27  *              your option) any later version.
28  *
29  * Patched to support 'add' and 'del' keywords for INET(4) addresses
30  * by Mrs. Brisby <mrs.brisby@nimh.org>
31  *
32  * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
33  *                     - gettext instead of catgets for i18n
34  *          10/1998  - Andi Kleen. Use interface list primitives.
35  *          20001008 - Bernd Eckenfels, Patch from RH for setting mtu
36  *                      (default AF was wrong)
37  */
38
39 /* #define KEEP_UNUSED */
40
41 /*
42  *
43  * Protocol Families.
44  *
45  */
46 #define HAVE_AFINET 1
47 #undef HAVE_AFIPX
48 #undef HAVE_AFATALK
49 #undef HAVE_AFNETROM
50 #undef HAVE_AFX25
51 #undef HAVE_AFECONET
52 #undef HAVE_AFASH
53
54 /*
55  *
56  * Device Hardware types.
57  *
58  */
59 #define HAVE_HWETHER    1
60 #define HAVE_HWPPP      1
61 #undef HAVE_HWSLIP
62
63
64 #include "inet_common.h"
65 #include <stdio.h>
66 #include <errno.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <fcntl.h>
71 #include <ctype.h>
72 #include <sys/ioctl.h>
73 #include <sys/types.h>
74 #include <net/if.h>
75 #include <net/if_arp.h>
76 #include "libbb.h"
77
78 #ifdef CONFIG_FEATURE_IPV6
79 # define HAVE_AFINET6 1
80 #else
81 # undef HAVE_AFINET6
82 #endif
83
84 #define _PATH_PROCNET_DEV               "/proc/net/dev"
85 #define _PATH_PROCNET_IFINET6           "/proc/net/if_inet6"
86 #define new(p) ((p) = xcalloc(1,sizeof(*(p))))
87 #define KRELEASE(maj,min,patch) ((maj) * 65536 + (min)*256 + (patch))
88
89 #ifdef HAVE_HWSLIP
90 #include <net/if_slip.h>
91 #endif
92
93 #if HAVE_AFINET6
94
95 #ifndef _LINUX_IN6_H
96 /*
97  *    This is in linux/include/net/ipv6.h.
98  */
99
100 struct in6_ifreq {
101         struct in6_addr ifr6_addr;
102         uint32_t ifr6_prefixlen;
103         unsigned int ifr6_ifindex;
104 };
105
106 #endif
107
108 #endif                                                  /* HAVE_AFINET6 */
109
110 #if HAVE_AFIPX
111 #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
112 #include <netipx/ipx.h>
113 #endif
114 #endif
115
116 /* Defines for glibc2.0 users. */
117 #ifndef SIOCSIFTXQLEN
118 #define SIOCSIFTXQLEN      0x8943
119 #define SIOCGIFTXQLEN      0x8942
120 #endif
121
122 /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
123 #ifndef ifr_qlen
124 #define ifr_qlen        ifr_ifru.ifru_mtu
125 #endif
126
127 #ifndef HAVE_TXQUEUELEN
128 #define HAVE_TXQUEUELEN 1
129 #endif
130
131 #ifndef IFF_DYNAMIC
132 #define IFF_DYNAMIC     0x8000  /* dialup device with changing addresses */
133 #endif
134
135 /* This structure defines protocol families and their handlers. */
136 struct aftype {
137         const char *name;
138         const char *title;
139         int af;
140         int alen;
141         char *(*print) (unsigned char *);
142         char *(*sprint) (struct sockaddr *, int numeric);
143         int (*input) (int type, char *bufp, struct sockaddr *);
144         void (*herror) (char *text);
145         int (*rprint) (int options);
146         int (*rinput) (int typ, int ext, char **argv);
147
148         /* may modify src */
149         int (*getmask) (char *src, struct sockaddr * mask, char *name);
150
151         int fd;
152         char *flag_file;
153 };
154
155 #ifdef KEEP_UNUSED
156
157 static int flag_unx;
158
159 #ifdef HAVE_AFIPX
160 static int flag_ipx;
161 #endif
162 #ifdef HAVE_AFX25
163 static int flag_ax25;
164 #endif
165 #ifdef HAVE_AFATALK
166 static int flag_ddp;
167 #endif
168 #ifdef HAVE_AFNETROM
169 static int flag_netrom;
170 #endif
171 static int flag_inet;
172
173 #ifdef HAVE_AFINET6
174 static int flag_inet6;
175 #endif
176 #ifdef HAVE_AFECONET
177 static int flag_econet;
178 #endif
179 #ifdef HAVE_AFX25
180 static int flag_x25 = 0;
181 #endif
182 #ifdef HAVE_AFASH
183 static int flag_ash;
184 #endif
185
186
187 static struct aftrans_t {
188         char *alias;
189         char *name;
190         int *flag;
191 } aftrans[] = {
192
193 #ifdef HAVE_AFX25
194         {
195         "ax25", "ax25", &flag_ax25},
196 #endif
197         {
198         "ip", "inet", &flag_inet},
199 #ifdef HAVE_AFINET6
200         {
201         "ip6", "inet6", &flag_inet6},
202 #endif
203 #ifdef HAVE_AFIPX
204         {
205         "ipx", "ipx", &flag_ipx},
206 #endif
207 #ifdef HAVE_AFATALK
208         {
209         "appletalk", "ddp", &flag_ddp},
210 #endif
211 #ifdef HAVE_AFNETROM
212         {
213         "netrom", "netrom", &flag_netrom},
214 #endif
215         {
216         "inet", "inet", &flag_inet},
217 #ifdef HAVE_AFINET6
218         {
219         "inet6", "inet6", &flag_inet6},
220 #endif
221 #ifdef HAVE_AFATALK
222         {
223         "ddp", "ddp", &flag_ddp},
224 #endif
225         {
226         "unix", "unix", &flag_unx}, {
227         "tcpip", "inet", &flag_inet},
228 #ifdef HAVE_AFECONET
229         {
230         "econet", "ec", &flag_econet},
231 #endif
232 #ifdef HAVE_AFX25
233         {
234         "x25", "x25", &flag_x25},
235 #endif
236 #ifdef HAVE_AFASH
237         {
238         "ash", "ash", &flag_ash},
239 #endif
240         {
241         0, 0, 0}
242 };
243
244 static char afname[256] = "";
245 #endif                                                  /* KEEP_UNUSED */
246
247 #if HAVE_AFUNIX
248
249 /* Display a UNIX domain address. */
250 static char *UNIX_print(unsigned char *ptr)
251 {
252         return (ptr);
253 }
254
255
256 /* Display a UNIX domain address. */
257 static char *UNIX_sprint(struct sockaddr *sap, int numeric)
258 {
259         static char buf[64];
260
261         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
262                 return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf));
263         return (UNIX_print(sap->sa_data));
264 }
265
266
267 static struct aftype unix_aftype = {
268         "unix", "UNIX Domain", AF_UNIX, 0,
269         UNIX_print, UNIX_sprint, NULL, NULL,
270         NULL, NULL, NULL,
271         -1,
272         "/proc/net/unix"
273 };
274 #endif                                                  /* HAVE_AFUNIX */
275
276 #if HAVE_AFINET
277
278 #ifdef KEEP_UNUSED
279 static void INET_reserror(char *text)
280 {
281         herror(text);
282 }
283
284 /* Display an Internet socket address. */
285 static char *INET_print(unsigned char *ptr)
286 {
287         return (inet_ntoa((*(struct in_addr *) ptr)));
288 }
289 #endif                                                  /* KEEP_UNUSED */
290
291 /* Display an Internet socket address. */
292 static char *INET_sprint(struct sockaddr *sap, int numeric)
293 {
294         static char buff[128];
295
296         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
297                 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
298
299         if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
300                                           numeric, 0xffffff00) != 0)
301                 return (NULL);
302
303         return (buff);
304 }
305
306 #ifdef KEEP_UNUSED
307 static char *INET_sprintmask(struct sockaddr *sap, int numeric,
308                                                          unsigned int netmask)
309 {
310         static char buff[128];
311
312         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
313                 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
314         if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
315                                           numeric, netmask) != 0)
316                 return (NULL);
317         return (buff);
318 }
319
320 static int INET_getsock(char *bufp, struct sockaddr *sap)
321 {
322         char *sp = bufp, *bp;
323         unsigned int i;
324         unsigned val;
325         struct sockaddr_in *sin;
326
327         sin = (struct sockaddr_in *) sap;
328         sin->sin_family = AF_INET;
329         sin->sin_port = 0;
330
331         val = 0;
332         bp = (char *) &val;
333         for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) {
334                 *sp = toupper(*sp);
335
336                 if ((*sp >= 'A') && (*sp <= 'F'))
337                         bp[i] |= (int) (*sp - 'A') + 10;
338                 else if ((*sp >= '0') && (*sp <= '9'))
339                         bp[i] |= (int) (*sp - '0');
340                 else
341                         return (-1);
342
343                 bp[i] <<= 4;
344                 sp++;
345                 *sp = toupper(*sp);
346
347                 if ((*sp >= 'A') && (*sp <= 'F'))
348                         bp[i] |= (int) (*sp - 'A') + 10;
349                 else if ((*sp >= '0') && (*sp <= '9'))
350                         bp[i] |= (int) (*sp - '0');
351                 else
352                         return (-1);
353
354                 sp++;
355         }
356         sin->sin_addr.s_addr = htonl(val);
357
358         return (sp - bufp);
359 }
360
361 static int INET_input(int type, char *bufp, struct sockaddr *sap)
362 {
363         switch (type) {
364         case 1:
365                 return (INET_getsock(bufp, sap));
366         case 256:
367                 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
368         default:
369                 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
370         }
371 }
372
373 static int INET_getnetmask(char *adr, struct sockaddr *m, char *name)
374 {
375         struct sockaddr_in *mask = (struct sockaddr_in *) m;
376         char *slash, *end;
377         int prefix;
378
379         if ((slash = strchr(adr, '/')) == NULL)
380                 return 0;
381
382         *slash++ = '\0';
383         prefix = strtoul(slash, &end, 0);
384         if (*end != '\0')
385                 return -1;
386
387         if (name) {
388                 sprintf(name, "/%d", prefix);
389         }
390         mask->sin_family = AF_INET;
391         mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix));
392         return 1;
393 }
394 #endif                                                  /* KEEP_UNUSED */
395
396 static struct aftype inet_aftype = {
397         "inet", "DARPA Internet", AF_INET, sizeof(unsigned long),
398         NULL /* UNUSED INET_print */ , INET_sprint,
399         NULL /* UNUSED INET_input */ , NULL /* UNUSED INET_reserror */ ,
400         NULL /*INET_rprint */ , NULL /*INET_rinput */ ,
401         NULL /* UNUSED INET_getnetmask */ ,
402         -1,
403         NULL
404 };
405
406 #endif                                                  /* HAVE_AFINET */
407
408 #if HAVE_AFINET6
409
410 #ifdef KEEP_UNUSED
411 static void INET6_reserror(char *text)
412 {
413         herror(text);
414 }
415
416 /* Display an Internet socket address. */
417 static char *INET6_print(unsigned char *ptr)
418 {
419         static char name[80];
420
421         inet_ntop(AF_INET6, (struct in6_addr *) ptr, name, 80);
422         return name;
423 }
424 #endif                                                  /* KEEP_UNUSED */
425
426 /* Display an Internet socket address. */
427 /* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
428 static char *INET6_sprint(struct sockaddr *sap, int numeric)
429 {
430         static char buff[128];
431
432         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
433                 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
434         if (INET6_rresolve
435                 (buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric) != 0)
436                 return safe_strncpy(buff, _("[UNKNOWN]"), sizeof(buff));
437         return (buff);
438 }
439
440 #ifdef KEEP_UNUSED
441 static int INET6_getsock(char *bufp, struct sockaddr *sap)
442 {
443         struct sockaddr_in6 *sin6;
444
445         sin6 = (struct sockaddr_in6 *) sap;
446         sin6->sin6_family = AF_INET6;
447         sin6->sin6_port = 0;
448
449         if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
450                 return (-1);
451
452         return 16;                      /* ?;) */
453 }
454
455 static int INET6_input(int type, char *bufp, struct sockaddr *sap)
456 {
457         switch (type) {
458         case 1:
459                 return (INET6_getsock(bufp, sap));
460         default:
461                 return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
462         }
463 }
464 #endif                                                  /* KEEP_UNUSED */
465
466 static struct aftype inet6_aftype = {
467         "inet6", "IPv6", AF_INET6, sizeof(struct in6_addr),
468         NULL /* UNUSED INET6_print */ , INET6_sprint,
469         NULL /* UNUSED INET6_input */ , NULL /* UNUSED INET6_reserror */ ,
470         NULL /*INET6_rprint */ , NULL /*INET6_rinput */ ,
471         NULL /* UNUSED INET6_getnetmask */ ,
472         -1,
473         NULL
474 };
475
476 #endif                                                  /* HAVE_AFINET6 */
477
478 /* Display an UNSPEC address. */
479 static char *UNSPEC_print(unsigned char *ptr)
480 {
481         static char buff[sizeof(struct sockaddr) * 3 + 1];
482         char *pos;
483         unsigned int i;
484
485         pos = buff;
486         for (i = 0; i < sizeof(struct sockaddr); i++) {
487                 /* careful -- not every libc's sprintf returns # bytes written */
488                 sprintf(pos, "%02X-", (*ptr++ & 0377));
489                 pos += 3;
490         }
491         /* Erase trailing "-".  Works as long as sizeof(struct sockaddr) != 0 */
492         *--pos = '\0';
493         return (buff);
494 }
495
496 /* Display an UNSPEC socket address. */
497 static char *UNSPEC_sprint(struct sockaddr *sap, int numeric)
498 {
499         static char buf[64];
500
501         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
502                 return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf));
503         return (UNSPEC_print((unsigned char *)sap->sa_data));
504 }
505
506 static struct aftype unspec_aftype = {
507         "unspec", "UNSPEC", AF_UNSPEC, 0,
508         UNSPEC_print, UNSPEC_sprint, NULL, NULL,
509         NULL,
510 };
511
512 static struct aftype * const aftypes[] = {
513 #if HAVE_AFUNIX
514         &unix_aftype,
515 #endif
516 #if HAVE_AFINET
517         &inet_aftype,
518 #endif
519 #if HAVE_AFINET6
520         &inet6_aftype,
521 #endif
522 #if HAVE_AFAX25
523         &ax25_aftype,
524 #endif
525 #if HAVE_AFNETROM
526         &netrom_aftype,
527 #endif
528 #if HAVE_AFROSE
529         &rose_aftype,
530 #endif
531 #if HAVE_AFIPX
532         &ipx_aftype,
533 #endif
534 #if HAVE_AFATALK
535         &ddp_aftype,
536 #endif
537 #if HAVE_AFECONET
538         &ec_aftype,
539 #endif
540 #if HAVE_AFASH
541         &ash_aftype,
542 #endif
543 #if HAVE_AFX25
544         &x25_aftype,
545 #endif
546         &unspec_aftype,
547         NULL
548 };
549
550 #ifdef KEEP_UNUSED
551 static short sVafinit = 0;
552
553 static void afinit()
554 {
555         unspec_aftype.title = _("UNSPEC");
556 #if HAVE_AFUNIX
557         unix_aftype.title = _("UNIX Domain");
558 #endif
559 #if HAVE_AFINET
560         inet_aftype.title = _("DARPA Internet");
561 #endif
562 #if HAVE_AFINET6
563         inet6_aftype.title = _("IPv6");
564 #endif
565 #if HAVE_AFAX25
566         ax25_aftype.title = _("AMPR AX.25");
567 #endif
568 #if HAVE_AFNETROM
569         netrom_aftype.title = _("AMPR NET/ROM");
570 #endif
571 #if HAVE_AFIPX
572         ipx_aftype.title = _("Novell IPX");
573 #endif
574 #if HAVE_AFATALK
575         ddp_aftype.title = _("Appletalk DDP");
576 #endif
577 #if HAVE_AFECONET
578         ec_aftype.title = _("Econet");
579 #endif
580 #if HAVE_AFX25
581         x25_aftype.title = _("CCITT X.25");
582 #endif
583 #if HAVE_AFROSE
584         rose_aftype.title = _("AMPR ROSE");
585 #endif
586 #if HAVE_AFASH
587         ash_aftype.title = _("Ash");
588 #endif
589         sVafinit = 1;
590 }
591
592 static int aftrans_opt(const char *arg)
593 {
594         struct aftrans_t *paft;
595         char *tmp1, *tmp2;
596         char buf[256];
597
598         safe_strncpy(buf, arg, sizeof(buf));
599
600         tmp1 = buf;
601
602         while (tmp1) {
603
604                 tmp2 = strchr(tmp1, ',');
605
606                 if (tmp2)
607                         *(tmp2++) = '\0';
608
609                 paft = aftrans;
610                 for (paft = aftrans; paft->alias; paft++) {
611                         if (strcmp(tmp1, paft->alias))
612                                 continue;
613                         if (strlen(paft->name) + strlen(afname) + 1 >= sizeof(afname)) {
614                                 bb_error_msg(_("Too many address family arguments."));
615                                 return (0);
616                         }
617                         if (paft->flag)
618                                 (*paft->flag)++;
619                         if (afname[0])
620                                 strcat(afname, ",");
621                         strcat(afname, paft->name);
622                         break;
623                 }
624                 if (!paft->alias) {
625                         bb_error_msg(_("Unknown address family `%s'."), tmp1);
626                         return (1);
627                 }
628                 tmp1 = tmp2;
629         }
630
631         return (0);
632 }
633
634 /* set the default AF list from the program name or a constant value    */
635 static void aftrans_def(char *tool, char *argv0, char *dflt)
636 {
637         char *tmp;
638         char *buf;
639
640         strcpy(afname, dflt);
641
642         if (!(tmp = strrchr(argv0, '/')))
643                 tmp = argv0;    /* no slash?! */
644         else
645                 tmp++;
646
647         if (!(buf = strdup(tmp)))
648                 return;
649
650         if (strlen(tool) >= strlen(tmp)) {
651                 free(buf);
652                 return;
653         }
654         tmp = buf + (strlen(tmp) - strlen(tool));
655
656         if (strcmp(tmp, tool) != 0) {
657                 free(buf);
658                 return;
659         }
660         *tmp = '\0';
661         if ((tmp = strchr(buf, '_')))
662                 *tmp = '\0';
663
664         afname[0] = '\0';
665         if (aftrans_opt(buf))
666                 strcpy(afname, buf);
667
668         free(buf);
669 }
670
671 /* Check our protocol family table for this family. */
672 static struct aftype *get_aftype(const char *name)
673 {
674         struct aftype * const *afp;
675
676 #ifdef KEEP_UNUSED
677         if (!sVafinit)
678                 afinit();
679 #endif                                                  /* KEEP_UNUSED */
680
681         afp = aftypes;
682         while (*afp != NULL) {
683                 if (!strcmp((*afp)->name, name))
684                         return (*afp);
685                 afp++;
686         }
687         if (strchr(name, ','))
688                 bb_error_msg(_("Please don't supply more than one address family."));
689         return (NULL);
690 }
691 #endif                                                  /* KEEP_UNUSED */
692
693 /* Check our protocol family table for this family. */
694 static struct aftype *get_afntype(int af)
695 {
696         struct aftype * const *afp;
697
698 #ifdef KEEP_UNUSED
699         if (!sVafinit)
700                 afinit();
701 #endif                                                  /* KEEP_UNUSED */
702
703         afp = aftypes;
704         while (*afp != NULL) {
705                 if ((*afp)->af == af)
706                         return (*afp);
707                 afp++;
708         }
709         return (NULL);
710 }
711
712 /* Check our protocol family table for this family and return its socket */
713 static int get_socket_for_af(int af)
714 {
715         struct aftype * const *afp;
716
717 #ifdef KEEP_UNUSED
718         if (!sVafinit)
719                 afinit();
720 #endif                                                  /* KEEP_UNUSED */
721
722         afp = aftypes;
723         while (*afp != NULL) {
724                 if ((*afp)->af == af)
725                         return (*afp)->fd;
726                 afp++;
727         }
728         return -1;
729 }
730
731 #ifdef KEEP_UNUSED
732 /* type: 0=all, 1=getroute */
733 static void print_aflist(int type)
734 {
735         int count = 0;
736         char *txt;
737         struct aftype * const *afp;
738
739 #ifdef KEEP_UNUSED
740         if (!sVafinit)
741                 afinit();
742 #endif                                                  /* KEEP_UNUSED */
743
744         afp = aftypes;
745         while (*afp != NULL) {
746                 if ((type == 1 && ((*afp)->rprint == NULL)) || ((*afp)->af == 0)) {
747                         afp++;
748                         continue;
749                 }
750                 if ((count % 3) == 0)
751                         fprintf(stderr, count ? "\n    " : "    ");
752                 txt = (*afp)->name;
753                 if (!txt)
754                         txt = "..";
755                 fprintf(stderr, "%s (%s) ", txt, _((*afp)->title));
756                 count++;
757                 afp++;
758         }
759         fprintf(stderr, "\n");
760 }
761 #endif                                                  /* KEEP_UNUSED */
762
763 struct user_net_device_stats {
764         unsigned long long rx_packets;  /* total packets received       */
765         unsigned long long tx_packets;  /* total packets transmitted    */
766         unsigned long long rx_bytes;    /* total bytes received         */
767         unsigned long long tx_bytes;    /* total bytes transmitted      */
768         unsigned long rx_errors;        /* bad packets received         */
769         unsigned long tx_errors;        /* packet transmit problems     */
770         unsigned long rx_dropped;       /* no space in linux buffers    */
771         unsigned long tx_dropped;       /* no space available in linux  */
772         unsigned long rx_multicast;     /* multicast packets received   */
773         unsigned long rx_compressed;
774         unsigned long tx_compressed;
775         unsigned long collisions;
776
777         /* detailed rx_errors: */
778         unsigned long rx_length_errors;
779         unsigned long rx_over_errors;   /* receiver ring buff overflow  */
780         unsigned long rx_crc_errors;    /* recved pkt with crc error    */
781         unsigned long rx_frame_errors;  /* recv'd frame alignment error */
782         unsigned long rx_fifo_errors;   /* recv'r fifo overrun          */
783         unsigned long rx_missed_errors; /* receiver missed packet     */
784         /* detailed tx_errors */
785         unsigned long tx_aborted_errors;
786         unsigned long tx_carrier_errors;
787         unsigned long tx_fifo_errors;
788         unsigned long tx_heartbeat_errors;
789         unsigned long tx_window_errors;
790 };
791
792 struct interface {
793         struct interface *next, *prev;
794         char name[IFNAMSIZ];    /* interface name        */
795         short type;                     /* if type               */
796         short flags;            /* various flags         */
797         int metric;                     /* routing metric        */
798         int mtu;                        /* MTU value             */
799         int tx_queue_len;       /* transmit queue length */
800         struct ifmap map;       /* hardware setup        */
801         struct sockaddr addr;   /* IP address            */
802         struct sockaddr dstaddr;        /* P-P IP address        */
803         struct sockaddr broadaddr;      /* IP broadcast address  */
804         struct sockaddr netmask;        /* IP network mask       */
805         struct sockaddr ipxaddr_bb;     /* IPX network address   */
806         struct sockaddr ipxaddr_sn;     /* IPX network address   */
807         struct sockaddr ipxaddr_e3;     /* IPX network address   */
808         struct sockaddr ipxaddr_e2;     /* IPX network address   */
809         struct sockaddr ddpaddr;        /* Appletalk DDP address */
810         struct sockaddr ecaddr; /* Econet address        */
811         int has_ip;
812         int has_ipx_bb;
813         int has_ipx_sn;
814         int has_ipx_e3;
815         int has_ipx_e2;
816         int has_ax25;
817         int has_ddp;
818         int has_econet;
819         char hwaddr[32];        /* HW address            */
820         int statistics_valid;
821         struct user_net_device_stats stats;     /* statistics            */
822         int keepalive;          /* keepalive value for SLIP */
823         int outfill;            /* outfill value for SLIP */
824 };
825
826
827 int interface_opt_a = 0;        /* show all interfaces          */
828
829 #ifdef KEEP_UNUSED
830 static int opt_i = 0;   /* show the statistics          */
831 static int opt_v = 0;   /* debugging output flag        */
832
833 static int addr_family = 0;     /* currently selected AF        */
834 #endif                                                  /* KEEP_UNUSED */
835
836 static struct interface *int_list, *int_last;
837 static int skfd = -1;   /* generic raw socket desc.     */
838
839
840 static int sockets_open(int family)
841 {
842         struct aftype * const *aft;
843         int sfd = -1;
844         static int force = -1;
845
846         if (force < 0) {
847                 force = 0;
848                 if (get_kernel_revision() < KRELEASE(2, 1, 0))
849                         force = 1;
850                 if (access("/proc/net", R_OK))
851                         force = 1;
852         }
853         for (aft = aftypes; *aft; aft++) {
854                 struct aftype *af = *aft;
855                 int type = SOCK_DGRAM;
856
857                 if (af->af == AF_UNSPEC)
858                         continue;
859                 if (family && family != af->af)
860                         continue;
861                 if (af->fd != -1) {
862                         sfd = af->fd;
863                         continue;
864                 }
865                 /* Check some /proc file first to not stress kmod */
866                 if (!family && !force && af->flag_file) {
867                         if (access(af->flag_file, R_OK))
868                                 continue;
869                 }
870 #if HAVE_AFNETROM
871                 if (af->af == AF_NETROM)
872                         type = SOCK_SEQPACKET;
873 #endif
874 #if HAVE_AFX25
875                 if (af->af == AF_X25)
876                         type = SOCK_SEQPACKET;
877 #endif
878                 af->fd = socket(af->af, type, 0);
879                 if (af->fd >= 0)
880                         sfd = af->fd;
881         }
882         if (sfd < 0) {
883                 bb_error_msg(_("No usable address families found."));
884         }
885         return sfd;
886 }
887
888 #ifdef CONFIG_FEATURE_CLEAN_UP
889 static void sockets_close(void)
890 {
891         struct aftype * const *aft;
892         for (aft = aftypes; *aft != NULL; aft++) {
893                 struct aftype *af = *aft;
894                 if( af->fd != -1 ) {
895                         close(af->fd);
896                         af->fd = -1;
897                 }
898         }
899 }
900 #endif
901
902 /* like strcmp(), but knows about numbers */
903 static int nstrcmp(const char *a, const char *b)
904 {
905         const char *a_ptr = a;
906         const char *b_ptr = b;
907
908         while (*a == *b) {
909                 if (*a == '\0') {
910                         return 0;
911                 }
912                 if (!isdigit(*a) && isdigit(*(a+1))) {
913                         a_ptr = a+1;
914                         b_ptr = b+1;
915                 }
916                 a++;
917                 b++;
918         }
919
920         if (isdigit(*a) && isdigit(*b)) {
921                 return atoi(a_ptr) > atoi(b_ptr) ? 1 : -1;
922         }
923         return *a - *b;
924 }
925
926 static struct interface *add_interface(char *name)
927 {
928         struct interface *ife, **nextp, *new;
929
930         for (ife = int_last; ife; ife = ife->prev) {
931                 int n = nstrcmp(ife->name, name);
932
933                 if (n == 0)
934                         return ife;
935                 if (n < 0)
936                         break;
937         }
938         new(new);
939         safe_strncpy(new->name, name, IFNAMSIZ);
940         nextp = ife ? &ife->next : &int_list;
941         new->prev = ife;
942         new->next = *nextp;
943         if (new->next)
944                 new->next->prev = new;
945         else
946                 int_last = new;
947         *nextp = new;
948         return new;
949 }
950
951
952 static int if_readconf(void)
953 {
954         int numreqs = 30;
955         struct ifconf ifc;
956         struct ifreq *ifr;
957         int n, err = -1;
958         int skfd2;
959
960         /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
961            (as of 2.1.128) */
962         skfd2 = get_socket_for_af(AF_INET);
963         if (skfd2 < 0) {
964                 bb_perror_msg(("warning: no inet socket available"));
965                 /* Try to soldier on with whatever socket we can get hold of.  */
966                 skfd2 = sockets_open(0);
967                 if (skfd2 < 0)
968                         return -1;
969         }
970
971         ifc.ifc_buf = NULL;
972         for (;;) {
973                 ifc.ifc_len = sizeof(struct ifreq) * numreqs;
974                 ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
975
976                 if (ioctl(skfd2, SIOCGIFCONF, &ifc) < 0) {
977                         perror("SIOCGIFCONF");
978                         goto out;
979                 }
980                 if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
981                         /* assume it overflowed and try again */
982                         numreqs += 10;
983                         continue;
984                 }
985                 break;
986         }
987
988         ifr = ifc.ifc_req;
989         for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
990                 add_interface(ifr->ifr_name);
991                 ifr++;
992         }
993         err = 0;
994
995   out:
996         free(ifc.ifc_buf);
997         return err;
998 }
999
1000 static char *get_name(char *name, char *p)
1001 {
1002         /* Extract <name>[:<alias>] from nul-terminated p where p matches
1003            <name>[:<alias>]: after leading whitespace.
1004            If match is not made, set name empty and return unchanged p */
1005         int namestart=0, nameend=0, aliasend;
1006         while (isspace(p[namestart]))
1007                 namestart++;
1008         nameend=namestart;
1009         while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
1010                 nameend++;
1011         if (p[nameend]==':') {
1012                 aliasend=nameend+1;
1013                 while (p[aliasend] && isdigit(p[aliasend]))
1014                         aliasend++;
1015                 if (p[aliasend]==':') {
1016                         nameend=aliasend;
1017                 }
1018                 if ((nameend-namestart)<IFNAMSIZ) {
1019                         memcpy(name,&p[namestart],nameend-namestart);
1020                         name[nameend-namestart]='\0';
1021                         p=&p[nameend];
1022                 } else {
1023                         /* Interface name too large */
1024                         name[0]='\0';
1025                 }
1026         } else {
1027                 /* first ':' not found - return empty */
1028                 name[0]='\0';
1029         }
1030         return p + 1;
1031 }
1032
1033 /* If scanf supports size qualifiers for %n conversions, then we can
1034  * use a modified fmt that simply stores the position in the fields
1035  * having no associated fields in the proc string.  Of course, we need
1036  * to zero them again when we're done.  But that is smaller than the
1037  * old approach of multiple scanf occurrences with large numbers of
1038  * args. */
1039
1040 /* static const char * const ss_fmt[] = { */
1041 /*      "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
1042 /*      "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
1043 /*      "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
1044 /* }; */
1045
1046         /* Lie about the size of the int pointed to for %n. */
1047 #if INT_MAX == LONG_MAX
1048 static const char * const ss_fmt[] = {
1049         "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
1050         "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
1051         "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
1052 };
1053 #else
1054 static const char * const ss_fmt[] = {
1055         "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
1056         "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
1057         "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
1058 };
1059
1060 #endif
1061
1062 static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
1063 {
1064         memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
1065
1066         sscanf(bp, ss_fmt[procnetdev_vsn],
1067                    &ife->stats.rx_bytes, /* missing for 0 */
1068                    &ife->stats.rx_packets,
1069                    &ife->stats.rx_errors,
1070                    &ife->stats.rx_dropped,
1071                    &ife->stats.rx_fifo_errors,
1072                    &ife->stats.rx_frame_errors,
1073                    &ife->stats.rx_compressed, /* missing for <= 1 */
1074                    &ife->stats.rx_multicast, /* missing for <= 1 */
1075                    &ife->stats.tx_bytes, /* missing for 0 */
1076                    &ife->stats.tx_packets,
1077                    &ife->stats.tx_errors,
1078                    &ife->stats.tx_dropped,
1079                    &ife->stats.tx_fifo_errors,
1080                    &ife->stats.collisions,
1081                    &ife->stats.tx_carrier_errors,
1082                    &ife->stats.tx_compressed /* missing for <= 1 */
1083                    );
1084
1085         if (procnetdev_vsn <= 1) {
1086                 if (procnetdev_vsn == 0) {
1087                         ife->stats.rx_bytes = 0;
1088                         ife->stats.tx_bytes = 0;
1089                 }
1090                 ife->stats.rx_multicast = 0;
1091                 ife->stats.rx_compressed = 0;
1092                 ife->stats.tx_compressed = 0;
1093         }
1094 }
1095
1096 static inline int procnetdev_version(char *buf)
1097 {
1098         if (strstr(buf, "compressed"))
1099                 return 2;
1100         if (strstr(buf, "bytes"))
1101                 return 1;
1102         return 0;
1103 }
1104
1105 static int if_readlist_proc(char *target)
1106 {
1107         static int proc_read;
1108         FILE *fh;
1109         char buf[512];
1110         struct interface *ife;
1111         int err, procnetdev_vsn;
1112
1113         if (proc_read)
1114                 return 0;
1115         if (!target)
1116                 proc_read = 1;
1117
1118         fh = fopen(_PATH_PROCNET_DEV, "r");
1119         if (!fh) {
1120                 bb_perror_msg(_("Warning: cannot open %s. Limited output."), _PATH_PROCNET_DEV);
1121                 return if_readconf();
1122         }
1123         fgets(buf, sizeof buf, fh);     /* eat line */
1124         fgets(buf, sizeof buf, fh);
1125
1126         procnetdev_vsn = procnetdev_version(buf);
1127
1128         err = 0;
1129         while (fgets(buf, sizeof buf, fh)) {
1130                 char *s, name[128];
1131
1132                 s = get_name(name, buf);
1133                 ife = add_interface(name);
1134                 get_dev_fields(s, ife, procnetdev_vsn);
1135                 ife->statistics_valid = 1;
1136                 if (target && !strcmp(target, name))
1137                         break;
1138         }
1139         if (ferror(fh)) {
1140                 perror(_PATH_PROCNET_DEV);
1141                 err = -1;
1142                 proc_read = 0;
1143         }
1144         fclose(fh);
1145         return err;
1146 }
1147
1148 static int if_readlist(void)
1149 {
1150         int err = if_readlist_proc(NULL);
1151
1152         if (!err)
1153                 err = if_readconf();
1154         return err;
1155 }
1156
1157 static int for_all_interfaces(int (*doit) (struct interface *, void *),
1158                                                           void *cookie)
1159 {
1160         struct interface *ife;
1161
1162         if (!int_list && (if_readlist() < 0))
1163                 return -1;
1164         for (ife = int_list; ife; ife = ife->next) {
1165                 int err = doit(ife, cookie);
1166
1167                 if (err)
1168                         return err;
1169         }
1170         return 0;
1171 }
1172
1173 /* Support for fetching an IPX address */
1174
1175 #if HAVE_AFIPX
1176 static int ipx_getaddr(int sock, int ft, struct ifreq *ifr)
1177 {
1178         ((struct sockaddr_ipx *) &ifr->ifr_addr)->sipx_type = ft;
1179         return ioctl(sock, SIOCGIFADDR, ifr);
1180 }
1181 #endif
1182
1183
1184 /* Fetch the interface configuration from the kernel. */
1185 static int if_fetch(struct interface *ife)
1186 {
1187         struct ifreq ifr;
1188         int fd;
1189         char *ifname = ife->name;
1190
1191         strcpy(ifr.ifr_name, ifname);
1192         if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
1193                 return (-1);
1194         ife->flags = ifr.ifr_flags;
1195
1196         strcpy(ifr.ifr_name, ifname);
1197         if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
1198                 memset(ife->hwaddr, 0, 32);
1199         else
1200                 memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
1201
1202         ife->type = ifr.ifr_hwaddr.sa_family;
1203
1204         strcpy(ifr.ifr_name, ifname);
1205         if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0)
1206                 ife->metric = 0;
1207         else
1208                 ife->metric = ifr.ifr_metric;
1209
1210         strcpy(ifr.ifr_name, ifname);
1211         if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0)
1212                 ife->mtu = 0;
1213         else
1214                 ife->mtu = ifr.ifr_mtu;
1215
1216 #ifdef HAVE_HWSLIP
1217         if (ife->type == ARPHRD_SLIP || ife->type == ARPHRD_CSLIP ||
1218                 ife->type == ARPHRD_SLIP6 || ife->type == ARPHRD_CSLIP6 ||
1219                 ife->type == ARPHRD_ADAPT) {
1220 #ifdef SIOCGOUTFILL
1221                 strcpy(ifr.ifr_name, ifname);
1222                 if (ioctl(skfd, SIOCGOUTFILL, &ifr) < 0)
1223                         ife->outfill = 0;
1224                 else
1225                         ife->outfill = (unsigned int) ifr.ifr_data;
1226 #endif
1227 #ifdef SIOCGKEEPALIVE
1228                 strcpy(ifr.ifr_name, ifname);
1229                 if (ioctl(skfd, SIOCGKEEPALIVE, &ifr) < 0)
1230                         ife->keepalive = 0;
1231                 else
1232                         ife->keepalive = (unsigned int) ifr.ifr_data;
1233 #endif
1234         }
1235 #endif
1236
1237 #ifdef SIOCGIFMAP
1238         strcpy(ifr.ifr_name, ifname);
1239         if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
1240                 ife->map = ifr.ifr_map;
1241         else
1242 #endif
1243                 memset(&ife->map, 0, sizeof(struct ifmap));
1244
1245 #ifdef HAVE_TXQUEUELEN
1246         strcpy(ifr.ifr_name, ifname);
1247         if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0)
1248                 ife->tx_queue_len = -1; /* unknown value */
1249         else
1250                 ife->tx_queue_len = ifr.ifr_qlen;
1251 #else
1252         ife->tx_queue_len = -1; /* unknown value */
1253 #endif
1254
1255 #if HAVE_AFINET
1256         /* IPv4 address? */
1257         fd = get_socket_for_af(AF_INET);
1258         if (fd >= 0) {
1259                 strcpy(ifr.ifr_name, ifname);
1260                 ifr.ifr_addr.sa_family = AF_INET;
1261                 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
1262                         ife->has_ip = 1;
1263                         ife->addr = ifr.ifr_addr;
1264                         strcpy(ifr.ifr_name, ifname);
1265                         if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0)
1266                                 memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
1267                         else
1268                                 ife->dstaddr = ifr.ifr_dstaddr;
1269
1270                         strcpy(ifr.ifr_name, ifname);
1271                         if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
1272                                 memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
1273                         else
1274                                 ife->broadaddr = ifr.ifr_broadaddr;
1275
1276                         strcpy(ifr.ifr_name, ifname);
1277                         if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)
1278                                 memset(&ife->netmask, 0, sizeof(struct sockaddr));
1279                         else
1280                                 ife->netmask = ifr.ifr_netmask;
1281                 } else
1282                         memset(&ife->addr, 0, sizeof(struct sockaddr));
1283         }
1284 #endif
1285
1286 #if HAVE_AFATALK
1287         /* DDP address maybe ? */
1288         fd = get_socket_for_af(AF_APPLETALK);
1289         if (fd >= 0) {
1290                 strcpy(ifr.ifr_name, ifname);
1291                 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
1292                         ife->ddpaddr = ifr.ifr_addr;
1293                         ife->has_ddp = 1;
1294                 }
1295         }
1296 #endif
1297
1298 #if HAVE_AFIPX
1299         /* Look for IPX addresses with all framing types */
1300         fd = get_socket_for_af(AF_IPX);
1301         if (fd >= 0) {
1302                 strcpy(ifr.ifr_name, ifname);
1303                 if (!ipx_getaddr(fd, IPX_FRAME_ETHERII, &ifr)) {
1304                         ife->has_ipx_bb = 1;
1305                         ife->ipxaddr_bb = ifr.ifr_addr;
1306                 }
1307                 strcpy(ifr.ifr_name, ifname);
1308                 if (!ipx_getaddr(fd, IPX_FRAME_SNAP, &ifr)) {
1309                         ife->has_ipx_sn = 1;
1310                         ife->ipxaddr_sn = ifr.ifr_addr;
1311                 }
1312                 strcpy(ifr.ifr_name, ifname);
1313                 if (!ipx_getaddr(fd, IPX_FRAME_8023, &ifr)) {
1314                         ife->has_ipx_e3 = 1;
1315                         ife->ipxaddr_e3 = ifr.ifr_addr;
1316                 }
1317                 strcpy(ifr.ifr_name, ifname);
1318                 if (!ipx_getaddr(fd, IPX_FRAME_8022, &ifr)) {
1319                         ife->has_ipx_e2 = 1;
1320                         ife->ipxaddr_e2 = ifr.ifr_addr;
1321                 }
1322         }
1323 #endif
1324
1325 #if HAVE_AFECONET
1326         /* Econet address maybe? */
1327         fd = get_socket_for_af(AF_ECONET);
1328         if (fd >= 0) {
1329                 strcpy(ifr.ifr_name, ifname);
1330                 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
1331                         ife->ecaddr = ifr.ifr_addr;
1332                         ife->has_econet = 1;
1333                 }
1334         }
1335 #endif
1336
1337         return 0;
1338 }
1339
1340
1341 static int do_if_fetch(struct interface *ife)
1342 {
1343         if (if_fetch(ife) < 0) {
1344                 char *errmsg;
1345
1346                 if (errno == ENODEV) {
1347                         /* Give better error message for this case. */
1348                         errmsg = _("Device not found");
1349                 } else {
1350                         errmsg = strerror(errno);
1351                 }
1352                 bb_error_msg(_("%s: error fetching interface information: %s"),
1353                                 ife->name, errmsg);
1354                 return -1;
1355         }
1356         return 0;
1357 }
1358
1359 /* This structure defines hardware protocols and their handlers. */
1360 struct hwtype {
1361         const char * const name;
1362         const char *title;
1363         int type;
1364         int alen;
1365         char *(*print) (unsigned char *);
1366         int (*input) (char *, struct sockaddr *);
1367         int (*activate) (int fd);
1368         int suppress_null_addr;
1369 };
1370
1371 static const struct hwtype unspec_hwtype = {
1372         "unspec", "UNSPEC", -1, 0,
1373         UNSPEC_print, NULL, NULL
1374 };
1375
1376 static const struct hwtype loop_hwtype = {
1377         "loop", "Local Loopback", ARPHRD_LOOPBACK, 0,
1378         NULL, NULL, NULL
1379 };
1380
1381 #if HAVE_HWETHER
1382 #include <net/if_arp.h>
1383
1384 #if (__GLIBC__ >=2 && __GLIBC_MINOR >= 1) || defined(_NEWLIB_VERSION)
1385 #include <net/ethernet.h>
1386 #else
1387 #include <linux/if_ether.h>
1388 #endif
1389
1390 /* Display an Ethernet address in readable format. */
1391 static char *pr_ether(unsigned char *ptr)
1392 {
1393         static char buff[64];
1394
1395         snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
1396                          (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
1397                          (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
1398                 );
1399         return (buff);
1400 }
1401
1402 #ifdef KEEP_UNUSED
1403 /* Input an Ethernet address and convert to binary. */
1404 static int in_ether(char *bufp, struct sockaddr *sap)
1405 {
1406         unsigned char *ptr;
1407         char c, *orig;
1408         int i;
1409         unsigned val;
1410
1411         sap->sa_family = ether_hwtype.type;
1412         ptr = sap->sa_data;
1413
1414         i = 0;
1415         orig = bufp;
1416         while ((*bufp != '\0') && (i < ETH_ALEN)) {
1417                 val = 0;
1418                 c = *bufp++;
1419                 if (isdigit(c))
1420                         val = c - '0';
1421                 else if (c >= 'a' && c <= 'f')
1422                         val = c - 'a' + 10;
1423                 else if (c >= 'A' && c <= 'F')
1424                         val = c - 'A' + 10;
1425                 else {
1426 #ifdef DEBUG
1427                         bb_error_msg(_("in_ether(%s): invalid ether address!\n"), orig);
1428 #endif
1429                         errno = EINVAL;
1430                         return (-1);
1431                 }
1432                 val <<= 4;
1433                 c = *bufp;
1434                 if (isdigit(c))
1435                         val |= c - '0';
1436                 else if (c >= 'a' && c <= 'f')
1437                         val |= c - 'a' + 10;
1438                 else if (c >= 'A' && c <= 'F')
1439                         val |= c - 'A' + 10;
1440                 else if (c == ':' || c == 0)
1441                         val >>= 4;
1442                 else {
1443 #ifdef DEBUG
1444                         bb_error_msg(_("in_ether(%s): invalid ether address!"), orig);
1445 #endif
1446                         errno = EINVAL;
1447                         return (-1);
1448                 }
1449                 if (c != 0)
1450                         bufp++;
1451                 *ptr++ = (unsigned char) (val & 0377);
1452                 i++;
1453
1454                 /* We might get a semicolon here - not required. */
1455                 if (*bufp == ':') {
1456 #ifdef DEBUG
1457                         if (i == ETH_ALEN) {
1458                                 bb_error_msg(_("in_ether(%s): trailing : ignored!"), orig);
1459                         }
1460 #endif
1461                         bufp++;
1462                 }
1463         }
1464
1465 #ifdef DEBUG
1466         /* That's it.  Any trailing junk? */
1467         if ((i == ETH_ALEN) && (*bufp != '\0')) {
1468                 bb_error_msg(_("in_ether(%s): trailing junk!"), orig);
1469                 errno = EINVAL;
1470                 return (-1);
1471         }
1472         bb_error_msg("in_ether(%s): %s", orig, pr_ether(sap->sa_data));
1473 #endif
1474
1475         return (0);
1476 }
1477 #endif                                                  /* KEEP_UNUSED */
1478
1479
1480 static const struct hwtype ether_hwtype = {
1481         "ether", "Ethernet", ARPHRD_ETHER, ETH_ALEN,
1482         pr_ether, NULL /* UNUSED in_ether */ , NULL
1483 };
1484
1485
1486 #endif                                                  /* HAVE_HWETHER */
1487
1488
1489 #if HAVE_HWPPP
1490
1491 #include <net/if_arp.h>
1492
1493 #ifdef KEEP_UNUSED
1494 /* Start the PPP encapsulation on the file descriptor. */
1495 static int do_ppp(int fd)
1496 {
1497         bb_error_msg(_("You cannot start PPP with this program."));
1498         return -1;
1499 }
1500 #endif                                                  /* KEEP_UNUSED */
1501
1502 static const struct hwtype ppp_hwtype = {
1503         "ppp", "Point-Point Protocol", ARPHRD_PPP, 0,
1504         NULL, NULL, NULL /* UNUSED do_ppp */ , 0
1505 };
1506
1507
1508 #endif                                                  /* HAVE_PPP */
1509
1510 static const struct hwtype * const hwtypes[] = {
1511
1512         &loop_hwtype,
1513
1514 #if HAVE_HWSLIP
1515         &slip_hwtype,
1516         &cslip_hwtype,
1517         &slip6_hwtype,
1518         &cslip6_hwtype,
1519         &adaptive_hwtype,
1520 #endif
1521 #if HAVE_HWSTRIP
1522         &strip_hwtype,
1523 #endif
1524 #if HAVE_HWASH
1525         &ash_hwtype,
1526 #endif
1527 #if HAVE_HWETHER
1528         &ether_hwtype,
1529 #endif
1530 #if HAVE_HWTR
1531         &tr_hwtype,
1532 #ifdef ARPHRD_IEEE802_TR
1533         &tr_hwtype1,
1534 #endif
1535 #endif
1536 #if HAVE_HWAX25
1537         &ax25_hwtype,
1538 #endif
1539 #if HAVE_HWNETROM
1540         &netrom_hwtype,
1541 #endif
1542 #if HAVE_HWROSE
1543         &rose_hwtype,
1544 #endif
1545 #if HAVE_HWTUNNEL
1546         &tunnel_hwtype,
1547 #endif
1548 #if HAVE_HWPPP
1549         &ppp_hwtype,
1550 #endif
1551 #if HAVE_HWHDLCLAPB
1552         &hdlc_hwtype,
1553         &lapb_hwtype,
1554 #endif
1555 #if HAVE_HWARC
1556         &arcnet_hwtype,
1557 #endif
1558 #if HAVE_HWFR
1559         &dlci_hwtype,
1560         &frad_hwtype,
1561 #endif
1562 #if HAVE_HWSIT
1563         &sit_hwtype,
1564 #endif
1565 #if HAVE_HWFDDI
1566         &fddi_hwtype,
1567 #endif
1568 #if HAVE_HWHIPPI
1569         &hippi_hwtype,
1570 #endif
1571 #if HAVE_HWIRDA
1572         &irda_hwtype,
1573 #endif
1574 #if HAVE_HWEC
1575         &ec_hwtype,
1576 #endif
1577 #if HAVE_HWX25
1578         &x25_hwtype,
1579 #endif
1580         &unspec_hwtype,
1581         NULL
1582 };
1583
1584 #ifdef KEEP_UNUSED
1585 static short sVhwinit = 0;
1586
1587 static void hwinit()
1588 {
1589         loop_hwtype.title = _("Local Loopback");
1590         unspec_hwtype.title = _("UNSPEC");
1591 #if HAVE_HWSLIP
1592         slip_hwtype.title = _("Serial Line IP");
1593         cslip_hwtype.title = _("VJ Serial Line IP");
1594         slip6_hwtype.title = _("6-bit Serial Line IP");
1595         cslip6_hwtype.title = _("VJ 6-bit Serial Line IP");
1596         adaptive_hwtype.title = _("Adaptive Serial Line IP");
1597 #endif
1598 #if HAVE_HWETHER
1599         ether_hwtype.title = _("Ethernet");
1600 #endif
1601 #if HAVE_HWASH
1602         ash_hwtype.title = _("Ash");
1603 #endif
1604 #if HAVE_HWFDDI
1605         fddi_hwtype.title = _("Fiber Distributed Data Interface");
1606 #endif
1607 #if HAVE_HWHIPPI
1608         hippi_hwtype.title = _("HIPPI");
1609 #endif
1610 #if HAVE_HWAX25
1611         ax25_hwtype.title = _("AMPR AX.25");
1612 #endif
1613 #if HAVE_HWROSE
1614         rose_hwtype.title = _("AMPR ROSE");
1615 #endif
1616 #if HAVE_HWNETROM
1617         netrom_hwtype.title = _("AMPR NET/ROM");
1618 #endif
1619 #if HAVE_HWX25
1620         x25_hwtype.title = _("generic X.25");
1621 #endif
1622 #if HAVE_HWTUNNEL
1623         tunnel_hwtype.title = _("IPIP Tunnel");
1624 #endif
1625 #if HAVE_HWPPP
1626         ppp_hwtype.title = _("Point-to-Point Protocol");
1627 #endif
1628 #if HAVE_HWHDLCLAPB
1629         hdlc_hwtype.title = _("(Cisco)-HDLC");
1630         lapb_hwtype.title = _("LAPB");
1631 #endif
1632 #if HAVE_HWARC
1633         arcnet_hwtype.title = _("ARCnet");
1634 #endif
1635 #if HAVE_HWFR
1636         dlci_hwtype.title = _("Frame Relay DLCI");
1637         frad_hwtype.title = _("Frame Relay Access Device");
1638 #endif
1639 #if HAVE_HWSIT
1640         sit_hwtype.title = _("IPv6-in-IPv4");
1641 #endif
1642 #if HAVE_HWIRDA
1643         irda_hwtype.title = _("IrLAP");
1644 #endif
1645 #if HAVE_HWTR
1646         tr_hwtype.title = _("16/4 Mbps Token Ring");
1647 #ifdef ARPHRD_IEEE802_TR
1648         tr_hwtype1.title = _("16/4 Mbps Token Ring (New)");
1649 #endif
1650 #endif
1651 #if HAVE_HWEC
1652         ec_hwtype.title = _("Econet");
1653 #endif
1654         sVhwinit = 1;
1655 }
1656 #endif                                                  /* KEEP_UNUSED */
1657
1658 #ifdef IFF_PORTSEL
1659 #if 0
1660 static const char * const if_port_text[][4] = {
1661         /* Keep in step with <linux/netdevice.h> */
1662         {"unknown", NULL, NULL, NULL},
1663         {"10base2", "bnc", "coax", NULL},
1664         {"10baseT", "utp", "tpe", NULL},
1665         {"AUI", "thick", "db15", NULL},
1666         {"100baseT", NULL, NULL, NULL},
1667         {"100baseTX", NULL, NULL, NULL},
1668         {"100baseFX", NULL, NULL, NULL},
1669         {NULL, NULL, NULL, NULL},
1670 };
1671 #else
1672 static const char * const if_port_text[] = {
1673         /* Keep in step with <linux/netdevice.h> */
1674         "unknown",
1675         "10base2",
1676         "10baseT",
1677         "AUI",
1678         "100baseT",
1679         "100baseTX",
1680         "100baseFX",
1681         NULL
1682 };
1683 #endif
1684 #endif
1685
1686 /* Check our hardware type table for this type. */
1687 static const struct hwtype *get_hwntype(int type)
1688 {
1689         const struct hwtype * const *hwp;
1690
1691 #ifdef KEEP_UNUSED
1692         if (!sVhwinit)
1693                 hwinit();
1694 #endif                                                  /* KEEP_UNUSED */
1695
1696         hwp = hwtypes;
1697         while (*hwp != NULL) {
1698                 if ((*hwp)->type == type)
1699                         return (*hwp);
1700                 hwp++;
1701         }
1702         return (NULL);
1703 }
1704
1705 /* return 1 if address is all zeros */
1706 static int hw_null_address(const struct hwtype *hw, void *ap)
1707 {
1708         unsigned int i;
1709         unsigned char *address = (unsigned char *) ap;
1710
1711         for (i = 0; i < hw->alen; i++)
1712                 if (address[i])
1713                         return 0;
1714         return 1;
1715 }
1716
1717 static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
1718
1719 static void print_bytes_scaled(unsigned long long ull, const char *end)
1720 {
1721         unsigned long long int_part;
1722         const char *ext;
1723         unsigned int frac_part;
1724         int i;
1725
1726         frac_part = 0;
1727         ext = TRext;
1728         int_part = ull;
1729         i = 4;
1730         do {
1731 #if 0
1732                 /* This does correct rounding and is a little larger.  But it
1733                  * uses KiB as the smallest displayed unit. */
1734                 if ((int_part < (1024*1024 - 51)) || !--i) {
1735                         i = 0;
1736                         int_part += 51;         /* 1024*.05 = 51.2 */
1737                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
1738                 }
1739                 int_part /= 1024;
1740                 ext += 3;       /* KiB, MiB, GiB, TiB */
1741 #else
1742                 if (int_part >= 1024) {
1743                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
1744                         int_part /= 1024;
1745                         ext += 3;       /* KiB, MiB, GiB, TiB */
1746                 }
1747                 --i;
1748 #endif
1749         } while (i);
1750
1751         printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
1752 }
1753
1754 static const char * const ife_print_flags_strs[] = {
1755         "UP ",
1756         "BROADCAST ",
1757         "DEBUG ",
1758         "LOOPBACK ",
1759         "POINTOPOINT ",
1760         "NOTRAILERS ",
1761         "RUNNING ",
1762         "NOARP ",
1763         "PROMISC ",
1764         "ALLMULTI ",
1765         "SLAVE ",
1766         "MASTER ",
1767         "MULTICAST ",
1768 #ifdef HAVE_DYNAMIC
1769         "DYNAMIC "
1770 #endif
1771 };
1772
1773 static const unsigned short ife_print_flags_mask[] = {
1774         IFF_UP,
1775         IFF_BROADCAST,
1776         IFF_DEBUG,
1777         IFF_LOOPBACK,
1778         IFF_POINTOPOINT,
1779         IFF_NOTRAILERS,
1780         IFF_RUNNING,
1781         IFF_NOARP,
1782         IFF_PROMISC,
1783         IFF_ALLMULTI,
1784         IFF_SLAVE,
1785         IFF_MASTER,
1786         IFF_MULTICAST,
1787 #ifdef HAVE_DYNAMIC
1788         IFF_DYNAMIC
1789 #endif
1790         0
1791 };
1792
1793 static void ife_print(struct interface *ptr)
1794 {
1795         struct aftype *ap;
1796         const struct hwtype *hw;
1797         int hf;
1798         int can_compress = 0;
1799
1800 #if HAVE_AFIPX
1801         static struct aftype *ipxtype = NULL;
1802 #endif
1803 #if HAVE_AFECONET
1804         static struct aftype *ectype = NULL;
1805 #endif
1806 #if HAVE_AFATALK
1807         static struct aftype *ddptype = NULL;
1808 #endif
1809 #if HAVE_AFINET6
1810         FILE *f;
1811         char addr6[40], devname[20];
1812         struct sockaddr_in6 sap;
1813         int plen, scope, dad_status, if_idx;
1814         char addr6p[8][5];
1815 #endif
1816
1817         ap = get_afntype(ptr->addr.sa_family);
1818         if (ap == NULL)
1819                 ap = get_afntype(0);
1820
1821         hf = ptr->type;
1822
1823         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
1824                 can_compress = 1;
1825
1826         hw = get_hwntype(hf);
1827         if (hw == NULL)
1828                 hw = get_hwntype(-1);
1829
1830         printf(_("%-9.9s Link encap:%s  "), ptr->name, _(hw->title));
1831         /* For some hardware types (eg Ash, ATM) we don't print the
1832            hardware address if it's null.  */
1833         if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
1834                                                                 hw->suppress_null_addr)))
1835                 printf(_("HWaddr %s  "), hw->print((unsigned char *)ptr->hwaddr));
1836 #ifdef IFF_PORTSEL
1837         if (ptr->flags & IFF_PORTSEL) {
1838                 printf(_("Media:%s"), if_port_text[ptr->map.port] /* [0] */);
1839                 if (ptr->flags & IFF_AUTOMEDIA)
1840                         printf(_("(auto)"));
1841         }
1842 #endif
1843         printf("\n");
1844
1845 #if HAVE_AFINET
1846         if (ptr->has_ip) {
1847                 printf(_("          %s addr:%s "), ap->name,
1848                            ap->sprint(&ptr->addr, 1));
1849                 if (ptr->flags & IFF_POINTOPOINT) {
1850                         printf(_(" P-t-P:%s "), ap->sprint(&ptr->dstaddr, 1));
1851                 }
1852                 if (ptr->flags & IFF_BROADCAST) {
1853                         printf(_(" Bcast:%s "), ap->sprint(&ptr->broadaddr, 1));
1854                 }
1855                 printf(_(" Mask:%s\n"), ap->sprint(&ptr->netmask, 1));
1856         }
1857 #endif
1858
1859 #if HAVE_AFINET6
1860
1861 #define IPV6_ADDR_ANY           0x0000U
1862
1863 #define IPV6_ADDR_UNICAST       0x0001U
1864 #define IPV6_ADDR_MULTICAST     0x0002U
1865 #define IPV6_ADDR_ANYCAST       0x0004U
1866
1867 #define IPV6_ADDR_LOOPBACK      0x0010U
1868 #define IPV6_ADDR_LINKLOCAL     0x0020U
1869 #define IPV6_ADDR_SITELOCAL     0x0040U
1870
1871 #define IPV6_ADDR_COMPATv4      0x0080U
1872
1873 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
1874
1875 #define IPV6_ADDR_MAPPED        0x1000U
1876 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
1877
1878         if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
1879                 while (fscanf
1880                            (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
1881                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
1882                                 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
1883                                 &dad_status, devname) != EOF) {
1884                         if (!strcmp(devname, ptr->name)) {
1885                                 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
1886                                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
1887                                                 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
1888                                 inet_pton(AF_INET6, addr6,
1889                                                   (struct sockaddr *) &sap.sin6_addr);
1890                                 sap.sin6_family = AF_INET6;
1891                                 printf(_("          inet6 addr: %s/%d"),
1892                                            inet6_aftype.sprint((struct sockaddr *) &sap, 1),
1893                                            plen);
1894                                 printf(_(" Scope:"));
1895                                 switch (scope & IPV6_ADDR_SCOPE_MASK) {
1896                                 case 0:
1897                                         printf(_("Global"));
1898                                         break;
1899                                 case IPV6_ADDR_LINKLOCAL:
1900                                         printf(_("Link"));
1901                                         break;
1902                                 case IPV6_ADDR_SITELOCAL:
1903                                         printf(_("Site"));
1904                                         break;
1905                                 case IPV6_ADDR_COMPATv4:
1906                                         printf(_("Compat"));
1907                                         break;
1908                                 case IPV6_ADDR_LOOPBACK:
1909                                         printf(_("Host"));
1910                                         break;
1911                                 default:
1912                                         printf(_("Unknown"));
1913                                 }
1914                                 printf("\n");
1915                         }
1916                 }
1917                 fclose(f);
1918         }
1919 #endif
1920
1921 #if HAVE_AFIPX
1922         if (ipxtype == NULL)
1923                 ipxtype = get_afntype(AF_IPX);
1924
1925         if (ipxtype != NULL) {
1926                 if (ptr->has_ipx_bb)
1927                         printf(_("          IPX/Ethernet II addr:%s\n"),
1928                                    ipxtype->sprint(&ptr->ipxaddr_bb, 1));
1929                 if (ptr->has_ipx_sn)
1930                         printf(_("          IPX/Ethernet SNAP addr:%s\n"),
1931                                    ipxtype->sprint(&ptr->ipxaddr_sn, 1));
1932                 if (ptr->has_ipx_e2)
1933                         printf(_("          IPX/Ethernet 802.2 addr:%s\n"),
1934                                    ipxtype->sprint(&ptr->ipxaddr_e2, 1));
1935                 if (ptr->has_ipx_e3)
1936                         printf(_("          IPX/Ethernet 802.3 addr:%s\n"),
1937                                    ipxtype->sprint(&ptr->ipxaddr_e3, 1));
1938         }
1939 #endif
1940
1941 #if HAVE_AFATALK
1942         if (ddptype == NULL)
1943                 ddptype = get_afntype(AF_APPLETALK);
1944         if (ddptype != NULL) {
1945                 if (ptr->has_ddp)
1946                         printf(_("          EtherTalk Phase 2 addr:%s\n"),
1947                                    ddptype->sprint(&ptr->ddpaddr, 1));
1948         }
1949 #endif
1950
1951 #if HAVE_AFECONET
1952         if (ectype == NULL)
1953                 ectype = get_afntype(AF_ECONET);
1954         if (ectype != NULL) {
1955                 if (ptr->has_econet)
1956                         printf(_("          econet addr:%s\n"),
1957                                    ectype->sprint(&ptr->ecaddr, 1));
1958         }
1959 #endif
1960
1961         printf("          ");
1962         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1963
1964         if (ptr->flags == 0) {
1965                 printf(_("[NO FLAGS] "));
1966         } else {
1967                 int i = 0;
1968                 do {
1969                         if (ptr->flags & ife_print_flags_mask[i]) {
1970                                 printf(_(ife_print_flags_strs[i]));
1971                         }
1972                 } while (ife_print_flags_mask[++i]);
1973         }
1974
1975         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1976         printf(_(" MTU:%d  Metric:%d"), ptr->mtu, ptr->metric ? ptr->metric : 1);
1977 #ifdef SIOCSKEEPALIVE
1978         if (ptr->outfill || ptr->keepalive)
1979                 printf(_("  Outfill:%d  Keepalive:%d"), ptr->outfill, ptr->keepalive);
1980 #endif
1981         printf("\n");
1982
1983         /* If needed, display the interface statistics. */
1984
1985         if (ptr->statistics_valid) {
1986                 /* XXX: statistics are currently only printed for the primary address,
1987                  *      not for the aliases, although strictly speaking they're shared
1988                  *      by all addresses.
1989                  */
1990                 printf("          ");
1991
1992                 printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1993                            ptr->stats.rx_packets, ptr->stats.rx_errors,
1994                            ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1995                            ptr->stats.rx_frame_errors);
1996                 if (can_compress)
1997                         printf("             compressed:%lu\n",
1998                                    ptr->stats.rx_compressed);
1999                 printf("          ");
2000                 printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
2001                            ptr->stats.tx_packets, ptr->stats.tx_errors,
2002                            ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
2003                            ptr->stats.tx_carrier_errors);
2004                 printf("          collisions:%lu ", ptr->stats.collisions);
2005                 if (can_compress)
2006                         printf("compressed:%lu ", ptr->stats.tx_compressed);
2007                 if (ptr->tx_queue_len != -1)
2008                         printf("txqueuelen:%d ", ptr->tx_queue_len);
2009                 printf("\n          R");
2010                 print_bytes_scaled(ptr->stats.rx_bytes, "  T");
2011                 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
2012
2013         }
2014
2015         if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
2016                  ptr->map.base_addr)) {
2017                 printf("          ");
2018                 if (ptr->map.irq)
2019                         printf(_("Interrupt:%d "), ptr->map.irq);
2020                 if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
2021                                                                                            I/O maps */
2022                         printf(_("Base address:0x%lx "),
2023                                    (unsigned long) ptr->map.base_addr);
2024                 if (ptr->map.mem_start) {
2025                         printf(_("Memory:%lx-%lx "), ptr->map.mem_start,
2026                                    ptr->map.mem_end);
2027                 }
2028                 if (ptr->map.dma)
2029                         printf(_("DMA chan:%x "), ptr->map.dma);
2030                 printf("\n");
2031         }
2032         printf("\n");
2033 }
2034
2035
2036 static int do_if_print(struct interface *ife, void *cookie)
2037 {
2038         int *opt_a = (int *) cookie;
2039         int res;
2040
2041         res = do_if_fetch(ife);
2042         if (res >= 0) {
2043                 if ((ife->flags & IFF_UP) || *opt_a)
2044                         ife_print(ife);
2045         }
2046         return res;
2047 }
2048
2049 static struct interface *lookup_interface(char *name)
2050 {
2051         struct interface *ife = NULL;
2052
2053         if (if_readlist_proc(name) < 0)
2054                 return NULL;
2055         ife = add_interface(name);
2056         return ife;
2057 }
2058
2059 /* for ipv4 add/del modes */
2060 static int if_print(char *ifname)
2061 {
2062         int res;
2063
2064         if (!ifname) {
2065                 res = for_all_interfaces(do_if_print, &interface_opt_a);
2066         } else {
2067                 struct interface *ife;
2068
2069                 ife = lookup_interface(ifname);
2070                 res = do_if_fetch(ife);
2071                 if (res >= 0)
2072                         ife_print(ife);
2073         }
2074         return res;
2075 }
2076
2077 int display_interfaces(char *ifname)
2078 {
2079         int status;
2080
2081         /* Create a channel to the NET kernel. */
2082         if ((skfd = sockets_open(0)) < 0) {
2083                 bb_perror_msg_and_die("socket");
2084         }
2085
2086         /* Do we have to show the current setup? */
2087         status = if_print(ifname);
2088 #ifdef CONFIG_FEATURE_CLEAN_UP
2089         sockets_close();
2090 #endif
2091         exit(status < 0);
2092 }