wireless: add support for defining wifi interfaces via procd service data
authorFelix Fietkau <nbd@nbd.name>
Mon, 3 Jun 2024 18:29:20 +0000 (20:29 +0200)
committerFelix Fietkau <nbd@nbd.name>
Tue, 4 Jun 2024 08:01:04 +0000 (10:01 +0200)
This makes it possible to dynamically define wifi interfaces from other
services without having to update the config.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
config.c
ubus.c
ubus.h
wireless.c

index a7f9c0b64a44567ecb11c32aa3b059883cc507a7..d187152b37ae9a3ac8d46a0d06890db6ae5f2bfb 100644 (file)
--- a/config.c
+++ b/config.c
@@ -27,6 +27,7 @@
 #include "proto.h"
 #include "wireless.h"
 #include "config.h"
+#include "ubus.h"
 
 bool config_init = false;
 
@@ -599,10 +600,6 @@ config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section
        if (!vif)
                return;
 
-       vif->vlan_idx = vif->sta_idx = 0;
-       vlist_update(&vif->vlans);
-       vlist_update(&vif->stations);
-
        if (s->anonymous)
                goto out;
 
@@ -637,6 +634,73 @@ out:
        vlist_flush(&vif->stations);
 }
 
+static void
+config_init_procd_wireless_interface(const char *wdev_name, const char *vif_name,
+                                    struct blob_attr *config,
+                                    struct blob_attr *vlans,
+                                    struct blob_attr *stations)
+{
+       struct wireless_interface *vif;
+       struct wireless_device *wdev;
+       struct blob_attr *cur;
+       char name[16];
+       int idx = 0;
+       int rem;
+
+       wdev = vlist_find(&wireless_devices, wdev_name, wdev, node);
+       if (!wdev) {
+               D(WIRELESS, "device %s not found!", wdev_name);
+               return;
+       }
+
+       vif = wireless_interface_create(wdev, config, vif_name);
+       if (!vif)
+               return;
+
+       blobmsg_for_each_attr(cur, vlans, rem) {
+               snprintf(name, sizeof(name), "%d", ++idx);
+               wireless_vlan_create(vif, cur, name);
+       }
+
+       blobmsg_for_each_attr(cur, stations, rem) {
+               snprintf(name, sizeof(name), "%d", ++idx);
+               wireless_station_create(vif, cur, name);
+       }
+
+       vlist_flush(&vif->vlans);
+       vlist_flush(&vif->stations);
+}
+
+static void
+config_procd_wireless_interface_cb(struct blob_attr *data)
+{
+       enum {
+               UDATA_ATTR_DEVICE,
+               UDATA_ATTR_CONFIG,
+               UDATA_ATTR_STATIONS,
+               UDATA_ATTR_VLANS,
+               __UDATA_ATTR_MAX,
+       };
+       static const struct blobmsg_policy policy[__UDATA_ATTR_MAX] = {
+               [UDATA_ATTR_DEVICE] = { "device", BLOBMSG_TYPE_STRING },
+               [UDATA_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE },
+               [UDATA_ATTR_STATIONS] = { "stations", BLOBMSG_TYPE_ARRAY },
+               [UDATA_ATTR_VLANS] = { "vlans", BLOBMSG_TYPE_ARRAY },
+       };
+       struct blob_attr *tb[__UDATA_ATTR_MAX];
+       const char *dev;
+
+       blobmsg_parse_attr(policy, __UDATA_ATTR_MAX, tb, data);
+       if (!tb[UDATA_ATTR_DEVICE] || !tb[UDATA_ATTR_CONFIG])
+               return;
+
+       dev = blobmsg_get_string(tb[UDATA_ATTR_DEVICE]);
+       config_init_procd_wireless_interface(dev, blobmsg_name(data),
+                                            tb[UDATA_ATTR_CONFIG],
+                                            tb[UDATA_ATTR_VLANS],
+                                            tb[UDATA_ATTR_STATIONS]);
+}
+
 static void
 config_init_wireless(void)
 {
@@ -685,6 +749,8 @@ config_init_wireless(void)
                config_parse_wireless_interface(wdev, s);
        }
 
+       netifd_ubus_get_procd_data("wifi-iface", config_procd_wireless_interface_cb);
+
        vlist_for_each_element(&wireless_devices, wdev, node)
                vlist_flush(&wdev->interfaces);
 }
