source: MondoRescue/branches/3.3/mindi-busybox/networking/ifconfig.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 15.6 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2/* ifconfig
3 *
4 * Similar to the standard Unix ifconfig, but with only the necessary
5 * parts for AF_INET, and without any printing of if info (for now).
6 *
7 * Bjorn Wesen, Axis Communications AB
8 *
9 *
10 * Authors of the original ifconfig was:
11 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
12 *
[2725]13 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]14 */
15
16/*
17 * Heavily modified by Manuel Novoa III Mar 6, 2001
18 *
19 * From initial port to busybox, removed most of the redundancy by
20 * converting to a table-driven approach. Added several (optional)
21 * args missing from initial port.
22 *
23 * Still missing: media, tunnel.
24 *
25 * 2002-04-20
26 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
27 */
28
[3232]29//usage:#define ifconfig_trivial_usage
30//usage: IF_FEATURE_IFCONFIG_STATUS("[-a]") " interface [address]"
31//usage:#define ifconfig_full_usage "\n\n"
32//usage: "Configure a network interface\n"
33//usage: "\n"
34//usage: IF_FEATURE_IPV6(
35//usage: " [add ADDRESS[/PREFIXLEN]]\n")
36//usage: IF_FEATURE_IPV6(
37//usage: " [del ADDRESS[/PREFIXLEN]]\n")
38//usage: " [[-]broadcast [ADDRESS]] [[-]pointopoint [ADDRESS]]\n"
39//usage: " [netmask ADDRESS] [dstaddr ADDRESS]\n"
40//usage: IF_FEATURE_IFCONFIG_SLIP(
41//usage: " [outfill NN] [keepalive NN]\n")
42//usage: " " IF_FEATURE_IFCONFIG_HW("[hw ether" IF_FEATURE_HWIB("|infiniband")" ADDRESS] ") "[metric NN] [mtu NN]\n"
43//usage: " [[-]trailers] [[-]arp] [[-]allmulti]\n"
44//usage: " [multicast] [[-]promisc] [txqueuelen NN] [[-]dynamic]\n"
45//usage: IF_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ(
46//usage: " [mem_start NN] [io_addr NN] [irq NN]\n")
47//usage: " [up|down] ..."
48
49#include "libbb.h"
50#include "inet_common.h"
[821]51#include <net/if.h>
52#include <net/if_arp.h>
53#include <netinet/in.h>
[3232]54#ifdef HAVE_NET_ETHERNET_H
55# include <net/ethernet.h>
[821]56#endif
57
[1765]58#if ENABLE_FEATURE_IFCONFIG_SLIP
[3621]59# include <linux/if_slip.h>
[821]60#endif
61
62/* I don't know if this is needed for busybox or not. Anyone? */
63#define QUESTIONABLE_ALIAS_CASE
64
65
66/* Defines for glibc2.0 users. */
67#ifndef SIOCSIFTXQLEN
68# define SIOCSIFTXQLEN 0x8943
69# define SIOCGIFTXQLEN 0x8942
70#endif
71
72/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
73#ifndef ifr_qlen
74# define ifr_qlen ifr_ifru.ifru_mtu
75#endif
76
77#ifndef IFF_DYNAMIC
78# define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
79#endif
80
[1765]81#if ENABLE_FEATURE_IPV6
[821]82struct in6_ifreq {
83 struct in6_addr ifr6_addr;
84 uint32_t ifr6_prefixlen;
85 int ifr6_ifindex;
86};
87#endif
88
89/*
90 * Here are the bit masks for the "flags" member of struct options below.
91 * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
92 * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
93 */
94#define N_CLR 0x01
95#define M_CLR 0x02
96#define N_SET 0x04
97#define M_SET 0x08
98#define N_ARG 0x10
99#define M_ARG 0x20
100
101#define M_MASK (M_CLR | M_SET | M_ARG)
102#define N_MASK (N_CLR | N_SET | N_ARG)
103#define SET_MASK (N_SET | M_SET)
104#define CLR_MASK (N_CLR | M_CLR)
105#define SET_CLR_MASK (SET_MASK | CLR_MASK)
106#define ARG_MASK (M_ARG | N_ARG)
107
108/*
109 * Here are the bit masks for the "arg_flags" member of struct options below.
110 */
111
112/*
113 * cast type:
114 * 00 int
115 * 01 char *
116 * 02 HOST_COPY in_ether
117 * 03 HOST_COPY INET_resolve
118 */
119#define A_CAST_TYPE 0x03
120/*
121 * map type:
122 * 00 not a map type (mem_start, io_addr, irq)
123 * 04 memstart (unsigned long)
124 * 08 io_addr (unsigned short)
125 * 0C irq (unsigned char)
126 */
127#define A_MAP_TYPE 0x0C
128#define A_ARG_REQ 0x10 /* Set if an arg is required. */
129#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */
130#define A_SET_AFTER 0x40 /* Set a flag at the end. */
131#define A_COLON_CHK 0x80 /* Is this needed? See below. */
[1765]132#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
[821]133#define A_HOSTNAME 0x100 /* Set if it is ip addr. */
134#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */
135#else
136#define A_HOSTNAME 0
137#define A_BROADCAST 0
138#endif
139
140/*
141 * These defines are for dealing with the A_CAST_TYPE field.
142 */
143#define A_CAST_CHAR_PTR 0x01
144#define A_CAST_RESOLVE 0x01
145#define A_CAST_HOST_COPY 0x02
146#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY
147#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE)
148
149/*
150 * These defines are for dealing with the A_MAP_TYPE field.
151 */
152#define A_MAP_ULONG 0x04 /* memstart */
153#define A_MAP_USHORT 0x08 /* io_addr */
154#define A_MAP_UCHAR 0x0C /* irq */
155
156/*
157 * Define the bit masks signifying which operations to perform for each arg.
158 */
159
160#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/)
161#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/)
162#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/)
163#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG)
164#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG)
165#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR)
166#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
167#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
168#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
169#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
170#define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
171#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR)
172#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR)
173#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
174#define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
175
176
177struct arg1opt {
178 const char *name;
[2725]179 unsigned short selector;
[821]180 unsigned short ifr_offset;
181};
182
183struct options {
184 const char *name;
[1765]185#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
[821]186 const unsigned int flags:6;
187 const unsigned int arg_flags:10;
188#else
189 const unsigned char flags;
190 const unsigned char arg_flags;
191#endif
192 const unsigned short selector;
193};
194
195#define ifreq_offsetof(x) offsetof(struct ifreq, x)
196
[3232]197/*
198 * Set up the tables. Warning! They must have corresponding order!
199 */
200
[821]201static const struct arg1opt Arg1Opt[] = {
[2725]202 { "SIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric) },
203 { "SIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu) },
204 { "SIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen) },
205 { "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) },
206 { "SIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask) },
207 { "SIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr) },
[1765]208#if ENABLE_FEATURE_IFCONFIG_HW
[2725]209 { "SIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr) },
[821]210#endif
[2725]211 { "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) },
[821]212#ifdef SIOCSKEEPALIVE
[2725]213 { "SKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data) },
[821]214#endif
215#ifdef SIOCSOUTFILL
[2725]216 { "SOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data) },
[821]217#endif
[1765]218#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
[2725]219 { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start) },
220 { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr) },
221 { "SIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq) },
[821]222#endif
[1765]223#if ENABLE_FEATURE_IPV6
[2725]224 { "SIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */
225 { "DIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */
[821]226#endif
[3232]227 /* Last entry is for unmatched (assumed to be hostname/address) arg. */
[2725]228 { "SIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr) },
[821]229};
230
231static const struct options OptArray[] = {
[2725]232 { "metric", N_ARG, ARG_METRIC, 0 },
233 { "mtu", N_ARG, ARG_MTU, 0 },
234 { "txqueuelen", N_ARG, ARG_TXQUEUELEN, 0 },
235 { "dstaddr", N_ARG, ARG_DSTADDR, 0 },
236 { "netmask", N_ARG, ARG_NETMASK, 0 },
237 { "broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST },
[1765]238#if ENABLE_FEATURE_IFCONFIG_HW
[2725]239 { "hw", N_ARG, ARG_HW, 0 },
[821]240#endif
[2725]241 { "pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT },
[821]242#ifdef SIOCSKEEPALIVE
[2725]243 { "keepalive", N_ARG, ARG_KEEPALIVE, 0 },
[821]244#endif
245#ifdef SIOCSOUTFILL
[2725]246 { "outfill", N_ARG, ARG_OUTFILL, 0 },
[821]247#endif
[1765]248#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
[2725]249 { "mem_start", N_ARG, ARG_MEM_START, 0 },
250 { "io_addr", N_ARG, ARG_IO_ADDR, 0 },
251 { "irq", N_ARG, ARG_IRQ, 0 },
[821]252#endif
[1765]253#if ENABLE_FEATURE_IPV6
[2725]254 { "add", N_ARG, ARG_ADD_DEL, 0 },
255 { "del", N_ARG, ARG_ADD_DEL, 0 },
[821]256#endif
[2725]257 { "arp", N_CLR | M_SET, 0, IFF_NOARP },
258 { "trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS },
259 { "promisc", N_SET | M_CLR, 0, IFF_PROMISC },
260 { "multicast", N_SET | M_CLR, 0, IFF_MULTICAST },
261 { "allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI },
262 { "dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC },
263 { "up", N_SET, 0, (IFF_UP | IFF_RUNNING) },
264 { "down", N_CLR, 0, IFF_UP },
265 { NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING) }
[821]266};
267
[2725]268int ifconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
269int ifconfig_main(int argc UNUSED_PARAM, char **argv)
[821]270{
271 struct ifreq ifr;
272 struct sockaddr_in sai;
[1765]273#if ENABLE_FEATURE_IFCONFIG_HW
[821]274 struct sockaddr sa;
275#endif
276 const struct arg1opt *a1op;
277 const struct options *op;
278 int sockfd; /* socket fd we use to manipulate stuff with */
279 int selector;
[1765]280#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
[821]281 unsigned int mask;
282 unsigned int did_flags;
283 unsigned int sai_hostname, sai_netmask;
284#else
285 unsigned char mask;
286 unsigned char did_flags;
287#endif
288 char *p;
[1765]289 /*char host[128];*/
290 const char *host = NULL; /* make gcc happy */
[821]291
292 did_flags = 0;
[1765]293#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
[821]294 sai_hostname = 0;
295 sai_netmask = 0;
296#endif
297
298 /* skip argv[0] */
299 ++argv;
300
[1765]301#if ENABLE_FEATURE_IFCONFIG_STATUS
[2725]302 if (argv[0] && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) {
[821]303 interface_opt_a = 1;
304 ++argv;
305 }
306#endif
307
[2725]308 if (!argv[0] || !argv[1]) { /* one or no args */
[1765]309#if ENABLE_FEATURE_IFCONFIG_STATUS
[2725]310 return display_interfaces(argv[0] /* can be NULL */);
[821]311#else
[1765]312 bb_error_msg_and_die("no support for status display");
[821]313#endif
314 }
315
316 /* Create a channel to the NET kernel. */
[1765]317 sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
[821]318
319 /* get interface name */
[2725]320 strncpy_IFNAMSIZ(ifr.ifr_name, *argv);
[821]321
322 /* Process the remaining arguments. */
[3232]323 while (*++argv != NULL) {
[821]324 p = *argv;
325 mask = N_MASK;
326 if (*p == '-') { /* If the arg starts with '-'... */
327 ++p; /* advance past it and */
328 mask = M_MASK; /* set the appropriate mask. */
329 }
330 for (op = OptArray; op->name; op++) { /* Find table entry. */
331 if (strcmp(p, op->name) == 0) { /* If name matches... */
[1765]332 mask &= op->flags;
333 if (mask) /* set the mask and go. */
[821]334 goto FOUND_ARG;
335 /* If we get here, there was a valid arg with an */
336 /* invalid '-' prefix. */
[1765]337 bb_error_msg_and_die("bad: '%s'", p-1);
[821]338 }
339 }
340
341 /* We fell through, so treat as possible hostname. */
[1765]342 a1op = Arg1Opt + ARRAY_SIZE(Arg1Opt) - 1;
[821]343 mask = op->arg_flags;
344 goto HOSTNAME;
345
[1765]346 FOUND_ARG:
[821]347 if (mask & ARG_MASK) {
348 mask = op->arg_flags;
[1765]349 if (mask & A_NETMASK & did_flags)
[821]350 bb_show_usage();
[3232]351 a1op = Arg1Opt + (op - OptArray);
[821]352 if (*++argv == NULL) {
[1765]353 if (mask & A_ARG_REQ)
[821]354 bb_show_usage();
[1765]355 --argv;
356 mask &= A_SET_AFTER; /* just for broadcast */
[821]357 } else { /* got an arg so process it */
[1765]358 HOSTNAME:
[821]359 did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
360 if (mask & A_CAST_HOST_COPY) {
[1765]361#if ENABLE_FEATURE_IFCONFIG_HW
[821]362 if (mask & A_CAST_RESOLVE) {
363#endif
[1765]364 host = *argv;
[3232]365 if (strcmp(host, "inet") == 0)
366 continue; /* compat stuff */
[821]367 sai.sin_family = AF_INET;
368 sai.sin_port = 0;
[2725]369 if (strcmp(host, "default") == 0) {
[821]370 /* Default is special, meaning 0.0.0.0. */
371 sai.sin_addr.s_addr = INADDR_ANY;
[1765]372 }
373#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
[3232]374 else if ((host[0] == '+' && !host[1])
375 && (mask & A_BROADCAST)
[1765]376 && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)
377 ) {
[821]378 /* + is special, meaning broadcast is derived. */
379 sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
[1765]380 }
[821]381#endif
[1765]382 else {
383 len_and_sockaddr *lsa;
[3232]384#if ENABLE_FEATURE_IPV6
385 char *prefix;
386 int prefix_len = 0;
387 prefix = strchr(host, '/');
388 if (prefix) {
389 prefix_len = xatou_range(prefix + 1, 0, 128);
390 *prefix = '\0';
391 }
392 resolve:
393#endif
[1765]394 lsa = xhost2sockaddr(host, 0);
395#if ENABLE_FEATURE_IPV6
[3232]396 if (lsa->u.sa.sa_family != AF_INET6 && prefix) {
397/* TODO: we do not support "ifconfig eth0 up 1.2.3.4/17".
398 * For now, just make it fail instead of silently ignoring "/17" part:
399 */
400 *prefix = '/';
401 goto resolve;
402 }
[2725]403 if (lsa->u.sa.sa_family == AF_INET6) {
[1765]404 int sockfd6;
405 struct in6_ifreq ifr6;
[821]406
[1765]407 sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
[3232]408 xioctl(sockfd6, SIOCGIFINDEX, &ifr);
[1765]409 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
410 ifr6.ifr6_prefixlen = prefix_len;
[3232]411 memcpy(&ifr6.ifr6_addr,
412 &lsa->u.sin6.sin6_addr,
413 sizeof(struct in6_addr));
[2725]414 ioctl_or_perror_and_die(sockfd6, a1op->selector, &ifr6, "SIOC%s", a1op->name);
[1765]415 if (ENABLE_FEATURE_CLEAN_UP)
416 free(lsa);
[821]417 continue;
418 }
419#endif
[2725]420 sai.sin_addr = lsa->u.sin.sin_addr;
[1765]421 if (ENABLE_FEATURE_CLEAN_UP)
422 free(lsa);
[821]423 }
[1765]424#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
425 if (mask & A_HOSTNAME)
[821]426 sai_hostname = sai.sin_addr.s_addr;
[1765]427 if (mask & A_NETMASK)
[821]428 sai_netmask = sai.sin_addr.s_addr;
429#endif
430 p = (char *) &sai;
[1765]431#if ENABLE_FEATURE_IFCONFIG_HW
[821]432 } else { /* A_CAST_HOST_COPY_IN_ETHER */
433 /* This is the "hw" arg case. */
[3232]434 smalluint hw_class = index_in_substrings("ether\0"
[2725]435 IF_FEATURE_HWIB("infiniband\0"), *argv) + 1;
436 if (!hw_class || !*++argv)
[821]437 bb_show_usage();
[1765]438 host = *argv;
[2725]439 if (hw_class == 1 ? in_ether(host, &sa) : in_ib(host, &sa))
[1765]440 bb_error_msg_and_die("invalid hw-addr %s", host);
[821]441 p = (char *) &sa;
442 }
443#endif
[3232]444 memcpy( ((char *)&ifr) + a1op->ifr_offset,
445 p, sizeof(struct sockaddr));
[821]446 } else {
[1765]447 /* FIXME: error check?? */
[821]448 unsigned long i = strtoul(*argv, NULL, 0);
[1765]449 p = ((char *)&ifr) + a1op->ifr_offset;
450#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
[821]451 if (mask & A_MAP_TYPE) {
[1765]452 xioctl(sockfd, SIOCGIFMAP, &ifr);
453 if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR)
[3232]454 *(unsigned char *) p = i;
[1765]455 else if (mask & A_MAP_USHORT)
[3232]456 *(unsigned short *) p = i;
[1765]457 else
[3232]458 *(unsigned long *) p = i;
[821]459 } else
460#endif
[1765]461 if (mask & A_CAST_CHAR_PTR)
[3232]462 *(caddr_t *) p = (caddr_t) i;
[1765]463 else /* A_CAST_INT */
[3232]464 *(int *) p = i;
[821]465 }
466
[2725]467 ioctl_or_perror_and_die(sockfd, a1op->selector, &ifr, "SIOC%s", a1op->name);
[821]468#ifdef QUESTIONABLE_ALIAS_CASE
469 if (mask & A_COLON_CHK) {
470 /*
471 * Don't do the set_flag() if the address is an alias with
[1765]472 * a '-' at the end, since it's deleted already! - Roman
[821]473 *
474 * Should really use regex.h here, not sure though how well
475 * it'll go with the cross-platform support etc.
476 */
477 char *ptr;
478 short int found_colon = 0;
[1765]479 for (ptr = ifr.ifr_name; *ptr; ptr++)
480 if (*ptr == ':')
[821]481 found_colon++;
[1765]482 if (found_colon && ptr[-1] == '-')
[821]483 continue;
484 }
485#endif
486 }
[1765]487 if (!(mask & A_SET_AFTER))
[821]488 continue;
489 mask = N_SET;
[3232]490 } /* if (mask & ARG_MASK) */
[821]491
[1765]492 xioctl(sockfd, SIOCGIFFLAGS, &ifr);
493 selector = op->selector;
494 if (mask & SET_MASK)
495 ifr.ifr_flags |= selector;
496 else
497 ifr.ifr_flags &= ~selector;
498 xioctl(sockfd, SIOCSIFFLAGS, &ifr);
499 } /* while () */
[821]500
[1765]501 if (ENABLE_FEATURE_CLEAN_UP)
502 close(sockfd);
503 return 0;
[821]504}
Note: See TracBrowser for help on using the repository browser.