wireless: rework and fix vlan/station config reload handling
authorFelix Fietkau <nbd@nbd.name>
Fri, 15 Sep 2023 11:06:02 +0000 (13:06 +0200)
committerFelix Fietkau <nbd@nbd.name>
Fri, 15 Sep 2023 11:08:03 +0000 (13:08 +0200)
The vif name of sections was not properly updated.
Rework the config structure to maintain stations/vlans in a vlist in the vif struct.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
config.c
scripts/netifd-wireless.sh
wireless.c
wireless.h

index e1c01e12994ba222e062e633e5094b2aafa65c02..8f4a383157d8881e41f9d259c94e0b28270dfb23 100644 (file)
--- a/config.c
+++ b/config.c
@@ -557,8 +557,8 @@ config_parse_wireless_device(struct uci_section *s)
        wireless_device_create(drv, s->e.name, b.head);
 }
 
-static struct wireless_interface*
-config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section *s)
+static void
+config_parse_wireless_vlan(struct wireless_interface *vif, struct uci_section *s)
 {
        char *name;
 
@@ -566,12 +566,12 @@ config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section
        sprintf(name, "@%s[%d]", s->type, config_section_idx(s));
 
        blob_buf_init(&b, 0);
-       uci_to_blob(&b, s, wdev->drv->interface.config);
-       return wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name);
+       uci_to_blob(&b, s, vif->wdev->drv->vlan.config);
+       wireless_vlan_create(vif, b.head, s->anonymous ? name : s->e.name);
 }
 
 static void
-config_parse_wireless_vlan(struct wireless_device *wdev, char *vif, struct uci_section *s)
+config_parse_wireless_station(struct wireless_interface *vif, struct uci_section *s)
 {
        char *name;
 
@@ -579,21 +579,62 @@ config_parse_wireless_vlan(struct wireless_device *wdev, char *vif, struct uci_s
        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);
+       uci_to_blob(&b, s, vif->wdev->drv->station.config);
+       wireless_station_create(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)
+config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section *s)
 {
+       struct wireless_interface *vif;
+       struct uci_element *f;
        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);
+       uci_to_blob(&b, s, wdev->drv->interface.config);
+       vif = wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name);
+       if (!vif)
+               return;
+
+       vif->vlan_idx = vif->sta_idx = 0;
+       vlist_update(&vif->vlans);
+       vlist_update(&vif->stations);
+
+       if (s->anonymous)
+               goto out;
+
+       uci_foreach_element(&uci_wireless->sections, f) {
+               struct uci_section *cur = uci_to_section(f);
+               const char *vif_name;
+
+               if (strcmp(cur->type, "wifi-vlan") != 0)
+                       continue;
+
+               vif_name = uci_lookup_option_string(uci_ctx, cur, "iface");
+               if (vif_name && strcmp(s->e.name, vif_name))
+                       continue;
+               config_parse_wireless_vlan(vif, cur);
+       }
+
+       uci_foreach_element(&uci_wireless->sections, f) {
+               struct uci_section *cur = uci_to_section(f);
+               const char *vif_name;
+
+               if (strcmp(cur->type, "wifi-station") != 0)
+                       continue;
+
+               vif_name = uci_lookup_option_string(uci_ctx, cur, "iface");
+               if (vif_name && strcmp(s->e.name, vif_name))
+                       continue;
+               config_parse_wireless_station(vif, cur);
+       }
+
+out:
+       vlist_flush(&vif->vlans);
+       vlist_flush(&vif->stations);
 }
 
 static void
@@ -623,16 +664,10 @@ 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);
-               wdev->sta_idx = 0;
-               vlist_update(&wdev->stations);
        }
 
        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;
@@ -647,42 +682,11 @@ config_init_wireless(void)
                        continue;
                }
 
-               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);
-               }
-
-               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);
-               }
+               config_parse_wireless_interface(wdev, 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);
-               vlist_flush(&wdev->stations);
-       }
 }
 
 