diff --git a/ubus.c b/ubus.c
index 3d24dc7ad729b5e5f94aa316b57901dbd9af1508..f8662c2a7b78be1f3a817affdd4a8e17124d5d9c 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -1445,3 +1445,45 @@ netifd_ubus_remove_interface(struct interface *iface)
        ubus_remove_object(ubus_ctx, &iface->ubus);
        free((void *) iface->ubus.name);
 }
+
+static void
+netifd_ubus_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+       struct blob_attr *srv, *in, *t, *data;
+       procd_data_cb cb = req->priv;
+       int rem, rem2, rem3, rem4;
+
+       blobmsg_for_each_attr(srv, msg, rem) {
+               if (!blobmsg_check_attr(srv, true) ||
+                   blobmsg_type(srv) != BLOBMSG_TYPE_TABLE)
+                       continue;
+               blobmsg_for_each_attr(in, srv, rem2) {
+                       if (!blobmsg_check_attr(in , true) ||
+                               blobmsg_type(in) != BLOBMSG_TYPE_TABLE)
+                               continue;
+                       blobmsg_for_each_attr(t, in, rem3) {
+                               if (!blobmsg_check_attr(t, true) ||
+                                       blobmsg_type(t) != BLOBMSG_TYPE_TABLE)
+                                       continue;
+                               blobmsg_for_each_attr(data, t, rem4) {
+                                       if (!blobmsg_check_attr(t, true) ||
+                                               blobmsg_type(t) != BLOBMSG_TYPE_TABLE)
+                                               continue;
+                                       cb(data);
+                               }
+                       }
+               }
+       }
+}
+
+void netifd_ubus_get_procd_data(const char *type, procd_data_cb cb)
+{
+       uint32_t id;
+
+       if (ubus_lookup_id(ubus_ctx, "service", &id))
+               return;
+
+       blob_buf_init(&b, 0);
+       blobmsg_add_string(&b, "type", type);
+       ubus_invoke(ubus_ctx, id, "get_data", b.head, netifd_ubus_data_cb, cb, 30000);
+}
diff --git a/ubus.h b/ubus.h
index dde71244afded121fd7a0ad80e428f2a6294ddb4..b9b4b6919a5f1cf802377f52ca95e6a73fe8024f 100644 (file)
--- a/ubus.h
+++ b/ubus.h
@@ -14,6 +14,8 @@
 #ifndef __NETIFD_UBUS_H
 #define __NETIFD_UBUS_H
 
+typedef void (*procd_data_cb)(struct blob_attr *data);
+
 extern struct ubus_context *ubus_ctx;
 
 int netifd_ubus_init(const char *path);
@@ -26,5 +28,6 @@ void netifd_ubus_remove_interface(struct interface *iface);
 void netifd_ubus_interface_event(struct interface *iface, bool up);
 void netifd_ubus_interface_notify(struct interface *iface, bool up);
 void netifd_ubus_device_notify(const char *event, struct blob_attr *data, int timeout);
+void netifd_ubus_get_procd_data(const char *type, procd_data_cb cb);
 
 #endif
index ea6e6b479fd1c4b120fde165b228a6c372dd24f2..3a03988506f9dee699ebbe221b050015fb561ac7 100644 (file)
@@ -138,7 +138,7 @@ static void
 put_container(struct blob_buf *buf, struct blob_attr *attr, const char *name)
 {
        void *c = blobmsg_open_table(buf, name);
-       blob_put_raw(buf, blob_data(attr), blob_len(attr));
+       blob_put_raw(buf, blobmsg_data(attr), blobmsg_len(attr));
        blobmsg_close_table(buf, c);
 }
 
@@ -1232,7 +1232,15 @@ struct wireless_interface* wireless_interface_create(struct wireless_device *wde
 
        vlist_add(&wdev->interfaces, &vif->node, vif->name);
 
-       return vlist_find(&wdev->interfaces, name, vif, node);
+       vif = vlist_find(&wdev->interfaces, name, vif, node);
+       if (!vif)
+               return NULL;
+
+       vif->vlan_idx = vif->sta_idx = 0;
+       vlist_update(&vif->vlans);
+       vlist_update(&vif->stations);
+
+       return vif;
 }
 
 /* ubus callback network.wireless.status, runs for every interface */