iprule: fix segfault (FS#1875)
[project/netifd.git] / interface.c
index 522e7fe0c40c888351857e7e6874ea7f8f2d8739..b508b10457551dc78b7bcc11bd54e0d5a35f84b4 100644 (file)
@@ -46,6 +46,7 @@ enum {
        IFACE_ATTR_DELEGATE,
        IFACE_ATTR_IP6IFACEID,
        IFACE_ATTR_FORCE_LINK,
+       IFACE_ATTR_IP6WEIGHT,
        IFACE_ATTR_MAX
 };
 
@@ -68,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 = {
@@ -239,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:
@@ -264,8 +267,16 @@ mark_interface_down(struct interface *iface)
 
        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);
@@ -273,7 +284,7 @@ mark_interface_down(struct interface *iface)
        system_flush_routes();
 }
 
-void
+static void
 __interface_set_down(struct interface *iface, bool force)
 {
        enum interface_state state = iface->state;
@@ -376,26 +387,28 @@ static void
 interface_main_dev_cb(struct device_user *dep, enum device_event ev)
 {
        struct interface *iface;
-       bool new_state = false;
 
        iface = container_of(dep, struct interface, main_dev);
        switch (ev) {
        case DEV_EVENT_ADD:
-               new_state = true;
+               interface_set_available(iface, true);
+               break;
        case DEV_EVENT_REMOVE:
-               interface_set_available(iface, new_state);
-               if (!new_state && dep->dev && dep->dev->external)
+               interface_set_available(iface, false);
+               if (dep->dev && dep->dev->external)
                        interface_set_main_dev(iface, NULL);
                break;
        case DEV_EVENT_UP:
-               new_state = true;
+               interface_set_enabled(iface, true);
+               break;
        case DEV_EVENT_DOWN:
-               interface_set_enabled(iface, new_state);
+               interface_set_enabled(iface, false);
                break;
        case DEV_EVENT_LINK_UP:
-               new_state = true;
+               interface_set_link_state(iface, true);
+               break;
        case DEV_EVENT_LINK_DOWN:
-               interface_set_link_state(iface, new_state);
+               interface_set_link_state(iface, false);
                break;
        case DEV_EVENT_TOPO_CHANGE:
                interface_proto_event(iface->proto, PROTO_CMD_RENEW, false);
@@ -497,6 +510,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))) ||
@@ -534,6 +548,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);
        }
 }
@@ -553,6 +568,7 @@ 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;
@@ -679,7 +695,7 @@ interface_handle_config_change(struct interface *iface)
                interface_do_free(iface);
                return;
        }
-       if (iface->autostart && iface->available)
+       if (iface->autostart)
                interface_set_up(iface);
        else if (iface->dynamic)
                set_config_state(iface, IFC_REMOVE);
@@ -842,6 +858,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))
@@ -1029,34 +1047,38 @@ out:
        return ret;
 }
 
-int
+void
 interface_set_up(struct interface *iface)
 {
        int ret;
+       const char *error = NULL;
 
        iface->autostart = true;
 
        if (iface->state != IFS_DOWN)
-               return 0;
+               return;
 
        interface_clear_errors(iface);
-       if (!iface->available) {
-               interface_add_error(iface, "interface", "NO_DEVICE", NULL, 0);
-               return -1;
-       }
-
-       if (iface->main_dev.dev) {
-               ret = device_claim(&iface->main_dev);
-               if (!ret)
-                       interface_check_state(iface);
-       }
-       else
-               ret = __interface_set_up(iface);
+       if (iface->available) {
+               if (iface->main_dev.dev) {
+                       ret = device_claim(&iface->main_dev);
+                       if (!ret)
+                               interface_check_state(iface);
+                       else
+                               error = "DEVICE_CLAIM_FAILED";
+               } else {
+                       ret = __interface_set_up(iface);
+                       if (ret)
+                               error = "SETUP_FAILED";
+               }
+       } else
+               error = "NO_DEVICE";
 
-       return ret;
+       if (error)
+               interface_add_error(iface, "interface", error, NULL, 0);
 }
 
-int
+void
 interface_set_down(struct interface *iface)
 {
        if (!iface) {
@@ -1066,8 +1088,15 @@ interface_set_down(struct interface *iface)
                iface->autostart = false;
                __interface_set_down(iface, false);
        }
+}
 
-       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
@@ -1076,7 +1105,7 @@ interface_start_pending(void)
        struct interface *iface;
 
        vlist_for_each_element(&interfaces, iface, node) {
-               if (iface->available && iface->autostart)
+               if (iface->autostart)
                        interface_set_up(iface);
        }
 }
@@ -1250,6 +1279,7 @@ interface_update(struct vlist_tree *tree, struct vlist_node *node_new,
                set_config_state(if_old, IFC_REMOVE);
        } else if (node_new) {
                D(INTERFACE, "Create interface '%s'\n", if_new->name);
+               interface_event(if_new, IFEV_CREATE);
                proto_init_interface(if_new, if_new->config);
                interface_claim_device(if_new);
                netifd_ubus_add_interface(if_new);