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.
21 /* After libbb.h, since it needs sys/types.h on some systems */
22 #include <sys/utsname.h>
25 #define MAX_OPT_DEPTH 10
26 #define EUNBALBRACK 10001
27 #define EUNDEFVAR 10002
28 #define EUNBALPER 10000
30 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
31 #define MAX_INTERFACE_LENGTH 10
34 #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
36 #define debug_noise(args...) /*fprintf(stderr, args)*/
38 /* Forward declaration */
39 struct interface_defn_t
;
41 typedef int execfn(char *command
);
45 int (*up
)(struct interface_defn_t
*ifd
, execfn
*e
) FAST_FUNC
;
46 int (*down
)(struct interface_defn_t
*ifd
, execfn
*e
) FAST_FUNC
;
49 struct address_family_t
{
52 const struct method_t
*method
;
55 struct mapping_defn_t
{
56 struct mapping_defn_t
*next
;
74 struct interface_defn_t
{
75 const struct address_family_t
*address_family
;
76 const struct method_t
*method
;
81 struct variable_t
*option
;
84 struct interfaces_file_t
{
85 llist_t
*autointerfaces
;
87 struct mapping_defn_t
*mappings
;
91 #define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
97 OPT_no_mappings
= 0x10,
99 #define DO_ALL (option_mask32 & OPT_do_all)
100 #define NO_ACT (option_mask32 & OPT_no_act)
101 #define VERBOSE (option_mask32 & OPT_verbose)
102 #define FORCE (option_mask32 & OPT_force)
103 #define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
108 const char *startup_PATH
;
110 #define G (*(struct globals*)&bb_common_bufsiz1)
111 #define INIT_G() do { } while (0)
114 #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
116 static void addstr(char **bufp
, const char *str
, size_t str_length
)
118 /* xasprintf trick will be smaller, but we are often
119 * called with str_length == 1 - don't want to have
120 * THAT much of malloc/freeing! */
122 int len
= (buf
? strlen(buf
) : 0);
124 buf
= xrealloc(buf
, len
+ str_length
);
125 /* copies at most str_length-1 chars! */
126 safe_strncpy(buf
+ len
, str
, str_length
);
130 static int strncmpz(const char *l
, const char *r
, size_t llen
)
132 int i
= strncmp(l
, r
, llen
);
135 return - (unsigned char)r
[llen
];
139 static char *get_var(const char *id
, size_t idlen
, struct interface_defn_t
*ifd
)
143 if (strncmpz(id
, "iface", idlen
) == 0) {
144 // ubuntu's ifup doesn't do this:
145 //static char *label_buf;
148 //label_buf = xstrdup(ifd->iface);
149 // Remove virtual iface suffix
150 //result = strchrnul(label_buf, ':');
156 if (strncmpz(id
, "label", idlen
) == 0) {
159 for (i
= 0; i
< ifd
->n_options
; i
++) {
160 if (strncmpz(id
, ifd
->option
[i
].name
, idlen
) == 0) {
161 return ifd
->option
[i
].value
;
167 # if ENABLE_FEATURE_IFUPDOWN_IP
168 static int count_netmask_bits(const char *dotted_quad
)
171 // unsigned a, b, c, d;
172 // /* Found a netmask... Check if it is dotted quad */
173 // if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
175 // if ((a|b|c|d) >> 8)
176 // return -1; /* one of numbers is >= 256 */
177 // d |= (a << 24) | (b << 16) | (c << 8); /* IP */
178 // d = ~d; /* 11110000 -> 00001111 */
180 /* Shorter version */
185 if (inet_aton(dotted_quad
, &ip
) == 0)
186 return -1; /* malformed dotted IP */
187 d
= ntohl(ip
.s_addr
); /* IP in host order */
188 d
= ~d
; /* 11110000 -> 00001111 */
189 if (d
& (d
+1)) /* check that it is in 00001111 form */
190 return -1; /* no it is not */
200 static char *parse(const char *command
, struct interface_defn_t
*ifd
)
202 size_t old_pos
[MAX_OPT_DEPTH
] = { 0 };
203 int okay
[MAX_OPT_DEPTH
] = { 1 };
210 addstr(&result
, command
, 1);
215 addstr(&result
, command
+ 1, 1);
218 addstr(&result
, command
, 1);
223 if (command
[1] == '[' && opt_depth
< MAX_OPT_DEPTH
) {
224 old_pos
[opt_depth
] = result
? strlen(result
) : 0;
229 addstr(&result
, "[", 1);
234 if (command
[1] == ']' && opt_depth
> 1) {
236 if (!okay
[opt_depth
]) {
237 result
[old_pos
[opt_depth
]] = '\0';
241 addstr(&result
, "]", 1);
251 nextpercent
= strchr(command
, '%');
258 varvalue
= get_var(command
, nextpercent
- command
, ifd
);
261 # if ENABLE_FEATURE_IFUPDOWN_IP
262 /* "hwaddress <class> <address>":
263 * unlike ifconfig, ip doesnt want <class>
264 * (usually "ether" keyword). Skip it. */
265 if (strncmp(command
, "hwaddress", 9) == 0) {
266 varvalue
= skip_whitespace(skip_non_whitespace(varvalue
));
269 addstr(&result
, varvalue
, strlen(varvalue
));
271 # if ENABLE_FEATURE_IFUPDOWN_IP
272 /* Sigh... Add a special case for 'ip' to convert from
273 * dotted quad to bit count style netmasks. */
274 if (strncmp(command
, "bnmask", 6) == 0) {
276 varvalue
= get_var("netmask", 7, ifd
);
278 res
= count_netmask_bits(varvalue
);
280 const char *argument
= utoa(res
);
281 addstr(&result
, argument
, strlen(argument
));
282 command
= nextpercent
+ 1;
288 okay
[opt_depth
- 1] = 0;
291 command
= nextpercent
+ 1;
312 /* execute() returns 1 for success and 0 for failure */
313 static int execute(const char *command
, struct interface_defn_t
*ifd
, execfn
*exec
)
318 out
= parse(command
, ifd
);
323 /* out == "": parsed ok but not all needed variables known, skip */
324 ret
= out
[0] ? (*exec
)(out
) : 1;
333 #endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */
336 #if ENABLE_FEATURE_IFUPDOWN_IPV6
338 static int FAST_FUNC
loopback_up6(struct interface_defn_t
*ifd
, execfn
*exec
)
340 # if ENABLE_FEATURE_IFUPDOWN_IP
342 result
= execute("ip addr add ::1 dev %iface%", ifd
, exec
);
343 result
+= execute("ip link set %iface% up", ifd
, exec
);
344 return ((result
== 2) ? 2 : 0);
346 return execute("ifconfig %iface% add ::1", ifd
, exec
);
350 static int FAST_FUNC
loopback_down6(struct interface_defn_t
*ifd
, execfn
*exec
)
352 # if ENABLE_FEATURE_IFUPDOWN_IP
353 return execute("ip link set %iface% down", ifd
, exec
);
355 return execute("ifconfig %iface% del ::1", ifd
, exec
);
359 static int FAST_FUNC
manual_up_down6(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
364 static int FAST_FUNC
static_up6(struct interface_defn_t
*ifd
, execfn
*exec
)
367 # if ENABLE_FEATURE_IFUPDOWN_IP
368 result
= execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd
, exec
);
369 result
+= execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd
, exec
);
370 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
371 result
+= execute("[[ip route add ::/0 via %gateway%]]", ifd
, exec
);
373 result
= execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd
, exec
);
374 result
+= execute("ifconfig %iface% add %address%/%netmask%", ifd
, exec
);
375 result
+= execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd
, exec
);
377 return ((result
== 3) ? 3 : 0);
380 static int FAST_FUNC
static_down6(struct interface_defn_t
*ifd
, execfn
*exec
)
382 # if ENABLE_FEATURE_IFUPDOWN_IP
383 return execute("ip link set %iface% down", ifd
, exec
);
385 return execute("ifconfig %iface% down", ifd
, exec
);
389 # if ENABLE_FEATURE_IFUPDOWN_IP
390 static int FAST_FUNC
v4tunnel_up(struct interface_defn_t
*ifd
, execfn
*exec
)
393 result
= execute("ip tunnel add %iface% mode sit remote "
394 "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd
, exec
);
395 result
+= execute("ip link set %iface% up", ifd
, exec
);
396 result
+= execute("ip addr add %address%/%netmask% dev %iface%", ifd
, exec
);
397 result
+= execute("[[ip route add ::/0 via %gateway%]]", ifd
, exec
);
398 return ((result
== 4) ? 4 : 0);
401 static int FAST_FUNC
v4tunnel_down(struct interface_defn_t
* ifd
, execfn
* exec
)
403 return execute("ip tunnel del %iface%", ifd
, exec
);
407 static const struct method_t methods6
[] = {
408 # if ENABLE_FEATURE_IFUPDOWN_IP
409 { "v4tunnel" , v4tunnel_up
, v4tunnel_down
, },
411 { "static" , static_up6
, static_down6
, },
412 { "manual" , manual_up_down6
, manual_up_down6
, },
413 { "loopback" , loopback_up6
, loopback_down6
, },
416 static const struct address_family_t addr_inet6
= {
418 ARRAY_SIZE(methods6
),
422 #endif /* FEATURE_IFUPDOWN_IPV6 */
425 #if ENABLE_FEATURE_IFUPDOWN_IPV4
427 static int FAST_FUNC
loopback_up(struct interface_defn_t
*ifd
, execfn
*exec
)
429 # if ENABLE_FEATURE_IFUPDOWN_IP
431 result
= execute("ip addr add 127.0.0.1/8 dev %iface%", ifd
, exec
);
432 result
+= execute("ip link set %iface% up", ifd
, exec
);
433 return ((result
== 2) ? 2 : 0);
435 return execute("ifconfig %iface% 127.0.0.1 up", ifd
, exec
);
439 static int FAST_FUNC
loopback_down(struct interface_defn_t
*ifd
, execfn
*exec
)
441 # if ENABLE_FEATURE_IFUPDOWN_IP
443 result
= execute("ip addr flush dev %iface%", ifd
, exec
);
444 result
+= execute("ip link set %iface% down", ifd
, exec
);
445 return ((result
== 2) ? 2 : 0);
447 return execute("ifconfig %iface% 127.0.0.1 down", ifd
, exec
);
451 static int FAST_FUNC
static_up(struct interface_defn_t
*ifd
, execfn
*exec
)
454 # if ENABLE_FEATURE_IFUPDOWN_IP
455 result
= execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
456 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd
, exec
);
457 result
+= execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd
, exec
);
458 result
+= execute("[[ip route add default via %gateway% dev %iface%]]", ifd
, exec
);
459 return ((result
== 3) ? 3 : 0);
461 /* ifconfig said to set iface up before it processes hw %hwaddress%,
462 * which then of course fails. Thus we run two separate ifconfig */
463 result
= execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
465 result
+= execute("ifconfig %iface% %address% netmask %netmask%"
466 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
468 result
+= execute("[[route add default gw %gateway% %iface%]]", ifd
, exec
);
469 return ((result
== 3) ? 3 : 0);
473 static int FAST_FUNC
static_down(struct interface_defn_t
*ifd
, execfn
*exec
)
476 # if ENABLE_FEATURE_IFUPDOWN_IP
477 result
= execute("ip addr flush dev %iface%", ifd
, exec
);
478 result
+= execute("ip link set %iface% down", ifd
, exec
);
480 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
481 /* Bringing the interface down deletes the routes in itself.
482 Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
484 result
+= execute("ifconfig %iface% down", ifd
, exec
);
486 return ((result
== 2) ? 2 : 0);
489 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
490 struct dhcp_client_t
{
492 const char *startcmd
;
496 static const struct dhcp_client_t ext_dhcp_clients
[] = {
498 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
502 "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
503 "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
506 "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
507 "pump -i %iface% -k",
510 "udhcpc " UDHCPC_CMD_OPTIONS
" -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
511 "[[ -s %script%]][[ %udhcpc_opts%]]",
512 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
515 # endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
517 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
518 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
521 # if ENABLE_FEATURE_IFUPDOWN_IP
522 /* ip doesn't up iface when it configures it (unlike ifconfig) */
523 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd
, exec
))
526 /* needed if we have hwaddress on dhcp iface */
527 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd
, exec
))
530 for (i
= 0; i
< ARRAY_SIZE(ext_dhcp_clients
); i
++) {
531 if (exists_execable(ext_dhcp_clients
[i
].name
))
532 return execute(ext_dhcp_clients
[i
].startcmd
, ifd
, exec
);
534 bb_error_msg("no dhcp clients found");
538 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
540 # if ENABLE_FEATURE_IFUPDOWN_IP
541 /* ip doesn't up iface when it configures it (unlike ifconfig) */
542 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd
, exec
))
545 /* needed if we have hwaddress on dhcp iface */
546 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd
, exec
))
549 return execute("udhcpc " UDHCPC_CMD_OPTIONS
" -p /var/run/udhcpc.%iface%.pid "
550 "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
554 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd UNUSED_PARAM
,
555 execfn
*exec UNUSED_PARAM
)
557 return 0; /* no dhcp support */
561 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
562 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
567 for (i
= 0; i
< ARRAY_SIZE(ext_dhcp_clients
); i
++) {
568 if (exists_execable(ext_dhcp_clients
[i
].name
)) {
569 result
= execute(ext_dhcp_clients
[i
].stopcmd
, ifd
, exec
);
576 bb_error_msg("warning: no dhcp clients found and stopped");
578 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
579 and it may come back up because udhcpc is still shutting down */
581 result
+= static_down(ifd
, exec
);
582 return ((result
== 3) ? 3 : 0);
585 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
589 "test -f /var/run/udhcpc.%iface%.pid && "
590 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
592 /* Also bring the hardware interface down since
593 killing the dhcp client alone doesn't do it.
594 This enables consecutive ifup->ifdown->ifup */
595 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
596 and it may come back up because udhcpc is still shutting down */
598 result
+= static_down(ifd
, exec
);
599 return ((result
== 3) ? 3 : 0);
602 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd UNUSED_PARAM
,
603 execfn
*exec UNUSED_PARAM
)
605 return 0; /* no dhcp support */
609 static int FAST_FUNC
manual_up_down(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
614 static int FAST_FUNC
bootp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
616 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
617 "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
618 " --returniffail --serverbcast", ifd
, exec
);
621 static int FAST_FUNC
ppp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
623 return execute("pon[[ %provider%]]", ifd
, exec
);
626 static int FAST_FUNC
ppp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
628 return execute("poff[[ %provider%]]", ifd
, exec
);
631 static int FAST_FUNC
wvdial_up(struct interface_defn_t
*ifd
, execfn
*exec
)
633 return execute("start-stop-daemon --start -x wvdial "
634 "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd
, exec
);
637 static int FAST_FUNC
wvdial_down(struct interface_defn_t
*ifd
, execfn
*exec
)
639 return execute("start-stop-daemon --stop -x wvdial "
640 "-p /var/run/wvdial.%iface% -s 2", ifd
, exec
);
643 static const struct method_t methods
[] = {
644 { "manual" , manual_up_down
, manual_up_down
, },
645 { "wvdial" , wvdial_up
, wvdial_down
, },
646 { "ppp" , ppp_up
, ppp_down
, },
647 { "static" , static_up
, static_down
, },
648 { "bootp" , bootp_up
, static_down
, },
649 { "dhcp" , dhcp_up
, dhcp_down
, },
650 { "loopback", loopback_up
, loopback_down
, },
653 static const struct address_family_t addr_inet
= {
659 #endif /* FEATURE_IFUPDOWN_IPV4 */
662 /* Returns pointer to the next word, or NULL.
663 * In 1st case, advances *buf to the word after this one.
665 static char *next_word(char **buf
)
670 /* Skip over leading whitespace */
671 word
= skip_whitespace(*buf
);
677 /* Find the length of this word (can't be 0) */
678 length
= strcspn(word
, " \t\n");
680 /* Unless we are already at NUL, store NUL and advance */
681 if (word
[length
] != '\0')
682 word
[length
++] = '\0';
684 *buf
= skip_whitespace(word
+ length
);
689 static const struct address_family_t
*get_address_family(const struct address_family_t
*const af
[], char *name
)
696 for (i
= 0; af
[i
]; i
++) {
697 if (strcmp(af
[i
]->name
, name
) == 0) {
704 static const struct method_t
*get_method(const struct address_family_t
*af
, char *name
)
710 /* TODO: use index_in_str_array() */
711 for (i
= 0; i
< af
->n_methods
; i
++) {
712 if (strcmp(af
->method
[i
].name
, name
) == 0) {
713 return &af
->method
[i
];
719 static struct interfaces_file_t
*read_interfaces(const char *filename
)
721 /* Let's try to be compatible.
723 * "man 5 interfaces" says:
724 * Lines starting with "#" are ignored. Note that end-of-line
725 * comments are NOT supported, comments must be on a line of their own.
726 * A line may be extended across multiple lines by making
727 * the last character a backslash.
729 * Seen elsewhere in example config file:
730 * A first non-blank "#" character makes the rest of the line
731 * be ignored. Blank lines are ignored. Lines may be indented freely.
732 * A "\" character at the very end of the line indicates the next line
733 * should be treated as a continuation of the current one.
735 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
736 struct mapping_defn_t
*currmap
= NULL
;
738 struct interface_defn_t
*currif
= NULL
;
739 struct interfaces_file_t
*defn
;
744 enum { NONE
, IFACE
, MAPPING
} currently_processing
= NONE
;
746 defn
= xzalloc(sizeof(*defn
));
747 f
= xfopen_for_read(filename
);
749 while ((buf
= xmalloc_fgetline(f
)) != NULL
) {
751 /* Trailing "\" concatenates lines */
753 while ((p
= last_char_is(buf
, '\\')) != NULL
) {
755 rest_of_line
= xmalloc_fgetline(f
);
758 p
= xasprintf("%s%s", buf
, rest_of_line
);
765 first_word
= next_word(&rest_of_line
);
766 if (!first_word
|| *first_word
== '#') {
768 continue; /* blank/comment line */
771 if (strcmp(first_word
, "mapping") == 0) {
772 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
773 currmap
= xzalloc(sizeof(*currmap
));
775 while ((first_word
= next_word(&rest_of_line
)) != NULL
) {
776 currmap
->match
= xrealloc_vector(currmap
->match
, 4, currmap
->n_matches
);
777 currmap
->match
[currmap
->n_matches
++] = xstrdup(first_word
);
779 /*currmap->max_mappings = 0; - done by xzalloc */
780 /*currmap->n_mappings = 0;*/
781 /*currmap->mapping = NULL;*/
782 /*currmap->script = NULL;*/
784 struct mapping_defn_t
**where
= &defn
->mappings
;
785 while (*where
!= NULL
) {
786 where
= &(*where
)->next
;
789 /*currmap->next = NULL;*/
791 debug_noise("Added mapping\n");
793 currently_processing
= MAPPING
;
794 } else if (strcmp(first_word
, "iface") == 0) {
795 static const struct address_family_t
*const addr_fams
[] = {
796 #if ENABLE_FEATURE_IFUPDOWN_IPV4
799 #if ENABLE_FEATURE_IFUPDOWN_IPV6
805 char *address_family_name
;
809 currif
= xzalloc(sizeof(*currif
));
810 iface_name
= next_word(&rest_of_line
);
811 address_family_name
= next_word(&rest_of_line
);
812 method_name
= next_word(&rest_of_line
);
814 if (method_name
== NULL
)
815 bb_error_msg_and_die("too few parameters for line \"%s\"", buf
);
817 /* ship any trailing whitespace */
818 rest_of_line
= skip_whitespace(rest_of_line
);
820 if (rest_of_line
[0] != '\0' /* && rest_of_line[0] != '#' */)
821 bb_error_msg_and_die("too many parameters \"%s\"", buf
);
823 currif
->iface
= xstrdup(iface_name
);
825 currif
->address_family
= get_address_family(addr_fams
, address_family_name
);
826 if (!currif
->address_family
)
827 bb_error_msg_and_die("unknown address type \"%s\"", address_family_name
);
829 currif
->method
= get_method(currif
->address_family
, method_name
);
831 bb_error_msg_and_die("unknown method \"%s\"", method_name
);
833 for (iface_list
= defn
->ifaces
; iface_list
; iface_list
= iface_list
->link
) {
834 struct interface_defn_t
*tmp
= (struct interface_defn_t
*) iface_list
->data
;
835 if ((strcmp(tmp
->iface
, currif
->iface
) == 0)
836 && (tmp
->address_family
== currif
->address_family
)
838 bb_error_msg_and_die("duplicate interface \"%s\"", tmp
->iface
);
841 llist_add_to_end(&(defn
->ifaces
), (char*)currif
);
843 debug_noise("iface %s %s %s\n", currif
->iface
, address_family_name
, method_name
);
844 currently_processing
= IFACE
;
845 } else if (strcmp(first_word
, "auto") == 0) {
846 while ((first_word
= next_word(&rest_of_line
)) != NULL
) {
848 /* Check the interface isnt already listed */
849 if (llist_find_str(defn
->autointerfaces
, first_word
)) {
850 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf
);
853 /* Add the interface to the list */
854 llist_add_to_end(&(defn
->autointerfaces
), xstrdup(first_word
));
855 debug_noise("\nauto %s\n", first_word
);
857 currently_processing
= NONE
;
859 switch (currently_processing
) {
861 if (rest_of_line
[0] == '\0')
862 bb_error_msg_and_die("option with empty value \"%s\"", buf
);
864 if (strcmp(first_word
, "up") != 0
865 && strcmp(first_word
, "down") != 0
866 && strcmp(first_word
, "pre-up") != 0
867 && strcmp(first_word
, "post-down") != 0
870 for (i
= 0; i
< currif
->n_options
; i
++) {
871 if (strcmp(currif
->option
[i
].name
, first_word
) == 0)
872 bb_error_msg_and_die("duplicate option \"%s\"", buf
);
875 if (currif
->n_options
>= currif
->max_options
) {
876 currif
->max_options
+= 10;
877 currif
->option
= xrealloc(currif
->option
,
878 sizeof(*currif
->option
) * currif
->max_options
);
880 debug_noise("\t%s=%s\n", first_word
, rest_of_line
);
881 currif
->option
[currif
->n_options
].name
= xstrdup(first_word
);
882 currif
->option
[currif
->n_options
].value
= xstrdup(rest_of_line
);
886 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
887 if (strcmp(first_word
, "script") == 0) {
888 if (currmap
->script
!= NULL
)
889 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf
);
890 currmap
->script
= xstrdup(next_word(&rest_of_line
));
891 } else if (strcmp(first_word
, "map") == 0) {
892 if (currmap
->n_mappings
>= currmap
->max_mappings
) {
893 currmap
->max_mappings
= currmap
->max_mappings
* 2 + 1;
894 currmap
->mapping
= xrealloc(currmap
->mapping
,
895 sizeof(char *) * currmap
->max_mappings
);
897 currmap
->mapping
[currmap
->n_mappings
] = xstrdup(next_word(&rest_of_line
));
898 currmap
->n_mappings
++;
900 bb_error_msg_and_die("misplaced option \"%s\"", buf
);
906 bb_error_msg_and_die("misplaced option \"%s\"", buf
);
910 } /* while (fgets) */
912 if (ferror(f
) != 0) {
913 /* ferror does NOT set errno! */
914 bb_error_msg_and_die("%s: I/O error", filename
);
921 static char *setlocalenv(const char *format
, const char *name
, const char *value
)
928 result
= xasprintf(format
, name
, value
);
930 for (dst
= src
= result
; (c
= *src
) != '=' && c
; src
++) {
933 if (c
>= 'a' && c
<= 'z')
935 if (isalnum(c
) || c
== '_')
938 overlapping_strcpy(dst
, src
);
943 static void set_environ(struct interface_defn_t
*iface
, const char *mode
)
948 if (G
.my_environ
!= NULL
) {
949 for (pp
= G
.my_environ
; *pp
; pp
++) {
955 /* note: last element will stay NULL: */
956 G
.my_environ
= xzalloc(sizeof(char *) * (iface
->n_options
+ 6));
959 for (i
= 0; i
< iface
->n_options
; i
++) {
960 if (strcmp(iface
->option
[i
].name
, "up") == 0
961 || strcmp(iface
->option
[i
].name
, "down") == 0
962 || strcmp(iface
->option
[i
].name
, "pre-up") == 0
963 || strcmp(iface
->option
[i
].name
, "post-down") == 0
967 *pp
++ = setlocalenv("IF_%s=%s", iface
->option
[i
].name
, iface
->option
[i
].value
);
970 *pp
++ = setlocalenv("%s=%s", "IFACE", iface
->iface
);
971 *pp
++ = setlocalenv("%s=%s", "ADDRFAM", iface
->address_family
->name
);
972 *pp
++ = setlocalenv("%s=%s", "METHOD", iface
->method
->name
);
973 *pp
++ = setlocalenv("%s=%s", "MODE", mode
);
975 *pp
++ = setlocalenv("%s=%s", "PATH", G
.startup_PATH
);
978 static int doit(char *str
)
980 if (option_mask32
& (OPT_no_act
|OPT_verbose
)) {
983 if (!(option_mask32
& OPT_no_act
)) {
990 case -1: /* failure */
993 execle(DEFAULT_SHELL
, DEFAULT_SHELL
, "-c", str
, (char *) NULL
, G
.my_environ
);
996 safe_waitpid(child
, &status
, 0);
997 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
1004 static int execute_all(struct interface_defn_t
*ifd
, const char *opt
)
1008 for (i
= 0; i
< ifd
->n_options
; i
++) {
1009 if (strcmp(ifd
->option
[i
].name
, opt
) == 0) {
1010 if (!doit(ifd
->option
[i
].value
)) {
1016 buf
= xasprintf("run-parts /etc/network/if-%s.d", opt
);
1017 /* heh, we don't bother free'ing it */
1021 static int check(char *str
)
1026 static int iface_up(struct interface_defn_t
*iface
)
1028 if (!iface
->method
->up(iface
, check
)) return -1;
1029 set_environ(iface
, "start");
1030 if (!execute_all(iface
, "pre-up")) return 0;
1031 if (!iface
->method
->up(iface
, doit
)) return 0;
1032 if (!execute_all(iface
, "up")) return 0;
1036 static int iface_down(struct interface_defn_t
*iface
)
1038 if (!iface
->method
->down(iface
,check
)) return -1;
1039 set_environ(iface
, "stop");
1040 if (!execute_all(iface
, "down")) return 0;
1041 if (!iface
->method
->down(iface
, doit
)) return 0;
1042 if (!execute_all(iface
, "post-down")) return 0;
1046 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
1047 static int popen2(FILE **in
, FILE **out
, char *command
, char *param
)
1049 char *argv
[3] = { command
, param
, NULL
};
1050 struct fd_pair infd
, outfd
;
1061 /* NB: close _first_, then move fds! */
1064 xmove_fd(infd
.rd
, 0);
1065 xmove_fd(outfd
.wr
, 1);
1066 BB_EXECVP_or_die(argv
);
1071 *in
= xfdopen_for_write(infd
.wr
);
1072 *out
= xfdopen_for_read(outfd
.rd
);
1076 static char *run_mapping(char *physical
, struct mapping_defn_t
*map
)
1082 char *logical
= xstrdup(physical
);
1084 /* Run the mapping script. Never fails. */
1085 pid
= popen2(&in
, &out
, map
->script
, physical
);
1087 /* Write mappings to stdin of mapping script. */
1088 for (i
= 0; i
< map
->n_mappings
; i
++) {
1089 fprintf(in
, "%s\n", map
->mapping
[i
]);
1092 safe_waitpid(pid
, &status
, 0);
1094 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0) {
1095 /* If the mapping script exited successfully, try to
1096 * grab a line of output and use that as the name of the
1097 * logical interface. */
1098 char *new_logical
= xmalloc_fgetline(out
);
1101 /* If we are able to read a line of output from the script,
1102 * remove any trailing whitespace and use this value
1103 * as the name of the logical interface. */
1104 char *pch
= new_logical
+ strlen(new_logical
) - 1;
1106 while (pch
>= new_logical
&& isspace(*pch
))
1110 logical
= new_logical
;
1118 #endif /* FEATURE_IFUPDOWN_MAPPING */
1120 static llist_t
*find_iface_state(llist_t
*state_list
, const char *iface
)
1122 unsigned iface_len
= strlen(iface
);
1123 llist_t
*search
= state_list
;
1126 if ((strncmp(search
->data
, iface
, iface_len
) == 0)
1127 && (search
->data
[iface_len
] == '=')
1131 search
= search
->link
;
1136 /* read the previous state from the state file */
1137 static llist_t
*read_iface_state(void)
1139 llist_t
*state_list
= NULL
;
1140 FILE *state_fp
= fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH
);
1143 char *start
, *end_ptr
;
1144 while ((start
= xmalloc_fgets(state_fp
)) != NULL
) {
1145 /* We should only need to check for a single character */
1146 end_ptr
= start
+ strcspn(start
, " \t\n");
1148 llist_add_to(&state_list
, start
);
1156 int ifupdown_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
1157 int ifupdown_main(int argc UNUSED_PARAM
, char **argv
)
1159 int (*cmds
)(struct interface_defn_t
*);
1160 struct interfaces_file_t
*defn
;
1161 llist_t
*target_list
= NULL
;
1162 const char *interfaces
= "/etc/network/interfaces";
1163 bool any_failures
= 0;
1167 G
.startup_PATH
= getenv("PATH");
1170 if (applet_name
[2] == 'u') {
1175 getopt32(argv
, OPTION_STR
, &interfaces
);
1178 if (DO_ALL
) bb_show_usage();
1180 if (!DO_ALL
) bb_show_usage();
1183 debug_noise("reading %s file:\n", interfaces
);
1184 defn
= read_interfaces(interfaces
);
1185 debug_noise("\ndone reading %s\n\n", interfaces
);
1187 /* Create a list of interfaces to work on */
1189 target_list
= defn
->autointerfaces
;
1191 llist_add_to_end(&target_list
, argv
[0]);
1194 /* Update the interfaces */
1195 while (target_list
) {
1196 llist_t
*iface_list
;
1197 struct interface_defn_t
*currif
;
1204 iface
= xstrdup(target_list
->data
);
1205 target_list
= target_list
->link
;
1207 pch
= strchr(iface
, '=');
1210 liface
= xstrdup(pch
+ 1);
1212 liface
= xstrdup(iface
);
1216 llist_t
*state_list
= read_iface_state();
1217 const llist_t
*iface_state
= find_iface_state(state_list
, iface
);
1219 if (cmds
== iface_up
) {
1222 bb_error_msg("interface %s already configured", iface
);
1228 bb_error_msg("interface %s not configured", iface
);
1232 llist_free(state_list
, free
);
1235 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
1236 if ((cmds
== iface_up
) && !NO_MAPPINGS
) {
1237 struct mapping_defn_t
*currmap
;
1239 for (currmap
= defn
->mappings
; currmap
; currmap
= currmap
->next
) {
1241 for (i
= 0; i
< currmap
->n_matches
; i
++) {
1242 if (fnmatch(currmap
->match
[i
], liface
, 0) != 0)
1245 printf("Running mapping script %s on %s\n", currmap
->script
, liface
);
1247 liface
= run_mapping(iface
, currmap
);
1254 iface_list
= defn
->ifaces
;
1255 while (iface_list
) {
1256 currif
= (struct interface_defn_t
*) iface_list
->data
;
1257 if (strcmp(liface
, currif
->iface
) == 0) {
1258 char *oldiface
= currif
->iface
;
1261 currif
->iface
= iface
;
1263 debug_noise("\nConfiguring interface %s (%s)\n", liface
, currif
->address_family
->name
);
1265 /* Call the cmds function pointer, does either iface_up() or iface_down() */
1266 cmds_ret
= cmds(currif
);
1267 if (cmds_ret
== -1) {
1268 bb_error_msg("don't seem to have all the variables for %s/%s",
1269 liface
, currif
->address_family
->name
);
1271 } else if (cmds_ret
== 0) {
1275 currif
->iface
= oldiface
;
1277 iface_list
= iface_list
->link
;
1283 if (!okay
&& !FORCE
) {
1284 bb_error_msg("ignoring unknown interface %s", liface
);
1286 } else if (!NO_ACT
) {
1287 /* update the state file */
1290 llist_t
*state_list
= read_iface_state();
1291 llist_t
*iface_state
= find_iface_state(state_list
, iface
);
1293 if (cmds
== iface_up
) {
1294 char * const newiface
= xasprintf("%s=%s", iface
, liface
);
1295 if (iface_state
== NULL
) {
1296 llist_add_to_end(&state_list
, newiface
);
1298 free(iface_state
->data
);
1299 iface_state
->data
= newiface
;
1302 /* Remove an interface from state_list */
1303 llist_unlink(&state_list
, iface_state
);
1304 free(llist_pop(&iface_state
));
1307 /* Actually write the new state */
1308 state_fp
= xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH
);
1312 fprintf(state_fp
, "%s\n", state
->data
);
1314 state
= state
->link
;
1317 llist_free(state_list
, free
);
1321 return any_failures
;