summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorHans Dedecker <dedeckeh@gmail.com>2014-02-11 08:30:51 +0000
committerSteven Barth <steven@midlink.org>2014-02-27 12:15:32 +0100
commitf35b76c61baf68dc5dd8440fc06d860f8dfacc44 (patch)
treec04889155d11b42469756d1744f716b7f0dfab25
parent3d26347d0042313be3c9347f8d82c4ccea67fe68 (diff)
netifd: Link layer state awareness support on interface level
The link layer state is monitored for a given interface; an interface will be setup when both enabled and link layer active. Likewise an interface will be teared down when either disabled or link layer down. Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
-rw-r--r--alias.c16
-rw-r--r--interface.c130
-rw-r--r--interface.h2
-rw-r--r--macvlan.c6
-rw-r--r--proto-shell.c1
-rw-r--r--vlan.c13
6 files changed, 131 insertions, 37 deletions
diff --git a/alias.c b/alias.c
index 4e0a6be..cef125f 100644
--- a/alias.c
+++ b/alias.c
@@ -51,7 +51,7 @@ static void alias_set_device(struct alias_device *alias, struct device *dev)
device_remove_user(&alias->dep);
alias->dev.hidden = !dev;
if (dev) {
- alias->dev.ifindex = dev->ifindex;
+ device_set_ifindex(&alias->dev, dev->ifindex);
strcpy(alias->dev.ifname, dev->ifname);
device_broadcast_event(&alias->dev, DEV_EVENT_UPDATE_IFNAME);
device_add_user(&alias->dep, dev);
@@ -83,14 +83,22 @@ alias_device_set_state(struct device *dev, bool state)
static void alias_device_cb(struct device_user *dep, enum device_event ev)
{
struct alias_device *alias;
- bool present = false;
+ bool new_state = false;
alias = container_of(dep, struct alias_device, dep);
switch (ev) {
case DEV_EVENT_ADD:
- present = true;
+ new_state = true;
case DEV_EVENT_REMOVE:
- device_set_present(&alias->dev, present);
+ device_set_present(&alias->dev, new_state);
+ break;
+ case DEV_EVENT_LINK_UP:
+ new_state = true;
+ case DEV_EVENT_LINK_DOWN:
+ device_set_link(&alias->dev, new_state);
+ break;
+ case DEV_EVENT_UPDATE_IFINDEX:
+ device_set_ifindex(&alias->dev, dep->dev->ifindex);
break;
default:
device_broadcast_event(&alias->dev, ev);
diff --git a/interface.c b/interface.c
index b2c8baf..f0fd43f 100644
--- a/interface.c
+++ b/interface.c
@@ -207,6 +207,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);
@@ -219,42 +222,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 c3a4379..9a7bb3d 100644
--- a/interface.h
+++ b/interface.h
@@ -102,6 +102,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/macvlan.c b/macvlan.c
index 28567dc..9c03cd8 100644
--- a/macvlan.c
+++ b/macvlan.c
@@ -69,6 +69,12 @@ macvlan_base_cb(struct device_user *dev, enum device_event ev)
case DEV_EVENT_REMOVE:
device_set_present(&mvdev->dev, false);
break;
+ case DEV_EVENT_LINK_UP:
+ device_set_link(&mvdev->dev, true);
+ break;
+ case DEV_EVENT_LINK_DOWN:
+ device_set_link(&mvdev->dev, false);
+ break;
default:
return;
}
diff --git a/proto-shell.c b/proto-shell.c
index 1a69c19..37859f4 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;
diff --git a/vlan.c b/vlan.c
index f70420a..9201979 100644
--- a/vlan.c
+++ b/vlan.c
@@ -70,14 +70,19 @@ static void vlan_dev_set_name(struct vlan_device *vldev, struct device *dev)
static void vlan_dev_cb(struct device_user *dep, enum device_event ev)
{
struct vlan_device *vldev;
+ bool new_state = false;
vldev = container_of(dep, struct vlan_device, dep);
switch(ev) {
case DEV_EVENT_ADD:
- device_set_present(&vldev->dev, true);
- break;
+ new_state = true;
case DEV_EVENT_REMOVE:
- device_set_present(&vldev->dev, false);
+ device_set_present(&vldev->dev, new_state);
+ break;
+ case DEV_EVENT_LINK_UP:
+ new_state = true;
+ case DEV_EVENT_LINK_DOWN:
+ device_set_link(&vldev->dev, new_state);
break;
case DEV_EVENT_UPDATE_IFNAME:
vlan_dev_set_name(vldev, dep->dev);
@@ -113,6 +118,8 @@ static struct device *get_vlan_device(struct device *dev, int id, bool create)
if (!create)
return NULL;
+ D(DEVICE, "Create vlan device '%s.%d'\n", dev->ifname, id);
+
vldev = calloc(1, sizeof(*vldev));
vldev->id = id;