netifd: fix disabling radio via config if reconf is being used
[project/netifd.git] / wireless.c
index b26c4e8c8f0b6a564526a60016975b00e11232b3..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,21 +832,20 @@ wireless_interface_init_config(struct wireless_interface *vif)
        if ((cur = tb[VIF_ATTR_NETWORK]))
                vif->network = cur;
 
-       cur = tb[VIF_ATTR_MODE];
-       if (cur)
-               vif->ap_mode = !strcmp(blobmsg_get_string(cur), "ap");
+       if ((cur = tb[VIF_ATTR_NETWORK_VLAN]))
+               vif->network_vlan = cur;
 
-       if (!vif->ap_mode)
-               return;
+       cur = tb[VIF_ATTR_MODE];
+       vif->ap_mode = cur && !strcmp(blobmsg_get_string(cur), "ap");
 
        cur = tb[VIF_ATTR_ISOLATE];
-       if (cur)
-               vif->isolate = blobmsg_get_bool(cur);
+       vif->isolate = vif->ap_mode && cur && blobmsg_get_bool(cur);
 
        cur = tb[VIF_ATTR_PROXYARP];
-       if (cur)
-               vif->proxyarp = blobmsg_get_bool(cur);
+       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 */
@@ -846,8 +874,6 @@ vif_update(struct vlist_tree *tree, struct vlist_node *node_new,
                wireless_interface_handle_link(vif_old, NULL, false);
                free(vif_old->config);
                vif_old->config = blob_memdup(vif_new->config);
-               vif_old->isolate = vif_new->isolate;
-               vif_old->ap_mode = vif_new->ap_mode;
                wireless_interface_init_config(vif_old);
                free(vif_new);
        } else if (vif_new) {
@@ -863,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 */
@@ -879,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 */
@@ -926,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 */
@@ -966,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
@@ -1049,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;
@@ -1412,10 +1444,11 @@ wireless_device_set_retry(struct wireless_device *wdev, struct blob_attr *data)
        struct blob_attr *val;
 
        blobmsg_parse(&retry_policy, 1, &val, blobmsg_data(data), blobmsg_data_len(data));
-       if (!val)
-               return UBUS_STATUS_INVALID_ARGUMENT;
-
-       wdev->retry = blobmsg_get_u32(val);
+       if (val)
+               wdev->retry = blobmsg_get_u32(val);
+       else
+               wdev->retry = WIRELESS_SETUP_RETRY;
+       __wireless_device_set_up(wdev, 0);
        netifd_log_message(L_NOTICE, "Wireless device '%s' set retry=%d\n", wdev->name, wdev->retry);
        return 0;
 }
@@ -1490,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);
@@ -1518,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)
@@ -1527,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) {