/* vi: set sw=4 ts=4: */
/*
* ifupdown for busybox
- * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>
+ * Copyright (c) 2002 Glenn McGrath
* Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
*
* Based on ifupdown v 0.6.4 by Anthony Towns
* Changes to upstream version
* Remove checks for kernel version, assume kernel version 2.2.0 or better.
* Lines in the interfaces file cannot wrap.
- * To adhere to the FHS, the default state file is /var/run/ifstate.
+ * To adhere to the FHS, the default state file is /var/run/ifstate
+ * (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
+ * configuration.
*
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
-#include "busybox.h"
#include <sys/utsname.h>
#include <fnmatch.h>
#include <getopt.h>
+#include "libbb.h"
+
#define MAX_OPT_DEPTH 10
#define EUNBALBRACK 10001
#define EUNDEFVAR 10002
#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
-#if ENABLE_FEATURE_IFUPDOWN_IP
-
-static unsigned count_bits(unsigned a)
-{
- unsigned result;
- result = (a & 0x55) + ((a >> 1) & 0x55);
- result = (result & 0x33) + ((result >> 2) & 0x33);
- return (result & 0x0F) + ((result >> 4) & 0x0F);
-}
-
-static int count_netmask_bits(char *dotted_quad)
-{
- unsigned result, a, b, c, d;
- /* Found a netmask... Check if it is dotted quad */
- if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
- return -1;
- // FIXME: will be confused by e.g. 255.0.255.0
- result = count_bits(a);
- result += count_bits(b);
- result += count_bits(c);
- result += count_bits(d);
- return (int)result;
-}
-#endif
-
static void addstr(char **bufp, const char *str, size_t str_length)
{
/* xasprintf trick will be smaller, but we are often
return NULL;
}
+#if ENABLE_FEATURE_IFUPDOWN_IP
+static int count_netmask_bits(const char *dotted_quad)
+{
+// int result;
+// unsigned a, b, c, d;
+// /* Found a netmask... Check if it is dotted quad */
+// if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+// return -1;
+// if ((a|b|c|d) >> 8)
+// return -1; /* one of numbers is >= 256 */
+// d |= (a << 24) | (b << 16) | (c << 8); /* IP */
+// d = ~d; /* 11110000 -> 00001111 */
+
+ /* Shorter version */
+ int result;
+ struct in_addr ip;
+ unsigned d;
+
+ if (inet_aton(dotted_quad, &ip) == 0)
+ return -1; /* malformed dotted IP */
+ d = ntohl(ip.s_addr); /* IP in host order */
+ d = ~d; /* 11110000 -> 00001111 */
+ if (d & (d+1)) /* check that it is in 00001111 form */
+ return -1; /* no it is not */
+ result = 32;
+ while (d) {
+ d >>= 1;
+ result--;
+ }
+ return result;
+}
+#endif
+
static char *parse(const char *command, struct interface_defn_t *ifd)
{
size_t old_pos[MAX_OPT_DEPTH] = { 0 };
if (strncmp(command, "bnmask", 6) == 0) {
unsigned res;
varvalue = get_var("netmask", 7, ifd);
- if (varvalue && (res = count_netmask_bits(varvalue)) > 0) {
- const char *argument = utoa(res);
- addstr(&result, argument, strlen(argument));
- command = nextpercent + 1;
- break;
+ if (varvalue) {
+ res = count_netmask_bits(varvalue);
+ if (res > 0) {
+ const char *argument = utoa(res);
+ addstr(&result, argument, strlen(argument));
+ command = nextpercent + 1;
+ break;
+ }
}
}
#endif
static const struct address_family_t addr_inet6 = {
"inet6",
- sizeof(methods6) / sizeof(struct method_t),
+ ARRAY_SIZE(methods6),
methods6
};
#endif /* FEATURE_IFUPDOWN_IPV6 */
result += execute("ifconfig %iface% %address% netmask %netmask%"
"[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
ifd, exec);
- result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+ result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
return ((result == 3) ? 3 : 0);
#endif
}
return ((result == 2) ? 2 : 0);
}
-#if !ENABLE_APP_UDHCPC
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
struct dhcp_client_t
{
const char *name;
};
static const struct dhcp_client_t ext_dhcp_clients[] = {
- { "udhcpc",
- "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
- "kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
- },
- { "pump",
- "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
- "pump -i %iface% -k",
+ { "dhcpcd",
+ "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
+ "dhcpcd -k %iface%",
},
{ "dhclient",
"dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
"kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
},
- { "dhcpcd",
- "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
- "dhcpcd -k %iface%",
+ { "pump",
+ "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
+ "pump -i %iface% -k",
+ },
+ { "udhcpc",
+ "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+ "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
},
};
-#endif
+#endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
{
-#if ENABLE_APP_UDHCPC
- return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
- "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
- ifd, exec);
-#else
- int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
- for (i = 0; i < nclients; i++) {
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+ int i;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* ip doesn't up iface when it configures it (unlike ifconfig) */
+ if (!execute("ip link set %iface% up", ifd, exec))
+ return 0;
+#endif
+ for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
if (exists_execable(ext_dhcp_clients[i].name))
return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
}
bb_error_msg("no dhcp clients found");
return 0;
+#elif ENABLE_APP_UDHCPC
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* ip doesn't up iface when it configures it (unlike ifconfig) */
+ if (!execute("ip link set %iface% up", ifd, exec))
+ return 0;
+#endif
+ return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
+ "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+ ifd, exec);
+#else
+ return 0; /* no dhcp support */
#endif
}
static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
{
-#if ENABLE_APP_UDHCPC
- return execute("kill -TERM "
- "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
-#else
- int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
- for (i = 0; i < nclients; i++) {
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
if (exists_execable(ext_dhcp_clients[i].name))
return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
}
bb_error_msg("no dhcp clients found, using static interface shutdown");
return static_down(ifd, exec);
+#elif ENABLE_APP_UDHCPC
+ return execute("kill "
+ "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+#else
+ return 0; /* no dhcp support */
#endif
}
static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
{
return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
- "[[ --server %server%]][[ --hwaddr %hwaddr%]] "
- "--returniffail --serverbcast", ifd, exec);
+ "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
+ " --returniffail --serverbcast", ifd, exec);
}
static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
static const struct address_family_t addr_inet = {
"inet",
- sizeof(methods) / sizeof(struct method_t),
+ ARRAY_SIZE(methods),
methods
};
free(buf);
}
if (ferror(f) != 0) {
- bb_perror_msg_and_die("%s", filename);
+ /* ferror does NOT set errno! */
+ bb_error_msg_and_die("%s: I/O error", filename);
}
fclose(f);
for (i = 0; i < iface->n_options; i++) {
if (strcmp(iface->option[i].name, "up") == 0
- || strcmp(iface->option[i].name, "down") == 0
- || strcmp(iface->option[i].name, "pre-up") == 0
- || strcmp(iface->option[i].name, "post-down") == 0) {
+ || strcmp(iface->option[i].name, "down") == 0
+ || strcmp(iface->option[i].name, "pre-up") == 0
+ || strcmp(iface->option[i].name, "post-down") == 0
+ ) {
continue;
}
*(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
llist_t *search = state_list;
while (search) {
- if ((strncmp(search->data, iface, iface_len) == 0) &&
- (search->data[iface_len] == '=')) {
+ if ((strncmp(search->data, iface, iface_len) == 0)
+ && (search->data[iface_len] == '=')) {
return search;
}
search = search->link;
return NULL;
}
+/* read the previous state from the state file */
+static llist_t *read_iface_state(void)
+{
+ llist_t *state_list = NULL;
+ FILE *state_fp = fopen(CONFIG_IFUPDOWN_IFSTATE_PATH, "r");
+
+ if (state_fp) {
+ char *start, *end_ptr;
+ while ((start = xmalloc_fgets(state_fp)) != NULL) {
+ /* We should only need to check for a single character */
+ end_ptr = start + strcspn(start, " \t\n");
+ *end_ptr = '\0';
+ llist_add_to(&state_list, start);
+ }
+ fclose(state_fp);
+ }
+ return state_list;
+}
+
+
int ifupdown_main(int argc, char **argv);
int ifupdown_main(int argc, char **argv)
{
int (*cmds)(struct interface_defn_t *) = NULL;
struct interfaces_file_t *defn;
- llist_t *state_list = NULL;
llist_t *target_list = NULL;
const char *interfaces = "/etc/network/interfaces";
- int any_failures = 0;
+ bool any_failures = 0;
cmds = iface_down;
if (applet_name[2] == 'u') {
cmds = iface_up;
}
- getopt32(argc, argv, OPTION_STR, &interfaces);
+ getopt32(argv, OPTION_STR, &interfaces);
if (argc - optind > 0) {
if (DO_ALL) bb_show_usage();
} else {
/* Create a list of interfaces to work on */
if (DO_ALL) {
- if (cmds == iface_up) {
- target_list = defn->autointerfaces;
- } else {
- /* iface_down */
- const llist_t *list = state_list;
- while (list) {
- llist_add_to_end(&target_list, xstrdup(list->data));
- list = list->link;
- }
- target_list = defn->autointerfaces;
- }
+ target_list = defn->autointerfaces;
} else {
llist_add_to_end(&target_list, argv[optind]);
}
char *iface;
char *liface;
char *pch;
- int okay = 0;
- int cmds_ret;
+ bool okay = 0;
+ unsigned cmds_ret;
iface = xstrdup(target_list->data);
target_list = target_list->link;
}
if (!FORCE) {
+ llist_t *state_list = read_iface_state();
const llist_t *iface_state = find_iface_state(state_list, iface);
if (cmds == iface_up) {
}
} else {
/* ifdown */
- if (iface_state) {
+ if (!iface_state) {
bb_error_msg("interface %s not configured", iface);
continue;
}
}
+ llist_free(state_list, free);
}
#if ENABLE_FEATURE_IFUPDOWN_MAPPING
iface_list = iface_list->link;
}
if (VERBOSE) {
- puts("");
+ bb_putchar('\n');
}
if (!okay && !FORCE) {
bb_error_msg("ignoring unknown interface %s", liface);
any_failures = 1;
- } else {
+ } else if (!NO_ACT) {
+ /* update the state file */
+ FILE *state_fp;
+ llist_t *state;
+ llist_t *state_list = read_iface_state();
llist_t *iface_state = find_iface_state(state_list, iface);
if (cmds == iface_up) {
- char *newiface = xasprintf("%s=%s", iface, liface);
+ char * const newiface = xasprintf("%s=%s", iface, liface);
if (iface_state == NULL) {
llist_add_to_end(&state_list, newiface);
} else {
iface_state->data = newiface;
}
} else {
- /* Remove an interface from the linked list */
+ /* Remove an interface from state_list */
+ llist_unlink(&state_list, iface_state);
free(llist_pop(&iface_state));
}
- }
- }
-
- /* Actually write the new state */
- if (!NO_ACT) {
- FILE *state_fp;
- state_fp = xfopen("/var/run/ifstate", "w");
- while (state_list) {
- if (state_list->data) {
- fprintf(state_fp, "%s\n", state_list->data);
+ /* Actually write the new state */
+ state_fp = xfopen(CONFIG_IFUPDOWN_IFSTATE_PATH, "w");
+ state = state_list;
+ while (state) {
+ if (state->data) {
+ fprintf(state_fp, "%s\n", state->data);
+ }
+ state = state->link;
}
- state_list = state_list->link;
+ fclose(state_fp);
+ llist_free(state_list, free);
}
- fclose(state_fp);
}
return any_failures;