index 2e600c1ae3ba6fe97fab47f2aece06811f125c30..7f088ccf1946a937b081707db645248d379a3149 100644 (file)
@@ -108,14 +108,16 @@ _wdev_wrapper() {
 }
 
 _wdev_notify_init() {
-       local command="$1"
-       local name="$2"
-       local value="$3"
+       local command="$1"; shift;
 
        json_init
        json_add_int "command" "$command"
        json_add_string "device" "$__netifd_device"
-       [ -n "$name" -a -n "$value" ] && json_add_string "$name" "$value"
+       while [ -n "$1" ]; do
+               local name="$1"; shift
+               local value="$1"; shift
+               json_add_string "$name" "$value"
+       done
        json_add_object "data"
 }
 
@@ -151,7 +153,7 @@ _wireless_add_vlan() {
        local name="$1"; shift
        local ifname="$1"; shift
 
-       _wdev_notify_init $CMD_SET_DATA "vlan" "$name"
+       _wdev_notify_init $CMD_SET_DATA interface "$__cur_interface" "vlan" "$name"
        json_add_string "ifname" "$ifname"
        _wdev_add_variables "$@"
        _wdev_notify
@@ -333,6 +335,7 @@ for_each_interface() {
                                continue
                        }
                fi
+               __cur_interface="$_w_iface"
                "$@" "$_w_iface"
                json_select ..
        done
index 898a01220011e41932180c8b0a4e79de50aa2002..1e7d757e01d7b9de26b1e9cd5c99869a893a209f 100644 (file)
@@ -205,9 +205,7 @@ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
                        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;
+               vlist_for_each_element(&vif->vlans, vlan, node) {
                        k = blobmsg_open_table(&b, vlan->name);
                        vif_config_add_bridge(&b, vlan->network, up);
                        put_container(&b, vlan->config, "config");
@@ -218,9 +216,7 @@ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up)
                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;
+               vlist_for_each_element(&vif->stations, sta, node) {
                        k = blobmsg_open_table(&b, sta->name);
                        put_container(&b, sta->config, "config");
                        if (sta->data)
@@ -311,15 +307,15 @@ wireless_device_free_state(struct wireless_device *wdev)
                free(vif->data);
                vif->data = NULL;
                vif->ifname = NULL;
-       }
-       vlist_for_each_element(&wdev->vlans, vlan, node) {
-               free(vlan->data);
-               vlan->data = NULL;
-               vlan->ifname = NULL;
-       }
-       vlist_for_each_element(&wdev->stations, sta, node) {
-               free(sta->data);
-               sta->data = NULL;
+               vlist_for_each_element(&vif->vlans, vlan, node) {
+                       free(vlan->data);
+                       vlan->data = NULL;
+                       vlan->ifname = NULL;
+               }
+               vlist_for_each_element(&vif->stations, sta, node) {
+                       free(sta->data);
+                       sta->data = NULL;
+               }
        }
 }
 
@@ -510,8 +506,6 @@ 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);
@@ -547,11 +541,12 @@ wireless_device_mark_down(struct wireless_device *wdev)
 
        netifd_log_message(L_NOTICE, "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)
+       vlist_for_each_element(&wdev->interfaces, vif, node) {
                wireless_interface_handle_link(vif, NULL, false);
+               vlist_for_each_element(&vif->vlans, vlan, node)
+                       wireless_vlan_handle_link(vlan, false);
+       }
 
        wireless_process_kill_all(wdev, SIGTERM, true);
 
@@ -623,10 +618,11 @@ wireless_device_mark_up(struct wireless_device *wdev)
        netifd_log_message(L_NOTICE, "Wireless device '%s' is now up\n", wdev->name);
        wdev->retry = WIRELESS_SETUP_RETRY;
        wdev->state = IFS_UP;
-       vlist_for_each_element(&wdev->interfaces, vif, node)
+       vlist_for_each_element(&wdev->interfaces, vif, node) {
                wireless_interface_handle_link(vif, NULL, true);
-       vlist_for_each_element(&wdev->vlans, vlan, node)
-               wireless_vlan_handle_link(vlan, true);
+               vlist_for_each_element(&vif->vlans, vlan, node)
+                       wireless_vlan_handle_link(vlan, true);
+       }
 }
 
 static void
