netifd: fix disabling radio via config if reconf is being used
[project/netifd.git] / wireless.c
index 118f5a7cf135b836210a30ca4d8e3ffcc06df21d..898a01220011e41932180c8b0a4e79de50aa2002 100644 (file)
@@ -62,18 +62,22 @@ static const struct uci_blob_param_list wdev_param = {
 enum {
        VIF_ATTR_DISABLED,
        VIF_ATTR_NETWORK,
+       VIF_ATTR_NETWORK_VLAN,
        VIF_ATTR_ISOLATE,
        VIF_ATTR_MODE,
        VIF_ATTR_PROXYARP,
+       VIF_ATTR_MCAST_TO_UCAST,
        __VIF_ATTR_MAX,
 };
 
 static const struct blobmsg_policy vif_policy[__VIF_ATTR_MAX] = {
        [VIF_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
        [VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY },
+       [VIF_ATTR_NETWORK_VLAN] = { .name = "network_vlan", .type = BLOBMSG_TYPE_ARRAY },
        [VIF_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
        [VIF_ATTR_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
        [VIF_ATTR_PROXYARP] = { .name = "proxy_arp", .type = BLOBMSG_TYPE_BOOL },
+       [VIF_ATTR_MCAST_TO_UCAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL },
 };
 
 static const struct uci_blob_param_list vif_param = {
@@ -84,14 +88,18 @@ static const struct uci_blob_param_list vif_param = {
 enum {
        VLAN_ATTR_DISABLED,
        VLAN_ATTR_NETWORK,
+       VLAN_ATTR_NETWORK_VLAN,
        VLAN_ATTR_ISOLATE,
+       VLAN_ATTR_MCAST_TO_UCAST,
        __VLAN_ATTR_MAX,
 };
 
 static const struct blobmsg_policy vlan_policy[__VLAN_ATTR_MAX] = {
        [VLAN_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
        [VLAN_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY },
+       [VLAN_ATTR_NETWORK_VLAN] = { .name = "network_vlan", .type = BLOBMSG_TYPE_ARRAY },
        [VLAN_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
+       [VLAN_ATTR_MCAST_TO_UCAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL },
 };
 
 static const struct uci_blob_param_list vlan_param = {
@@ -137,7 +145,7 @@ vif_config_add_bridge(struct blob_buf *buf, struct blob_attr *networks, bool pre
        struct device *dev = NULL, *orig_dev;
        struct blob_attr *cur;
        const char *network;
-       int rem;
+       size_t rem;
 
        if (!networks)
                return;
@@ -315,12 +323,23 @@ wireless_device_free_state(struct wireless_device *wdev)
        }
 }
 
+static void wireless_device_set_mcast_to_unicast(struct device *dev, int val)
+{
+       if (val < 0) {
+               dev->settings.flags &= ~DEV_OPT_MULTICAST_TO_UNICAST;
+               return;
+       }
+
+       dev->settings.multicast_to_unicast = !!val;
+       dev->settings.flags |= DEV_OPT_MULTICAST_TO_UNICAST;
+}
+
 static void wireless_interface_handle_link(struct wireless_interface *vif, const char *ifname, bool up)
 {
        struct interface *iface;
        struct blob_attr *cur;
        const char *network;
-       int rem;
+       size_t rem;
 
        if (!vif->network || !vif->ifname)
                return;
@@ -336,6 +355,7 @@ static void wireless_interface_handle_link(struct wireless_interface *vif, const
                        dev->wireless_proxyarp = vif->proxyarp;
                        dev->wireless = true;
                        dev->wireless_ap = vif->ap_mode;
+                       wireless_device_set_mcast_to_unicast(dev, vif->multicast_to_unicast);
                        dev->bpdu_filter = dev->wireless_ap;
                }
        }
@@ -347,7 +367,7 @@ static void wireless_interface_handle_link(struct wireless_interface *vif, const
                if (!iface)
                        continue;
 
-               interface_handle_link(iface, ifname, NULL, up, true);
+               interface_handle_link(iface, ifname, vif->network_vlan, up, true);
        }
 }
 
@@ -356,7 +376,7 @@ static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up)
        struct interface *iface;
        struct blob_attr *cur;
        const char *network;
-       int rem;
+       size_t rem;
 
        if (!vlan->network || !vlan->ifname)
                return;
@@ -368,6 +388,7 @@ static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up)
                        dev->wireless = true;
                        dev->wireless_ap = true;
                        dev->bpdu_filter = true;
+                       wireless_device_set_mcast_to_unicast(dev, vlan->multicast_to_unicast);
                }
        }
 
@@ -378,7 +399,7 @@ static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up)
                if (!iface)
                        continue;
 
-               interface_handle_link(iface, vlan->ifname, NULL, up, true);
+               interface_handle_link(iface, vlan->ifname, vlan->network_vlan, up, true);
        }
 }
 
@@ -475,7 +496,7 @@ __wireless_device_set_up(struct wireless_device *wdev, int force)
        if (!wdev->autostart)
                return;
 
-       if (!force && (wdev->state != IFS_DOWN || config_init))
+       if ((!force && wdev->state != IFS_DOWN) || config_init)
                return;
 
        free(wdev->prev_config);
