diff --git a/executor-scripts/linux/link b/executor-scripts/linux/link index aec3a7c..2aa78f9 100755 --- a/executor-scripts/linux/link +++ b/executor-scripts/linux/link @@ -29,12 +29,14 @@ is_vlan() { } case "$PHASE" in -up|down) +pre-up|post-down) + UP_DOWN="${PHASE##*-}" + if is_vlan; then ADD_DEL="add" - [ "$PHASE" = "down" ] && ADD_DEL="delete" + [ "$UP_DOWN" = "down" ] && ADD_DEL="delete" - if [ -e /sys/class/net/$IFACE ]; then + if [ "$UP_DOWN" = "up" -a -e /sys/class/net/$IFACE ]; then exit 0 fi @@ -43,16 +45,19 @@ up|down) echo "Device $IF_VLAN_RAW_DEVICE for $IFACE does not exist" exit 1 fi - fi - if ! [ -d /proc/net/vlan ]; then - echo "Loading 8021q kernel module for VLAN support" - ${MOCK} modprobe 8021q + if ! [ -d /proc/net/vlan ]; then + echo "Loading 8021q kernel module for VLAN support" + ${MOCK} modprobe 8021q + fi fi ${MOCK} ip link $ADD_DEL link "$IF_VLAN_RAW_DEVICE" name "$IFACE" type vlan id "$IF_VLAN_ID" + [ "$UP_DOWN" = "down" ] && exit 0 + + ${MOCK} ip link set $UP_DOWN dev $IFACE $IF_LINK_OPTIONS else - ${MOCK} ip link set $PHASE dev $IFACE $IF_LINK_OPTIONS + ${MOCK} ip link set $UP_DOWN dev $IFACE $IF_LINK_OPTIONS fi ;; depend) diff --git a/executor-scripts/linux/static b/executor-scripts/linux/static index f03b61d..b1c9f8d 100755 --- a/executor-scripts/linux/static +++ b/executor-scripts/linux/static @@ -1,5 +1,6 @@ #!/bin/sh +[ -z "$IF_METRIC" ] && IF_METRIC="1" [ -n "$IF_VRF_TABLE" ] && VRF_TABLE="table $IF_VRF_TABLE" [ -n "$IF_METRIC" ] && METRIC="metric $IF_METRIC" diff --git a/libifupdown/dict.h b/libifupdown/dict.h index 9a48ad2..2ede9a2 100644 --- a/libifupdown/dict.h +++ b/libifupdown/dict.h @@ -35,6 +35,9 @@ struct lif_dict_entry { #define LIF_DICT_FOREACH_SAFE(iter, iter_next, dict) \ LIF_LIST_FOREACH_SAFE((iter), (iter_next), (dict)->list.head) +#define LIF_DICT_FOREACH_REVERSE(iter, dict) \ + LIF_LIST_FOREACH_REVERSE((iter), (dict)->list.tail) + typedef int (*lif_dict_cmp_t)(const void *, const void *); extern void lif_dict_init(struct lif_dict *dict); diff --git a/libifupdown/lifecycle.c b/libifupdown/lifecycle.c index d2a32e3..a70fcc1 100644 --- a/libifupdown/lifecycle.c +++ b/libifupdown/lifecycle.c @@ -43,21 +43,33 @@ handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[ return true; } +static inline bool +handle_single_executor_for_phase(const struct lif_dict_entry *entry, const struct lif_execute_opts *opts, char *const envp[]) +{ + if (strcmp(entry->key, "use")) + return true; + + const char *cmd = entry->data; + if (!lif_maybe_run_executor(opts, envp, cmd)) + return false; + + return true; +} + static bool -handle_executors_for_phase(const struct lif_execute_opts *opts, char *const envp[], struct lif_interface *iface) +handle_executors_for_phase(const struct lif_execute_opts *opts, char *const envp[], struct lif_interface *iface, bool up) { struct lif_node *iter; - LIF_DICT_FOREACH(iter, &iface->vars) + if (up) { - struct lif_dict_entry *entry = iter->data; - - if (strcmp(entry->key, "use")) - continue; - - const char *cmd = entry->data; - if (!lif_maybe_run_executor(opts, envp, cmd)) - return false; + LIF_DICT_FOREACH(iter, &iface->vars) + handle_single_executor_for_phase(iter->data, opts, envp); + } + else + { + LIF_DICT_FOREACH_REVERSE(iter, &iface->vars) + handle_single_executor_for_phase(iter->data, opts, envp); } return true; @@ -219,7 +231,7 @@ lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interfac build_environment(&envp, opts, iface, lifname, phase, up ? "start" : "stop"); - if (!handle_executors_for_phase(opts, envp, iface)) + if (!handle_executors_for_phase(opts, envp, iface, up)) goto handle_error; if (!handle_commands_for_phase(opts, envp, iface, phase)) @@ -255,7 +267,11 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par /* already up or down, skip */ if (up == iface->is_up) + { + fprintf(stderr, "ifupdown: skipping dependent interface %s (of %s)\n", + iface->ifname, parent->ifname); continue; + } if (opts->verbose) fprintf(stderr, "ifupdown: changing state of dependent interface %s (of %s) to %s\n", diff --git a/libifupdown/list.h b/libifupdown/list.h index 4a495b5..877354e 100644 --- a/libifupdown/list.h +++ b/libifupdown/list.h @@ -42,4 +42,7 @@ extern void lif_node_delete(struct lif_node *node, struct lif_list *list); #define LIF_LIST_FOREACH_SAFE(iter, iter_next, head) \ for ((iter) = (head), (iter_next) = (iter)->next; (iter) != NULL; (iter) = (iter_next), (iter_next) = (iter) != NULL ? (iter)->next : NULL) +#define LIF_LIST_FOREACH_REVERSE(iter, tail) \ + for ((iter) = (tail); (iter) != NULL; (iter) = (iter)->prev) + #endif diff --git a/tests/fixtures/vlan-complex.interfaces b/tests/fixtures/vlan-complex.interfaces new file mode 100644 index 0000000..e06b617 --- /dev/null +++ b/tests/fixtures/vlan-complex.interfaces @@ -0,0 +1,18 @@ +# From Alpine issue #11885. +iface lo inet loopback + +auto eth0 +iface eth0 + address 1.2.3.4/24 + address abcd:ef12:3456:3::4/64 + mtu 8000 + +auto servers +iface servers + address 1.2.10.4/24 + gateway 1.2.10.1 + address abcd:ef12:3456:10::4/64 + gateway abcd:ef12:3456:10::1 + mtu 8000 + vlan-raw-device eth0 + vlan_id 5 diff --git a/tests/ifquery_test b/tests/ifquery_test index 26c0c6f..9d86e28 100755 --- a/tests/ifquery_test +++ b/tests/ifquery_test @@ -32,7 +32,8 @@ tests_init \ tunnel_ifupdown2_rewrite \ gre_dependency \ vlan_explicit_learned_dependency \ - vlan_guessed_learned_dependency + vlan_guessed_learned_dependency \ + vlan_complex_learned_dependency noargs_body() { atf_check -s exit:1 -e ignore ifquery -S/dev/null @@ -206,3 +207,14 @@ vlan_guessed_learned_dependency_body() { -o match:"use vlan" \ ifquery -E $EXECUTORS_LINUX -i $FIXTURES/vlan.interfaces eth0.8 } + +vlan_complex_learned_dependency_body() { + atf_check -s exit:0 -o match:"requires eth0" \ + -o match:"use vlan" \ + -o match:"address 1.2.10.4/24" \ + -o match:"gateway 1.2.10.1" \ + -o match:"address abcd:ef12:3456:10::4/64" \ + -o match:"gateway abcd:ef12:3456:10::1" \ + -o match:"vlan-raw-device eth0" \ + ifquery -E $EXECUTORS_LINUX -i $FIXTURES/vlan-complex.interfaces servers +} diff --git a/tests/linux/link_test b/tests/linux/link_test index 1cb2784..6a8ff24 100755 --- a/tests/linux/link_test +++ b/tests/linux/link_test @@ -15,51 +15,51 @@ tests_init \ vlan_guessed_depend up_body() { - export IFACE=lo PHASE=up MOCK=echo + export IFACE=lo PHASE=pre-up MOCK=echo atf_check -s exit:0 -o match:'ip link set up dev lo' \ ${EXECUTOR} } down_body() { - export IFACE=lo PHASE=down MOCK=echo + export IFACE=lo PHASE=post-down MOCK=echo atf_check -s exit:0 -o match:'ip link set down dev lo' \ ${EXECUTOR} } mtu_body() { - export IFACE=eth0 PHASE=up MOCK=echo IF_MTU=1492 + export IFACE=eth0 PHASE=pre-up MOCK=echo IF_MTU=1492 atf_check -s exit:0 -o match:'ip link set up dev eth0 mtu 1492' \ ${EXECUTOR} } vlan_explicit_up_body() { - export IFACE=servers PHASE=up MOCK=echo \ + export IFACE=servers PHASE=pre-up MOCK=echo \ IF_VLAN_RAW_DEVICE="eth0" IF_VLAN_ID="123" atf_check -s exit:0 -o match:'ip link add link eth0 name servers type vlan id 123' \ ${EXECUTOR} } vlan_explicit_down_body() { - export IFACE=servers PHASE=down MOCK=echo \ + export IFACE=servers PHASE=post-down MOCK=echo \ IF_VLAN_RAW_DEVICE="eth0" IF_VLAN_ID="123" atf_check -s exit:0 -o match:'ip link delete link eth0 name servers type vlan id 123' \ ${EXECUTOR} } vlan_guessed_up_body() { - export IFACE=eth0.8 PHASE=up MOCK=echo + export IFACE=eth0.8 PHASE=pre-up MOCK=echo atf_check -s exit:0 -o match:'ip link add link eth0 name eth0.8 type vlan id 8' \ ${EXECUTOR} } vlan_guessed_down_body() { - export IFACE=eth0.8 PHASE=down MOCK=echo + export IFACE=eth0.8 PHASE=post-down MOCK=echo atf_check -s exit:0 -o match:'ip link delete link eth0 name eth0.8 type vlan id 8' \ ${EXECUTOR} } vlan_explicit_depend_body() { - export IFACE=servers PHASE=up MOCK=echo \ + export IFACE=servers PHASE=pre-up MOCK=echo \ IF_VLAN_RAW_DEVICE="eth0" IF_VLAN_ID="123" atf_check -s exit:0 -o match:'eth0' \ ${EXECUTOR}