interface: report link up events for force_link interfaces
[project/netifd.git] / interface.c
index ef70dea..b9833d3 100644 (file)
@@ -74,6 +74,11 @@ const struct uci_blob_param_list interface_attr_list = {
 };
 
 static void
+set_config_state(struct interface *iface, enum interface_config_state s);
+static void
+interface_event(struct interface *iface, enum interface_event ev);
+
+static void
 interface_error_flush(struct interface *iface)
 {
        struct interface_error *error, *tmp;
@@ -139,7 +144,7 @@ void interface_add_error(struct interface *iface, const char *subsystem,
                memcpy(dest, data[i], datalen[i]);
                dest += datalen[i];
        }
-       error->data[n_data++] = NULL;
+       error->data[n_data] = NULL;
 
        if (subsystem)
                error->subsystem = strcpy(d_subsys, subsystem);
@@ -184,6 +189,9 @@ interface_add_data(struct interface *iface, const struct blob_attr *data)
        }
 
        n = calloc(1, sizeof(*n) + len);
+       if (!n)
+               return UBUS_STATUS_UNKNOWN_ERROR;
+
        memcpy(n->data, data, len);
        n->node.key = blobmsg_name(n->data);
        avl_insert(&iface->data, &n->node);
@@ -192,6 +200,25 @@ interface_add_data(struct interface *iface, const struct blob_attr *data)
        return 0;
 }
 
+int interface_parse_data(struct interface *iface, const struct blob_attr *attr)
+{
+       struct blob_attr *cur;
+       int rem, ret;
+
+       iface->updated = 0;
+
+       blob_for_each_attr(cur, attr, rem) {
+               ret = interface_add_data(iface, cur);
+               if (ret)
+                       return ret;
+       }
+
+       if (iface->updated && iface->state == IFS_UP)
+               interface_event(iface, IFEV_UPDATE);
+
+       return 0;
+}
+
 static void
 interface_event(struct interface *iface, enum interface_event ev)
 {
@@ -233,10 +260,12 @@ mark_interface_down(struct interface *iface)
        if (state == IFS_DOWN)
                return;
 
+       iface->link_up_event = false;
        iface->state = IFS_DOWN;
        if (state == IFS_UP)
                interface_event(iface, IFEV_DOWN);
        interface_ip_set_enabled(&iface->config_ip, false);
+       interface_ip_set_enabled(&iface->proto_ip, false);
        interface_ip_flush(&iface->proto_ip);
        interface_flush_state(iface);
        system_flush_routes();
@@ -256,9 +285,6 @@ __interface_set_down(struct interface *iface, bool force)
                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:
@@ -330,17 +356,22 @@ interface_set_link_state(struct interface *iface, bool new_state)
        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);
+
+       if (new_state && iface->force_link && iface->state == IFS_UP && !iface->link_up_event) {
+               interface_event(iface, IFEV_LINK_UP);
+               iface->link_up_event = true;
+       }
 }
 
 static void
-interface_ext_cb(struct device_user *dep, enum device_event ev)
+interface_ext_dev_cb(struct device_user *dep, enum device_event ev)
 {
        if (ev == DEV_EVENT_REMOVE)
                device_remove_user(dep);
 }
 
 static void
-interface_cb(struct device_user *dep, enum device_event ev)
+interface_main_dev_cb(struct device_user *dep, enum device_event ev)
 {
        struct interface *iface;
        bool new_state = false;
@@ -372,6 +403,24 @@ interface_cb(struct device_user *dep, enum device_event ev)
        }
 }
 
+static void
+interface_l3_dev_cb(struct device_user *dep, enum device_event ev)
+{
+       struct interface *iface;
+
+       iface = container_of(dep, struct interface, l3_dev);
+       if (iface->l3_dev.dev == iface->main_dev.dev)
+               return;
+
+       switch (ev) {
+       case DEV_EVENT_LINK_DOWN:
+               interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, false);
+               break;
+       default:
+               break;
+       }
+}
+
 void
 interface_set_available(struct interface *iface, bool new_state)
 {
@@ -507,8 +556,7 @@ interface_alias_cb(struct interface_user *dep, struct interface *iface, enum int
        case IFEV_FREE:
                interface_remove_user(dep);
                break;
-       case IFEV_RELOAD:
-       case IFEV_UPDATE:
+       default:
                break;
        }
 }
@@ -626,10 +674,12 @@ interface_handle_config_change(struct interface *iface)
        }
        if (iface->autostart && iface->available)
                interface_set_up(iface);
+       else if (iface->dynamic)
+               set_config_state(iface, IFC_REMOVE);
 }
 
 static void
-interface_proto_cb(struct interface_proto_state *state, enum interface_proto_event ev)
+interface_proto_event_cb(struct interface_proto_state *state, enum interface_proto_event ev)
 {
        struct interface *iface = state->iface;
 
@@ -644,6 +694,7 @@ interface_proto_cb(struct interface_proto_state *state, enum interface_proto_eve
                        interface_set_l3_dev(iface, iface->main_dev.dev);
 
                interface_ip_set_enabled(&iface->config_ip, true);
+               interface_ip_set_enabled(&iface->proto_ip, true);
                system_flush_routes();
                iface->state = IFS_UP;
                iface->start_time = system_get_rtime();
@@ -688,7 +739,7 @@ void interface_set_proto_state(struct interface *iface, struct interface_proto_s
        if (!state)
                return;
 
-       state->proto_event = interface_proto_cb;
+       state->proto_event = interface_proto_event_cb;
        state->iface = iface;
 }
 
@@ -712,8 +763,9 @@ interface_alloc(const char *name, struct blob_attr *config)
        avl_init(&iface->data, avl_strcmp, false, NULL);
        iface->config_ip.enabled = false;
 
-       iface->main_dev.cb = interface_cb;
-       iface->ext_dev.cb = interface_ext_cb;
+       iface->main_dev.cb = interface_main_dev_cb;
+       iface->l3_dev.cb = interface_l3_dev_cb;
+       iface->ext_dev.cb = interface_ext_dev_cb;
 
        blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb,
                      blob_data(config), blob_len(config));
@@ -822,7 +874,6 @@ static bool __interface_add(struct interface *iface, struct blob_attr *config, b
                        iface->ifname = blobmsg_data(cur);
        }
 
-
        iface->config = config;
        vlist_add(&interfaces, &iface->node, iface->name);
        return true;
@@ -853,6 +904,7 @@ interface_set_l3_dev(struct interface *iface, struct device *dev)
                return;
 
        interface_ip_set_enabled(&iface->config_ip, false);
+       interface_ip_set_enabled(&iface->proto_ip, false);
        interface_ip_flush(&iface->proto_ip);
        device_add_user(&iface->l3_dev, dev);
 
@@ -862,6 +914,7 @@ interface_set_l3_dev(struct interface *iface, struct device *dev)
                                return;
                }
                interface_ip_set_enabled(&iface->config_ip, enabled);
+               interface_ip_set_enabled(&iface->proto_ip, enabled);
        }
 }