@@ -659,10 +680,16 @@ wdev_set_config_state(struct wireless_device *wdev, enum interface_config_state
        if (wdev->config_state != IFC_NORMAL)
                return;
 
+       wdev->config_update = false;
+       if (!wdev->disabled && s == IFC_RELOAD && wdev->reconf && wdev->state == IFS_UP) {
+               wireless_device_reconf(wdev);
+               return;
+       }
+
        wdev->config_state = s;
        if (wdev->state == IFS_DOWN)
                wdev_handle_config_change(wdev);
-       else if (!wdev->reconf || wdev->state != IFS_UP)
+       else
                __wireless_device_set_down(wdev);
 }
 
@@ -682,6 +709,8 @@ wdev_change_config(struct wireless_device *wdev, struct wireless_device *wd_new)
        struct blob_attr *new_config = wd_new->config;
        bool disabled = wd_new->disabled;
 
+       wdev->reconf = wd_new->reconf;
+       wdev->serialize = wd_new->serialize;
        free(wd_new);
 
        wdev_prepare_prev_config(wdev);
@@ -692,7 +721,7 @@ wdev_change_config(struct wireless_device *wdev, struct wireless_device *wd_new)
        free(wdev->config);
        wdev->config = blob_memdup(new_config);
        wdev->disabled = disabled;
-       wdev_set_config_state(wdev, IFC_RELOAD);
+       wdev->config_update = true;
 }
 
 static void
@@ -803,6 +832,9 @@ wireless_interface_init_config(struct wireless_interface *vif)
        if ((cur = tb[VIF_ATTR_NETWORK]))
                vif->network = cur;
 
+       if ((cur = tb[VIF_ATTR_NETWORK_VLAN]))
+               vif->network_vlan = cur;
+
        cur = tb[VIF_ATTR_MODE];
        vif->ap_mode = cur && !strcmp(blobmsg_get_string(cur), "ap");
 
@@ -811,6 +843,9 @@ wireless_interface_init_config(struct wireless_interface *vif)
 
        cur = tb[VIF_ATTR_PROXYARP];
        vif->proxyarp = vif->ap_mode && cur && blobmsg_get_bool(cur);
+
+       cur = tb[VIF_ATTR_MCAST_TO_UCAST];
+       vif->multicast_to_unicast = cur ? blobmsg_get_bool(cur) : -1;
 }
 
 /* vlist update call for wireless interface list */
@@ -854,7 +889,7 @@ vif_update(struct vlist_tree *tree, struct vlist_node *node_new,
                free(vif_old);
        }
 
-       wdev_set_config_state(wdev, IFC_RELOAD);
+       wdev->config_update = true;
 }
 
 /* parse blob config into the vlan object */
@@ -870,9 +905,15 @@ wireless_vlan_init_config(struct wireless_vlan *vlan)
        if ((cur = tb[VLAN_ATTR_NETWORK]))
                vlan->network = cur;
 
+       if ((cur = tb[VLAN_ATTR_NETWORK_VLAN]))
+               vlan->network_vlan = cur;
+
        cur = tb[VLAN_ATTR_ISOLATE];
        if (cur)
                vlan->isolate = blobmsg_get_bool(cur);
+
+       cur = tb[VLAN_ATTR_MCAST_TO_UCAST];
+       vlan->multicast_to_unicast = cur ? blobmsg_get_bool(cur) : -1;
 }
 
 /* vlist update call for vlan list */
@@ -917,7 +958,7 @@ vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
                free(vlan_old);
        }
 
-       wdev_set_config_state(wdev, IFC_RELOAD);
+       wdev->config_update = true;
 }
 
 /* vlist update call for station list */
@@ -957,7 +998,7 @@ station_update(struct vlist_tree *tree, struct vlist_node *node_new,
                free(sta_old);
        }
 
-       wdev_set_config_state(wdev, IFC_RELOAD);
+       wdev->config_update = true;
 }
 
 static void
@@ -1040,7 +1081,7 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
        wdev->serialize = cur && blobmsg_get_bool(cur);
 
        cur = tb[WDEV_ATTR_RECONF];
-       wdev->reconf = cur && blobmsg_get_bool(cur);
+       wdev->reconf = !cur || blobmsg_get_bool(cur);
 
        wdev->retry_setup_failed = false;
        wdev->autostart = true;
@@ -1482,9 +1523,7 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
                else
                        pdata = &wdev->data;
 
-               if (*pdata)
-                       return UBUS_STATUS_INVALID_ARGUMENT;
-
+               free(*pdata);
                *pdata = blob_memdup(cur);
                if (vif)
                        wireless_interface_set_data(vif);
@@ -1510,8 +1549,11 @@ wireless_start_pending(void)
 {
        struct wireless_device *wdev;
 
-       vlist_for_each_element(&wireless_devices, wdev, node)
+       vlist_for_each_element(&wireless_devices, wdev, node) {
+               if (wdev->config_update)
+                       wdev_set_config_state(wdev, IFC_RELOAD);
                __wireless_device_set_up(wdev, 0);
+       }
 }
 
 void wireless_device_hotplug_event(const char *name, bool add)
@@ -1519,7 +1561,7 @@ void wireless_device_hotplug_event(const char *name, bool add)
        struct wireless_interface *vif;
        struct wireless_device *wdev;
        const char *s;
-       int len;
+       size_t len;
 
        s = strstr(name, ".sta");
        if (s) {