@@ -848,6 +844,16 @@ wireless_interface_init_config(struct wireless_interface *vif)
        vif->multicast_to_unicast = cur ? blobmsg_get_bool(cur) : -1;
 }
 
+static void
+vif_free(struct wireless_interface *vif)
+{
+       vlist_flush_all(&vif->vlans);
+       vlist_flush_all(&vif->stations);
+       free((void *) vif->section);
+       free(vif->config);
+       free(vif);
+}
+
 /* vlist update call for wireless interface list */
 static void
 vif_update(struct vlist_tree *tree, struct vlist_node *node_new,
@@ -884,9 +890,7 @@ vif_update(struct vlist_tree *tree, struct vlist_node *node_new,
        } else if (vif_old) {
                D(WIRELESS, "Delete wireless interface %s on device %s\n", vif_old->name, wdev->name);
                wireless_interface_handle_link(vif_old, NULL, false);
-               free((void *) vif_old->section);
-               free(vif_old->config);
-               free(vif_old);
+               vif_free(vif_old);
        }
 
        wdev->config_update = true;
@@ -921,14 +925,10 @@ 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;
+       struct wireless_vlan *vlan_old = container_of_safe(node_old, struct wireless_vlan, node);
+       struct wireless_vlan *vlan_new = container_of_safe(node_new, struct wireless_vlan, node);
+       struct wireless_interface *vif = container_of(tree, struct wireless_interface, vlans);
+       struct wireless_device *wdev = vif->wdev;
 
        if (vlan_old && vlan_new) {
                free((void *) vlan_old->section);
@@ -951,7 +951,7 @@ vlan_update(struct vlist_tree *tree, struct vlist_node *node_new,
                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);
+               D(WIRELESS, "Delete wireless vlan %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);
@@ -966,14 +966,10 @@ 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;
+       struct wireless_station *sta_old = container_of_safe(node_old, struct wireless_station, node);
+       struct wireless_station *sta_new = container_of_safe(node_new, struct wireless_station, node);
+       struct wireless_interface *vif = container_of(tree, struct wireless_interface, stations);
+       struct wireless_device *wdev = vif->wdev;
 
        if (sta_old && sta_new) {
                free((void *) sta_old->section);
@@ -1088,10 +1084,6 @@ 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;
-       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;
@@ -1108,12 +1100,12 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo
 
 /* creates a wireless station object. Called by config */
 void
-wireless_station_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section)
+wireless_station_create(struct wireless_interface *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_buf;
        char name[8];
 
        blobmsg_parse(sta_policy, __STA_ATTR_MAX, tb, blob_data(data), blob_len(data));
@@ -1122,18 +1114,15 @@ wireless_station_create(struct wireless_device *wdev, char *vif, struct blob_att
        if (cur && blobmsg_get_bool(cur))
                return;
 
-       sprintf(name, "%d", wdev->sta_idx++);
+       sprintf(name, "%d", vif->sta_idx++);
 
        sta = calloc_a(sizeof(*sta),
-                      &name_buf, strlen(name) + 1,
-                      &vif_buf, strlen(vif) + 1);
+                      &name_buf, strlen(name) + 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);
+       vlist_add(&vif->stations, &sta->node, sta->name);
 }
 
 /* ubus callback network.wireless.status, runs for every interface, encode the station */
@@ -1151,12 +1140,12 @@ wireless_station_status(struct wireless_station *sta, struct blob_buf *b)
 
 /* create a vlan object. Called by config */
 void
-wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section)
+wireless_vlan_create(struct wireless_interface *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_buf;
        char name[8];
 
        blobmsg_parse(vlan_policy, __VLAN_ATTR_MAX, tb, blob_data(data), blob_len(data));
@@ -1165,19 +1154,14 @@ wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *
        if (cur && blobmsg_get_bool(cur))
                return;
 
-       sprintf(name, "%d", wdev->vlan_idx++);
+       sprintf(name, "%d", vif->vlan_idx++);
 
-       vlan = calloc_a(sizeof(*vlan),
-                      &name_buf, strlen(name) + 1,
-                      &vif_buf, strlen(vif) + 1);
+       vlan = calloc_a(sizeof(*vlan), &name_buf, strlen(name) + 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);
+       vlist_add(&vif->vlans, &vlan->node, vlan->name);
 }
 
 /* ubus callback network.wireless.status, runs for every interface, encode the vlan informations */
@@ -1220,6 +1204,12 @@ struct wireless_interface* wireless_interface_create(struct wireless_device *wde
        vif->section = section;
        vif->isolate = false;
 
+       vlist_init(&vif->vlans, avl_strcmp, vlan_update);
+       vif->vlans.keep_old = true;
+
+       vlist_init(&vif->stations, avl_strcmp, station_update);
+       vif->stations.keep_old = true;
+
        vlist_add(&wdev->interfaces, &vif->node, vif->name);
 
        return vlist_find(&wdev->interfaces, name, vif, node);
@@ -1240,14 +1230,12 @@ wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b)
                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);
+       vlist_for_each_element(&iface->vlans, vlan, node)
+               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);
+       vlist_for_each_element(&iface->stations, sta, node)
+               wireless_station_status(sta, b);
        blobmsg_close_array(b, j);
        blobmsg_close_table(b, i);
 }
