source: MondoRescue/branches/3.3/mindi-busybox/networking/ifupdown.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: 36.5 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * ifupdown for busybox
4 * Copyright (c) 2002 Glenn McGrath
5 * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
6 *
7 * Based on ifupdown v 0.6.4 by Anthony Towns
8 * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
9 *
10 * Changes to upstream version
11 * Remove checks for kernel version, assume kernel version 2.2.0 or better.
12 * Lines in the interfaces file cannot wrap.
13 * To adhere to the FHS, the default state file is /var/run/ifstate
14 * (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
15 * configuration.
16 *
17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
18 */
19
20//usage:#define ifup_trivial_usage
21//usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
22//usage:#define ifup_full_usage "\n\n"
23//usage: " -a De/configure all interfaces automatically"
24//usage: "\n -i FILE Use FILE for interface definitions"
25//usage: "\n -n Print out what would happen, but don't do it"
26//usage: IF_FEATURE_IFUPDOWN_MAPPING(
27//usage: "\n (note: doesn't disable mappings)"
28//usage: "\n -m Don't run any mappings"
29//usage: )
30//usage: "\n -v Print out what would happen before doing it"
31//usage: "\n -f Force de/configuration"
32//usage:
33//usage:#define ifdown_trivial_usage
34//usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
35//usage:#define ifdown_full_usage "\n\n"
36//usage: " -a De/configure all interfaces automatically"
37//usage: "\n -i FILE Use FILE for interface definitions"
38//usage: "\n -n Print out what would happen, but don't do it"
39//usage: IF_FEATURE_IFUPDOWN_MAPPING(
40//usage: "\n (note: doesn't disable mappings)"
41//usage: "\n -m Don't run any mappings"
42//usage: )
43//usage: "\n -v Print out what would happen before doing it"
44//usage: "\n -f Force de/configuration"
45
46#include "libbb.h"
47#include "common_bufsiz.h"
48/* After libbb.h, since it needs sys/types.h on some systems */
49#include <sys/utsname.h>
50#include <fnmatch.h>
51
52#define MAX_OPT_DEPTH 10
53
54#if ENABLE_FEATURE_IFUPDOWN_MAPPING
55#define MAX_INTERFACE_LENGTH 10
56#endif
57
58#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
59
60#define debug_noise(args...) /*fprintf(stderr, args)*/
61
62/* Forward declaration */
63struct interface_defn_t;
64
65typedef int execfn(char *command);
66
67struct method_t {
68 const char *name;
69 int (*up)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
70 int (*down)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
71};
72
73struct address_family_t {
74 const char *name;
75 int n_methods;
76 const struct method_t *method;
77};
78
79struct mapping_defn_t {
80 struct mapping_defn_t *next;
81
82 int max_matches;
83 int n_matches;
84 char **match;
85
86 char *script;
87
88 int n_mappings;
89 char **mapping;
90};
91
92struct variable_t {
93 char *name;
94 char *value;
95};
96
97struct interface_defn_t {
98 const struct address_family_t *address_family;
99 const struct method_t *method;
100
101 char *iface;
102 int n_options;
103 struct variable_t *option;
104};
105
106struct interfaces_file_t {
107 llist_t *autointerfaces;
108 llist_t *ifaces;
109 struct mapping_defn_t *mappings;
110};
111
112
113#define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
114enum {
115 OPT_do_all = 0x1,
116 OPT_no_act = 0x2,
117 OPT_verbose = 0x4,
118 OPT_force = 0x8,
119 OPT_no_mappings = 0x10,
120};
121#define DO_ALL (option_mask32 & OPT_do_all)
122#define NO_ACT (option_mask32 & OPT_no_act)
123#define VERBOSE (option_mask32 & OPT_verbose)
124#define FORCE (option_mask32 & OPT_force)
125#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
126
127
128struct globals {
129 char **my_environ;
130 const char *startup_PATH;
131 char *shell;
132} FIX_ALIASING;
133#define G (*(struct globals*)bb_common_bufsiz1)
134#define INIT_G() do { setup_common_bufsiz(); } while (0)
135
136
137static const char keywords_up_down[] ALIGN1 =
138 "up\0"
139 "down\0"
140 "pre-up\0"
141 "post-down\0"
142;
143
144
145#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
146
147static void addstr(char **bufp, const char *str, size_t str_length)
148{
149 /* xasprintf trick will be smaller, but we are often
150 * called with str_length == 1 - don't want to have
151 * THAT much of malloc/freeing! */
152 char *buf = *bufp;
153 int len = (buf ? strlen(buf) : 0);
154 str_length++;
155 buf = xrealloc(buf, len + str_length);
156 /* copies at most str_length-1 chars! */
157 safe_strncpy(buf + len, str, str_length);
158 *bufp = buf;
159}
160
161static int strncmpz(const char *l, const char *r, size_t llen)
162{
163 int i = strncmp(l, r, llen);
164
165 if (i == 0)
166 return - (unsigned char)r[llen];
167 return i;
168}
169
170static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
171{
172 int i;
173
174 if (strncmpz(id, "iface", idlen) == 0) {
175 // ubuntu's ifup doesn't do this:
176 //static char *label_buf;
177 //char *result;
178 //free(label_buf);
179 //label_buf = xstrdup(ifd->iface);
180 // Remove virtual iface suffix
181 //result = strchrnul(label_buf, ':');
182 //*result = '\0';
183 //return label_buf;
184
185 return ifd->iface;
186 }
187 if (strncmpz(id, "label", idlen) == 0) {
188 return ifd->iface;
189 }
190 for (i = 0; i < ifd->n_options; i++) {
191 if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
192 return ifd->option[i].value;
193 }
194 }
195 return NULL;
196}
197
198# if ENABLE_FEATURE_IFUPDOWN_IP
199static int count_netmask_bits(const char *dotted_quad)
200{
201// int result;
202// unsigned a, b, c, d;
203// /* Found a netmask... Check if it is dotted quad */
204// if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
205// return -1;
206// if ((a|b|c|d) >> 8)
207// return -1; /* one of numbers is >= 256 */
208// d |= (a << 24) | (b << 16) | (c << 8); /* IP */
209// d = ~d; /* 11110000 -> 00001111 */
210
211 /* Shorter version */
212 int result;
213 struct in_addr ip;
214 unsigned d;
215
216 if (inet_aton(dotted_quad, &ip) == 0)
217 return -1; /* malformed dotted IP */
218 d = ntohl(ip.s_addr); /* IP in host order */
219 d = ~d; /* 11110000 -> 00001111 */
220 if (d & (d+1)) /* check that it is in 00001111 form */
221 return -1; /* no it is not */
222 result = 32;
223 while (d) {
224 d >>= 1;
225 result--;
226 }
227 return result;
228}
229# endif
230
231static char *parse(const char *command, struct interface_defn_t *ifd)
232{
233 size_t old_pos[MAX_OPT_DEPTH] = { 0 };
234 smallint okay[MAX_OPT_DEPTH] = { 1 };
235 int opt_depth = 1;
236 char *result = NULL;
237
238 while (*command) {
239 switch (*command) {
240 default:
241 addstr(&result, command, 1);
242 command++;
243 break;
244 case '\\':
245 if (command[1])
246 command++;
247 addstr(&result, command, 1);
248 command++;
249 break;
250 case '[':
251 if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
252 old_pos[opt_depth] = result ? strlen(result) : 0;
253 okay[opt_depth] = 1;
254 opt_depth++;
255 command += 2;
256 } else {
257 addstr(&result, command, 1);
258 command++;
259 }
260 break;
261 case ']':
262 if (command[1] == ']' && opt_depth > 1) {
263 opt_depth--;
264 if (!okay[opt_depth]) {
265 result[old_pos[opt_depth]] = '\0';
266 }
267 command += 2;
268 } else {
269 addstr(&result, command, 1);
270 command++;
271 }
272 break;
273 case '%':
274 {
275 char *nextpercent;
276 char *varvalue;
277
278 command++;
279 nextpercent = strchr(command, '%');
280 if (!nextpercent) {
281 /* Unterminated %var% */
282 free(result);
283 return NULL;
284 }
285
286 varvalue = get_var(command, nextpercent - command, ifd);
287
288 if (varvalue) {
289# if ENABLE_FEATURE_IFUPDOWN_IP
290 /* "hwaddress <class> <address>":
291 * unlike ifconfig, ip doesnt want <class>
292 * (usually "ether" keyword). Skip it. */
293 if (is_prefixed_with(command, "hwaddress")) {
294 varvalue = skip_whitespace(skip_non_whitespace(varvalue));
295 }
296# endif
297 addstr(&result, varvalue, strlen(varvalue));
298 } else {
299# if ENABLE_FEATURE_IFUPDOWN_IP
300 /* Sigh... Add a special case for 'ip' to convert from
301 * dotted quad to bit count style netmasks. */
302 if (is_prefixed_with(command, "bnmask")) {
303 unsigned res;
304 varvalue = get_var("netmask", 7, ifd);
305 if (varvalue) {
306 res = count_netmask_bits(varvalue);
307 if (res > 0) {
308 const char *argument = utoa(res);
309 addstr(&result, argument, strlen(argument));
310 command = nextpercent + 1;
311 break;
312 }
313 }
314 }
315# endif
316 okay[opt_depth - 1] = 0;
317 }
318
319 command = nextpercent + 1;
320 }
321 break;
322 }
323 }
324
325 if (opt_depth > 1) {
326 /* Unbalanced bracket */
327 free(result);
328 return NULL;
329 }
330
331 if (!okay[0]) {
332 /* Undefined variable and we aren't in a bracket */
333 free(result);
334 return NULL;
335 }
336
337 return result;
338}
339
340/* execute() returns 1 for success and 0 for failure */
341static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
342{
343 char *out;
344 int ret;
345
346 out = parse(command, ifd);
347 if (!out) {
348 /* parse error? */
349 return 0;
350 }
351 /* out == "": parsed ok but not all needed variables known, skip */
352 ret = out[0] ? (*exec)(out) : 1;
353
354 free(out);
355 if (ret != 1) {
356 return 0;
357 }
358 return 1;
359}
360
361#endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */
362
363
364#if ENABLE_FEATURE_IFUPDOWN_IPV6
365
366static int FAST_FUNC loopback_up6(struct interface_defn_t *ifd, execfn *exec)
367{
368# if ENABLE_FEATURE_IFUPDOWN_IP
369 int result;
370 result = execute("ip addr add ::1 dev %iface%", ifd, exec);
371 result += execute("ip link set %iface% up", ifd, exec);
372 return ((result == 2) ? 2 : 0);
373# else
374 return execute("ifconfig %iface% add ::1", ifd, exec);
375# endif
376}
377
378static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec)
379{
380# if ENABLE_FEATURE_IFUPDOWN_IP
381 return execute("ip link set %iface% down", ifd, exec);
382# else
383 return execute("ifconfig %iface% del ::1", ifd, exec);
384# endif
385}
386
387static int FAST_FUNC manual_up_down6(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
388{
389 return 1;
390}
391
392static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec)
393{
394 int result;
395# if ENABLE_FEATURE_IFUPDOWN_IP
396 result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
397 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
398 /* Reportedly, IPv6 needs "dev %iface%", but IPv4 does not: */
399 result += execute("[[ip route add ::/0 via %gateway% dev %iface%]][[ metric %metric%]]", ifd, exec);
400# else
401 result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
402 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
403 result += execute("[[route -A inet6 add ::/0 gw %gateway%[[ metric %metric%]]]]", ifd, exec);
404# endif
405 return ((result == 3) ? 3 : 0);
406}
407
408static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec)
409{
410# if ENABLE_FEATURE_IFUPDOWN_IP
411 return execute("ip link set %iface% down", ifd, exec);
412# else
413 return execute("ifconfig %iface% down", ifd, exec);
414# endif
415}
416
417# if ENABLE_FEATURE_IFUPDOWN_IP
418static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
419{
420 int result;
421 result = execute("ip tunnel add %iface% mode sit remote "
422 "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
423 result += execute("ip link set %iface% up", ifd, exec);
424 result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
425 /* Reportedly, IPv6 needs "dev %iface%", but IPv4 does not: */
426 result += execute("[[ip route add ::/0 via %gateway% dev %iface%]]", ifd, exec);
427 return ((result == 4) ? 4 : 0);
428}
429
430static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
431{
432 return execute("ip tunnel del %iface%", ifd, exec);
433}
434# endif
435
436static const struct method_t methods6[] = {
437# if ENABLE_FEATURE_IFUPDOWN_IP
438 { "v4tunnel" , v4tunnel_up , v4tunnel_down , },
439# endif
440 { "static" , static_up6 , static_down6 , },
441 { "manual" , manual_up_down6 , manual_up_down6 , },
442 { "loopback" , loopback_up6 , loopback_down6 , },
443};
444
445static const struct address_family_t addr_inet6 = {
446 "inet6",
447 ARRAY_SIZE(methods6),
448 methods6
449};
450
451#endif /* FEATURE_IFUPDOWN_IPV6 */
452
453
454#if ENABLE_FEATURE_IFUPDOWN_IPV4
455
456static int FAST_FUNC loopback_up(struct interface_defn_t *ifd, execfn *exec)
457{
458# if ENABLE_FEATURE_IFUPDOWN_IP
459 int result;
460 result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
461 result += execute("ip link set %iface% up", ifd, exec);
462 return ((result == 2) ? 2 : 0);
463# else
464 return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
465# endif
466}
467
468static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec)
469{
470# if ENABLE_FEATURE_IFUPDOWN_IP
471 int result;
472 result = execute("ip addr flush dev %iface%", ifd, exec);
473 result += execute("ip link set %iface% down", ifd, exec);
474 return ((result == 2) ? 2 : 0);
475# else
476 return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
477# endif
478}
479
480static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
481{
482 int result;
483# if ENABLE_FEATURE_IFUPDOWN_IP
484 result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
485 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
486 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
487 result += execute("[[ip route add default via %gateway% dev %iface%[[ metric %metric%]]]]", ifd, exec);
488 return ((result == 3) ? 3 : 0);
489# else
490 /* ifconfig said to set iface up before it processes hw %hwaddress%,
491 * which then of course fails. Thus we run two separate ifconfig */
492 result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
493 ifd, exec);
494 result += execute("ifconfig %iface% %address% netmask %netmask%"
495 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
496 ifd, exec);
497 result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec);
498 return ((result == 3) ? 3 : 0);
499# endif
500}
501
502static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
503{
504 int result;
505# if ENABLE_FEATURE_IFUPDOWN_IP
506 result = execute("ip addr flush dev %iface%", ifd, exec);
507 result += execute("ip link set %iface% down", ifd, exec);
508# else
509 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
510 /* Bringing the interface down deletes the routes in itself.
511 Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
512 result = 1;
513 result += execute("ifconfig %iface% down", ifd, exec);
514# endif
515 return ((result == 2) ? 2 : 0);
516}
517
518# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
519struct dhcp_client_t {
520 const char *name;
521 const char *startcmd;
522 const char *stopcmd;
523};
524
525static const struct dhcp_client_t ext_dhcp_clients[] = {
526 { "dhcpcd",
527 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
528 "dhcpcd -k %iface%",
529 },
530 { "dhclient",
531 "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
532 "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
533 },
534 { "pump",
535 "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
536 "pump -i %iface% -k",
537 },
538 { "udhcpc",
539 "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -x hostname:%hostname%]][[ -c %client%]]"
540 "[[ -s %script%]][[ %udhcpc_opts%]]",
541 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
542 },
543};
544# endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
545
546# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
547static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
548{
549 unsigned i;
550# if ENABLE_FEATURE_IFUPDOWN_IP
551 /* ip doesn't up iface when it configures it (unlike ifconfig) */
552 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
553 return 0;
554# else
555 /* needed if we have hwaddress on dhcp iface */
556 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
557 return 0;
558# endif
559 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
560 if (executable_exists(ext_dhcp_clients[i].name))
561 return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
562 }
563 bb_error_msg("no dhcp clients found");
564 return 0;
565}
566# elif ENABLE_UDHCPC
567static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
568{
569# if ENABLE_FEATURE_IFUPDOWN_IP
570 /* ip doesn't up iface when it configures it (unlike ifconfig) */
571 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
572 return 0;
573# else
574 /* needed if we have hwaddress on dhcp iface */
575 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
576 return 0;
577# endif
578 return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
579 "-i %iface%[[ -x hostname:%hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
580 ifd, exec);
581}
582# else
583static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM,
584 execfn *exec UNUSED_PARAM)
585{
586 return 0; /* no dhcp support */
587}
588# endif
589
590# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
591static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
592{
593 int result = 0;
594 unsigned i;
595
596 for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
597 if (executable_exists(ext_dhcp_clients[i].name)) {
598 result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
599 if (result)
600 break;
601 }
602 }
603
604 if (!result)
605 bb_error_msg("warning: no dhcp clients found and stopped");
606
607 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
608 and it may come back up because udhcpc is still shutting down */
609 usleep(100000);
610 result += static_down(ifd, exec);
611 return ((result == 3) ? 3 : 0);
612}
613# elif ENABLE_UDHCPC
614static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
615{
616 int result;
617 result = execute(
618 "test -f /var/run/udhcpc.%iface%.pid && "
619 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
620 ifd, exec);
621 /* Also bring the hardware interface down since
622 killing the dhcp client alone doesn't do it.
623 This enables consecutive ifup->ifdown->ifup */
624 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
625 and it may come back up because udhcpc is still shutting down */
626 usleep(100000);
627 result += static_down(ifd, exec);
628 return ((result == 3) ? 3 : 0);
629}
630# else
631static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM,
632 execfn *exec UNUSED_PARAM)
633{
634 return 0; /* no dhcp support */
635}
636# endif
637
638static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
639{
640 return 1;
641}
642
643static int FAST_FUNC bootp_up(struct interface_defn_t *ifd, execfn *exec)
644{
645 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
646 "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
647 " --returniffail --serverbcast", ifd, exec);
648}
649
650static int FAST_FUNC ppp_up(struct interface_defn_t *ifd, execfn *exec)
651{
652 return execute("pon[[ %provider%]]", ifd, exec);
653}
654
655static int FAST_FUNC ppp_down(struct interface_defn_t *ifd, execfn *exec)
656{
657 return execute("poff[[ %provider%]]", ifd, exec);
658}
659
660static int FAST_FUNC wvdial_up(struct interface_defn_t *ifd, execfn *exec)
661{
662 return execute("start-stop-daemon --start -x wvdial "
663 "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
664}
665
666static int FAST_FUNC wvdial_down(struct interface_defn_t *ifd, execfn *exec)
667{
668 return execute("start-stop-daemon --stop -x wvdial "
669 "-p /var/run/wvdial.%iface% -s 2", ifd, exec);
670}
671
672static const struct method_t methods[] = {
673 { "manual" , manual_up_down, manual_up_down, },
674 { "wvdial" , wvdial_up , wvdial_down , },
675 { "ppp" , ppp_up , ppp_down , },
676 { "static" , static_up , static_down , },
677 { "bootp" , bootp_up , static_down , },
678 { "dhcp" , dhcp_up , dhcp_down , },
679 { "loopback", loopback_up , loopback_down , },
680};
681
682static const struct address_family_t addr_inet = {
683 "inet",
684 ARRAY_SIZE(methods),
685 methods
686};
687
688#endif /* FEATURE_IFUPDOWN_IPV4 */
689
690static int FAST_FUNC link_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
691{
692 return 1;
693}
694
695static const struct method_t link_methods[] = {
696 { "none", link_up_down, link_up_down }
697};
698
699static const struct address_family_t addr_link = {
700 "link", ARRAY_SIZE(link_methods), link_methods
701};
702
703/* Returns pointer to the next word, or NULL.
704 * In 1st case, advances *buf to the word after this one.
705 */
706static char *next_word(char **buf)
707{
708 unsigned length;
709 char *word;
710
711 /* Skip over leading whitespace */
712 word = skip_whitespace(*buf);
713
714 /* Stop on EOL */
715 if (*word == '\0')
716 return NULL;
717
718 /* Find the length of this word (can't be 0) */
719 length = strcspn(word, " \t\n");
720
721 /* Unless we are already at NUL, store NUL and advance */
722 if (word[length] != '\0')
723 word[length++] = '\0';
724
725 *buf = skip_whitespace(word + length);
726
727 return word;
728}
729
730static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
731{
732 int i;
733
734 if (!name)
735 return NULL;
736
737 for (i = 0; af[i]; i++) {
738 if (strcmp(af[i]->name, name) == 0) {
739 return af[i];
740 }
741 }
742 return NULL;
743}
744
745static const struct method_t *get_method(const struct address_family_t *af, char *name)
746{
747 int i;
748
749 if (!name)
750 return NULL;
751 /* TODO: use index_in_str_array() */
752 for (i = 0; i < af->n_methods; i++) {
753 if (strcmp(af->method[i].name, name) == 0) {
754 return &af->method[i];
755 }
756 }
757 return NULL;
758}
759
760static struct interfaces_file_t *read_interfaces(const char *filename, struct interfaces_file_t *defn)
761{
762 /* Let's try to be compatible.
763 *
764 * "man 5 interfaces" says:
765 * Lines starting with "#" are ignored. Note that end-of-line
766 * comments are NOT supported, comments must be on a line of their own.
767 * A line may be extended across multiple lines by making
768 * the last character a backslash.
769 *
770 * Seen elsewhere in example config file:
771 * A first non-blank "#" character makes the rest of the line
772 * be ignored. Blank lines are ignored. Lines may be indented freely.
773 * A "\" character at the very end of the line indicates the next line
774 * should be treated as a continuation of the current one.
775 *
776 * Lines beginning with "source" are used to include stanzas from
777 * other files, so configuration can be split into many files.
778 * The word "source" is followed by the path of file to be sourced.
779 */
780#if ENABLE_FEATURE_IFUPDOWN_MAPPING
781 struct mapping_defn_t *currmap = NULL;
782#endif
783 struct interface_defn_t *currif = NULL;
784 FILE *f;
785 char *buf;
786 char *first_word;
787 char *rest_of_line;
788 enum { NONE, IFACE, MAPPING } currently_processing = NONE;
789
790 if (!defn)
791 defn = xzalloc(sizeof(*defn));
792
793 debug_noise("reading %s file:\n", filename);
794 f = xfopen_for_read(filename);
795
796 while ((buf = xmalloc_fgetline(f)) != NULL) {
797#if ENABLE_DESKTOP
798 /* Trailing "\" concatenates lines */
799 char *p;
800 while ((p = last_char_is(buf, '\\')) != NULL) {
801 *p = '\0';
802 rest_of_line = xmalloc_fgetline(f);
803 if (!rest_of_line)
804 break;
805 p = xasprintf("%s%s", buf, rest_of_line);
806 free(buf);
807 free(rest_of_line);
808 buf = p;
809 }
810#endif
811 rest_of_line = buf;
812 first_word = next_word(&rest_of_line);
813 if (!first_word || *first_word == '#') {
814 free(buf);
815 continue; /* blank/comment line */
816 }
817
818 if (strcmp(first_word, "mapping") == 0) {
819#if ENABLE_FEATURE_IFUPDOWN_MAPPING
820 currmap = xzalloc(sizeof(*currmap));
821
822 while ((first_word = next_word(&rest_of_line)) != NULL) {
823 currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches);
824 currmap->match[currmap->n_matches++] = xstrdup(first_word);
825 }
826 /*currmap->n_mappings = 0;*/
827 /*currmap->mapping = NULL;*/
828 /*currmap->script = NULL;*/
829 {
830 struct mapping_defn_t **where = &defn->mappings;
831 while (*where != NULL) {
832 where = &(*where)->next;
833 }
834 *where = currmap;
835 /*currmap->next = NULL;*/
836 }
837 debug_noise("Added mapping\n");
838#endif
839 currently_processing = MAPPING;
840 } else if (strcmp(first_word, "iface") == 0) {
841 static const struct address_family_t *const addr_fams[] = {
842#if ENABLE_FEATURE_IFUPDOWN_IPV4
843 &addr_inet,
844#endif
845#if ENABLE_FEATURE_IFUPDOWN_IPV6
846 &addr_inet6,
847#endif
848 &addr_link,
849 NULL
850 };
851 char *iface_name;
852 char *address_family_name;
853 char *method_name;
854
855 currif = xzalloc(sizeof(*currif));
856 iface_name = next_word(&rest_of_line);
857 address_family_name = next_word(&rest_of_line);
858 method_name = next_word(&rest_of_line);
859
860 if (method_name == NULL)
861 bb_error_msg_and_die("too few parameters for line \"%s\"", buf);
862
863 /* ship any trailing whitespace */
864 rest_of_line = skip_whitespace(rest_of_line);
865
866 if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */)
867 bb_error_msg_and_die("too many parameters \"%s\"", buf);
868
869 currif->iface = xstrdup(iface_name);
870
871 currif->address_family = get_address_family(addr_fams, address_family_name);
872 if (!currif->address_family)
873 bb_error_msg_and_die("unknown address type \"%s\"", address_family_name);
874
875 currif->method = get_method(currif->address_family, method_name);
876 if (!currif->method)
877 bb_error_msg_and_die("unknown method \"%s\"", method_name);
878#if 0
879// Allegedly, Debian allows a duplicate definition:
880// iface eth0 inet static
881// address 192.168.0.15
882// netmask 255.255.0.0
883// gateway 192.168.0.1
884//
885// iface eth0 inet static
886// address 10.0.0.1
887// netmask 255.255.255.0
888//
889// This adds *two* addresses to eth0 (probably requires use of "ip", not "ifconfig"
890//
891 llist_t *iface_list;
892 for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
893 struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
894 if ((strcmp(tmp->iface, currif->iface) == 0)
895 && (tmp->address_family == currif->address_family)
896 ) {
897 bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface);
898 }
899 }
900#endif
901 llist_add_to_end(&(defn->ifaces), (char*)currif);
902
903 debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
904 currently_processing = IFACE;
905 } else if (strcmp(first_word, "auto") == 0) {
906 while ((first_word = next_word(&rest_of_line)) != NULL) {
907
908 /* Check the interface isnt already listed */
909 if (llist_find_str(defn->autointerfaces, first_word)) {
910 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
911 }
912
913 /* Add the interface to the list */
914 llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word));
915 debug_noise("\nauto %s\n", first_word);
916 }
917 currently_processing = NONE;
918 } else if (strcmp(first_word, "source") == 0) {
919 read_interfaces(next_word(&rest_of_line), defn);
920 } else {
921 switch (currently_processing) {
922 case IFACE:
923 if (rest_of_line[0] == '\0')
924 bb_error_msg_and_die("option with empty value \"%s\"", buf);
925
926 if (strcmp(first_word, "post-up") == 0)
927 first_word += 5; /* "up" */
928 else if (strcmp(first_word, "pre-down") == 0)
929 first_word += 4; /* "down" */
930
931 /* If not one of "up", "down",... words... */
932 if (index_in_strings(keywords_up_down, first_word) < 0) {
933 int i;
934 for (i = 0; i < currif->n_options; i++) {
935 if (strcmp(currif->option[i].name, first_word) == 0)
936 bb_error_msg_and_die("duplicate option \"%s\"", buf);
937 }
938 }
939 debug_noise("\t%s=%s\n", first_word, rest_of_line);
940 currif->option = xrealloc_vector(currif->option, 4, currif->n_options);
941 currif->option[currif->n_options].name = xstrdup(first_word);
942 currif->option[currif->n_options].value = xstrdup(rest_of_line);
943 currif->n_options++;
944 break;
945 case MAPPING:
946#if ENABLE_FEATURE_IFUPDOWN_MAPPING
947 if (strcmp(first_word, "script") == 0) {
948 if (currmap->script != NULL)
949 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf);
950 currmap->script = xstrdup(next_word(&rest_of_line));
951 } else if (strcmp(first_word, "map") == 0) {
952 currmap->mapping = xrealloc_vector(currmap->mapping, 2, currmap->n_mappings);
953 currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
954 currmap->n_mappings++;
955 } else {
956 bb_error_msg_and_die("misplaced option \"%s\"", buf);
957 }
958#endif
959 break;
960 case NONE:
961 default:
962 bb_error_msg_and_die("misplaced option \"%s\"", buf);
963 }
964 }
965 free(buf);
966 } /* while (fgets) */
967
968 if (ferror(f) != 0) {
969 /* ferror does NOT set errno! */
970 bb_error_msg_and_die("%s: I/O error", filename);
971 }
972 fclose(f);
973 debug_noise("\ndone reading %s\n\n", filename);
974
975 return defn;
976}
977
978static char *setlocalenv(const char *format, const char *name, const char *value)
979{
980 char *result;
981 char *dst;
982 char *src;
983 char c;
984
985 result = xasprintf(format, name, value);
986
987 for (dst = src = result; (c = *src) != '=' && c; src++) {
988 if (c == '-')
989 c = '_';
990 if (c >= 'a' && c <= 'z')
991 c -= ('a' - 'A');
992 if (isalnum(c) || c == '_')
993 *dst++ = c;
994 }
995 overlapping_strcpy(dst, src);
996
997 return result;
998}
999
1000static void set_environ(struct interface_defn_t *iface, const char *mode, const char *opt)
1001{
1002 int i;
1003 char **pp;
1004
1005 if (G.my_environ != NULL) {
1006 for (pp = G.my_environ; *pp; pp++) {
1007 free(*pp);
1008 }
1009 free(G.my_environ);
1010 }
1011
1012 /* note: last element will stay NULL: */
1013 G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 7));
1014 pp = G.my_environ;
1015
1016 for (i = 0; i < iface->n_options; i++) {
1017 if (index_in_strings(keywords_up_down, iface->option[i].name) >= 0) {
1018 continue;
1019 }
1020 *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
1021 }
1022
1023 *pp++ = setlocalenv("%s=%s", "IFACE", iface->iface);
1024 *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
1025 *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name);
1026 *pp++ = setlocalenv("%s=%s", "MODE", mode);
1027 *pp++ = setlocalenv("%s=%s", "PHASE", opt);
1028 if (G.startup_PATH)
1029 *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH);
1030}
1031
1032static int doit(char *str)
1033{
1034 if (option_mask32 & (OPT_no_act|OPT_verbose)) {
1035 puts(str);
1036 }
1037 if (!(option_mask32 & OPT_no_act)) {
1038 pid_t child;
1039 int status;
1040
1041 fflush_all();
1042 child = vfork();
1043 if (child < 0) /* failure */
1044 return 0;
1045 if (child == 0) { /* child */
1046 execle(G.shell, G.shell, "-c", str, (char *) NULL, G.my_environ);
1047 _exit(127);
1048 }
1049 safe_waitpid(child, &status, 0);
1050 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1051 return 0;
1052 }
1053 }
1054 return 1;
1055}
1056
1057static int execute_all(struct interface_defn_t *ifd, const char *opt)
1058{
1059 int i;
1060 char *buf;
1061 for (i = 0; i < ifd->n_options; i++) {
1062 if (strcmp(ifd->option[i].name, opt) == 0) {
1063 if (!doit(ifd->option[i].value)) {
1064 return 0;
1065 }
1066 }
1067 }
1068
1069 buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
1070 /* heh, we don't bother free'ing it */
1071 return doit(buf);
1072}
1073
1074static int check(char *str)
1075{
1076 return str != NULL;
1077}
1078
1079static int iface_up(struct interface_defn_t *iface)
1080{
1081 if (!iface->method->up(iface, check)) return -1;
1082 set_environ(iface, "start", "pre-up");
1083 if (!execute_all(iface, "pre-up")) return 0;
1084 if (!iface->method->up(iface, doit)) return 0;
1085 set_environ(iface, "start", "post-up");
1086 if (!execute_all(iface, "up")) return 0;
1087 return 1;
1088}
1089
1090static int iface_down(struct interface_defn_t *iface)
1091{
1092 if (!iface->method->down(iface, check)) return -1;
1093 set_environ(iface, "stop", "pre-down");
1094 if (!execute_all(iface, "down")) return 0;
1095 if (!iface->method->down(iface, doit)) return 0;
1096 set_environ(iface, "stop", "post-down");
1097 if (!execute_all(iface, "post-down")) return 0;
1098 return 1;
1099}
1100
1101#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1102static int popen2(FILE **in, FILE **out, char *command, char *param)
1103{
1104 char *argv[3] = { command, param, NULL };
1105 struct fd_pair infd, outfd;
1106 pid_t pid;
1107
1108 xpiped_pair(infd);
1109 xpiped_pair(outfd);
1110
1111 fflush_all();
1112 pid = xvfork();
1113
1114 if (pid == 0) {
1115 /* Child */
1116 /* NB: close _first_, then move fds! */
1117 close(infd.wr);
1118 close(outfd.rd);
1119 xmove_fd(infd.rd, 0);
1120 xmove_fd(outfd.wr, 1);
1121 BB_EXECVP_or_die(argv);
1122 }
1123 /* parent */
1124 close(infd.rd);
1125 close(outfd.wr);
1126 *in = xfdopen_for_write(infd.wr);
1127 *out = xfdopen_for_read(outfd.rd);
1128 return pid;
1129}
1130
1131static char *run_mapping(char *physical, struct mapping_defn_t *map)
1132{
1133 FILE *in, *out;
1134 int i, status;
1135 pid_t pid;
1136
1137 char *logical = xstrdup(physical);
1138
1139 /* Run the mapping script. Never fails. */
1140 pid = popen2(&in, &out, map->script, physical);
1141
1142 /* Write mappings to stdin of mapping script. */
1143 for (i = 0; i < map->n_mappings; i++) {
1144 fprintf(in, "%s\n", map->mapping[i]);
1145 }
1146 fclose(in);
1147 safe_waitpid(pid, &status, 0);
1148
1149 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
1150 /* If the mapping script exited successfully, try to
1151 * grab a line of output and use that as the name of the
1152 * logical interface. */
1153 char *new_logical = xmalloc_fgetline(out);
1154
1155 if (new_logical) {
1156 /* If we are able to read a line of output from the script,
1157 * remove any trailing whitespace and use this value
1158 * as the name of the logical interface. */
1159 char *pch = new_logical + strlen(new_logical) - 1;
1160
1161 while (pch >= new_logical && isspace(*pch))
1162 *(pch--) = '\0';
1163
1164 free(logical);
1165 logical = new_logical;
1166 }
1167 }
1168
1169 fclose(out);
1170
1171 return logical;
1172}
1173#endif /* FEATURE_IFUPDOWN_MAPPING */
1174
1175static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1176{
1177 llist_t *search = state_list;
1178
1179 while (search) {
1180 char *after_iface = is_prefixed_with(search->data, iface);
1181 if (after_iface
1182 && *after_iface == '='
1183 ) {
1184 return search;
1185 }
1186 search = search->link;
1187 }
1188 return NULL;
1189}
1190
1191/* read the previous state from the state file */
1192static llist_t *read_iface_state(void)
1193{
1194 llist_t *state_list = NULL;
1195 FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH);
1196
1197 if (state_fp) {
1198 char *start, *end_ptr;
1199 while ((start = xmalloc_fgets(state_fp)) != NULL) {
1200 /* We should only need to check for a single character */
1201 end_ptr = start + strcspn(start, " \t\n");
1202 *end_ptr = '\0';
1203 llist_add_to(&state_list, start);
1204 }
1205 fclose(state_fp);
1206 }
1207 return state_list;
1208}
1209
1210
1211int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1212int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1213{
1214 int (*cmds)(struct interface_defn_t *);
1215 struct interfaces_file_t *defn;
1216 llist_t *target_list = NULL;
1217 const char *interfaces = "/etc/network/interfaces";
1218 bool any_failures = 0;
1219
1220 INIT_G();
1221
1222 G.startup_PATH = getenv("PATH");
1223 G.shell = xstrdup(get_shell_name());
1224
1225 cmds = iface_down;
1226 if (applet_name[2] == 'u') {
1227 /* ifup command */
1228 cmds = iface_up;
1229 }
1230
1231 getopt32(argv, OPTION_STR, &interfaces);
1232 argv += optind;
1233 if (argv[0]) {
1234 if (DO_ALL) bb_show_usage();
1235 } else {
1236 if (!DO_ALL) bb_show_usage();
1237 }
1238
1239 defn = read_interfaces(interfaces, NULL);
1240
1241 /* Create a list of interfaces to work on */
1242 if (DO_ALL) {
1243 target_list = defn->autointerfaces;
1244 } else {
1245 llist_add_to_end(&target_list, argv[0]);
1246 }
1247
1248 /* Update the interfaces */
1249 while (target_list) {
1250 llist_t *iface_list;
1251 struct interface_defn_t *currif;
1252 char *iface;
1253 char *liface;
1254 char *pch;
1255 bool okay = 0;
1256 int cmds_ret;
1257 bool curr_failure = 0;
1258
1259 iface = xstrdup(target_list->data);
1260 target_list = target_list->link;
1261
1262 pch = strchr(iface, '=');
1263 if (pch) {
1264 *pch = '\0';
1265 liface = xstrdup(pch + 1);
1266 } else {
1267 liface = xstrdup(iface);
1268 }
1269
1270 if (!FORCE) {
1271 llist_t *state_list = read_iface_state();
1272 const llist_t *iface_state = find_iface_state(state_list, iface);
1273
1274 if (cmds == iface_up) {
1275 /* ifup */
1276 if (iface_state) {
1277 bb_error_msg("interface %s already configured", iface);
1278 goto next;
1279 }
1280 } else {
1281 /* ifdown */
1282 if (!iface_state) {
1283 bb_error_msg("interface %s not configured", iface);
1284 goto next;
1285 }
1286 }
1287 llist_free(state_list, free);
1288 }
1289
1290#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1291 if ((cmds == iface_up) && !NO_MAPPINGS) {
1292 struct mapping_defn_t *currmap;
1293
1294 for (currmap = defn->mappings; currmap; currmap = currmap->next) {
1295 int i;
1296 for (i = 0; i < currmap->n_matches; i++) {
1297 if (fnmatch(currmap->match[i], liface, 0) != 0)
1298 continue;
1299 if (VERBOSE) {
1300 printf("Running mapping script %s on %s\n", currmap->script, liface);
1301 }
1302 liface = run_mapping(iface, currmap);
1303 break;
1304 }
1305 }
1306 }
1307#endif
1308
1309 iface_list = defn->ifaces;
1310 while (iface_list) {
1311 currif = (struct interface_defn_t *) iface_list->data;
1312 if (strcmp(liface, currif->iface) == 0) {
1313 char *oldiface = currif->iface;
1314
1315 okay = 1;
1316 currif->iface = iface;
1317
1318 debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
1319
1320 /* Call the cmds function pointer, does either iface_up() or iface_down() */
1321 cmds_ret = cmds(currif);
1322 if (cmds_ret == -1) {
1323 bb_error_msg("don't have all variables for %s/%s",
1324 liface, currif->address_family->name);
1325 any_failures = curr_failure = 1;
1326 } else if (cmds_ret == 0) {
1327 any_failures = curr_failure = 1;
1328 }
1329
1330 currif->iface = oldiface;
1331 }
1332 iface_list = iface_list->link;
1333 }
1334 if (VERBOSE) {
1335 bb_putchar('\n');
1336 }
1337
1338 if (!okay && !FORCE) {
1339 bb_error_msg("ignoring unknown interface %s", liface);
1340 any_failures = 1;
1341 } else if (!NO_ACT) {
1342 /* update the state file */
1343 FILE *state_fp;
1344 llist_t *state;
1345 llist_t *state_list = read_iface_state();
1346 llist_t *iface_state = find_iface_state(state_list, iface);
1347
1348 if (cmds == iface_up && !curr_failure) {
1349 char *newiface = xasprintf("%s=%s", iface, liface);
1350 if (!iface_state) {
1351 llist_add_to_end(&state_list, newiface);
1352 } else {
1353 free(iface_state->data);
1354 iface_state->data = newiface;
1355 }
1356 } else {
1357 /* Remove an interface from state_list */
1358 llist_unlink(&state_list, iface_state);
1359 free(llist_pop(&iface_state));
1360 }
1361
1362 /* Actually write the new state */
1363 state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
1364 state = state_list;
1365 while (state) {
1366 if (state->data) {
1367 fprintf(state_fp, "%s\n", state->data);
1368 }
1369 state = state->link;
1370 }
1371 fclose(state_fp);
1372 llist_free(state_list, free);
1373 }
1374 next:
1375 free(iface);
1376 free(liface);
1377 }
1378
1379 return any_failures;
1380}
Note: See TracBrowser for help on using the repository browser.