From 7335c3e19ea09674245dda3653d3f40705e68d6b Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Tue, 19 Nov 2013 12:17:09 +0100 Subject: [PATCH] netifd: Link layer state support on interface level Patch implements handling of link layer support on interface level. An interface will go into the setup state when it's enabled and the underlying link state is enabled. Vice versa an interface will go to the down state when it's either disabled or underlying link state is disabled. Testing has been done with PPP, IPoE, tunnel and static interfaces Signed-off-by: Hans Dedecker --- interface.c | 130 ++++++++++++++++++++++++++++++++++++++------------ interface.h | 2 + proto-shell.c | 1 + 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/interface.c b/interface.c index 9c208a2..4b381a4 100644 --- a/interface.c +++ b/interface.c @@ -200,6 +200,9 @@ mark_interface_down(struct interface *iface) { enum interface_state state = iface->state; + if (state == IFS_DOWN) + return; + iface->state = IFS_DOWN; if (state == IFS_UP) interface_event(iface, IFEV_DOWN); @@ -212,42 +215,112 @@ mark_interface_down(struct interface *iface) void __interface_set_down(struct interface *iface, bool force) { - if (iface->state == IFS_DOWN || - iface->state == IFS_TEARDOWN) + switch (iface->state) { + case IFS_UP: + interface_event(iface, IFEV_DOWN); + case IFS_SETUP: + iface->state = IFS_TEARDOWN; + interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, force); + if (force) + interface_flush_state(iface); + + if (iface->dynamic) + vlist_delete(&interfaces, &iface->node); + break; + + case IFS_DOWN: + if (iface->main_dev.dev) + device_release(&iface->main_dev); + case IFS_TEARDOWN: + default: + break; + } +} + +static int +__interface_set_up(struct interface *iface) +{ + int ret; + + netifd_log_message(L_NOTICE, "Interface '%s' is setting up now\n", iface->name); + + iface->state = IFS_SETUP; + ret = interface_proto_event(iface->proto, PROTO_CMD_SETUP, false); + if (ret) + mark_interface_down(iface); + + return ret; +} + +static void +interface_check_state(struct interface *iface) +{ + switch (iface->state) { + case IFS_UP: + if (!iface->enabled || !iface->link_state) { + mark_interface_down(iface); + interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, false); + } + break; + case IFS_DOWN: + if (iface->enabled && iface->link_state && !config_init) + __interface_set_up(iface); + break; + default: + break; + } +} + +static void +interface_set_enabled(struct interface *iface, bool new_state) +{ + if (iface->enabled == new_state) return; - if (iface->state == IFS_UP) - interface_event(iface, IFEV_DOWN); - iface->state = IFS_TEARDOWN; - interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, force); - if (force) - interface_flush_state(iface); + netifd_log_message(L_NOTICE, "Interface '%s' is %s\n", iface->name, new_state ? "enabled" : "disabled"); + iface->enabled = new_state; + interface_check_state(iface); +} - if (iface->dynamic) - vlist_delete(&interfaces, &iface->node); +static void +interface_set_link_state(struct interface *iface, bool new_state) +{ + if (iface->link_state == new_state) + return; + + netifd_log_message(L_NOTICE, "Interface '%s' has link connectivity %s\n", iface->name, new_state ? "" : "loss"); + iface->link_state = new_state; + interface_check_state(iface); } static void interface_cb(struct device_user *dep, enum device_event ev) { struct interface *iface; - bool new_state; + bool new_state = false; iface = container_of(dep, struct interface, main_dev); switch (ev) { case DEV_EVENT_ADD: new_state = true; - break; case DEV_EVENT_REMOVE: - new_state = false; + interface_set_available(iface, new_state); + if (!new_state && dep->dev->external) + interface_set_main_dev(iface, NULL); + break; + case DEV_EVENT_UP: + new_state = true; + case DEV_EVENT_DOWN: + interface_set_enabled(iface, new_state); + break; + case DEV_EVENT_LINK_UP: + new_state = true; + case DEV_EVENT_LINK_DOWN: + interface_set_link_state(iface, new_state); break; default: - return; + break; } - - interface_set_available(iface, new_state); - if (!new_state && dep->dev->external) - interface_set_main_dev(iface, NULL); } void @@ -684,7 +757,7 @@ interface_set_l3_dev(struct interface *iface, struct device *dev) void interface_set_main_dev(struct interface *iface, struct device *dev) { - bool set_l3 = (iface->main_dev.dev == iface->l3_dev.dev); + bool set_l3 = (!dev || iface->main_dev.dev == iface->l3_dev.dev); bool claimed = iface->l3_dev.claimed; if (iface->main_dev.dev == dev) @@ -694,8 +767,10 @@ interface_set_main_dev(struct interface *iface, struct device *dev) interface_set_l3_dev(iface, dev); device_add_user(&iface->main_dev, dev); - if (!dev) + if (!dev) { + interface_set_link_state(iface, false); return; + } if (claimed) device_claim(&iface->l3_dev); @@ -794,18 +869,13 @@ interface_set_up(struct interface *iface) if (iface->main_dev.dev) { ret = device_claim(&iface->main_dev); - if (ret) - return ret; - } - - iface->state = IFS_SETUP; - ret = interface_proto_event(iface->proto, PROTO_CMD_SETUP, false); - if (ret) { - mark_interface_down(iface); - return ret; + if (!ret) + interface_check_state(iface); } + else + ret = __interface_set_up(iface); - return 0; + return ret; } int diff --git a/interface.h b/interface.h index 4b7de59..1004bdd 100644 --- a/interface.h +++ b/interface.h @@ -95,6 +95,8 @@ struct interface { bool autostart; bool config_autostart; bool device_config; + bool enabled; + bool link_state; bool dynamic; time_t start_time; diff --git a/proto-shell.c b/proto-shell.c index 6bbfe10..27fe265 100644 --- a/proto-shell.c +++ b/proto-shell.c @@ -158,6 +158,7 @@ proto_shell_handler(struct interface_proto_state *proto, action = "setup"; state->last_error = -1; proto_shell_clear_host_dep(state); + state->sm = S_SETUP; } else { if (state->sm == S_TEARDOWN) return 0; -- 2.30.2