summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Dedecker <dedeckeh@gmail.com>2013-11-19 12:17:09 +0100
committerFelix Fietkau <nbd@openwrt.org>2013-12-08 18:43:51 +0100
commit7335c3e19ea09674245dda3653d3f40705e68d6b (patch)
tree56aac2a32502da6f4bc5ae157b9c39eb98cada52
parentc439b52400978dd3799c66e1f632ee68d2c7c9eb (diff)
downloadnetifd-7335c3e19ea09674245dda3653d3f40705e68d6b.tar.gz
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 <dedeckeh@gmail.com>
-rw-r--r--interface.c130
-rw-r--r--interface.h2
-rw-r--r--proto-shell.c1
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;