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