netifd: Check interface state only when main device is set during interface_change_config
[project/netifd.git] / interface.c
index fa8e55e..a4d7b8d 100644 (file)
@@ -43,6 +43,7 @@ enum {
        IFACE_ATTR_IP6TABLE,
        IFACE_ATTR_IP6CLASS,
        IFACE_ATTR_DELEGATE,
+       IFACE_ATTR_FORCE_LINK,
        IFACE_ATTR_MAX
 };
 
@@ -62,6 +63,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_IP6TABLE] = { .name = "ip6table", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_IP6CLASS] = { .name = "ip6class", .type = BLOBMSG_TYPE_ARRAY },
        [IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL },
+       [IFACE_ATTR_FORCE_LINK] = { .name = "force_link", .type = BLOBMSG_TYPE_BOOL },
 };
 
 static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = {
@@ -265,15 +267,17 @@ __interface_set_up(struct interface *iface)
 static void
 interface_check_state(struct interface *iface)
 {
+       bool link_state = iface->link_state || iface->force_link;
+
        switch (iface->state) {
        case IFS_UP:
-               if (!iface->enabled || !iface->link_state) {
+               if (!iface->enabled || !link_state) {
                        mark_interface_down(iface);
                        interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, false);
                }
                break;
        case IFS_DOWN:
-               if (iface->autostart && iface->enabled && iface->link_state && !config_init)
+               if (iface->autostart && iface->enabled && link_state && !config_init)
                        __interface_set_up(iface);
                break;
        default:
@@ -577,6 +581,9 @@ interface_proto_cb(struct interface_proto_state *state, enum interface_proto_eve
                        return;
                }
 
+               if (!iface->l3_dev.dev)
+                       interface_set_l3_dev(iface, iface->main_dev.dev);
+
                interface_ip_set_enabled(&iface->config_ip, true);
                system_flush_routes();
                iface->state = IFS_UP;
@@ -592,6 +599,8 @@ interface_proto_cb(struct interface_proto_state *state, enum interface_proto_eve
                mark_interface_down(iface);
                if (iface->main_dev.dev)
                        device_release(&iface->main_dev);
+               if (iface->l3_dev.dev)
+                       device_remove_user(&iface->l3_dev);
                interface_handle_config_change(iface);
                break;
        case IFPEV_LINK_LOST:
@@ -654,6 +663,7 @@ interface_alloc(const char *name, struct blob_attr *config)
        proto_attach_interface(iface, proto_name);
 
        iface->autostart = blobmsg_get_bool_default(tb[IFACE_ATTR_AUTO], true);
+       iface->force_link = blobmsg_get_bool_default(tb[IFACE_ATTR_FORCE_LINK], false);
        iface->proto_ip.no_defaultroute =
                !blobmsg_get_bool_default(tb[IFACE_ATTR_DEFAULTROUTE], true);
        iface->proto_ip.no_dns =
@@ -765,15 +775,12 @@ interface_set_l3_dev(struct interface *iface, struct device *dev)
 void
 interface_set_main_dev(struct interface *iface, struct device *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)
                return;
 
-       if (set_l3)
-               interface_set_l3_dev(iface, dev);
-
+       interface_set_available(iface, false);
        device_add_user(&iface->main_dev, dev);
        if (!dev) {
                interface_set_link_state(iface, false);
@@ -801,7 +808,7 @@ interface_remove_link(struct interface *iface, struct device *dev)
        if (dev != iface->main_dev.dev)
                return UBUS_STATUS_INVALID_ARGUMENT;
 
-       device_remove_user(&iface->main_dev);
+       interface_set_main_dev(iface, NULL);
        return 0;
 }
 
@@ -984,6 +991,7 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
        if_old->ifname = if_new->ifname;
        if_old->parent_ifname = if_new->parent_ifname;
        if_old->proto_handler = if_new->proto_handler;
+       if_old->force_link = if_new->force_link;
 
        if_old->proto_ip.no_dns = if_new->proto_ip.no_dns;
        interface_replace_dns(&if_old->config_ip, &if_new->config_ip);
@@ -1012,6 +1020,8 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
        }
 
        interface_write_resolv_conf();
+       if (if_old->main_dev.dev)
+               interface_check_state(if_old);
 
 out:
        if_new->config = NULL;