Files
wlan-ap/patches/0001-netifd-several-backports-required-by-ipq807x.patch
John Crispin 7ae17f821b ipq807x: update kernel patches and add new boards
Signed-off-by: John Crispin <john@phrozen.org>
2021-01-18 08:03:29 +01:00

1600 lines
50 KiB
Diff

From f5e2764d23b0f7273b5a25490e67cdbaaeded8af Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Sat, 20 Jun 2020 16:27:12 +0200
Subject: [PATCH 01/40] netifd: several backports required by ipq807x
IPQ807x uses the mac80211.sh/hostapd.sh from owrt HEAD, which require
these patches.
Signed-off-by: John Crispin <john@phrozen.org>
---
...add-support-for-tracking-wifi-vlan-s.patch | 703 ++++++++++++++++++
...add-support-for-tracking-wifi-statio.patch | 427 +++++++++++
...s-method-for-reloading-configuration.patch | 123 +++
...conf-opt-in-and-allow-serializing-co.patch | 249 +++++++
.../0005-netifd-fix-wdev-data-lifetime.patch | 42 ++
5 files changed, 1544 insertions(+)
create mode 100644 package/network/config/netifd/patches/0001-netifd-wireless-add-support-for-tracking-wifi-vlan-s.patch
create mode 100644 package/network/config/netifd/patches/0002-netifd-wireless-add-support-for-tracking-wifi-statio.patch
create mode 100644 package/network/config/netifd/patches/0003-wireless-add-ubus-method-for-reloading-configuration.patch
create mode 100644 package/network/config/netifd/patches/0004-wireless-make-reconf-opt-in-and-allow-serializing-co.patch
create mode 100644 package/network/config/netifd/patches/0005-netifd-fix-wdev-data-lifetime.patch
diff --git a/package/network/config/netifd/patches/0001-netifd-wireless-add-support-for-tracking-wifi-vlan-s.patch b/package/network/config/netifd/patches/0001-netifd-wireless-add-support-for-tracking-wifi-vlan-s.patch
new file mode 100644
index 0000000000..48185fc16c
--- /dev/null
+++ b/package/network/config/netifd/patches/0001-netifd-wireless-add-support-for-tracking-wifi-vlan-s.patch
@@ -0,0 +1,703 @@
+From 4ce33cee34e413f69082a80a833f3ed1baf8a761 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Mon, 25 May 2020 11:49:18 +0200
+Subject: [PATCH 1/2] netifd: wireless: add support for tracking wifi-vlan
+ sections
+
+This new section allows us to create apvlan settings for hostapd.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ config.c | 42 ++++++-
+ scripts/netifd-wireless.sh | 42 ++++++-
+ wireless.c | 247 +++++++++++++++++++++++++++++++++++--
+ wireless.h | 23 +++-
+ 4 files changed, 337 insertions(+), 17 deletions(-)
+
+diff --git a/config.c b/config.c
+index 843c53f..1bb3737 100644
+--- a/config.c
++++ b/config.c
+@@ -338,7 +338,7 @@ config_parse_wireless_device(struct uci_section *s)
+ wireless_device_create(drv, s->e.name, b.head);
+ }
+
+-static void
++static struct wireless_interface*
+ config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section *s)
+ {
+ char *name;
+@@ -348,7 +348,20 @@ config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section
+
+ blob_buf_init(&b, 0);
+ uci_to_blob(&b, s, wdev->drv->interface.config);
+- wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name);
++ return wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name);
++}
++
++static void
++config_parse_wireless_vlan(struct wireless_device *wdev, char *vif, struct uci_section *s)
++{
++ char *name;
++
++ name = alloca(strlen(s->type) + 16);
++ sprintf(name, "@%s[%d]", s->type, config_section_idx(s));
++
++ blob_buf_init(&b, 0);
++ uci_to_blob(&b, s, wdev->drv->vlan.config);
++ wireless_vlan_create(wdev, vif, b.head, s->anonymous ? name : s->e.name);
+ }
+
+ static void
+@@ -378,10 +391,14 @@ config_init_wireless(void)
+ vlist_for_each_element(&wireless_devices, wdev, node) {
+ wdev->vif_idx = 0;
+ vlist_update(&wdev->interfaces);
++ wdev->vlan_idx = 0;
++ vlist_update(&wdev->vlans);
+ }
+
+ uci_foreach_element(&uci_wireless->sections, e) {
+ struct uci_section *s = uci_to_section(e);
++ struct wireless_interface *vif;
++ struct uci_element *f;
+
+ if (strcmp(s->type, "wifi-iface") != 0)
+ continue;
+@@ -396,11 +413,28 @@ config_init_wireless(void)
+ continue;
+ }
+
+- config_parse_wireless_interface(wdev, s);
++ vif = config_parse_wireless_interface(wdev, s);
++
++ if (!vif || s->anonymous)
++ continue;
++ uci_foreach_element(&uci_wireless->sections, f) {
++ struct uci_section *s = uci_to_section(f);
++ const char *vif_name;
++
++ if (strcmp(s->type, "wifi-vlan") != 0)
++ continue;
++
++ vif_name = uci_lookup_option_string(uci_ctx, s, "iface");
++ if (vif_name && strcmp(e->name, vif_name))
++ continue;
++ config_parse_wireless_vlan(wdev, vif->name, s);
++ }
+ }
+
+- vlist_for_each_element(&wireless_devices, wdev, node)
++ vlist_for_each_element(&wireless_devices, wdev, node) {
+ vlist_flush(&wdev->interfaces);
++ vlist_flush(&wdev->vlans);
++ }
+ }
+
+ int
+diff --git a/scripts/netifd-wireless.sh b/scripts/netifd-wireless.sh
+index 6bc48c6..41e380f 100644
+--- a/scripts/netifd-wireless.sh
++++ b/scripts/netifd-wireless.sh
+@@ -108,12 +108,13 @@ _wdev_wrapper() {
+
+ _wdev_notify_init() {
+ local command="$1"
+- local interface="$2"
++ local name="$2"
++ local value="$3"
+
+ json_init
+ json_add_int "command" "$command"
+ json_add_string "device" "$__netifd_device"
+- [ -n "$interface" ] && json_add_string "interface" "$interface"
++ [ -n "$name" -a -n "$value" ] && json_add_string "$name" "$value"
+ json_add_object "data"
+ }
+
+@@ -139,7 +140,17 @@ _wireless_add_vif() {
+ local name="$1"; shift
+ local ifname="$1"; shift
+
+- _wdev_notify_init $CMD_SET_DATA "$name"
++ _wdev_notify_init $CMD_SET_DATA "interface" "$name"
++ json_add_string "ifname" "$ifname"
++ _wdev_add_variables "$@"
++ _wdev_notify
++}
++
++_wireless_add_vlan() {
++ local name="$1"; shift
++ local ifname="$1"; shift
++
++ _wdev_notify_init $CMD_SET_DATA "vlan" "$name"
+ json_add_string "ifname" "$ifname"
+ _wdev_add_variables "$@"
+ _wdev_notify
+@@ -182,6 +193,7 @@ _wireless_set_retry() {
+
+ _wdev_wrapper \
+ wireless_add_vif \
++ wireless_add_vlan \
+ wireless_set_up \
+ wireless_set_data \
+ wireless_add_process \
+@@ -306,6 +318,21 @@ for_each_interface() {
+ json_select ..
+ }
+
++for_each_vlan() {
++ local _w_vlans _w_vlan
++
++ json_get_keys _w_vlans vlans
++ json_select vlans
++ for _w_vlan in $_w_vlans; do
++ json_select "$_w_vlan"
++ json_select config
++ "$@" "$_w_vlan"
++ json_select ..
++ json_select ..
++ done
++ json_select ..
++}
++
+ _wdev_common_device_config() {
+ config_add_string channel hwmode htmode noscan
+ }
+@@ -314,6 +341,10 @@ _wdev_common_iface_config() {
+ config_add_string mode ssid encryption 'key:wpakey'
+ }
+
++_wdev_common_vlan_config() {
++ config_add_string name vid iface
++}
++
+ init_wireless_driver() {
+ name="$1"; shift
+ cmd="$1"; shift
+@@ -336,6 +367,11 @@ init_wireless_driver() {
+ eval "drv_$1_init_iface_config"
+ json_close_array
+
++ json_add_array vlan
++ _wdev_common_vlan_config
++ eval "drv_$1_init_vlan_config"
++ json_close_array
++
+ json_dump
+ }
+ ;;
+diff --git a/wireless.c b/wireless.c
+index c8e196f..b0b35a1 100644
+--- a/wireless.c
++++ b/wireless.c
+@@ -64,6 +64,24 @@ static const struct uci_blob_param_list vif_param = {
+ .params = vif_policy,
+ };
+
++enum {
++ VLAN_ATTR_DISABLED,
++ VLAN_ATTR_NETWORK,
++ VLAN_ATTR_ISOLATE,
++ __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_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
++};
++
++static const struct uci_blob_param_list vlan_param = {
++ .n_params = ARRAY_SIZE(vlan_policy),
++ .params = vlan_policy,
++};
++
+ static void
+ wireless_handler_stop(struct wireless_device *wdev)
+ {
+@@ -125,7 +143,8 @@ static void
+ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
+ {
+ struct wireless_interface *vif;
+- void *l, *i;
++ struct wireless_vlan *vlan;
++ void *l, *i, *j, *k;
+
+ blob_buf_init(&b, 0);
+ put_container(&b, wdev->config, "config");
+@@ -139,9 +158,23 @@ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
+ put_container(&b, vif->config, "config");
+ if (vif->data)
+ blobmsg_add_blob(&b, vif->data);
++
++ j = blobmsg_open_table(&b, "vlans");
++ vlist_for_each_element(&wdev->vlans, vlan, node) {
++ if (strcmp(vlan->vif, vif->name))
++ continue;
++ k = blobmsg_open_table(&b, vlan->name);
++ vif_config_add_bridge(&b, vlan->network, up);
++ put_container(&b, vlan->config, "config");
++ if (vlan->data)
++ blobmsg_add_blob(&b, vlan->data);
++ blobmsg_close_table(&b, k);
++ }
++ blobmsg_close_table(&b, j);
+ blobmsg_close_table(&b, i);
+ }
+ blobmsg_close_table(&b, l);
++
+ }
+
+ static bool
+@@ -208,6 +241,7 @@ static void
+ wireless_device_free_state(struct wireless_device *wdev)
+ {
+ struct wireless_interface *vif;
++ struct wireless_vlan *vlan;
+
+ wireless_handler_stop(wdev);
+ uloop_timeout_cancel(&wdev->script_check);
+@@ -220,6 +254,11 @@ wireless_device_free_state(struct wireless_device *wdev)
+ vif->data = NULL;
+ vif->ifname = NULL;
+ }
++ vlist_for_each_element(&wdev->vlans, vlan, node) {
++ free(vlan->data);
++ vlan->data = NULL;
++ vlan->ifname = NULL;
++ }
+ }
+
+ static void wireless_interface_handle_link(struct wireless_interface *vif, bool up)
+@@ -252,6 +291,36 @@ static void wireless_interface_handle_link(struct wireless_interface *vif, bool
+ }
+ }
+
++static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up)
++{
++ struct interface *iface;
++ struct blob_attr *cur;
++ const char *network;
++ int rem;
++
++ if (!vlan->network || !vlan->ifname)
++ return;
++
++ if (up) {
++ struct device *dev = device_get(vlan->ifname, 2);
++ if (dev) {
++ dev->wireless_isolate = vlan->isolate;
++ dev->wireless = true;
++ dev->wireless_ap = true;
++ }
++ }
++
++ blobmsg_for_each_attr(cur, vlan->network, rem) {
++ network = blobmsg_data(cur);
++
++ iface = vlist_find(&interfaces, network, iface, node);
++ if (!iface)
++ continue;
++
++ interface_handle_link(iface, vlan->ifname, up, true);
++ }
++}
++
+ static void
+ wireless_device_setup_cancel(struct wireless_device *wdev)
+ {
+@@ -356,6 +425,7 @@ wireless_device_free(struct wireless_device *wdev)
+ {
+ wireless_handler_stop(wdev);
+ vlist_flush_all(&wdev->interfaces);
++ vlist_flush_all(&wdev->vlans);
+ avl_delete(&wireless_devices.avl, &wdev->node.avl);
+ free(wdev->config);
+ free(wdev->prev_config);
+@@ -384,9 +454,13 @@ static void
+ wireless_device_mark_down(struct wireless_device *wdev)
+ {
+ struct wireless_interface *vif;
++ struct wireless_vlan *vlan;
+
+ D(WIRELESS, "Wireless device '%s' is now down\n", wdev->name);
+
++ vlist_for_each_element(&wdev->vlans, vlan, node)
++ wireless_vlan_handle_link(vlan, false);
++
+ vlist_for_each_element(&wdev->interfaces, vif, node)
+ wireless_interface_handle_link(vif, false);
+
+@@ -447,6 +521,7 @@ static void
+ wireless_device_mark_up(struct wireless_device *wdev)
+ {
+ struct wireless_interface *vif;
++ struct wireless_vlan *vlan;
+
+ if (wdev->cancel) {
+ wdev->cancel = false;
+@@ -458,6 +533,8 @@ wireless_device_mark_up(struct wireless_device *wdev)
+ wdev->state = IFS_UP;
+ vlist_for_each_element(&wdev->interfaces, vif, node)
+ wireless_interface_handle_link(vif, true);
++ vlist_for_each_element(&wdev->vlans, vlan, node)
++ wireless_vlan_handle_link(vlan, true);
+ }
+
+ static void
+@@ -576,18 +653,20 @@ wireless_add_handler(const char *script, const char *name, json_object *obj)
+ {
+ struct wireless_driver *drv;
+ char *name_str, *script_str;
+- json_object *dev_config_obj, *iface_config_obj;
+- struct uci_blob_param_list *dev_config, *iface_config;
++ json_object *dev_config_obj, *iface_config_obj, *vlan_config_obj;
++ struct uci_blob_param_list *dev_config, *iface_config, *vlan_config;
+
+ dev_config_obj = json_get_field(obj, "device", json_type_array);
+ iface_config_obj = json_get_field(obj, "iface", json_type_array);
++ vlan_config_obj = json_get_field(obj, "vlan", json_type_array);
+
+- if (!dev_config_obj || !iface_config_obj)
++ if (!dev_config_obj || !iface_config_obj || !vlan_config_obj)
+ return;
+
+ drv = calloc_a(sizeof(*drv),
+ &dev_config, sizeof(*dev_config) + sizeof(void *),
+ &iface_config, sizeof(*iface_config) + sizeof(void *),
++ &vlan_config, sizeof(*vlan_config) + sizeof(void *),
+ &name_str, strlen(name) + 1,
+ &script_str, strlen(script) + 1);
+
+@@ -602,8 +681,13 @@ wireless_add_handler(const char *script, const char *name, json_object *obj)
+ iface_config->next[0] = &vif_param;
+ drv->interface.config = iface_config;
+
++ vlan_config->n_next = 1;
++ vlan_config->next[0] = &vlan_param;
++ drv->vlan.config = vlan_config;
++
+ drv->device.buf = netifd_handler_parse_config(drv->device.config, dev_config_obj);
+ drv->interface.buf = netifd_handler_parse_config(drv->interface.config, iface_config_obj);
++ drv->vlan.buf = netifd_handler_parse_config(drv->vlan.config, vlan_config_obj);
+
+ drv->node.key = drv->name;
+ avl_insert(&wireless_drivers, &drv->node);
+@@ -690,6 +774,67 @@ vif_update(struct vlist_tree *tree, struct vlist_node *node_new,
+ wdev_set_config_state(wdev, IFC_RELOAD);
+ }
+
++static void
++wireless_vlan_init_config(struct wireless_vlan *vlan)
++{
++ struct blob_attr *tb[__VLAN_ATTR_MAX];
++ struct blob_attr *cur;
++
++ vlan->network = NULL;
++ blobmsg_parse(vlan_policy, __VLAN_ATTR_MAX, tb, blob_data(vlan->config), blob_len(vlan->config));
++
++ if ((cur = tb[VLAN_ATTR_NETWORK]))
++ vlan->network = cur;
++
++ cur = tb[VLAN_ATTR_ISOLATE];
++ if (cur)
++ vlan->isolate = blobmsg_get_bool(cur);
++}
++
++static void
++vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
++ struct vlist_node *node_old)
++{
++ struct wireless_vlan *vlan_old = container_of(node_old, struct wireless_vlan, node);
++ struct wireless_vlan *vlan_new = container_of(node_new, struct wireless_vlan, node);
++ struct wireless_device *wdev;
++
++ if (vlan_old)
++ wdev = vlan_old->wdev;
++ else
++ wdev = vlan_new->wdev;
++
++ if (vlan_old && vlan_new) {
++ free((void *) vlan_old->section);
++ vlan_old->section = strdup(vlan_new->section);
++ if (blob_attr_equal(vlan_old->config, vlan_new->config)) {
++ free(vlan_new);
++ return;
++ }
++
++ D(WIRELESS, "Update wireless vlan %s on device %s\n", vlan_new->name, wdev->name);
++ wireless_vlan_handle_link(vlan_old, false);
++ free(vlan_old->config);
++ vlan_old->config = blob_memdup(vlan_new->config);
++ vlan_old->isolate = vlan_new->isolate;
++ wireless_vlan_init_config(vlan_old);
++ free(vlan_new);
++ } else if (vlan_new) {
++ D(WIRELESS, "Create new wireless vlan %s on device %s\n", vlan_new->name, wdev->name);
++ vlan_new->section = strdup(vlan_new->section);
++ vlan_new->config = blob_memdup(vlan_new->config);
++ wireless_vlan_init_config(vlan_new);
++ } else if (vlan_old) {
++ D(WIRELESS, "Delete wireless interface %s on device %s\n", vlan_old->name, wdev->name);
++ wireless_vlan_handle_link(vlan_old, false);
++ free((void *) vlan_old->section);
++ free(vlan_old->config);
++ free(vlan_old);
++ }
++
++ wdev_set_config_state(wdev, IFC_RELOAD);
++}
++
+ static void
+ wireless_proc_poll_fd(struct uloop_fd *fd, unsigned int events)
+ {
+@@ -774,6 +919,8 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
+ INIT_LIST_HEAD(&wdev->script_proc);
+ vlist_init(&wdev->interfaces, avl_strcmp, vif_update);
+ wdev->interfaces.keep_old = true;
++ vlist_init(&wdev->vlans, avl_strcmp, vlan_update);
++ wdev->vlans.keep_old = true;
+
+ wdev->timeout.cb = wireless_device_setup_timeout;
+ wdev->script_task.cb = wireless_device_script_task_cb;
+@@ -788,7 +935,51 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
+ vlist_add(&wireless_devices, &wdev->node, wdev->name);
+ }
+
+-void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section)
++void
++wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section)
++{
++ struct wireless_vlan *vlan;
++ struct blob_attr *tb[__VLAN_ATTR_MAX];
++ struct blob_attr *cur;
++ char *name_buf, *vif_buf;
++ char name[8];
++
++ blobmsg_parse(vlan_policy, __VLAN_ATTR_MAX, tb, blob_data(data), blob_len(data));
++
++ cur = tb[VLAN_ATTR_DISABLED];
++ if (cur && blobmsg_get_bool(cur))
++ return;
++
++ sprintf(name, "%d", wdev->vlan_idx++);
++
++ vlan = calloc_a(sizeof(*vlan),
++ &name_buf, strlen(name) + 1,
++ &vif_buf, strlen(vif) + 1);
++ vlan->name = strcpy(name_buf, name);
++ vlan->vif = strcpy(vif_buf, vif);
++ vlan->wdev = wdev;
++ vlan->config = data;
++ vlan->section = section;
++ vlan->isolate = false;
++
++ vlist_add(&wdev->vlans, &vlan->node, vlan->name);
++}
++
++static void
++wireless_vlan_status(struct wireless_vlan *vlan, struct blob_buf *b)
++{
++ void *i;
++
++ i = blobmsg_open_table(b, NULL);
++ if (vlan->section)
++ blobmsg_add_string(b, "section", vlan->section);
++ if (vlan->ifname)
++ blobmsg_add_string(b, "ifname", vlan->ifname);
++ put_container(b, vlan->config, "config");
++ blobmsg_close_table(b, i);
++}
++
++struct wireless_interface* wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section)
+ {
+ struct wireless_interface *vif;
+ struct blob_attr *tb[__VIF_ATTR_MAX];
+@@ -800,7 +991,7 @@ void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *d
+
+ cur = tb[VIF_ATTR_DISABLED];
+ if (cur && blobmsg_get_bool(cur))
+- return;
++ return NULL;
+
+ sprintf(name, "%d", wdev->vif_idx++);
+
+@@ -813,12 +1004,14 @@ void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *d
+ vif->isolate = false;
+
+ vlist_add(&wdev->interfaces, &vif->node, vif->name);
++ return vif;
+ }
+
+ static void
+ wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b)
+ {
+- void *i;
++ struct wireless_vlan *vlan;
++ void *i, *j;
+
+ i = blobmsg_open_table(b, NULL);
+ if (iface->section)
+@@ -826,6 +1019,11 @@ wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b)
+ if (iface->ifname)
+ blobmsg_add_string(b, "ifname", iface->ifname);
+ put_container(b, iface->config, "config");
++ j = blobmsg_open_array(b, "vlans");
++ vlist_for_each_element(&iface->wdev->vlans, vlan, node)
++ if (!strcmp(iface->name, vlan->vif))
++ wireless_vlan_status(vlan, b);
++ blobmsg_close_array(b, j);
+ blobmsg_close_table(b, i);
+ }
+
+@@ -894,6 +1092,26 @@ wireless_interface_set_data(struct wireless_interface *vif)
+ vif->ifname = blobmsg_data(cur);
+ }
+
++static void
++wireless_vlan_set_data(struct wireless_vlan *vlan)
++{
++ enum {
++ VLAN_DATA_IFNAME,
++ __VLAN_DATA_MAX,
++ };
++ static const struct blobmsg_policy data_policy[__VLAN_DATA_MAX] = {
++ [VLAN_DATA_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
++ };
++ struct blob_attr *tb[__VLAN_DATA_MAX];
++ struct blob_attr *cur;
++
++ blobmsg_parse(data_policy, __VLAN_DATA_MAX, tb,
++ blobmsg_data(vlan->data), blobmsg_data_len(vlan->data));
++
++ if ((cur = tb[VLAN_DATA_IFNAME]))
++ vlan->ifname = blobmsg_data(cur);
++}
++
+ static int
+ wireless_device_add_process(struct wireless_device *wdev, struct blob_attr *data)
+ {
+@@ -1011,15 +1229,18 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
+ enum {
+ NOTIFY_ATTR_COMMAND,
+ NOTIFY_ATTR_VIF,
++ NOTIFY_ATTR_VLAN,
+ NOTIFY_ATTR_DATA,
+ __NOTIFY_MAX,
+ };
+ static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
+ [NOTIFY_ATTR_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_INT32 },
+ [NOTIFY_ATTR_VIF] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
++ [NOTIFY_ATTR_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_STRING },
+ [NOTIFY_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
+ };
+ struct wireless_interface *vif = NULL;
++ struct wireless_vlan *vlan = NULL;
+ struct blob_attr *tb[__NOTIFY_MAX];
+ struct blob_attr *cur, **pdata;
+
+@@ -1034,13 +1255,19 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
+ return UBUS_STATUS_NOT_FOUND;
+ }
+
++ if ((cur = tb[NOTIFY_ATTR_VLAN]) != NULL) {
++ vlan = vlist_find(&wdev->vlans, blobmsg_data(cur), vlan, node);
++ if (!vlan)
++ return UBUS_STATUS_NOT_FOUND;
++ }
++
+ cur = tb[NOTIFY_ATTR_DATA];
+ if (!cur)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ switch (blobmsg_get_u32(tb[NOTIFY_ATTR_COMMAND])) {
+ case NOTIFY_CMD_UP:
+- if (vif)
++ if (vif || vlan)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (wdev->state != IFS_SETUP)
+@@ -1051,6 +1278,8 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
+ case NOTIFY_CMD_SET_DATA:
+ if (vif)
+ pdata = &vif->data;
++ else if (vlan)
++ pdata = &vlan->data;
+ else
+ pdata = &wdev->data;
+
+@@ -1060,6 +1289,8 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
+ *pdata = blob_memdup(cur);
+ if (vif)
+ wireless_interface_set_data(vif);
++ else if (vlan)
++ wireless_vlan_set_data(vlan);
+ break;
+ case NOTIFY_CMD_PROCESS_ADD:
+ return wireless_device_add_process(wdev, cur);
+diff --git a/wireless.h b/wireless.h
+index f734770..8d0a1f0 100644
+--- a/wireless.h
++++ b/wireless.h
+@@ -30,7 +30,7 @@ struct wireless_driver {
+ struct {
+ char *buf;
+ struct uci_blob_param_list *config;
+- } device, interface;
++ } device, interface, vlan;
+ };
+
+ struct wireless_device {
+@@ -43,6 +43,7 @@ struct wireless_device {
+
+ struct wireless_driver *drv;
+ struct vlist_tree interfaces;
++ struct vlist_tree vlans;
+ char *name;
+
+ struct netifd_process script_task;
+@@ -70,6 +71,7 @@ struct wireless_device {
+ int retry;
+
+ int vif_idx;
++ int vlan_idx;
+ };
+
+ struct wireless_interface {
+@@ -88,6 +90,22 @@ struct wireless_interface {
+ bool ap_mode;
+ };
+
++struct wireless_vlan {
++ struct vlist_node node;
++ const char *section;
++ char *name;
++
++ struct wireless_device *wdev;
++ char *vif;
++
++ struct blob_attr *config;
++ struct blob_attr *data;
++
++ const char *ifname;
++ struct blob_attr *network;
++ bool isolate;
++};
++
+ struct wireless_process {
+ struct list_head list;
+
+@@ -103,7 +121,8 @@ void wireless_device_set_down(struct wireless_device *wdev);
+ void wireless_device_reconf(struct wireless_device *wdev);
+ void wireless_device_status(struct wireless_device *wdev, struct blob_buf *b);
+ void wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b);
+-void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section);
++struct wireless_interface* wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section);
++void wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section);
+ int wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
+ struct ubus_request_data *req);
+
+--
+2.20.1
+
diff --git a/package/network/config/netifd/patches/0002-netifd-wireless-add-support-for-tracking-wifi-statio.patch b/package/network/config/netifd/patches/0002-netifd-wireless-add-support-for-tracking-wifi-statio.patch
new file mode 100644
index 0000000000..d849fb09e0
--- /dev/null
+++ b/package/network/config/netifd/patches/0002-netifd-wireless-add-support-for-tracking-wifi-statio.patch
@@ -0,0 +1,427 @@
+From a56b457656218e5b6142b6238286ea54801ac4b2 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Mon, 25 May 2020 11:49:19 +0200
+Subject: [PATCH 2/2] netifd: wireless: add support for tracking wifi-station
+ sections
+
+This new section allows us to assign mac specific key/vid settings to a
+station.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ config.c | 29 ++++++++
+ scripts/netifd-wireless.sh | 24 +++++++
+ wireless.c | 134 ++++++++++++++++++++++++++++++++++++-
+ wireless.h | 17 ++++-
+ 4 files changed, 200 insertions(+), 4 deletions(-)
+
+diff --git a/config.c b/config.c
+index 1bb3737..b1b4bf3 100644
+--- a/config.c
++++ b/config.c
+@@ -364,6 +364,19 @@ config_parse_wireless_vlan(struct wireless_device *wdev, char *vif, struct uci_s
+ wireless_vlan_create(wdev, vif, b.head, s->anonymous ? name : s->e.name);
+ }
+
++static void
++config_parse_wireless_station(struct wireless_device *wdev, char *vif, struct uci_section *s)
++{
++ char *name;
++
++ name = alloca(strlen(s->type) + 16);
++ sprintf(name, "@%s[%d]", s->type, config_section_idx(s));
++
++ blob_buf_init(&b, 0);
++ uci_to_blob(&b, s, wdev->drv->station.config);
++ wireless_station_create(wdev, vif, b.head, s->anonymous ? name : s->e.name);
++}
++
+ static void
+ config_init_wireless(void)
+ {
+@@ -393,6 +406,8 @@ config_init_wireless(void)
+ vlist_update(&wdev->interfaces);
+ wdev->vlan_idx = 0;
+ vlist_update(&wdev->vlans);
++ wdev->sta_idx = 0;
++ vlist_update(&wdev->stations);
+ }
+
+ uci_foreach_element(&uci_wireless->sections, e) {
+@@ -429,11 +444,25 @@ config_init_wireless(void)
+ continue;
+ config_parse_wireless_vlan(wdev, vif->name, s);
+ }
++
++ uci_foreach_element(&uci_wireless->sections, f) {
++ struct uci_section *s = uci_to_section(f);
++ const char *vif_name;
++
++ if (strcmp(s->type, "wifi-station") != 0)
++ continue;
++
++ vif_name = uci_lookup_option_string(uci_ctx, s, "iface");
++ if (vif_name && strcmp(e->name, vif_name))
++ continue;
++ config_parse_wireless_station(wdev, vif->name, s);
++ }
+ }
+
+ vlist_for_each_element(&wireless_devices, wdev, node) {
+ vlist_flush(&wdev->interfaces);
+ vlist_flush(&wdev->vlans);
++ vlist_flush(&wdev->stations);
+ }
+ }
+
+diff --git a/scripts/netifd-wireless.sh b/scripts/netifd-wireless.sh
+index 41e380f..d51380d 100644
+--- a/scripts/netifd-wireless.sh
++++ b/scripts/netifd-wireless.sh
+@@ -333,6 +333,21 @@ for_each_vlan() {
+ json_select ..
+ }
+
++for_each_station() {
++ local _w_stas _w_sta
++
++ json_get_keys _w_stas stas
++ json_select stas
++ for _w_sta in $_w_stas; do
++ json_select "$_w_sta"
++ json_select config
++ "$@" "$_w_sta"
++ json_select ..
++ json_select ..
++ done
++ json_select ..
++}
++
+ _wdev_common_device_config() {
+ config_add_string channel hwmode htmode noscan
+ }
+@@ -345,6 +360,10 @@ _wdev_common_vlan_config() {
+ config_add_string name vid iface
+ }
+
++_wdev_common_station_config() {
++ config_add_string mac key vid iface
++}
++
+ init_wireless_driver() {
+ name="$1"; shift
+ cmd="$1"; shift
+@@ -372,6 +391,11 @@ init_wireless_driver() {
+ eval "drv_$1_init_vlan_config"
+ json_close_array
+
++ json_add_array station
++ _wdev_common_station_config
++ eval "drv_$1_init_station_config"
++ json_close_array
++
+ json_dump
+ }
+ ;;
+diff --git a/wireless.c b/wireless.c
+index b0b35a1..efb7992 100644
+--- a/wireless.c
++++ b/wireless.c
+@@ -82,6 +82,20 @@ static const struct uci_blob_param_list vlan_param = {
+ .params = vlan_policy,
+ };
+
++enum {
++ STA_ATTR_DISABLED,
++ __STA_ATTR_MAX,
++};
++
++static const struct blobmsg_policy sta_policy[__STA_ATTR_MAX] = {
++ [STA_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
++};
++
++static const struct uci_blob_param_list station_param = {
++ .n_params = ARRAY_SIZE(sta_policy),
++ .params = sta_policy,
++};
++
+ static void
+ wireless_handler_stop(struct wireless_device *wdev)
+ {
+@@ -144,6 +158,7 @@ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
+ {
+ struct wireless_interface *vif;
+ struct wireless_vlan *vlan;
++ struct wireless_station *sta;
+ void *l, *i, *j, *k;
+
+ blob_buf_init(&b, 0);
+@@ -171,6 +186,18 @@ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
+ blobmsg_close_table(&b, k);
+ }
+ blobmsg_close_table(&b, j);
++
++ j = blobmsg_open_table(&b, "stas");
++ vlist_for_each_element(&wdev->stations, sta, node) {
++ if (strcmp(sta->vif, vif->name))
++ continue;
++ k = blobmsg_open_table(&b, sta->name);
++ put_container(&b, sta->config, "config");
++ if (sta->data)
++ blobmsg_add_blob(&b, sta->data);
++ blobmsg_close_table(&b, k);
++ }
++ blobmsg_close_table(&b, j);
+ blobmsg_close_table(&b, i);
+ }
+ blobmsg_close_table(&b, l);
+@@ -242,6 +269,7 @@ wireless_device_free_state(struct wireless_device *wdev)
+ {
+ struct wireless_interface *vif;
+ struct wireless_vlan *vlan;
++ struct wireless_station *sta;
+
+ wireless_handler_stop(wdev);
+ uloop_timeout_cancel(&wdev->script_check);
+@@ -259,6 +287,10 @@ wireless_device_free_state(struct wireless_device *wdev)
+ vlan->data = NULL;
+ vlan->ifname = NULL;
+ }
++ vlist_for_each_element(&wdev->stations, sta, node) {
++ free(sta->data);
++ sta->data = NULL;
++ }
+ }
+
+ static void wireless_interface_handle_link(struct wireless_interface *vif, bool up)
+@@ -426,6 +458,7 @@ wireless_device_free(struct wireless_device *wdev)
+ wireless_handler_stop(wdev);
+ vlist_flush_all(&wdev->interfaces);
+ vlist_flush_all(&wdev->vlans);
++ vlist_flush_all(&wdev->stations);
+ avl_delete(&wireless_devices.avl, &wdev->node.avl);
+ free(wdev->config);
+ free(wdev->prev_config);
+@@ -653,20 +686,22 @@ wireless_add_handler(const char *script, const char *name, json_object *obj)
+ {
+ struct wireless_driver *drv;
+ char *name_str, *script_str;
+- json_object *dev_config_obj, *iface_config_obj, *vlan_config_obj;
+- struct uci_blob_param_list *dev_config, *iface_config, *vlan_config;
++ json_object *dev_config_obj, *iface_config_obj, *vlan_config_obj, *station_config_obj;
++ struct uci_blob_param_list *dev_config, *iface_config, *vlan_config, *station_config;
+
+ dev_config_obj = json_get_field(obj, "device", json_type_array);
+ iface_config_obj = json_get_field(obj, "iface", json_type_array);
+ vlan_config_obj = json_get_field(obj, "vlan", json_type_array);
++ station_config_obj = json_get_field(obj, "station", json_type_array);
+
+- if (!dev_config_obj || !iface_config_obj || !vlan_config_obj)
++ if (!dev_config_obj || !iface_config_obj || !vlan_config_obj || !station_config_obj)
+ return;
+
+ drv = calloc_a(sizeof(*drv),
+ &dev_config, sizeof(*dev_config) + sizeof(void *),
+ &iface_config, sizeof(*iface_config) + sizeof(void *),
+ &vlan_config, sizeof(*vlan_config) + sizeof(void *),
++ &station_config, sizeof(*station_config) + sizeof(void *),
+ &name_str, strlen(name) + 1,
+ &script_str, strlen(script) + 1);
+
+@@ -685,9 +720,14 @@ wireless_add_handler(const char *script, const char *name, json_object *obj)
+ vlan_config->next[0] = &vlan_param;
+ drv->vlan.config = vlan_config;
+
++ station_config->n_next = 1;
++ station_config->next[0] = &station_param;
++ drv->station.config = station_config;
++
+ drv->device.buf = netifd_handler_parse_config(drv->device.config, dev_config_obj);
+ drv->interface.buf = netifd_handler_parse_config(drv->interface.config, iface_config_obj);
+ drv->vlan.buf = netifd_handler_parse_config(drv->vlan.config, vlan_config_obj);
++ drv->station.buf = netifd_handler_parse_config(drv->station.config, station_config_obj);
+
+ drv->node.key = drv->name;
+ avl_insert(&wireless_drivers, &drv->node);
+@@ -835,6 +875,45 @@ vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
+ wdev_set_config_state(wdev, IFC_RELOAD);
+ }
+
++static void
++station_update(struct vlist_tree *tree, struct vlist_node *node_new,
++ struct vlist_node *node_old)
++{
++ struct wireless_station *sta_old = container_of(node_old, struct wireless_station, node);
++ struct wireless_station *sta_new = container_of(node_new, struct wireless_station, node);
++ struct wireless_device *wdev;
++
++ if (sta_old)
++ wdev = sta_old->wdev;
++ else
++ wdev = sta_new->wdev;
++
++ if (sta_old && sta_new) {
++ free((void *) sta_old->section);
++ sta_old->section = strdup(sta_new->section);
++ if (blob_attr_equal(sta_old->config, sta_new->config)) {
++ free(sta_new);
++ return;
++ }
++
++ D(WIRELESS, "Update wireless station %s on device %s\n", sta_new->name, wdev->name);
++ free(sta_old->config);
++ sta_old->config = blob_memdup(sta_new->config);
++ free(sta_new);
++ } else if (sta_new) {
++ D(WIRELESS, "Create new wireless station %s on device %s\n", sta_new->name, wdev->name);
++ sta_new->section = strdup(sta_new->section);
++ sta_new->config = blob_memdup(sta_new->config);
++ } else if (sta_old) {
++ D(WIRELESS, "Delete wireless station %s on device %s\n", sta_old->name, wdev->name);
++ free((void *) sta_old->section);
++ free(sta_old->config);
++ free(sta_old);
++ }
++
++ wdev_set_config_state(wdev, IFC_RELOAD);
++}
++
+ static void
+ wireless_proc_poll_fd(struct uloop_fd *fd, unsigned int events)
+ {
+@@ -921,6 +1000,8 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
+ wdev->interfaces.keep_old = true;
+ vlist_init(&wdev->vlans, avl_strcmp, vlan_update);
+ wdev->vlans.keep_old = true;
++ vlist_init(&wdev->stations, avl_strcmp, station_update);
++ wdev->stations.keep_old = true;
+
+ wdev->timeout.cb = wireless_device_setup_timeout;
+ wdev->script_task.cb = wireless_device_script_task_cb;
+@@ -935,6 +1016,47 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
+ vlist_add(&wireless_devices, &wdev->node, wdev->name);
+ }
+
++void
++wireless_station_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section)
++{
++ struct wireless_station *sta;
++ struct blob_attr *tb[__STA_ATTR_MAX];
++ struct blob_attr *cur;
++ char *name_buf, *vif_buf;
++ char name[8];
++
++ blobmsg_parse(sta_policy, __STA_ATTR_MAX, tb, blob_data(data), blob_len(data));
++
++ cur = tb[STA_ATTR_DISABLED];
++ if (cur && blobmsg_get_bool(cur))
++ return;
++
++ sprintf(name, "%d", wdev->vlan_idx++);
++
++ sta = calloc_a(sizeof(*sta),
++ &name_buf, strlen(name) + 1,
++ &vif_buf, strlen(vif) + 1);
++ sta->name = strcpy(name_buf, name);
++ sta->vif = strcpy(vif_buf, vif);
++ sta->wdev = wdev;
++ sta->config = data;
++ sta->section = section;
++
++ vlist_add(&wdev->stations, &sta->node, sta->name);
++}
++
++static void
++wireless_station_status(struct wireless_station *sta, struct blob_buf *b)
++{
++ void *i;
++
++ i = blobmsg_open_table(b, NULL);
++ if (sta->section)
++ blobmsg_add_string(b, "section", sta->section);
++ put_container(b, sta->config, "config");
++ blobmsg_close_table(b, i);
++}
++
+ void
+ wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section)
+ {
+@@ -1011,6 +1133,7 @@ static void
+ wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b)
+ {
+ struct wireless_vlan *vlan;
++ struct wireless_station *sta;
+ void *i, *j;
+
+ i = blobmsg_open_table(b, NULL);
+@@ -1024,6 +1147,11 @@ wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b)
+ if (!strcmp(iface->name, vlan->vif))
+ wireless_vlan_status(vlan, b);
+ blobmsg_close_array(b, j);
++ j = blobmsg_open_array(b, "stations");
++ vlist_for_each_element(&iface->wdev->stations, sta, node)
++ if (!strcmp(iface->name, sta->vif))
++ wireless_station_status(sta, b);
++ blobmsg_close_array(b, j);
+ blobmsg_close_table(b, i);
+ }
+
+diff --git a/wireless.h b/wireless.h
+index 8d0a1f0..5fedd20 100644
+--- a/wireless.h
++++ b/wireless.h
+@@ -30,7 +30,7 @@ struct wireless_driver {
+ struct {
+ char *buf;
+ struct uci_blob_param_list *config;
+- } device, interface, vlan;
++ } device, interface, vlan, station;
+ };
+
+ struct wireless_device {
+@@ -44,6 +44,7 @@ struct wireless_device {
+ struct wireless_driver *drv;
+ struct vlist_tree interfaces;
+ struct vlist_tree vlans;
++ struct vlist_tree stations;
+ char *name;
+
+ struct netifd_process script_task;
+@@ -72,6 +73,7 @@ struct wireless_device {
+
+ int vif_idx;
+ int vlan_idx;
++ int sta_idx;
+ };
+
+ struct wireless_interface {
+@@ -106,6 +108,18 @@ struct wireless_vlan {
+ bool isolate;
+ };
+
++struct wireless_station {
++ struct vlist_node node;
++ const char *section;
++ char *name;
++
++ struct wireless_device *wdev;
++ char *vif;
++
++ struct blob_attr *config;
++ struct blob_attr *data;
++};
++
+ struct wireless_process {
+ struct list_head list;
+
+@@ -123,6 +137,7 @@ void wireless_device_status(struct wireless_device *wdev, struct blob_buf *b);
+ void wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b);
+ struct wireless_interface* wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section);
+ void wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section);
++void wireless_station_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section);
+ int wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
+ struct ubus_request_data *req);
+
+--
+2.20.1
+
diff --git a/package/network/config/netifd/patches/0003-wireless-add-ubus-method-for-reloading-configuration.patch b/package/network/config/netifd/patches/0003-wireless-add-ubus-method-for-reloading-configuration.patch
new file mode 100644
index 0000000000..26dc1afaf6
--- /dev/null
+++ b/package/network/config/netifd/patches/0003-wireless-add-ubus-method-for-reloading-configuration.patch
@@ -0,0 +1,123 @@
+From 7a723d0c797d28823f3d0eae9c72aca3ae69d976 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 25 Oct 2019 14:02:03 +0200
+Subject: [PATCH 3/4] wireless: add ubus method for reloading configuration
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ ubus.c | 23 +++++++++++++++++++++++
+ wireless.c | 18 +++++++++++++-----
+ wireless.h | 1 +
+ 3 files changed, 37 insertions(+), 5 deletions(-)
+
+diff --git a/ubus.c b/ubus.c
+index 150d818..5a2a339 100644
+--- a/ubus.c
++++ b/ubus.c
+@@ -1081,6 +1081,28 @@ get_wdev(struct blob_attr *msg, int *ret)
+ return wdev;
+ }
+
++static int
++netifd_handle_wdev_reconf(struct ubus_context *ctx, struct ubus_object *obj,
++ struct ubus_request_data *req, const char *method,
++ struct blob_attr *msg)
++{
++ struct wireless_device *wdev;
++ int ret;
++
++ wdev = get_wdev(msg, &ret);
++ if (ret == UBUS_STATUS_NOT_FOUND)
++ return ret;
++
++ if (wdev) {
++ wireless_device_reconf(wdev);
++ } else {
++ vlist_for_each_element(&wireless_devices, wdev, node)
++ wireless_device_reconf(wdev);
++ }
++
++ return 0;
++}
++
+ static int
+ netifd_handle_wdev_up(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+@@ -1189,6 +1211,7 @@ netifd_handle_wdev_notify(struct ubus_context *ctx, struct ubus_object *obj,
+ static struct ubus_method wireless_object_methods[] = {
+ { .name = "up", .handler = netifd_handle_wdev_up },
+ { .name = "down", .handler = netifd_handle_wdev_down },
++ { .name = "reconf", .handler = netifd_handle_wdev_reconf },
+ { .name = "status", .handler = netifd_handle_wdev_status },
+ { .name = "notify", .handler = netifd_handle_wdev_notify },
+ { .name = "get_validate", .handler = netifd_handle_wdev_get_validate },
+diff --git a/wireless.c b/wireless.c
+index 387f4ba..9986e9a 100644
+--- a/wireless.c
++++ b/wireless.c
+@@ -282,7 +282,7 @@ wireless_device_run_handler(struct wireless_device *wdev, bool up)
+ }
+
+ static void
+-__wireless_device_set_up(struct wireless_device *wdev)
++__wireless_device_set_up(struct wireless_device *wdev, int force)
+ {
+ if (wdev->disabled)
+ return;
+@@ -293,7 +293,7 @@ __wireless_device_set_up(struct wireless_device *wdev)
+ if (!wdev->autostart)
+ return;
+
+- if (wdev->state != IFS_DOWN || config_init)
++ if (!force && (wdev->state != IFS_DOWN || config_init))
+ return;
+
+ free(wdev->prev_config);
+@@ -320,7 +320,7 @@ wdev_handle_config_change(struct wireless_device *wdev)
+ switch(state) {
+ case IFC_NORMAL:
+ case IFC_RELOAD:
+- __wireless_device_set_up(wdev);
++ __wireless_device_set_up(wdev, 0);
+
+ wdev->config_state = IFC_NORMAL;
+ break;
+@@ -363,7 +363,15 @@ wireless_device_set_up(struct wireless_device *wdev)
+ {
+ wdev->retry = WIRELESS_SETUP_RETRY;
+ wdev->autostart = true;
+- __wireless_device_set_up(wdev);
++ __wireless_device_set_up(wdev, 0);
++}
++
++void
++wireless_device_reconf(struct wireless_device *wdev)
++{
++ wdev->retry = WIRELESS_SETUP_RETRY;
++ wdev->autostart = true;
++ __wireless_device_set_up(wdev, 1);
+ }
+
+ static void
+@@ -1001,5 +1009,5 @@ wireless_start_pending(void)
+ struct wireless_device *wdev;
+
+ vlist_for_each_element(&wireless_devices, wdev, node)
+- __wireless_device_set_up(wdev);
++ __wireless_device_set_up(wdev, 0);
+ }
+diff --git a/wireless.h b/wireless.h
+index 3498bd8..bade738 100644
+--- a/wireless.h
++++ b/wireless.h
+@@ -93,6 +93,7 @@ struct wireless_process {
+ void wireless_device_create(struct wireless_driver *drv, const char *name, struct blob_attr *data);
+ void wireless_device_set_up(struct wireless_device *wdev);
+ void wireless_device_set_down(struct wireless_device *wdev);
++void wireless_device_reconf(struct wireless_device *wdev);
+ void wireless_device_status(struct wireless_device *wdev, struct blob_buf *b);
+ void wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b);
+ void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section);
+--
+2.25.1
+
diff --git a/package/network/config/netifd/patches/0004-wireless-make-reconf-opt-in-and-allow-serializing-co.patch b/package/network/config/netifd/patches/0004-wireless-make-reconf-opt-in-and-allow-serializing-co.patch
new file mode 100644
index 0000000000..04bf4397db
--- /dev/null
+++ b/package/network/config/netifd/patches/0004-wireless-make-reconf-opt-in-and-allow-serializing-co.patch
@@ -0,0 +1,249 @@
+From e15147c272201eb17320c10ec95919e641bd03c5 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 25 Oct 2019 14:06:30 +0200
+Subject: [PATCH 4/4] wireless: make reconf opt-in and allow serializing
+ configuration
+
+Add option 'reconf' to make dynamic re-configuration opt-in.
+Also add option 'serialize' to 'wifi-device' section and if set
+configure interfaces of wireless devices one-by-one.
+Both options are disabled by default.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ wireless.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++------
+ wireless.h | 7 +++++
+ 2 files changed, 88 insertions(+), 10 deletions(-)
+
+diff --git a/wireless.c b/wireless.c
+index 9986e9a..c8e196f 100644
+--- a/wireless.c
++++ b/wireless.c
+@@ -23,13 +23,25 @@ struct vlist_tree wireless_devices;
+ struct avl_tree wireless_drivers;
+ static struct blob_buf b;
+ static int drv_fd;
++static LIST_HEAD(handlers);
++static bool handler_pending;
+
+-static const struct blobmsg_policy wdev_policy =
+- { .name = "disabled", .type = BLOBMSG_TYPE_BOOL };
++enum {
++ WDEV_ATTR_DISABLED,
++ WDEV_ATTR_RECONF,
++ WDEV_ATTR_SERIALIZE,
++ __WDEV_ATTR_MAX,
++};
++
++static const struct blobmsg_policy wdev_policy[__WDEV_ATTR_MAX] = {
++ [WDEV_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
++ [WDEV_ATTR_RECONF] = { .name = "reconf", .type = BLOBMSG_TYPE_BOOL },
++ [WDEV_ATTR_SERIALIZE] = { .name = "serialize", .type = BLOBMSG_TYPE_BOOL },
++};
+
+ static const struct uci_blob_param_list wdev_param = {
+- .n_params = 1,
+- .params = &wdev_policy,
++ .n_params = ARRAY_SIZE(wdev_policy),
++ .params = wdev_policy,
+ };
+
+ enum {
+@@ -52,6 +64,15 @@ static const struct uci_blob_param_list vif_param = {
+ .params = vif_policy,
+ };
+
++static void
++wireless_handler_stop(struct wireless_device *wdev)
++{
++ if (wdev->handler_pending) {
++ wdev->handler_pending = false;
++ list_del(&wdev->handler);
++ }
++}
++
+ static void
+ put_container(struct blob_buf *buf, struct blob_attr *attr, const char *name)
+ {
+@@ -188,6 +209,7 @@ wireless_device_free_state(struct wireless_device *wdev)
+ {
+ struct wireless_interface *vif;
+
++ wireless_handler_stop(wdev);
+ uloop_timeout_cancel(&wdev->script_check);
+ uloop_timeout_cancel(&wdev->timeout);
+ wireless_complete_kill_request(wdev);
+@@ -236,6 +258,7 @@ wireless_device_setup_cancel(struct wireless_device *wdev)
+ if (wdev->cancel)
+ return;
+
++ wireless_handler_stop(wdev);
+ D(WIRELESS, "Cancel wireless device '%s' setup\n", wdev->name);
+ wdev->cancel = true;
+ uloop_timeout_set(&wdev->timeout, 10 * 1000);
+@@ -250,6 +273,17 @@ wireless_device_run_handler(struct wireless_device *wdev, bool up)
+ int i = 0;
+ int fds[2] = { -1, -1 };
+
++ wireless_handler_stop(wdev);
++
++ if (handler_pending && wdev->serialize) {
++ wdev->handler_action = up;
++ wdev->handler_pending = true;
++ list_add_tail(&wdev->handler, &handlers);
++ return;
++ }
++ if (wdev->serialize)
++ handler_pending = true;
++
+ D(WIRELESS, "Wireless device '%s' run %s handler\n", wdev->name, action);
+ if (!up && wdev->prev_config) {
+ config = blobmsg_format_json(wdev->prev_config, true);
+@@ -281,6 +315,21 @@ wireless_device_run_handler(struct wireless_device *wdev, bool up)
+ free(config);
+ }
+
++static void
++wireless_handler_next(void)
++{
++ struct wireless_device *wdev;
++
++ if (handler_pending)
++ return;
++ if (list_empty(&handlers))
++ return;
++ wdev = list_first_entry(&handlers, struct wireless_device, handler);
++ list_del(&wdev->handler);
++ wdev->handler_pending = false;
++ wireless_device_run_handler(wdev, wdev->handler_action);
++}
++
+ static void
+ __wireless_device_set_up(struct wireless_device *wdev, int force)
+ {
+@@ -305,6 +354,7 @@ __wireless_device_set_up(struct wireless_device *wdev, int force)
+ static void
+ wireless_device_free(struct wireless_device *wdev)
+ {
++ wireless_handler_stop(wdev);
+ vlist_flush_all(&wdev->interfaces);
+ avl_delete(&wireless_devices.avl, &wdev->node.avl);
+ free(wdev->config);
+@@ -353,6 +403,10 @@ wireless_device_setup_timeout(struct uloop_timeout *timeout)
+ {
+ struct wireless_device *wdev = container_of(timeout, struct wireless_device, timeout);
+
++ if (wdev->handler_pending) {
++ wdev->handler_pending = false;
++ list_del(&wdev->handler);
++ }
+ netifd_kill_process(&wdev->script_task);
+ wdev->script_task.cb(&wdev->script_task, -1);
+ wireless_device_mark_down(wdev);
+@@ -371,7 +425,7 @@ wireless_device_reconf(struct wireless_device *wdev)
+ {
+ wdev->retry = WIRELESS_SETUP_RETRY;
+ wdev->autostart = true;
+- __wireless_device_set_up(wdev, 1);
++ __wireless_device_set_up(wdev, wdev->reconf && (wdev->state == IFS_UP));
+ }
+
+ static void
+@@ -433,6 +487,11 @@ wireless_device_script_task_cb(struct netifd_process *proc, int ret)
+ default:
+ break;
+ }
++
++ if (wdev->serialize) {
++ handler_pending = false;
++ wireless_handler_next();
++ }
+ }
+
+ void
+@@ -452,7 +511,7 @@ wdev_set_config_state(struct wireless_device *wdev, enum interface_config_state
+ wdev->config_state = s;
+ if (wdev->state == IFS_DOWN)
+ wdev_handle_config_change(wdev);
+- else
++ else if (!wdev->reconf || wdev->state != IFS_UP)
+ __wireless_device_set_down(wdev);
+ }
+
+@@ -501,6 +560,7 @@ wdev_update(struct vlist_tree *tree, struct vlist_node *node_new,
+ struct wireless_device *wd_new = container_of(node_new, struct wireless_device, node);
+
+ if (wd_old && wd_new) {
++ D(WIRELESS, "Update wireless device '%s'\n", wd_old->name);
+ wdev_change_config(wd_old, wd_new);
+ } else if (wd_old) {
+ D(WIRELESS, "Delete wireless device '%s'\n", wd_old->name);
+@@ -686,18 +746,29 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
+ {
+ struct wireless_device *wdev;
+ char *name_buf;
+- struct blob_attr *disabled;
++ struct blob_attr *tb[__WDEV_ATTR_MAX];
++ struct blob_attr *cur;
+
+- blobmsg_parse(&wdev_policy, 1, &disabled, blob_data(data), blob_len(data));
++ blobmsg_parse(wdev_policy, __WDEV_ATTR_MAX, tb, blob_data(data), blob_len(data));
+
+ wdev = calloc_a(sizeof(*wdev), &name_buf, strlen(name) + 1);
+- if (disabled && blobmsg_get_bool(disabled))
+- wdev->disabled = true;
++
++ cur = tb[WDEV_ATTR_DISABLED];
++ wdev->disabled = cur && blobmsg_get_bool(cur);
++
+ wdev->drv = drv;
+ wdev->state = IFS_DOWN;
+ wdev->config_state = IFC_NORMAL;
+ wdev->name = strcpy(name_buf, name);
+ wdev->config = data;
++ wdev->handler_pending = false;
++
++ cur = tb[WDEV_ATTR_SERIALIZE];
++ wdev->serialize = cur && blobmsg_get_bool(cur);
++
++ cur = tb[WDEV_ATTR_RECONF];
++ wdev->reconf = cur && blobmsg_get_bool(cur);
++
+ wdev->retry_setup_failed = false;
+ wdev->autostart = true;
+ INIT_LIST_HEAD(&wdev->script_proc);
+diff --git a/wireless.h b/wireless.h
+index bade738..f734770 100644
+--- a/wireless.h
++++ b/wireless.h
+@@ -15,6 +15,7 @@
+ #define __NETIFD_WIRELESS_H
+
+ #include <libubox/utils.h>
++#include <libubox/list.h>
+ #include "interface.h"
+
+ extern struct vlist_tree wireless_devices;
+@@ -35,6 +36,11 @@ struct wireless_driver {
+ struct wireless_device {
+ struct vlist_node node;
+
++ struct list_head handler;
++ bool handler_action;
++ bool handler_pending;
++ bool serialize;
++
+ struct wireless_driver *drv;
+ struct vlist_tree interfaces;
+ char *name;
+@@ -59,6 +65,7 @@ struct wireless_device {
+
+ enum interface_state state;
+ enum interface_config_state config_state;
++ bool reconf;
+ bool cancel;
+ int retry;
+
+--
+2.25.1
+
diff --git a/package/network/config/netifd/patches/0005-netifd-fix-wdev-data-lifetime.patch b/package/network/config/netifd/patches/0005-netifd-fix-wdev-data-lifetime.patch
new file mode 100644
index 0000000000..5ccefc5117
--- /dev/null
+++ b/package/network/config/netifd/patches/0005-netifd-fix-wdev-data-lifetime.patch
@@ -0,0 +1,42 @@
+From 1aee9aea386a510eb9e0f11fdb57984539294f0d Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 23 Jun 2020 10:09:13 +0200
+Subject: [PATCH V2] netifd: fix wdev->data lifetime
+
+The reconf patch breaks wifi down under certain conditions. The root cause
+is that during reload the wdev state gets flushed. This has the effect, that
+the phy is lost from the state resulting in teardown breaking.
+Fix this by changing the lifetime of wdev->data.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+Changes in V2
+* free() call was in the wrong place
+
+ wireless.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/wireless.c b/wireless.c
+index efb7992..e295f28 100644
+--- a/wireless.c
++++ b/wireless.c
+@@ -275,8 +275,6 @@ wireless_device_free_state(struct wireless_device *wdev)
+ uloop_timeout_cancel(&wdev->script_check);
+ uloop_timeout_cancel(&wdev->timeout);
+ wireless_complete_kill_request(wdev);
+- free(wdev->data);
+- wdev->data = NULL;
+ vlist_for_each_element(&wdev->interfaces, vif, node) {
+ free(vif->data);
+ vif->data = NULL;
+@@ -460,6 +458,7 @@ wireless_device_free(struct wireless_device *wdev)
+ vlist_flush_all(&wdev->vlans);
+ vlist_flush_all(&wdev->stations);
+ avl_delete(&wireless_devices.avl, &wdev->node.avl);
++ free(wdev->data);
+ free(wdev->config);
+ free(wdev->prev_config);
+ free(wdev);
+--
+2.25.1
+
--
2.25.1