nterface-ip: remove superfluous iface check in interface_ip_set_enabled()
[project/netifd.git] / interface.c
index ef70dea..2a23984 100644 (file)
@@ -35,6 +35,7 @@ enum {
        IFACE_ATTR_PEERDNS,
        IFACE_ATTR_DNS,
        IFACE_ATTR_DNS_SEARCH,
+       IFACE_ATTR_DNS_METRIC,
        IFACE_ATTR_METRIC,
        IFACE_ATTR_INTERFACE,
        IFACE_ATTR_IP6ASSIGN,
@@ -45,6 +46,7 @@ enum {
        IFACE_ATTR_DELEGATE,
        IFACE_ATTR_IP6IFACEID,
        IFACE_ATTR_FORCE_LINK,
+       IFACE_ATTR_IP6WEIGHT,
        IFACE_ATTR_MAX
 };
 
@@ -57,6 +59,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_METRIC] = { .name = "metric", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
        [IFACE_ATTR_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
+       [IFACE_ATTR_DNS_METRIC] = { .name = "dns_metric", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_IP6ASSIGN] = { .name = "ip6assign", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING },
@@ -66,6 +69,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL },
        [IFACE_ATTR_IP6IFACEID] = { .name = "ip6ifaceid", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_FORCE_LINK] = { .name = "force_link", .type = BLOBMSG_TYPE_BOOL },
+       [IFACE_ATTR_IP6WEIGHT] = { .name = "ip6weight", .type = BLOBMSG_TYPE_INT32 },
 };
 
 const struct uci_blob_param_list interface_attr_list = {
@@ -74,6 +78,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;
@@ -87,11 +96,11 @@ interface_error_flush(struct interface *iface)
 static void
 interface_clear_errors(struct interface *iface)
 {
-        /* don't flush the errors in case the configured protocol handler matches the
+       /* don't flush the errors in case the configured protocol handler matches the
            running protocol handler and is having the last error capability */
        if (!(iface->proto &&
-              (iface->proto->handler->flags & PROTO_FLAG_LASTERROR) &&
-              (iface->proto->handler->name == iface->proto_handler->name)))
+             (iface->proto->handler->flags & PROTO_FLAG_LASTERROR) &&
+             (iface->proto->handler->name == iface->proto_handler->name)))
                interface_error_flush(iface);
 }
 
@@ -103,12 +112,12 @@ void interface_add_error(struct interface *iface, const char *subsystem,
        int *datalen = NULL;
        char *dest, *d_subsys, *d_code;
 
-        /* if the configured protocol handler has the last error support capability,
+       /* if the configured protocol handler has the last error support capability,
            errors should only be added if the running protocol handler matches the
            configured one */
        if (iface->proto &&
-            (iface->proto->handler->flags & PROTO_FLAG_LASTERROR) &&
-            (iface->proto->handler->name != iface->proto_handler->name))
+           (iface->proto->handler->flags & PROTO_FLAG_LASTERROR) &&
+           (iface->proto->handler->name != iface->proto_handler->name))
                return;
 
        if (n_data) {
@@ -139,7 +148,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 +193,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 +204,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)
 {
@@ -210,6 +241,7 @@ interface_event(struct interface *iface, enum interface_event ev)
                adev = iface->l3_dev.dev;
                /* fall through */
        case IFEV_DOWN:
+       case IFEV_UP_FAILED:
                alias_notify_device(iface->name, adev);
                break;
        default:
@@ -233,10 +265,20 @@ mark_interface_down(struct interface *iface)
        if (state == IFS_DOWN)
                return;
 
+       iface->link_up_event = false;
        iface->state = IFS_DOWN;
-       if (state == IFS_UP)
+       switch (state) {
+       case IFS_UP:
                interface_event(iface, IFEV_DOWN);
+               break;
+       case IFS_SETUP:
+               interface_event(iface, IFEV_UP_FAILED);
+               break;
+       default:
+               break;
+       }
        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 +298,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 +369,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 +416,25 @@ 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:
+               if (iface->proto_handler->flags & PROTO_FLAG_TEARDOWN_ON_L3_LINK_DOWN)
+                       interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, false);
+               break;
+       default:
+               break;
+       }
+}
+
 void
 interface_set_available(struct interface *iface, bool new_state)
 {
@@ -445,6 +508,7 @@ interface_merge_assignment_data(struct interface *old, struct interface *new)
        bool changed = (old->assignment_hint != new->assignment_hint ||
                        old->assignment_length != new->assignment_length ||
                        old->assignment_iface_id_selection != new->assignment_iface_id_selection ||
+                       old->assignment_weight != new->assignment_weight ||
                        (old->assignment_iface_id_selection == IFID_FIXED &&
                         memcmp(&old->assignment_fixed_iface_id, &new->assignment_fixed_iface_id,
                                sizeof(old->assignment_fixed_iface_id))) ||
@@ -482,6 +546,7 @@ interface_merge_assignment_data(struct interface *old, struct interface *new)
                old->assignment_length = new->assignment_length;
                old->assignment_iface_id_selection = new->assignment_iface_id_selection;
                old->assignment_fixed_iface_id = new->assignment_fixed_iface_id;
+               old->assignment_weight = new->assignment_weight;
                interface_refresh_assignments(true);
        }
 }
