network: add support for configuring extra peers via a separate json file
authorFelix Fietkau <nbd@nbd.name>
Thu, 1 Sep 2022 17:42:10 +0000 (19:42 +0200)
committerFelix Fietkau <nbd@nbd.name>
Thu, 1 Sep 2022 17:42:12 +0000 (19:42 +0200)
Peers added to this file are only used locally and not advertised on the
network. Peers should use IP addresses that are part of locally announced
or otherwise configured subnets

Signed-off-by: Felix Fietkau <nbd@nbd.name>
examples/net0.peers [new file with mode: 0644]
examples/test-net0.sh
host.c
host.h
network.c
network.h
pex.c

diff --git a/examples/net0.peers b/examples/net0.peers
new file mode 100644 (file)
index 0000000..74cbff9
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "test": {
+               "key": "uEXsoKYc31VH/kzv55eHVmKlzvmoGOPwA7xeTjGgLXc=",
+               "endpoint": "192.168.1.4:51830",
+               "ipaddr": [ "192.168.99.1" ]
+       }
+}
+
index 449289929bcbb93dca6c495bece544fb795d6d7c..fa3a37ea608815b247a5d062d291fb0f0299181a 100755 (executable)
@@ -15,5 +15,6 @@ ip link add dev $ifname type wireguard > /dev/null 2>&1
        "tunnels": {
                "vx0": "l2-tunnel"
        },
+       "peer_data": [ "'"$PWD/net0.peers"'" ],
        "update-cmd": "'"$PWD/../scripts/update-cmd.pl"'"
 }'
diff --git a/host.c b/host.c
index e7a8e83a0476b2fc0e1b35daaca884db17e20713..71fc8408ef3cfaa1a2064190f4e4e253fd390a52 100644 (file)
--- a/host.c
+++ b/host.c
@@ -3,9 +3,11 @@
  * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
  */
 #include <libubox/avl-cmp.h>
+#include <libubox/blobmsg_json.h>
 #include "unetd.h"
 
 static LIST_HEAD(old_hosts);
