Patch from Denis Vlasenko turning static const int (which gets emitted into
[oweals/busybox.git] / networking / ifconfig.c
1 /* ifconfig
2  *
3  * Similar to the standard Unix ifconfig, but with only the necessary
4  * parts for AF_INET, and without any printing of if info (for now).
5  *
6  * Bjorn Wesen, Axis Communications AB
7  *
8  *
9  * Authors of the original ifconfig was:
10  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
11  *
12  * This program is free software; you can redistribute it
13  * and/or  modify it under  the terms of  the GNU General
14  * Public  License as  published  by  the  Free  Software
15  * Foundation;  either  version 2 of the License, or  (at
16  * your option) any later version.
17  *
18  * $Id: ifconfig.c,v 1.30 2004/03/31 11:30:08 andersen Exp $
19  *
20  */
21
22 /*
23  * Heavily modified by Manuel Novoa III       Mar 6, 2001
24  *
25  * From initial port to busybox, removed most of the redundancy by
26  * converting to a table-driven approach.  Added several (optional)
27  * args missing from initial port.
28  *
29  * Still missing:  media, tunnel.
30  *
31  * 2002-04-20
32  * IPV6 support added by Bart Visscher <magick@linux-fan.com>
33  */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>             /* strcmp and friends */
38 #include <ctype.h>              /* isdigit and friends */
39 #include <stddef.h>             /* offsetof */
40 #include <unistd.h>
41 #include <netdb.h>
42 #include <sys/ioctl.h>
43 #include <net/if.h>
44 #include <net/if_arp.h>
45 #include <netinet/in.h>
46 #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
47 #include <netpacket/packet.h>
48 #include <net/ethernet.h>
49 #else
50 #include <sys/types.h>
51 #include <netinet/if_ether.h>
52 #endif
53 #include "inet_common.h"
54 #include "busybox.h"
55
56 #ifdef CONFIG_FEATURE_IFCONFIG_SLIP
57 # include <net/if_slip.h>
58 #endif
59
60 /* I don't know if this is needed for busybox or not.  Anyone? */
61 #define QUESTIONABLE_ALIAS_CASE
62
63
64 /* Defines for glibc2.0 users. */
65 #ifndef SIOCSIFTXQLEN
66 # define SIOCSIFTXQLEN      0x8943
67 # define SIOCGIFTXQLEN      0x8942
68 #endif
69
70 /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
71 #ifndef ifr_qlen
72 # define ifr_qlen        ifr_ifru.ifru_mtu
73 #endif
74
75 #ifndef IFF_DYNAMIC
76 # define IFF_DYNAMIC     0x8000 /* dialup device with changing addresses */
77 #endif
78
79 #ifdef CONFIG_FEATURE_IPV6
80 struct in6_ifreq {
81         struct in6_addr ifr6_addr;
82         uint32_t ifr6_prefixlen;
83         int ifr6_ifindex;
84 };
85 #endif
86
87 /*
88  * Here are the bit masks for the "flags" member of struct options below.
89  * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
90  * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
91  */
92 #define N_CLR            0x01
93 #define M_CLR            0x02
94 #define N_SET            0x04
95 #define M_SET            0x08
96 #define N_ARG            0x10
97 #define M_ARG            0x20
98
99 #define M_MASK           (M_CLR | M_SET | M_ARG)
100 #define N_MASK           (N_CLR | N_SET | N_ARG)
101 #define SET_MASK         (N_SET | M_SET)
102 #define CLR_MASK         (N_CLR | M_CLR)
103 #define SET_CLR_MASK     (SET_MASK | CLR_MASK)
104 #define ARG_MASK         (M_ARG | N_ARG)
105
106 /*
107  * Here are the bit masks for the "arg_flags" member of struct options below.
108  */
109
110 /*
111  * cast type:
112  *   00 int
113  *   01 char *
114  *   02 HOST_COPY in_ether
115  *   03 HOST_COPY INET_resolve
116  */
117 #define A_CAST_TYPE      0x03
118 /*
119  * map type:
120  *   00 not a map type (mem_start, io_addr, irq)
121  *   04 memstart (unsigned long)
122  *   08 io_addr  (unsigned short)
123  *   0C irq      (unsigned char)
124  */
125 #define A_MAP_TYPE       0x0C
126 #define A_ARG_REQ        0x10   /* Set if an arg is required. */
127 #define A_NETMASK        0x20   /* Set if netmask (check for multiple sets). */
128 #define A_SET_AFTER      0x40   /* Set a flag at the end. */
129 #define A_COLON_CHK      0x80   /* Is this needed?  See below. */
130 #ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
131 #define A_HOSTNAME      0x100   /* Set if it is ip addr. */
132 #define A_BROADCAST     0x200   /* Set if it is broadcast addr. */
133 #else
134 #define A_HOSTNAME          0
135 #define A_BROADCAST         0
136 #endif
137
138 /*
139  * These defines are for dealing with the A_CAST_TYPE field.
140  */
141 #define A_CAST_CHAR_PTR  0x01
142 #define A_CAST_RESOLVE   0x01
143 #define A_CAST_HOST_COPY 0x02
144 #define A_CAST_HOST_COPY_IN_ETHER    A_CAST_HOST_COPY
145 #define A_CAST_HOST_COPY_RESOLVE     (A_CAST_HOST_COPY | A_CAST_RESOLVE)
146
147 /*
148  * These defines are for dealing with the A_MAP_TYPE field.
149  */
150 #define A_MAP_ULONG      0x04   /* memstart */
151 #define A_MAP_USHORT     0x08   /* io_addr */
152 #define A_MAP_UCHAR      0x0C   /* irq */
153
154 /*
155  * Define the bit masks signifying which operations to perform for each arg.
156  */
157
158 #define ARG_METRIC       (A_ARG_REQ /*| A_CAST_INT*/)
159 #define ARG_MTU          (A_ARG_REQ /*| A_CAST_INT*/)
160 #define ARG_TXQUEUELEN   (A_ARG_REQ /*| A_CAST_INT*/)
161 #define ARG_MEM_START    (A_ARG_REQ | A_MAP_ULONG)
162 #define ARG_IO_ADDR      (A_ARG_REQ | A_MAP_ULONG)
163 #define ARG_IRQ          (A_ARG_REQ | A_MAP_UCHAR)
164 #define ARG_DSTADDR      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
165 #define ARG_NETMASK      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
166 #define ARG_BROADCAST    (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
167 #define ARG_HW           (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
168 #define ARG_POINTOPOINT  (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
169 #define ARG_KEEPALIVE    (A_ARG_REQ | A_CAST_CHAR_PTR)
170 #define ARG_OUTFILL      (A_ARG_REQ | A_CAST_CHAR_PTR)
171 #define ARG_HOSTNAME     (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
172 #define ARG_ADD_DEL      (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
173
174
175 /*
176  * Set up the tables.  Warning!  They must have corresponding order!
177  */
178
179 struct arg1opt {
180         const char *name;
181         int selector;
182         unsigned short ifr_offset;
183 };
184
185 struct options {
186         const char *name;
187 #ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
188         const unsigned int flags:6;
189         const unsigned int arg_flags:10;
190 #else
191         const unsigned char flags;
192         const unsigned char arg_flags;
193 #endif
194         const unsigned short selector;
195 };
196
197 #define ifreq_offsetof(x)  offsetof(struct ifreq, x)
198
199 static const struct arg1opt Arg1Opt[] = {
200         {"SIOCSIFMETRIC",  SIOCSIFMETRIC,  ifreq_offsetof(ifr_metric)},
201         {"SIOCSIFMTU",     SIOCSIFMTU,     ifreq_offsetof(ifr_mtu)},
202         {"SIOCSIFTXQLEN",  SIOCSIFTXQLEN,  ifreq_offsetof(ifr_qlen)},
203         {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
204         {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
205         {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
206 #ifdef CONFIG_FEATURE_IFCONFIG_HW
207         {"SIOCSIFHWADDR",  SIOCSIFHWADDR,  ifreq_offsetof(ifr_hwaddr)},
208 #endif
209         {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
210 #ifdef SIOCSKEEPALIVE
211         {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
212 #endif
213 #ifdef SIOCSOUTFILL
214         {"SIOCSOUTFILL",   SIOCSOUTFILL,   ifreq_offsetof(ifr_data)},
215 #endif
216 #ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
217         {"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.mem_start)},
218         {"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.base_addr)},
219         {"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.irq)},
220 #endif
221         /* Last entry if for unmatched (possibly hostname) arg. */
222 #ifdef CONFIG_FEATURE_IPV6
223         {"SIOCSIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
224         {"SIOCDIFADDR",    SIOCDIFADDR,    ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
225 #endif
226         {"SIOCSIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr)},
227 };
228
229 static const struct options OptArray[] = {
230         {"metric",      N_ARG,         ARG_METRIC,      0},
231         {"mtu",         N_ARG,         ARG_MTU,         0},
232         {"txqueuelen",  N_ARG,         ARG_TXQUEUELEN,  0},
233         {"dstaddr",     N_ARG,         ARG_DSTADDR,     0},
234         {"netmask",     N_ARG,         ARG_NETMASK,     0},
235         {"broadcast",   N_ARG | M_CLR, ARG_BROADCAST,   IFF_BROADCAST},
236 #ifdef CONFIG_FEATURE_IFCONFIG_HW
237         {"hw",          N_ARG, ARG_HW,                  0},
238 #endif
239         {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
240 #ifdef SIOCSKEEPALIVE
241         {"keepalive",   N_ARG,         ARG_KEEPALIVE,   0},
242 #endif
243 #ifdef SIOCSOUTFILL
244         {"outfill",     N_ARG,         ARG_OUTFILL,     0},
245 #endif
246 #ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
247         {"mem_start",   N_ARG,         ARG_MEM_START,   0},
248         {"io_addr",     N_ARG,         ARG_IO_ADDR,     0},
249         {"irq",         N_ARG,         ARG_IRQ,         0},
250 #endif
251 #ifdef CONFIG_FEATURE_IPV6
252         {"add",         N_ARG,         ARG_ADD_DEL,     0},
253         {"del",         N_ARG,         ARG_ADD_DEL,     0},
254 #endif
255         {"arp",         N_CLR | M_SET, 0,               IFF_NOARP},
256         {"trailers",    N_CLR | M_SET, 0,               IFF_NOTRAILERS},
257         {"promisc",     N_SET | M_CLR, 0,               IFF_PROMISC},
258         {"multicast",   N_SET | M_CLR, 0,               IFF_MULTICAST},
259         {"allmulti",    N_SET | M_CLR, 0,               IFF_ALLMULTI},
260         {"dynamic",     N_SET | M_CLR, 0,               IFF_DYNAMIC},
261         {"up",          N_SET,         0,               (IFF_UP | IFF_RUNNING)},
262         {"down",        N_CLR,         0,               IFF_UP},
263         {NULL,          0,             ARG_HOSTNAME,    (IFF_UP | IFF_RUNNING)}
264 };
265
266 /*
267  * A couple of prototypes.
268  */
269
270 #ifdef CONFIG_FEATURE_IFCONFIG_HW
271 static int in_ether(char *bufp, struct sockaddr *sap);
272 #endif
273
274 #ifdef CONFIG_FEATURE_IFCONFIG_STATUS
275 extern int interface_opt_a;
276 extern int display_interfaces(char *ifname);
277 #endif
278
279 /*
280  * Our main function.
281  */
282
283 int ifconfig_main(int argc, char **argv)
284 {
285         struct ifreq ifr;
286         struct sockaddr_in sai;
287 #ifdef CONFIG_FEATURE_IPV6
288         struct sockaddr_in6 sai6;
289 #endif
290 #ifdef CONFIG_FEATURE_IFCONFIG_HW
291         struct sockaddr sa;
292 #endif
293         const struct arg1opt *a1op;
294         const struct options *op;
295         int sockfd;                     /* socket fd we use to manipulate stuff with */
296         int goterr;
297         int selector;
298 #ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
299         unsigned int mask;
300         unsigned int did_flags;
301         unsigned int sai_hostname, sai_netmask;
302 #else
303         unsigned char mask;
304         unsigned char did_flags;
305 #endif
306         char *p;
307         char host[128];
308
309         goterr = 0;
310         did_flags = 0;
311 #ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
312         sai_hostname = 0;
313         sai_netmask = 0;
314 #endif
315
316         /* skip argv[0] */
317         ++argv;
318         --argc;
319
320 #ifdef CONFIG_FEATURE_IFCONFIG_STATUS
321         if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) {
322                 interface_opt_a = 1;
323                 --argc;
324                 ++argv;
325         }
326 #endif
327
328         if (argc <= 1) {
329 #ifdef CONFIG_FEATURE_IFCONFIG_STATUS
330                 return display_interfaces(argc ? *argv : NULL);
331 #else
332                 bb_error_msg_and_die
333                         ("ifconfig was not compiled with interface status display support.");
334 #endif
335         }
336
337         /* Create a channel to the NET kernel. */
338         if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
339                 bb_perror_msg_and_die("socket");
340         }
341
342         /* get interface name */
343         safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
344
345         /* Process the remaining arguments. */
346         while (*++argv != (char *) NULL) {
347                 p = *argv;
348                 mask = N_MASK;
349                 if (*p == '-') {        /* If the arg starts with '-'... */
350                         ++p;            /*    advance past it and */
351                         mask = M_MASK;  /*    set the appropriate mask. */
352                 }
353                 for (op = OptArray; op->name; op++) {   /* Find table entry. */
354                         if (strcmp(p, op->name) == 0) { /* If name matches... */
355                                 if ((mask &= op->flags)) {      /* set the mask and go. */
356                                         goto FOUND_ARG;
357                                 }
358                                 /* If we get here, there was a valid arg with an */
359                                 /* invalid '-' prefix. */
360                                 ++goterr;
361                                 goto LOOP;
362                         }
363                 }
364
365                 /* We fell through, so treat as possible hostname. */
366                 a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
367                 mask = op->arg_flags;
368                 goto HOSTNAME;
369
370           FOUND_ARG:
371                 if (mask & ARG_MASK) {
372                         mask = op->arg_flags;
373                         a1op = Arg1Opt + (op - OptArray);
374                         if (mask & A_NETMASK & did_flags) {
375                                 bb_show_usage();
376                         }
377                         if (*++argv == NULL) {
378                                 if (mask & A_ARG_REQ) {
379                                         bb_show_usage();
380                                 } else {
381                                         --argv;
382                                         mask &= A_SET_AFTER;    /* just for broadcast */
383                                 }
384                         } else {        /* got an arg so process it */
385                           HOSTNAME:
386                                 did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
387                                 if (mask & A_CAST_HOST_COPY) {
388 #ifdef CONFIG_FEATURE_IFCONFIG_HW
389                                         if (mask & A_CAST_RESOLVE) {
390 #endif
391 #ifdef CONFIG_FEATURE_IPV6
392                                                 char *prefix;
393                                                 int prefix_len = 0;
394 #endif
395
396                                                 safe_strncpy(host, *argv, (sizeof host));
397 #ifdef CONFIG_FEATURE_IPV6
398                                                 if ((prefix = strchr(host, '/'))) {
399                                                         if (safe_strtoi(prefix + 1, &prefix_len) ||
400                                                                 (prefix_len < 0) || (prefix_len > 128))
401                                                         {
402                                                                 ++goterr;
403                                                                 goto LOOP;
404                                                         }
405                                                         *prefix = 0;
406                                                 }
407 #endif
408
409                                                 sai.sin_family = AF_INET;
410                                                 sai.sin_port = 0;
411                                                 if (!strcmp(host, bb_INET_default)) {
412                                                         /* Default is special, meaning 0.0.0.0. */
413                                                         sai.sin_addr.s_addr = INADDR_ANY;
414 #ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
415                                                 } else if (((host[0] == '+') && !host[1]) && (mask & A_BROADCAST) &&
416                                                                    (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)) {
417                                                         /* + is special, meaning broadcast is derived. */
418                                                         sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
419 #endif
420 #ifdef CONFIG_FEATURE_IPV6
421                                                 } else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) {
422                                                         int sockfd6;
423                                                         struct in6_ifreq ifr6;
424
425                                                         memcpy((char *) &ifr6.ifr6_addr,
426                                                                    (char *) &sai6.sin6_addr,
427                                                                    sizeof(struct in6_addr));
428
429                                                         /* Create a channel to the NET kernel. */
430                                                         if ((sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
431                                                                 bb_perror_msg_and_die("socket6");
432                                                         }
433                                                         if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) {
434                                                                 perror("SIOGIFINDEX");
435                                                                 ++goterr;
436                                                                 continue;
437                                                         }
438                                                         ifr6.ifr6_ifindex = ifr.ifr_ifindex;
439                                                         ifr6.ifr6_prefixlen = prefix_len;
440                                                         if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) {
441                                                                 perror(a1op->name);
442                                                                 ++goterr;
443                                                         }
444                                                         continue;
445 #endif
446                                                 } else if (inet_aton(host, &sai.sin_addr) == 0) {
447                                                         /* It's not a dotted quad. */
448                                                         struct hostent *hp;
449                                                         if ((hp = gethostbyname(host)) == (struct hostent *)NULL) {
450                                                                 ++goterr;
451                                                                 continue;
452                                                         }
453                                                         memcpy((char *) &sai.sin_addr, (char *) hp->h_addr_list[0],
454                                                         sizeof(struct in_addr));
455                                                 }
456 #ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
457                                                 if (mask & A_HOSTNAME) {
458                                                         sai_hostname = sai.sin_addr.s_addr;
459                                                 }
460                                                 if (mask & A_NETMASK) {
461                                                         sai_netmask = sai.sin_addr.s_addr;
462                                                 }
463 #endif
464                                                 p = (char *) &sai;
465 #ifdef CONFIG_FEATURE_IFCONFIG_HW
466                                         } else {        /* A_CAST_HOST_COPY_IN_ETHER */
467                                                 /* This is the "hw" arg case. */
468                                                 if (strcmp("ether", *argv) || (*++argv == NULL)) {
469                                                         bb_show_usage();
470                                                 }
471                                                 safe_strncpy(host, *argv, (sizeof host));
472                                                 if (in_ether(host, &sa)) {
473                                                         bb_error_msg("invalid hw-addr %s", host);
474                                                         ++goterr;
475                                                         continue;
476                                                 }
477                                                 p = (char *) &sa;
478                                         }
479 #endif
480                                         memcpy((((char *) (&ifr)) + a1op->ifr_offset),
481                                                    p, sizeof(struct sockaddr));
482                                 } else {
483                                         unsigned long i = strtoul(*argv, NULL, 0);
484
485                                         p = ((char *) (&ifr)) + a1op->ifr_offset;
486 #ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
487                                         if (mask & A_MAP_TYPE) {
488                                                 if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) {
489                                                         ++goterr;
490                                                         continue;
491                                                 }
492                                                 if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) {
493                                                         *((unsigned char *) p) = i;
494                                                 } else if (mask & A_MAP_USHORT) {
495                                                         *((unsigned short *) p) = i;
496                                                 } else {
497                                                         *((unsigned long *) p) = i;
498                                                 }
499                                         } else
500 #endif
501                                         if (mask & A_CAST_CHAR_PTR) {
502                                                 *((caddr_t *) p) = (caddr_t) i;
503                                         } else {        /* A_CAST_INT */
504                                                 *((int *) p) = i;
505                                         }
506                                 }
507
508                                 if (ioctl(sockfd, a1op->selector, &ifr) < 0) {
509                                         perror(a1op->name);
510                                         ++goterr;
511                                         continue;
512                                 }
513 #ifdef QUESTIONABLE_ALIAS_CASE
514                                 if (mask & A_COLON_CHK) {
515                                         /*
516                                          * Don't do the set_flag() if the address is an alias with
517                                          * a - at the end, since it's deleted already! - Roman
518                                          *
519                                          * Should really use regex.h here, not sure though how well
520                                          * it'll go with the cross-platform support etc.
521                                          */
522                                         char *ptr;
523                                         short int found_colon = 0;
524
525                                         for (ptr = ifr.ifr_name; *ptr; ptr++) {
526                                                 if (*ptr == ':') {
527                                                         found_colon++;
528                                                 }
529                                         }
530
531                                         if (found_colon && *(ptr - 1) == '-') {
532                                                 continue;
533                                         }
534                                 }
535 #endif
536                         }
537                         if (!(mask & A_SET_AFTER)) {
538                                 continue;
539                         }
540                         mask = N_SET;
541                 }
542
543                 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
544                         perror("SIOCGIFFLAGS");
545                         ++goterr;
546                 } else {
547                         selector = op->selector;
548                         if (mask & SET_MASK) {
549                                 ifr.ifr_flags |= selector;
550                         } else {
551                                 ifr.ifr_flags &= ~selector;
552                         }
553                         if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
554                                 perror("SIOCSIFFLAGS");
555                                 ++goterr;
556                         }
557                 }
558           LOOP:
559                 continue;
560         }                                       /* end of while-loop */
561
562         if (ENABLE_FEATURE_CLEAN_UP) close(sockfd);
563         return goterr;
564 }
565
566 #ifdef CONFIG_FEATURE_IFCONFIG_HW
567 /* Input an Ethernet address and convert to binary. */
568 static int in_ether(char *bufp, struct sockaddr *sap)
569 {
570         char *ptr;
571         int i, j;
572         unsigned char val;
573         unsigned char c;
574
575         sap->sa_family = ARPHRD_ETHER;
576         ptr = sap->sa_data;
577
578         i = 0;
579         do {
580                 j = val = 0;
581
582                 /* We might get a semicolon here - not required. */
583                 if (i && (*bufp == ':')) {
584                         bufp++;
585                 }
586
587                 do {
588                         c = *bufp;
589                         if (((unsigned char)(c - '0')) <= 9) {
590                                 c -= '0';
591                         } else if (((unsigned char)((c|0x20) - 'a')) <= 5) {
592                                 c = (c|0x20) - ('a'-10);
593                         } else if (j && (c == ':' || c == 0)) {
594                                 break;
595                         } else {
596                                 return -1;
597                         }
598                         ++bufp;
599                         val <<= 4;
600                         val += c;
601                 } while (++j < 2);
602                 *ptr++ = val;
603         } while (++i < ETH_ALEN);
604
605         return (int) (*bufp);   /* Error if we don't end at end of string. */
606 }
607 #endif