network: add support for the local_network option
authorFelix Fietkau <nbd@nbd.name>
Mon, 27 Jan 2025 11:39:59 +0000 (12:39 +0100)
committerFelix Fietkau <nbd@nbd.name>
Mon, 27 Jan 2025 11:40:04 +0000 (12:40 +0100)
This lets unetd automatically query netifd for local addresses in order to
add broadcast addresses as PEX hosts for discovering peers.

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

index fe1b2c901b19c534388ad418972e519fa3ebcbd0..721d48ef614c085b3deca3e2fe2c76e2049cea18 100644 (file)
--- a/network.c
+++ b/network.c
@@ -57,6 +57,7 @@ const struct blobmsg_policy network_policy[__NETWORK_ATTR_MAX] = {
        [NETWORK_ATTR_DOMAIN] = { "domain", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_UPDATE_CMD] = { "update-cmd", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_TUNNELS] = { "tunnels", BLOBMSG_TYPE_TABLE },
+       [NETWORK_ATTR_LOCAL_NET] = { "local_network", BLOBMSG_TYPE_ARRAY },
        [NETWORK_ATTR_AUTH_CONNECT] = { "auth_connect", BLOBMSG_TYPE_ARRAY },
        [NETWORK_ATTR_PEER_DATA] = { "peer_data", BLOBMSG_TYPE_ARRAY },
 };
@@ -656,6 +657,10 @@ network_set_config(struct network *net, struct blob_attr *config)
        if ((cur = tb[NETWORK_ATTR_TUNNELS]) != NULL)
                net->config.tunnels = cur;
 
