From f35b76c61baf68dc5dd8440fc06d860f8dfacc44 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Tue, 11 Feb 2014 08:30:51 +0000 Subject: [PATCH] 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 --- alias.c | 16 +++++-- interface.c | 130 ++++++++++++++++++++++++++++++++++++++------------ interface.h | 2 + macvlan.c | 6 +++ proto-shell.c | 1 + vlan.c | 13 +++-- 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; -- 2.25.1