netifd: Link layer state support on interface level
authorHans Dedecker <dedeckeh@gmail.com>
Tue, 19 Nov 2013 11:17:09 +0000 (12:17 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 8 Dec 2013 17:43:51 +0000 (18:43 +0100)
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 <dedeckeh@gmail.com>
interface.c
interface.h
proto-shell.c

index 9c208a283e40a2da7716651922aaa0b0a4e44b58..4b381a431cbe3c0ae27e8afb15800cf903870b8d 100644 (file)
@@ -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
index 4b7de5997d3055e1e85938230d3017f0356fa433..1004bddc2e61e843f7efe409e880b932f02c4962 100644 (file)
@@ -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;
index 6bbfe10ea6696f13607c23ca731d36dd1b9b5dd6..27fe2652912273b614c97c67ef71f19b69da303e 100644 (file)
@@ -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;