+       if ((cur = tb[NETWORK_ATTR_LOCAL_NET]) != NULL &&
+           blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
+               net->config.local_network = cur;
+
        if ((cur = tb[NETWORK_ATTR_AUTH_CONNECT]) != NULL &&
            blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
                net->config.auth_connect = cur;
index bfd4c78d9b900c8811c9ff82ee7f408facd44ab4..f713fd35688bb0ff75517114841d8df9ce6d2b60 100644 (file)
--- a/network.h
+++ b/network.h
@@ -37,6 +37,7 @@ struct network {
                const char *domain;
                struct blob_attr *tunnels;
                struct blob_attr *net_data;
+               struct blob_attr *local_network;
                struct blob_attr *auth_connect;
                struct blob_attr *peer_data;
        } config;
@@ -87,6 +88,7 @@ enum {
        NETWORK_ATTR_KEEPALIVE,
        NETWORK_ATTR_DOMAIN,
        NETWORK_ATTR_TUNNELS,
+       NETWORK_ATTR_LOCAL_NET,
        NETWORK_ATTR_AUTH_CONNECT,
        NETWORK_ATTR_PEER_DATA,
        __NETWORK_ATTR_MAX,
diff --git a/pex.c b/pex.c
index 8a13c80c2ccac6a1f3c29445f23a066252abb3d4..db143fc71c91a4f7bd2d3d86133d5a80a1bbe171 100644 (file)
--- a/pex.c
+++ b/pex.c
@@ -740,8 +740,9 @@ network_pex_fd_cb(struct uloop_fd *fd, unsigned int events)
        }
 }
 
-void network_pex_create_host(struct network *net, union network_endpoint *ep,
-                            unsigned int timeout)
+struct network_pex_host *
+network_pex_create_host(struct network *net, union network_endpoint *ep,
+                       unsigned int timeout)
 {
        struct network_pex *pex = &net->pex;
        struct network_pex_host *host;
@@ -768,6 +769,7 @@ void network_pex_create_host(struct network *net, union network_endpoint *ep,
 out:
        if (timeout && (new_host || host->timeout))
                host->timeout = timeout + unet_gettime();
+       return host;
 }
 
 static void
@@ -812,6 +814,84 @@ network_pex_open_auth_connect(struct network *net)
 }
 
 
+static void
+__network_pex_reload_iface(struct network *net, struct blob_attr *data)
+{
+       static const struct blobmsg_policy policy[] = {
+               { "address", BLOBMSG_TYPE_STRING },
+               { "mask", BLOBMSG_TYPE_INT32 },
+       };
+       struct network_pex_host *host;
+       struct blob_attr *tb[2], *cur;
+       size_t rem;
+
+       if (!data)
+               return;
+
+       blobmsg_for_each_attr(cur, data, rem) {
+               union network_endpoint ep = {};
+               uint32_t mask;
+
+               blobmsg_parse_attr(policy, ARRAY_SIZE(policy), tb, cur);
+               if (!tb[0] || !tb[1])
+                       continue;
+
+               mask = blobmsg_get_u32(tb[1]);
+               if (mask >= 31 || !mask)
+                       continue;
+
+               if (network_get_endpoint(&ep, AF_INET, blobmsg_get_string(tb[0]),
+                                        UNETD_GLOBAL_PEX_PORT, 0) < 0)
+                       continue;
+
+               *(uint32_t *)&ep.in.sin_addr |= htonl((~0U) >> mask);
+               host = network_pex_create_host(net, &ep, 0);
+               host->interface = true;
+       }
+}
+
+static void
+__network_pex_reload(struct network *net)
+{
+       struct network_pex *pex = &net->pex;
+       struct network_pex_host *host, *tmp;
+       struct blob_attr *cur;
+       size_t rem;
+
+       if (!net->config.local_network)
+               return;
+
+       list_for_each_entry_safe(host, tmp, &pex->hosts, list)
+               if (host->interface)
+                       network_pex_free_host(net, host);
+
+       blobmsg_for_each_attr(cur, net->config.local_network, rem) {
+               const char *name = blobmsg_get_string(cur);
+               struct blob_attr *addrs;
+
+               addrs = unetd_ubus_get_network_addr_list(name);
+               __network_pex_reload_iface(net, addrs);
+       }
+}
+
+static void
+network_pex_reload_cb(struct uloop_timeout *t)
+{
+       struct network *net;
+
+       avl_for_each_element(&networks, net, node)
+               __network_pex_reload(net);
+}
+
+void network_pex_reload(void)
+{
+       static struct uloop_timeout timer = {
+               .cb = network_pex_reload_cb,
+       };
+
+       uloop_timeout_set(&timer, 1);
+}
+
 int network_pex_open(struct network *net)
 {
        struct network_host *local_host = net->net_config.local_host;
@@ -822,6 +902,7 @@ int network_pex_open(struct network *net)
        int fd;
 
        network_pex_open_auth_connect(net);
+       __network_pex_reload(net);
 
        if (!local_host || !local_host->peer.pex_port)
                return 0;
diff --git a/pex.h b/pex.h
index 271d998bc62106782f27e99307d020f9d2d746fa..f0173a65ca3a32a729e290053e816560263e91b3 100644 (file)
--- a/pex.h
+++ b/pex.h
@@ -18,6 +18,7 @@ struct network_pex_host {
        uint64_t timeout;
        uint64_t last_active;
        uint64_t last_ping;
+       bool interface;
        union network_endpoint endpoint;
 };
 
@@ -76,11 +77,13 @@ void network_pex_init(struct network *net);
 int network_pex_open(struct network *net);
 void network_pex_close(struct network *net);
 void network_pex_free(struct network *net);
+void network_pex_reload();
 
 void network_pex_event(struct network *net, struct network_peer *peer,
                       enum pex_event ev);
-void network_pex_create_host(struct network *net, union network_endpoint *ep,
-                            unsigned int timeout);
+struct network_pex_host *
+network_pex_create_host(struct network *net, union network_endpoint *ep,
+                       unsigned int timeout);
 
 void network_stun_init(struct network *net);
 void network_stun_free(struct network *net);
diff --git a/ubus.c b/ubus.c
index 2377acc2ef553667d49f43200359a3c526284433..6e289da5c152e7e88eb8733ddd64d46bbfe4bd88 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -8,6 +8,7 @@
 #include "enroll.h"
 
 static struct ubus_auto_conn conn;
+static struct ubus_subscriber sub;
 static struct blob_buf b;
 
 static int
@@ -446,6 +447,7 @@ ubus_connect_handler(struct ubus_context *ctx)
 {
        int ret;
 
+       ubus_register_subscriber(ctx, &sub);
        ret = ubus_add_object(ctx, &unetd_object);
        if (ret)
                fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
@@ -514,6 +516,47 @@ void unetd_ubus_netifd_update(struct blob_attr *data)
        ubus_invoke(&conn.ctx, id, "notify_proto", data, NULL, NULL, 5000);
 }
 
+static void
+ubus_network_status_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+       static const struct blobmsg_policy policy =
+               { "ipv4-address", BLOBMSG_TYPE_ARRAY };
+       struct blob_attr *attr, *cur;
+       size_t rem;
+
+       blobmsg_parse_attr(&policy, 1, &attr, msg);
+       if (!attr)
+               return;
+
+       if (blobmsg_check_array(attr, BLOBMSG_TYPE_TABLE) < 0)
+               return;
+
+       blobmsg_for_each_attr(cur, attr, rem)
+               blobmsg_add_blob(&b, cur);
+}
+
+struct blob_attr *unetd_ubus_get_network_addr_list(const char *name)
+{
+       char *objname;
+       uint32_t id;
+       size_t len;
+
+       if (strlen(name) > 64)
+               return NULL;
+
+       len = sizeof("network.interface.") + strlen(name) + 1;
+       objname = alloca(len);
+       snprintf(objname, len, "network.interface.%s", name);
+
+       if (ubus_lookup_id(&conn.ctx, objname, &id))
+               return NULL;
+
+       blob_buf_init(&b, 0);
+       ubus_invoke(&conn.ctx, id, "status", b.head, ubus_network_status_cb, NULL, 10000);
+
+       return b.head;
+}
+
 void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep)
 {
        uint32_t id;
@@ -543,8 +586,25 @@ void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep
        ubus_invoke(&conn.ctx, id, "add_host_route", b.head, NULL, NULL, -1);
 }
 