@@ -1496,7 +1484,9 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
        }
 
        if ((cur = tb[NOTIFY_ATTR_VLAN]) != NULL) {
-               vlan = vlist_find(&wdev->vlans, blobmsg_data(cur), vlan, node);
+               if (!vif)
+                       return UBUS_STATUS_NOT_FOUND;
+               vlan = vlist_find(&vif->vlans, blobmsg_data(cur), vlan, node);
                if (!vlan)
                        return UBUS_STATUS_NOT_FOUND;
        }
@@ -1516,19 +1506,19 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data,
                wireless_device_mark_up(wdev);
                break;
        case NOTIFY_CMD_SET_DATA:
-               if (vif)
-                       pdata = &vif->data;
-               else if (vlan)
+               if (vlan)
                        pdata = &vlan->data;
+               else if (vif)
+                       pdata = &vif->data;
                else
                        pdata = &wdev->data;
 
                free(*pdata);
                *pdata = blob_memdup(cur);
-               if (vif)
-                       wireless_interface_set_data(vif);
-               else if (vlan)
+               if (vlan)
                        wireless_vlan_set_data(vlan);
+               else if (vif)
+                       wireless_interface_set_data(vif);
                break;
        case NOTIFY_CMD_PROCESS_ADD:
                return wireless_device_add_process(wdev, cur);
index 4539bbc442f0516f5929d47f49de56ebd55d31c8..eaf75f72e2cff084ee9400e527fca66629c9079b 100644 (file)
@@ -44,8 +44,6 @@ 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;
@@ -73,8 +71,6 @@ struct wireless_device {
        int retry;
 
        int vif_idx;
-       int vlan_idx;
-       int sta_idx;
 };
 
 struct wireless_interface {
@@ -82,6 +78,8 @@ struct wireless_interface {
        const char *section;
        char *name;
 
+       struct vlist_tree vlans;
+       struct vlist_tree stations;
        struct wireless_device *wdev;
 
        struct blob_attr *config;
@@ -94,6 +92,8 @@ struct wireless_interface {
        bool isolate;
        bool ap_mode;
        int multicast_to_unicast;
+       int vlan_idx;
+       int sta_idx;
 };
 
 struct wireless_vlan {
@@ -101,9 +101,6 @@ struct wireless_vlan {
        const char *section;
        char *name;
 
-       struct wireless_device *wdev;
-       char *vif;
-
        struct blob_attr *config;
        struct blob_attr *data;
 
@@ -119,9 +116,6 @@ struct wireless_station {
        const char *section;
        char *name;
 
-       struct wireless_device *wdev;
-       char *vif;
-
        struct blob_attr *config;
        struct blob_attr *data;
 };
@@ -143,8 +137,8 @@ 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);
 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);
+void wireless_vlan_create(struct wireless_interface *vif, struct blob_attr *data, const char *section);
+void wireless_station_create(struct wireless_interface *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);