@@ -501,14 +566,14 @@ interface_alias_cb(struct interface_user *dep, struct interface *iface, enum int
                interface_set_available(alias, true);
                break;
        case IFEV_DOWN:
+       case IFEV_UP_FAILED:
                interface_set_available(alias, false);
                interface_set_main_dev(alias, NULL);
                break;
        case IFEV_FREE:
                interface_remove_user(dep);
                break;
-       case IFEV_RELOAD:
-       case IFEV_UPDATE:
+       default:
                break;
        }
 }
@@ -537,6 +602,8 @@ interface_claim_device(struct interface *iface)
        if (iface->parent_iface.iface)
                interface_remove_user(&iface->parent_iface);
 
+       device_lock();
+
        if (iface->parent_ifname) {
                parent = vlist_find(&interfaces, iface->parent_ifname, parent, node);
                iface->parent_iface.cb = interface_alias_cb;
@@ -552,6 +619,8 @@ interface_claim_device(struct interface *iface)
        if (dev)
                interface_set_main_dev(iface, dev);
 
+       device_unlock();
+
        if (iface->proto_handler->flags & PROTO_FLAG_INIT_AVAILABLE)
                interface_set_available(iface, true);
 }
@@ -626,17 +695,20 @@ 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;
 
        switch (ev) {
        case IFPEV_UP:
                if (iface->state != IFS_SETUP) {
-                       interface_event(iface, IFEV_UPDATE);
+                       if (iface->state == IFS_UP && iface->updated)
+                               interface_event(iface, IFEV_UPDATE);
                        return;
                }
 
@@ -644,6 +716,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 +761,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 +785,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));
@@ -738,6 +812,9 @@ interface_alloc(const char *name, struct blob_attr *config)
        if ((cur = tb[IFACE_ATTR_DNS_SEARCH]))
                interface_add_dns_search_list(&iface->config_ip, cur);
 
+       if ((cur = tb[IFACE_ATTR_DNS_METRIC]))
+               iface->dns_metric = blobmsg_get_u32(cur);
+
        if ((cur = tb[IFACE_ATTR_METRIC]))
                iface->metric = blobmsg_get_u32(cur);
 
@@ -779,6 +856,8 @@ interface_alloc(const char *name, struct blob_attr *config)
        if ((cur = tb[IFACE_ATTR_IP6CLASS]))
                interface_add_assignment_classes(iface, cur);
 
+       if ((cur = tb[IFACE_ATTR_IP6WEIGHT]))
+               iface->assignment_weight = blobmsg_get_u32(cur);
 
        if ((cur = tb[IFACE_ATTR_IP4TABLE])) {
                if (!system_resolve_rt_table(blobmsg_data(cur), &iface->ip4table))
@@ -822,7 +901,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 +931,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 +941,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);
        }
 }
 
@@ -1006,6 +1086,15 @@ interface_set_down(struct interface *iface)
        return 0;
 }
 
+int
+interface_renew(struct interface *iface)
+{
+       if (iface->state == IFS_TEARDOWN || iface->state == IFS_DOWN)
+               return -1;
+
+       return interface_proto_event(iface->proto, PROTO_CMD_RENEW, false);
+}
+
 void
 interface_start_pending(void)
 {
@@ -1028,10 +1117,12 @@ set_config_state(struct interface *iface, enum interface_config_state s)
 }
 
 void
-interface_update_start(struct interface *iface)
+interface_update_start(struct interface *iface, const bool keep_old)
 {
        iface->updated = 0;
-       interface_ip_update_start(&iface->proto_ip);
+
+       if (!keep_old)
+               interface_ip_update_start(&iface->proto_ip);
 }
 
 void
@@ -1127,6 +1218,7 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
        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->dns_metric = if_new->dns_metric;
 
        if_old->proto_ip.no_dns = if_new->proto_ip.no_dns;
        interface_replace_dns(&if_old->config_ip, &if_new->config_ip);