+static int
+unetd_netifd_sub_cb(struct ubus_context *ctx, struct ubus_object *obj,
+                   struct ubus_request_data *req,
+                   const char *method, struct blob_attr *msg)
+{
+       network_pex_reload();
+       return 0;
+}
+
+static bool
+unetd_new_object_sub_cb(struct ubus_context *ctx, struct ubus_subscriber *sub, const char *path)
+{
+       return path && !strcmp(path, "network.interface");
+}
+
 void unetd_ubus_init(void)
 {
+       sub.cb = unetd_netifd_sub_cb;
+       sub.new_obj_cb = unetd_new_object_sub_cb;
        conn.cb = ubus_connect_handler;
        ubus_auto_connect(&conn);
 }
diff --git a/ubus.h b/ubus.h
index 1c428948020d21fa07665658c5b5ac42c1602126..90a6e7658413485fc3ec68bae34dba88b75a53c7 100644 (file)
--- a/ubus.h
+++ b/ubus.h
@@ -11,6 +11,7 @@ void unetd_ubus_notify(const char *type, struct blob_attr *data);
 void unetd_ubus_network_notify(struct network *net);
 void unetd_ubus_netifd_update(struct blob_attr *data);
 void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep);
+struct blob_attr *unetd_ubus_get_network_addr_list(const char *name);
 #else
 static inline void unetd_ubus_init(void)
 {
@@ -27,6 +28,10 @@ static inline void unetd_ubus_netifd_update(struct blob_attr *data)
 static inline void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep)
 {
 }
+static inline struct blob_attr *unetd_ubus_get_network_addr_list(const char *name)
+{
+       return NULL;
+}
 #endif
 
 #endif