1 /* vi: set sw=4 ts=4: */
4 * Copyright (c) 2002 Glenn McGrath
5 * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
7 * Based on ifupdown v 0.6.4 by Anthony Towns
8 * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
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
17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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"
30 //usage: "\n -v Print out what would happen before doing it"
31 //usage: "\n -f Force de/configuration"
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"
43 //usage: "\n -v Print out what would happen before doing it"
44 //usage: "\n -f Force de/configuration"
47 /* After libbb.h, since it needs sys/types.h on some systems */
48 #include <sys/utsname.h>
51 #define MAX_OPT_DEPTH 10
53 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
54 #define MAX_INTERFACE_LENGTH 10
57 #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
59 #define debug_noise(args...) /*fprintf(stderr, args)*/
61 /* Forward declaration */
62 struct interface_defn_t
;
64 typedef int execfn(char *command
);
68 int (*up
)(struct interface_defn_t
*ifd
, execfn
*e
) FAST_FUNC
;
69 int (*down
)(struct interface_defn_t
*ifd
, execfn
*e
) FAST_FUNC
;
72 struct address_family_t
{
75 const struct method_t
*method
;
78 struct mapping_defn_t
{
79 struct mapping_defn_t
*next
;
96 struct interface_defn_t
{
97 const struct address_family_t
*address_family
;
98 const struct method_t
*method
;
102 struct variable_t
*option
;
105 struct interfaces_file_t
{
106 llist_t
*autointerfaces
;
108 struct mapping_defn_t
*mappings
;
112 #define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
118 OPT_no_mappings
= 0x10,
120 #define DO_ALL (option_mask32 & OPT_do_all)
121 #define NO_ACT (option_mask32 & OPT_no_act)
122 #define VERBOSE (option_mask32 & OPT_verbose)
123 #define FORCE (option_mask32 & OPT_force)
124 #define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
129 const char *startup_PATH
;
132 #define G (*(struct globals*)&bb_common_bufsiz1)
133 #define INIT_G() do { } while (0)
136 static const char keywords_up_down
[] ALIGN1
=
144 #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
146 static void addstr(char **bufp
, const char *str
, size_t str_length
)
148 /* xasprintf trick will be smaller, but we are often
149 * called with str_length == 1 - don't want to have
150 * THAT much of malloc/freeing! */
152 int len
= (buf
? strlen(buf
) : 0);
154 buf
= xrealloc(buf
, len
+ str_length
);
155 /* copies at most str_length-1 chars! */
156 safe_strncpy(buf
+ len
, str
, str_length
);
160 static int strncmpz(const char *l
, const char *r
, size_t llen
)
162 int i
= strncmp(l
, r
, llen
);
165 return - (unsigned char)r
[llen
];
169 static char *get_var(const char *id
, size_t idlen
, struct interface_defn_t
*ifd
)
173 if (strncmpz(id
, "iface", idlen
) == 0) {
174 // ubuntu's ifup doesn't do this:
175 //static char *label_buf;
178 //label_buf = xstrdup(ifd->iface);
179 // Remove virtual iface suffix
180 //result = strchrnul(label_buf, ':');
186 if (strncmpz(id
, "label", idlen
) == 0) {
189 for (i
= 0; i
< ifd
->n_options
; i
++) {
190 if (strncmpz(id
, ifd
->option
[i
].name
, idlen
) == 0) {
191 return ifd
->option
[i
].value
;
197 # if ENABLE_FEATURE_IFUPDOWN_IP
198 static int count_netmask_bits(const char *dotted_quad
)
201 // unsigned a, b, c, d;
202 // /* Found a netmask... Check if it is dotted quad */
203 // if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
205 // if ((a|b|c|d) >> 8)
206 // return -1; /* one of numbers is >= 256 */
207 // d |= (a << 24) | (b << 16) | (c << 8); /* IP */
208 // d = ~d; /* 11110000 -> 00001111 */
210 /* Shorter version */
215 if (inet_aton(dotted_quad
, &ip
) == 0)
216 return -1; /* malformed dotted IP */
217 d
= ntohl(ip
.s_addr
); /* IP in host order */
218 d
= ~d
; /* 11110000 -> 00001111 */
219 if (d
& (d
+1)) /* check that it is in 00001111 form */
220 return -1; /* no it is not */
230 static char *parse(const char *command
, struct interface_defn_t
*ifd
)
232 size_t old_pos
[MAX_OPT_DEPTH
] = { 0 };
233 smallint okay
[MAX_OPT_DEPTH
] = { 1 };
240 addstr(&result
, command
, 1);
246 addstr(&result
, command
, 1);
250 if (command
[1] == '[' && opt_depth
< MAX_OPT_DEPTH
) {
251 old_pos
[opt_depth
] = result
? strlen(result
) : 0;
256 addstr(&result
, command
, 1);
261 if (command
[1] == ']' && opt_depth
> 1) {
263 if (!okay
[opt_depth
]) {
264 result
[old_pos
[opt_depth
]] = '\0';
268 addstr(&result
, command
, 1);
278 nextpercent
= strchr(command
, '%');
280 /* Unterminated %var% */
285 varvalue
= get_var(command
, nextpercent
- command
, ifd
);
288 # if ENABLE_FEATURE_IFUPDOWN_IP
289 /* "hwaddress <class> <address>":
290 * unlike ifconfig, ip doesnt want <class>
291 * (usually "ether" keyword). Skip it. */
292 if (strncmp(command
, "hwaddress", 9) == 0) {
293 varvalue
= skip_whitespace(skip_non_whitespace(varvalue
));
296 addstr(&result
, varvalue
, strlen(varvalue
));
298 # if ENABLE_FEATURE_IFUPDOWN_IP
299 /* Sigh... Add a special case for 'ip' to convert from
300 * dotted quad to bit count style netmasks. */
301 if (strncmp(command
, "bnmask", 6) == 0) {
303 varvalue
= get_var("netmask", 7, ifd
);
305 res
= count_netmask_bits(varvalue
);
307 const char *argument
= utoa(res
);
308 addstr(&result
, argument
, strlen(argument
));
309 command
= nextpercent
+ 1;
315 okay
[opt_depth
- 1] = 0;
318 command
= nextpercent
+ 1;
325 /* Unbalanced bracket */
331 /* Undefined variable and we aren't in a bracket */
339 /* execute() returns 1 for success and 0 for failure */
340 static int execute(const char *command
, struct interface_defn_t
*ifd
, execfn
*exec
)
345 out
= parse(command
, ifd
);
350 /* out == "": parsed ok but not all needed variables known, skip */
351 ret
= out
[0] ? (*exec
)(out
) : 1;
360 #endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */
363 #if ENABLE_FEATURE_IFUPDOWN_IPV6
365 static int FAST_FUNC
loopback_up6(struct interface_defn_t
*ifd
, execfn
*exec
)
367 # if ENABLE_FEATURE_IFUPDOWN_IP
369 result
= execute("ip addr add ::1 dev %iface%", ifd
, exec
);
370 result
+= execute("ip link set %iface% up", ifd
, exec
);
371 return ((result
== 2) ? 2 : 0);
373 return execute("ifconfig %iface% add ::1", ifd
, exec
);
377 static int FAST_FUNC
loopback_down6(struct interface_defn_t
*ifd
, execfn
*exec
)
379 # if ENABLE_FEATURE_IFUPDOWN_IP
380 return execute("ip link set %iface% down", ifd
, exec
);
382 return execute("ifconfig %iface% del ::1", ifd
, exec
);
386 static int FAST_FUNC
manual_up_down6(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
391 static int FAST_FUNC
static_up6(struct interface_defn_t
*ifd
, execfn
*exec
)
394 # if ENABLE_FEATURE_IFUPDOWN_IP
395 result
= execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd
, exec
);
396 result
+= execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd
, exec
);
397 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
398 result
+= execute("[[ip route add ::/0 via %gateway%]][[ prio %metric%]]", ifd
, exec
);
400 result
= execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd
, exec
);
401 result
+= execute("ifconfig %iface% add %address%/%netmask%", ifd
, exec
);
402 result
+= execute("[[route -A inet6 add ::/0 gw %gateway%[[ metric %metric%]]]]", ifd
, exec
);
404 return ((result
== 3) ? 3 : 0);
407 static int FAST_FUNC
static_down6(struct interface_defn_t
*ifd
, execfn
*exec
)
409 # if ENABLE_FEATURE_IFUPDOWN_IP
410 return execute("ip link set %iface% down", ifd
, exec
);
412 return execute("ifconfig %iface% down", ifd
, exec
);
416 # if ENABLE_FEATURE_IFUPDOWN_IP
417 static int FAST_FUNC
v4tunnel_up(struct interface_defn_t
*ifd
, execfn
*exec
)
420 result
= execute("ip tunnel add %iface% mode sit remote "
421 "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd
, exec
);
422 result
+= execute("ip link set %iface% up", ifd
, exec
);
423 result
+= execute("ip addr add %address%/%netmask% dev %iface%", ifd
, exec
);
424 result
+= execute("[[ip route add ::/0 via %gateway%]]", ifd
, exec
);
425 return ((result
== 4) ? 4 : 0);
428 static int FAST_FUNC
v4tunnel_down(struct interface_defn_t
* ifd
, execfn
* exec
)
430 return execute("ip tunnel del %iface%", ifd
, exec
);
434 static const struct method_t methods6
[] = {
435 # if ENABLE_FEATURE_IFUPDOWN_IP
436 { "v4tunnel" , v4tunnel_up
, v4tunnel_down
, },
438 { "static" , static_up6
, static_down6
, },
439 { "manual" , manual_up_down6
, manual_up_down6
, },
440 { "loopback" , loopback_up6
, loopback_down6
, },
443 static const struct address_family_t addr_inet6
= {
445 ARRAY_SIZE(methods6
),
449 #endif /* FEATURE_IFUPDOWN_IPV6 */
452 #if ENABLE_FEATURE_IFUPDOWN_IPV4
454 static int FAST_FUNC
loopback_up(struct interface_defn_t
*ifd
, execfn
*exec
)
456 # if ENABLE_FEATURE_IFUPDOWN_IP
458 result
= execute("ip addr add 127.0.0.1/8 dev %iface%", ifd
, exec
);
459 result
+= execute("ip link set %iface% up", ifd
, exec
);
460 return ((result
== 2) ? 2 : 0);
462 return execute("ifconfig %iface% 127.0.0.1 up", ifd
, exec
);
466 static int FAST_FUNC
loopback_down(struct interface_defn_t
*ifd
, execfn
*exec
)
468 # if ENABLE_FEATURE_IFUPDOWN_IP
470 result
= execute("ip addr flush dev %iface%", ifd
, exec
);
471 result
+= execute("ip link set %iface% down", ifd
, exec
);
472 return ((result
== 2) ? 2 : 0);
474 return execute("ifconfig %iface% 127.0.0.1 down", ifd
, exec
);
478 static int FAST_FUNC
static_up(struct interface_defn_t
*ifd
, execfn
*exec
)
481 # if ENABLE_FEATURE_IFUPDOWN_IP
482 result
= execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
483 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd
, exec
);
484 result
+= execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd
, exec
);
485 result
+= execute("[[ip route add default via %gateway% dev %iface%[[ prio %metric%]]]]", ifd
, exec
);
486 return ((result
== 3) ? 3 : 0);
488 /* ifconfig said to set iface up before it processes hw %hwaddress%,
489 * which then of course fails. Thus we run two separate ifconfig */
490 result
= execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
492 result
+= execute("ifconfig %iface% %address% netmask %netmask%"
493 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
495 result
+= execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd
, exec
);
496 return ((result
== 3) ? 3 : 0);
500 static int FAST_FUNC
static_down(struct interface_defn_t
*ifd
, execfn
*exec
)
503 # if ENABLE_FEATURE_IFUPDOWN_IP
504 result
= execute("ip addr flush dev %iface%", ifd
, exec
);
505 result
+= execute("ip link set %iface% down", ifd
, exec
);
507 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
508 /* Bringing the interface down deletes the routes in itself.
509 Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
511 result
+= execute("ifconfig %iface% down", ifd
, exec
);
513 return ((result
== 2) ? 2 : 0);
516 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
517 struct dhcp_client_t
{
519 const char *startcmd
;
523 static const struct dhcp_client_t ext_dhcp_clients
[] = {
525 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
529 "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
530 "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
533 "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
534 "pump -i %iface% -k",
537 "udhcpc " UDHCPC_CMD_OPTIONS
" -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
538 "[[ -s %script%]][[ %udhcpc_opts%]]",
539 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
542 # endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
544 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
545 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
548 # if ENABLE_FEATURE_IFUPDOWN_IP
549 /* ip doesn't up iface when it configures it (unlike ifconfig) */
550 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd
, exec
))
553 /* needed if we have hwaddress on dhcp iface */
554 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd
, exec
))
557 for (i
= 0; i
< ARRAY_SIZE(ext_dhcp_clients
); i
++) {
558 if (exists_execable(ext_dhcp_clients
[i
].name
))
559 return execute(ext_dhcp_clients
[i
].startcmd
, ifd
, exec
);
561 bb_error_msg("no dhcp clients found");
565 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
567 # if ENABLE_FEATURE_IFUPDOWN_IP
568 /* ip doesn't up iface when it configures it (unlike ifconfig) */
569 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd
, exec
))
572 /* needed if we have hwaddress on dhcp iface */
573 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd
, exec
))
576 return execute("udhcpc " UDHCPC_CMD_OPTIONS
" -p /var/run/udhcpc.%iface%.pid "
577 "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
581 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd UNUSED_PARAM
,
582 execfn
*exec UNUSED_PARAM
)
584 return 0; /* no dhcp support */
588 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
589 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
594 for (i
= 0; i
< ARRAY_SIZE(ext_dhcp_clients
); i
++) {
595 if (exists_execable(ext_dhcp_clients
[i
].name
)) {
596 result
= execute(ext_dhcp_clients
[i
].stopcmd
, ifd
, exec
);
603 bb_error_msg("warning: no dhcp clients found and stopped");
605 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
606 and it may come back up because udhcpc is still shutting down */
608 result
+= static_down(ifd
, exec
);
609 return ((result
== 3) ? 3 : 0);
612 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
616 "test -f /var/run/udhcpc.%iface%.pid && "
617 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
619 /* Also bring the hardware interface down since
620 killing the dhcp client alone doesn't do it.
621 This enables consecutive ifup->ifdown->ifup */
622 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
623 and it may come back up because udhcpc is still shutting down */
625 result
+= static_down(ifd
, exec
);
626 return ((result
== 3) ? 3 : 0);
629 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd UNUSED_PARAM
,
630 execfn
*exec UNUSED_PARAM
)
632 return 0; /* no dhcp support */
636 static int FAST_FUNC
manual_up_down(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
641 static int FAST_FUNC
bootp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
643 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
644 "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
645 " --returniffail --serverbcast", ifd
, exec
);
648 static int FAST_FUNC
ppp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
650 return execute("pon[[ %provider%]]", ifd
, exec
);
653 static int FAST_FUNC
ppp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
655 return execute("poff[[ %provider%]]", ifd
, exec
);
658 static int FAST_FUNC
wvdial_up(struct interface_defn_t
*ifd
, execfn
*exec
)
660 return execute("start-stop-daemon --start -x wvdial "
661 "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd
, exec
);
664 static int FAST_FUNC
wvdial_down(struct interface_defn_t
*ifd
, execfn
*exec
)
666 return execute("start-stop-daemon --stop -x wvdial "
667 "-p /var/run/wvdial.%iface% -s 2", ifd
, exec
);
670 static const struct method_t methods
[] = {
671 { "manual" , manual_up_down
, manual_up_down
, },
672 { "wvdial" , wvdial_up
, wvdial_down
, },
673 { "ppp" , ppp_up
, ppp_down
, },
674 { "static" , static_up
, static_down
, },
675 { "bootp" , bootp_up
, static_down
, },
676 { "dhcp" , dhcp_up
, dhcp_down
, },
677 { "loopback", loopback_up
, loopback_down
, },
680 static const struct address_family_t addr_inet
= {
686 #endif /* FEATURE_IFUPDOWN_IPV4 */
689 /* Returns pointer to the next word, or NULL.
690 * In 1st case, advances *buf to the word after this one.
692 static char *next_word(char **buf
)
697 /* Skip over leading whitespace */
698 word
= skip_whitespace(*buf
);
704 /* Find the length of this word (can't be 0) */
705 length
= strcspn(word
, " \t\n");
707 /* Unless we are already at NUL, store NUL and advance */
708 if (word
[length
] != '\0')
709 word
[length
++] = '\0';
711 *buf
= skip_whitespace(word
+ length
);
716 static const struct address_family_t
*get_address_family(const struct address_family_t
*const af
[], char *name
)
723 for (i
= 0; af
[i
]; i
++) {
724 if (strcmp(af
[i
]->name
, name
) == 0) {
731 static const struct method_t
*get_method(const struct address_family_t
*af
, char *name
)
737 /* TODO: use index_in_str_array() */
738 for (i
= 0; i
< af
->n_methods
; i
++) {
739 if (strcmp(af
->method
[i
].name
, name
) == 0) {
740 return &af
->method
[i
];
746 static struct interfaces_file_t
*read_interfaces(const char *filename
)
748 /* Let's try to be compatible.
750 * "man 5 interfaces" says:
751 * Lines starting with "#" are ignored. Note that end-of-line
752 * comments are NOT supported, comments must be on a line of their own.
753 * A line may be extended across multiple lines by making
754 * the last character a backslash.
756 * Seen elsewhere in example config file:
757 * A first non-blank "#" character makes the rest of the line
758 * be ignored. Blank lines are ignored. Lines may be indented freely.
759 * A "\" character at the very end of the line indicates the next line
760 * should be treated as a continuation of the current one.
762 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
763 struct mapping_defn_t
*currmap
= NULL
;
765 struct interface_defn_t
*currif
= NULL
;
766 struct interfaces_file_t
*defn
;
771 enum { NONE
, IFACE
, MAPPING
} currently_processing
= NONE
;
773 defn
= xzalloc(sizeof(*defn
));
774 f
= xfopen_for_read(filename
);
776 while ((buf
= xmalloc_fgetline(f
)) != NULL
) {
778 /* Trailing "\" concatenates lines */
780 while ((p
= last_char_is(buf
, '\\')) != NULL
) {
782 rest_of_line
= xmalloc_fgetline(f
);
785 p
= xasprintf("%s%s", buf
, rest_of_line
);
792 first_word
= next_word(&rest_of_line
);
793 if (!first_word
|| *first_word
== '#') {
795 continue; /* blank/comment line */
798 if (strcmp(first_word
, "mapping") == 0) {
799 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
800 currmap
= xzalloc(sizeof(*currmap
));
802 while ((first_word
= next_word(&rest_of_line
)) != NULL
) {
803 currmap
->match
= xrealloc_vector(currmap
->match
, 4, currmap
->n_matches
);
804 currmap
->match
[currmap
->n_matches
++] = xstrdup(first_word
);
806 /*currmap->n_mappings = 0;*/
807 /*currmap->mapping = NULL;*/
808 /*currmap->script = NULL;*/
810 struct mapping_defn_t
**where
= &defn
->mappings
;
811 while (*where
!= NULL
) {
812 where
= &(*where
)->next
;
815 /*currmap->next = NULL;*/
817 debug_noise("Added mapping\n");
819 currently_processing
= MAPPING
;
820 } else if (strcmp(first_word
, "iface") == 0) {
821 static const struct address_family_t
*const addr_fams
[] = {
822 #if ENABLE_FEATURE_IFUPDOWN_IPV4
825 #if ENABLE_FEATURE_IFUPDOWN_IPV6
831 char *address_family_name
;
835 currif
= xzalloc(sizeof(*currif
));
836 iface_name
= next_word(&rest_of_line
);
837 address_family_name
= next_word(&rest_of_line
);
838 method_name
= next_word(&rest_of_line
);
840 if (method_name
== NULL
)
841 bb_error_msg_and_die("too few parameters for line \"%s\"", buf
);
843 /* ship any trailing whitespace */
844 rest_of_line
= skip_whitespace(rest_of_line
);
846 if (rest_of_line
[0] != '\0' /* && rest_of_line[0] != '#' */)
847 bb_error_msg_and_die("too many parameters \"%s\"", buf
);
849 currif
->iface
= xstrdup(iface_name
);
851 currif
->address_family
= get_address_family(addr_fams
, address_family_name
);
852 if (!currif
->address_family
)
853 bb_error_msg_and_die("unknown address type \"%s\"", address_family_name
);
855 currif
->method
= get_method(currif
->address_family
, method_name
);
857 bb_error_msg_and_die("unknown method \"%s\"", method_name
);
859 for (iface_list
= defn
->ifaces
; iface_list
; iface_list
= iface_list
->link
) {
860 struct interface_defn_t
*tmp
= (struct interface_defn_t
*) iface_list
->data
;
861 if ((strcmp(tmp
->iface
, currif
->iface
) == 0)
862 && (tmp
->address_family
== currif
->address_family
)
864 bb_error_msg_and_die("duplicate interface \"%s\"", tmp
->iface
);
867 llist_add_to_end(&(defn
->ifaces
), (char*)currif
);
869 debug_noise("iface %s %s %s\n", currif
->iface
, address_family_name
, method_name
);
870 currently_processing
= IFACE
;
871 } else if (strcmp(first_word
, "auto") == 0) {
872 while ((first_word
= next_word(&rest_of_line
)) != NULL
) {
874 /* Check the interface isnt already listed */
875 if (llist_find_str(defn
->autointerfaces
, first_word
)) {
876 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf
);
879 /* Add the interface to the list */
880 llist_add_to_end(&(defn
->autointerfaces
), xstrdup(first_word
));
881 debug_noise("\nauto %s\n", first_word
);
883 currently_processing
= NONE
;
885 switch (currently_processing
) {
887 if (rest_of_line
[0] == '\0')
888 bb_error_msg_and_die("option with empty value \"%s\"", buf
);
890 if (strcmp(first_word
, "post-up") == 0)
891 first_word
+= 5; /* "up" */
892 else if (strcmp(first_word
, "pre-down") == 0)
893 first_word
+= 4; /* "down" */
895 /* If not one of "up", "down",... words... */
896 if (index_in_strings(keywords_up_down
, first_word
) < 0) {
898 for (i
= 0; i
< currif
->n_options
; i
++) {
899 if (strcmp(currif
->option
[i
].name
, first_word
) == 0)
900 bb_error_msg_and_die("duplicate option \"%s\"", buf
);
903 debug_noise("\t%s=%s\n", first_word
, rest_of_line
);
904 currif
->option
= xrealloc_vector(currif
->option
, 4, currif
->n_options
);
905 currif
->option
[currif
->n_options
].name
= xstrdup(first_word
);
906 currif
->option
[currif
->n_options
].value
= xstrdup(rest_of_line
);
910 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
911 if (strcmp(first_word
, "script") == 0) {
912 if (currmap
->script
!= NULL
)
913 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf
);
914 currmap
->script
= xstrdup(next_word(&rest_of_line
));
915 } else if (strcmp(first_word
, "map") == 0) {
916 currmap
->mapping
= xrealloc_vector(currmap
->mapping
, 2, currmap
->n_mappings
);
917 currmap
->mapping
[currmap
->n_mappings
] = xstrdup(next_word(&rest_of_line
));
918 currmap
->n_mappings
++;
920 bb_error_msg_and_die("misplaced option \"%s\"", buf
);
926 bb_error_msg_and_die("misplaced option \"%s\"", buf
);
930 } /* while (fgets) */
932 if (ferror(f
) != 0) {
933 /* ferror does NOT set errno! */
934 bb_error_msg_and_die("%s: I/O error", filename
);
941 static char *setlocalenv(const char *format
, const char *name
, const char *value
)
948 result
= xasprintf(format
, name
, value
);
950 for (dst
= src
= result
; (c
= *src
) != '=' && c
; src
++) {
953 if (c
>= 'a' && c
<= 'z')
955 if (isalnum(c
) || c
== '_')
958 overlapping_strcpy(dst
, src
);
963 static void set_environ(struct interface_defn_t
*iface
, const char *mode
, const char *opt
)
968 if (G
.my_environ
!= NULL
) {
969 for (pp
= G
.my_environ
; *pp
; pp
++) {
975 /* note: last element will stay NULL: */
976 G
.my_environ
= xzalloc(sizeof(char *) * (iface
->n_options
+ 7));
979 for (i
= 0; i
< iface
->n_options
; i
++) {
980 if (index_in_strings(keywords_up_down
, iface
->option
[i
].name
) >= 0) {
983 *pp
++ = setlocalenv("IF_%s=%s", iface
->option
[i
].name
, iface
->option
[i
].value
);
986 *pp
++ = setlocalenv("%s=%s", "IFACE", iface
->iface
);
987 *pp
++ = setlocalenv("%s=%s", "ADDRFAM", iface
->address_family
->name
);
988 *pp
++ = setlocalenv("%s=%s", "METHOD", iface
->method
->name
);
989 *pp
++ = setlocalenv("%s=%s", "MODE", mode
);
990 *pp
++ = setlocalenv("%s=%s", "PHASE", opt
);
992 *pp
++ = setlocalenv("%s=%s", "PATH", G
.startup_PATH
);
995 static int doit(char *str
)
997 if (option_mask32
& (OPT_no_act
|OPT_verbose
)) {
1000 if (!(option_mask32
& OPT_no_act
)) {
1006 if (child
< 0) /* failure */
1008 if (child
== 0) { /* child */
1009 execle(G
.shell
, G
.shell
, "-c", str
, (char *) NULL
, G
.my_environ
);
1012 safe_waitpid(child
, &status
, 0);
1013 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
1020 static int execute_all(struct interface_defn_t
*ifd
, const char *opt
)
1024 for (i
= 0; i
< ifd
->n_options
; i
++) {
1025 if (strcmp(ifd
->option
[i
].name
, opt
) == 0) {
1026 if (!doit(ifd
->option
[i
].value
)) {
1032 buf
= xasprintf("run-parts /etc/network/if-%s.d", opt
);
1033 /* heh, we don't bother free'ing it */
1037 static int check(char *str
)
1042 static int iface_up(struct interface_defn_t
*iface
)
1044 if (!iface
->method
->up(iface
, check
)) return -1;
1045 set_environ(iface
, "start", "pre-up");
1046 if (!execute_all(iface
, "pre-up")) return 0;
1047 if (!iface
->method
->up(iface
, doit
)) return 0;
1048 set_environ(iface
, "start", "post-up");
1049 if (!execute_all(iface
, "up")) return 0;
1053 static int iface_down(struct interface_defn_t
*iface
)
1055 if (!iface
->method
->down(iface
, check
)) return -1;
1056 set_environ(iface
, "stop", "pre-down");
1057 if (!execute_all(iface
, "down")) return 0;
1058 if (!iface
->method
->down(iface
, doit
)) return 0;
1059 set_environ(iface
, "stop", "post-down");
1060 if (!execute_all(iface
, "post-down")) return 0;
1064 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
1065 static int popen2(FILE **in
, FILE **out
, char *command
, char *param
)
1067 char *argv
[3] = { command
, param
, NULL
};
1068 struct fd_pair infd
, outfd
;
1079 /* NB: close _first_, then move fds! */
1082 xmove_fd(infd
.rd
, 0);
1083 xmove_fd(outfd
.wr
, 1);
1084 BB_EXECVP_or_die(argv
);
1089 *in
= xfdopen_for_write(infd
.wr
);
1090 *out
= xfdopen_for_read(outfd
.rd
);
1094 static char *run_mapping(char *physical
, struct mapping_defn_t
*map
)
1100 char *logical
= xstrdup(physical
);
1102 /* Run the mapping script. Never fails. */
1103 pid
= popen2(&in
, &out
, map
->script
, physical
);
1105 /* Write mappings to stdin of mapping script. */
1106 for (i
= 0; i
< map
->n_mappings
; i
++) {
1107 fprintf(in
, "%s\n", map
->mapping
[i
]);
1110 safe_waitpid(pid
, &status
, 0);
1112 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0) {
1113 /* If the mapping script exited successfully, try to
1114 * grab a line of output and use that as the name of the
1115 * logical interface. */
1116 char *new_logical
= xmalloc_fgetline(out
);
1119 /* If we are able to read a line of output from the script,
1120 * remove any trailing whitespace and use this value
1121 * as the name of the logical interface. */
1122 char *pch
= new_logical
+ strlen(new_logical
) - 1;
1124 while (pch
>= new_logical
&& isspace(*pch
))
1128 logical
= new_logical
;
1136 #endif /* FEATURE_IFUPDOWN_MAPPING */
1138 static llist_t
*find_iface_state(llist_t
*state_list
, const char *iface
)
1140 unsigned iface_len
= strlen(iface
);
1141 llist_t
*search
= state_list
;
1144 if ((strncmp(search
->data
, iface
, iface_len
) == 0)
1145 && (search
->data
[iface_len
] == '=')
1149 search
= search
->link
;
1154 /* read the previous state from the state file */
1155 static llist_t
*read_iface_state(void)
1157 llist_t
*state_list
= NULL
;
1158 FILE *state_fp
= fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH
);
1161 char *start
, *end_ptr
;
1162 while ((start
= xmalloc_fgets(state_fp
)) != NULL
) {
1163 /* We should only need to check for a single character */
1164 end_ptr
= start
+ strcspn(start
, " \t\n");
1166 llist_add_to(&state_list
, start
);
1174 int ifupdown_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
1175 int ifupdown_main(int argc UNUSED_PARAM
, char **argv
)
1177 int (*cmds
)(struct interface_defn_t
*);
1178 struct interfaces_file_t
*defn
;
1179 llist_t
*target_list
= NULL
;
1180 const char *interfaces
= "/etc/network/interfaces";
1181 bool any_failures
= 0;
1185 G
.startup_PATH
= getenv("PATH");
1186 G
.shell
= xstrdup(get_shell_name());
1189 if (applet_name
[2] == 'u') {
1194 getopt32(argv
, OPTION_STR
, &interfaces
);
1197 if (DO_ALL
) bb_show_usage();
1199 if (!DO_ALL
) bb_show_usage();
1202 debug_noise("reading %s file:\n", interfaces
);
1203 defn
= read_interfaces(interfaces
);
1204 debug_noise("\ndone reading %s\n\n", interfaces
);
1206 /* Create a list of interfaces to work on */
1208 target_list
= defn
->autointerfaces
;
1210 llist_add_to_end(&target_list
, argv
[0]);
1213 /* Update the interfaces */
1214 while (target_list
) {
1215 llist_t
*iface_list
;
1216 struct interface_defn_t
*currif
;
1223 iface
= xstrdup(target_list
->data
);
1224 target_list
= target_list
->link
;
1226 pch
= strchr(iface
, '=');
1229 liface
= xstrdup(pch
+ 1);
1231 liface
= xstrdup(iface
);
1235 llist_t
*state_list
= read_iface_state();
1236 const llist_t
*iface_state
= find_iface_state(state_list
, iface
);
1238 if (cmds
== iface_up
) {
1241 bb_error_msg("interface %s already configured", iface
);
1247 bb_error_msg("interface %s not configured", iface
);
1251 llist_free(state_list
, free
);
1254 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
1255 if ((cmds
== iface_up
) && !NO_MAPPINGS
) {
1256 struct mapping_defn_t
*currmap
;
1258 for (currmap
= defn
->mappings
; currmap
; currmap
= currmap
->next
) {
1260 for (i
= 0; i
< currmap
->n_matches
; i
++) {
1261 if (fnmatch(currmap
->match
[i
], liface
, 0) != 0)
1264 printf("Running mapping script %s on %s\n", currmap
->script
, liface
);
1266 liface
= run_mapping(iface
, currmap
);
1273 iface_list
= defn
->ifaces
;
1274 while (iface_list
) {
1275 currif
= (struct interface_defn_t
*) iface_list
->data
;
1276 if (strcmp(liface
, currif
->iface
) == 0) {
1277 char *oldiface
= currif
->iface
;
1280 currif
->iface
= iface
;
1282 debug_noise("\nConfiguring interface %s (%s)\n", liface
, currif
->address_family
->name
);
1284 /* Call the cmds function pointer, does either iface_up() or iface_down() */
1285 cmds_ret
= cmds(currif
);
1286 if (cmds_ret
== -1) {
1287 bb_error_msg("don't seem to have all the variables for %s/%s",
1288 liface
, currif
->address_family
->name
);
1290 } else if (cmds_ret
== 0) {
1294 currif
->iface
= oldiface
;
1296 iface_list
= iface_list
->link
;
1302 if (!okay
&& !FORCE
) {
1303 bb_error_msg("ignoring unknown interface %s", liface
);
1305 } else if (!NO_ACT
) {
1306 /* update the state file */
1309 llist_t
*state_list
= read_iface_state();
1310 llist_t
*iface_state
= find_iface_state(state_list
, iface
);
1312 if (cmds
== iface_up
&& !any_failures
) {
1313 char *newiface
= xasprintf("%s=%s", iface
, liface
);
1315 llist_add_to_end(&state_list
, newiface
);
1317 free(iface_state
->data
);
1318 iface_state
->data
= newiface
;
1321 /* Remove an interface from state_list */
1322 llist_unlink(&state_list
, iface_state
);
1323 free(llist_pop(&iface_state
));
1326 /* Actually write the new state */
1327 state_fp
= xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH
);
1331 fprintf(state_fp
, "%s\n", state
->data
);
1333 state
= state
->link
;
1336 llist_free(state_list
, free
);
1343 return any_failures
;