+static struct blob_buf b;
 
 static int avl_key_cmp(const void *k1, const void *k2, void *ptr)
 {
@@ -83,41 +85,43 @@ network_host_add_group(struct network *net, struct network_host *host,
        group->members[group->n_members - 1] = host;
 }
 
+enum {
+       NETWORK_HOST_KEY,
+       NETWORK_HOST_GROUPS,
+       NETWORK_HOST_IPADDR,
+       NETWORK_HOST_SUBNET,
+       NETWORK_HOST_PORT,
+       NETWORK_HOST_PEX_PORT,
+       NETWORK_HOST_ENDPOINT,
+       NETWORK_HOST_GATEWAY,
+       __NETWORK_HOST_MAX
+};
+
+static const struct blobmsg_policy host_policy[__NETWORK_HOST_MAX] = {
+       [NETWORK_HOST_KEY] = { "key", BLOBMSG_TYPE_STRING },
+       [NETWORK_HOST_GROUPS] = { "groups", BLOBMSG_TYPE_ARRAY },
+       [NETWORK_HOST_IPADDR] = { "ipaddr", BLOBMSG_TYPE_ARRAY },
+       [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY },
+       [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 },
+       [NETWORK_HOST_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 },
+       [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING },
+       [NETWORK_HOST_GATEWAY] = { "gateway", BLOBMSG_TYPE_STRING },
+};
+
 static void
-network_host_create(struct network *net, struct blob_attr *attr)
+network_host_create(struct network *net, struct blob_attr *attr, bool dynamic)
 {
-       enum {
-               NETWORK_HOST_KEY,
-               NETWORK_HOST_GROUPS,
-               NETWORK_HOST_IPADDR,
-               NETWORK_HOST_SUBNET,
-               NETWORK_HOST_PORT,
-               NETWORK_HOST_PEX_PORT,
-               NETWORK_HOST_ENDPOINT,
-               NETWORK_HOST_GATEWAY,
-               __NETWORK_HOST_MAX
-       };
-       static const struct blobmsg_policy policy[__NETWORK_HOST_MAX] = {
-               [NETWORK_HOST_KEY] = { "key", BLOBMSG_TYPE_STRING },
-               [NETWORK_HOST_GROUPS] = { "groups", BLOBMSG_TYPE_ARRAY },
-               [NETWORK_HOST_IPADDR] = { "ipaddr", BLOBMSG_TYPE_ARRAY },
-               [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY },
-               [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 },
-               [NETWORK_HOST_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 },
-               [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING },
-               [NETWORK_HOST_GATEWAY] = { "gateway", BLOBMSG_TYPE_STRING },
-       };
        struct blob_attr *tb[__NETWORK_HOST_MAX];
        struct blob_attr *cur, *ipaddr, *subnet;
        uint8_t key[CURVE25519_KEY_SIZE];
-       struct network_host *host;
+       struct network_host *host = NULL;
        struct network_peer *peer;
        int ipaddr_len, subnet_len;
-       const char *name, *endpoint, *gateway;
-       char *name_buf, *endpoint_buf, *gateway_buf;
+       const char *endpoint, *gateway;
+       char *endpoint_buf, *gateway_buf;
        int rem;
 
-       blobmsg_parse(policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
+       blobmsg_parse(host_policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
 
        if (!tb[NETWORK_HOST_KEY])
                return;
@@ -137,7 +141,7 @@ network_host_create(struct network *net, struct blob_attr *attr)
        else
                endpoint = NULL;
 
-       if ((cur = tb[NETWORK_HOST_GATEWAY]) != NULL)
+       if (!dynamic && (cur = tb[NETWORK_HOST_GATEWAY]) != NULL)
                gateway = blobmsg_get_string(cur);
        else
                gateway = NULL;
@@ -146,18 +150,41 @@ network_host_create(struct network *net, struct blob_attr *attr)
                       sizeof(key)) != sizeof(key))
                return;
 
-       name = blobmsg_name(attr);
-       host = avl_find_element(&net->hosts, name, host, node);
-       if (host)
-               return;
+       if (dynamic) {
+               struct network_dynamic_peer *dyn_peer;
+
+               /* don't override/alter hosts configured via network data */
+               peer = vlist_find(&net->peers, key, peer, node);
+               if (peer && !peer->dynamic &&
+                       peer->node.version == net->peers.version)
+                       return;
+
+               dyn_peer = calloc_a(sizeof(*dyn_peer),
+                               &ipaddr, ipaddr_len,
+                               &subnet, subnet_len,
+                               &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0);
+               list_add_tail(&dyn_peer->list, &net->dynamic_peers);
+               peer = &dyn_peer->peer;
+       } else {
+               const char *name;
+               char *name_buf;
+
+               name = blobmsg_name(attr);
+               host = avl_find_element(&net->hosts, name, host, node);
+               if (host)
+                       return;
 
-       host = calloc_a(sizeof(*host),
-                       &name_buf, strlen(name) + 1,
-                       &ipaddr, ipaddr_len,
-                       &subnet, subnet_len,
-                       &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0,
-                       &gateway_buf, gateway ? strlen(endpoint) + 1 : 0);
-       peer = &host->peer;
+               host = calloc_a(sizeof(*host),
+                               &name_buf, strlen(name) + 1,
+                               &ipaddr, ipaddr_len,
+                               &subnet, subnet_len,
+                               &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0,
+                               &gateway_buf, gateway ? strlen(endpoint) + 1 : 0);
+               host->node.key = strcpy(name_buf, name);
+               peer = &host->peer;
+       }
+
+       peer->dynamic = dynamic;
        if ((cur = tb[NETWORK_HOST_IPADDR]) != NULL && ipaddr_len)
                peer->ipaddr = memcpy(ipaddr, cur, ipaddr_len);
        if ((cur = tb[NETWORK_HOST_SUBNET]) != NULL && subnet_len)
@@ -172,16 +199,19 @@ network_host_create(struct network *net, struct blob_attr *attr)
                peer->pex_port = net->net_config.pex_port;
        if (endpoint)
                peer->endpoint = strcpy(endpoint_buf, endpoint);
-       if (gateway)
-               host->gateway = strcpy(gateway_buf, gateway);
        memcpy(peer->key, key, sizeof(key));
-       host->node.key = strcpy(name_buf, name);
 
        memcpy(&peer->local_addr.network_id,
                   &net->net_config.addr.network_id,
                   sizeof(peer->local_addr.network_id));
        network_fill_host_addr(&peer->local_addr, peer->key);
 
+       if (!host)
+               return;
+
+       if (gateway)
+               host->gateway = strcpy(gateway_buf, gateway);
+
        blobmsg_for_each_attr(cur, tb[NETWORK_HOST_GROUPS], rem) {
                if (!blobmsg_check_attr(cur, false) ||
                    blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
@@ -200,6 +230,36 @@ network_host_create(struct network *net, struct blob_attr *attr)
        }
 }
 
+static void
+network_hosts_load_dynamic_file(struct network *net, const char *file)
+{
+       struct blob_attr *cur;
+       int rem;
+
+       blob_buf_init(&b, 0);
+
+    if (!blobmsg_add_json_from_file(&b, file))
+               return;
+
+       blob_for_each_attr(cur, b.head, rem)
+               network_host_create(net, cur, true);
+}
+
+static void
+network_hosts_load_dynamic(struct network *net)
+{
+       struct blob_attr *cur;
+       int rem;
+
+       if (!net->config.peer_data)
+               return;
+
+       blobmsg_for_each_attr(cur, net->config.peer_data, rem)
+               network_hosts_load_dynamic_file(net, blobmsg_get_string(cur));
+
+       blob_buf_free(&b);
+}
+
 void network_hosts_update_start(struct network *net)
 {
        struct network_host *host, *htmp;
@@ -216,11 +276,18 @@ void network_hosts_update_start(struct network *net)
        vlist_update(&net->peers);
 }
 
-void network_hosts_update_done(struct network *net)
+static void
+__network_hosts_update_done(struct network *net, bool free_net)
 {
        struct network_host *local, *host, *tmp;
+       struct network_dynamic_peer *dyn, *dyn_tmp;
+       LIST_HEAD(old_dynamic);
        const char *local_name;
 
+       list_splice_init(&net->dynamic_peers, &old_dynamic);
+       if (free_net)
+               goto out;
+
        local = net->net_config.local_host;
        if (!local)
                goto out;
@@ -240,20 +307,36 @@ void network_hosts_update_done(struct network *net)
                vlist_add(&net->peers, &host->peer.node, host->peer.key);
        }
 
+       network_hosts_load_dynamic(net);
+
+       list_for_each_entry(dyn, &net->dynamic_peers, list)
+               vlist_add(&net->peers, &dyn->peer.node, &dyn->peer.key);
+
 out:
        vlist_flush(&net->peers);
 
+       list_for_each_entry_safe(dyn, dyn_tmp, &old_dynamic, list) {
+               list_del(&dyn->list);
+               free(dyn);
+       }
+
        list_for_each_entry_safe(host, tmp, &old_hosts, node.list) {
                list_del(&host->node.list);
                free(host);
        }
 }
 
+void network_hosts_update_done(struct network *net)
+{
+       return __network_hosts_update_done(net, false);
+}
+
 static void
 network_hosts_connect_cb(struct uloop_timeout *t)
 {
        struct network *net = container_of(t, struct network, connect_timer);
        struct network_host *host;
+       struct network_peer *peer;
        union network_endpoint *ep;
 
        avl_for_each_element(&net->hosts, host, node)
@@ -265,12 +348,7 @@ network_hosts_connect_cb(struct uloop_timeout *t)
 
        wg_peer_refresh(net);
 
-       avl_for_each_element(&net->hosts, host, node) {
-               struct network_peer *peer = &host->peer;
-
-               if (!network_host_is_peer(host))
-                       continue;
-
+       vlist_for_each_element(&net->peers, peer, node) {
                if (peer->state.connected)
                        continue;
 
@@ -300,11 +378,12 @@ void network_hosts_add(struct network *net, struct blob_attr *hosts)
        int rem;
 
        blobmsg_for_each_attr(cur, hosts, rem)
-               network_host_create(net, cur);
+               network_host_create(net, cur, false);
 }
 
 void network_hosts_init(struct network *net)
 {
+       INIT_LIST_HEAD(&net->dynamic_peers);
        avl_init(&net->hosts, avl_strcmp, false, NULL);
        vlist_init(&net->peers, avl_key_cmp, network_peer_update);
        avl_init(&net->groups, avl_strcmp, false, NULL);
@@ -315,5 +394,5 @@ void network_hosts_free(struct network *net)
 {
        uloop_timeout_cancel(&net->connect_timer);
        network_hosts_update_start(net);
-       network_hosts_update_done(net);
+       __network_hosts_update_done(net, true);
 }
diff --git a/host.h b/host.h
index 2f8f881d7e204fe91baf4640ef2f3193fd1db1b3..75fc9490ed534a75c9b1e3b170637254d1f9ee47 100644 (file)
--- a/host.h
+++ b/host.h
@@ -14,6 +14,7 @@ struct network_peer {
        struct blob_attr *subnet;
        int port;
        int pex_port;
+       bool dynamic;
 
        struct {
                int connect_attempt;
@@ -34,6 +35,12 @@ struct network_peer {
        } state;
 };
 
+struct network_dynamic_peer {
+       struct list_head list;
+
+       struct network_peer peer;
+};
+
 struct network_host {
        struct avl_node node;
 
@@ -66,7 +73,7 @@ static inline const char *network_peer_name(struct network_peer *peer)
 {
        struct network_host *host;
 
-       if (!peer)
+       if (!peer || peer->dynamic)
                return "(none)";
 
        host = container_of(peer, struct network_host, peer);
index beb023010fd9320047dbc2bdd6eaf6a18a533ad0..48b0716c04ca3156ed9564cff2945f034ee3fbba 100644 (file)
--- a/network.c
+++ b/network.c
@@ -55,6 +55,7 @@ const struct blobmsg_policy network_policy[__NETWORK_ATTR_MAX] = {
        [NETWORK_ATTR_UPDATE_CMD] = { "update-cmd", BLOBMSG_TYPE_STRING },
        [NETWORK_ATTR_TUNNELS] = { "tunnels", BLOBMSG_TYPE_TABLE },
        [NETWORK_ATTR_AUTH_CONNECT] = { "auth_connect", BLOBMSG_TYPE_ARRAY },
+       [NETWORK_ATTR_PEER_DATA] = { "peer_data", BLOBMSG_TYPE_ARRAY },
 };
 
 AVL_TREE(networks, avl_strcmp, false, NULL);
@@ -537,6 +538,10 @@ network_set_config(struct network *net, struct blob_attr *config)
            blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
                net->config.auth_connect = cur;
 
+       if ((cur = tb[NETWORK_ATTR_PEER_DATA]) != NULL &&
+           blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
+               net->config.peer_data = cur;
+
        if ((cur = tb[NETWORK_ATTR_KEY]) == NULL)
                goto invalid;
 
index 933921aaf3228e340a1370586aa5f8c3f56e134c..38bb8e26f4c05c5af75d147f294d117b31883baf 100644 (file)
--- a/network.h
+++ b/network.h
@@ -38,6 +38,7 @@ struct network {
                struct blob_attr *tunnels;
                struct blob_attr *net_data;
                struct blob_attr *auth_connect;
+               struct blob_attr *peer_data;
        } config;
 
        struct {
@@ -58,6 +59,8 @@ struct network {
 
        int ifindex;
        struct network_host *prev_local_host;
+
+       struct list_head dynamic_peers;
        struct avl_tree hosts;
        struct vlist_tree peers;
 
@@ -82,6 +85,7 @@ enum {
        NETWORK_ATTR_DOMAIN,
        NETWORK_ATTR_TUNNELS,
        NETWORK_ATTR_AUTH_CONNECT,
+       NETWORK_ATTR_PEER_DATA,
        __NETWORK_ATTR_MAX,
 };
 
diff --git a/pex.c b/pex.c
index 5234e10509e5eaf5f64054dc52973ca838b70718..c15982de76d24e32e1cc15eaab56987390671185 100644 (file)
--- a/pex.c
+++ b/pex.c
@@ -578,7 +578,7 @@ network_pex_recv_update_response(struct network *net, const uint8_t *data, size_
 
        uloop_timeout_set(&net->reload_timer, no_prev_data ? 1 : UNETD_DATA_UPDATE_DELAY);
        vlist_for_each_element(&net->peers, peer, node) {
-               if (!peer->state.connected)
+               if (!peer->state.connected || !peer->pex_port)
                        continue;
                network_pex_send_update_request(net, peer, NULL);
        }
@@ -716,7 +716,7 @@ network_pex_open_auth_connect(struct network *net)
        vlist_for_each_element(&net->peers, peer, node) {
                union network_endpoint ep = {};
 
-               if (!peer->endpoint)
+               if (!peer->endpoint || peer->dynamic)
                        continue;
 
                if (network_get_endpoint(&ep, peer->endpoint,