From 26dc52789d41be50c7ea653aa64f55d7582f5b89 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 1 Sep 2022 19:42:10 +0200 Subject: [PATCH] network: add support for configuring extra peers via a separate json file 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 --- examples/net0.peers | 8 ++ examples/test-net0.sh | 1 + host.c | 179 ++++++++++++++++++++++++++++++------------ host.h | 9 ++- network.c | 5 ++ network.h | 4 + pex.c | 4 +- 7 files changed, 157 insertions(+), 53 deletions(-) create mode 100644 examples/net0.peers diff --git a/examples/net0.peers b/examples/net0.peers new file mode 100644 index 0000000..74cbff9 --- /dev/null +++ b/examples/net0.peers @@ -0,0 +1,8 @@ +{ + "test": { + "key": "uEXsoKYc31VH/kzv55eHVmKlzvmoGOPwA7xeTjGgLXc=", + "endpoint": "192.168.1.4:51830", + "ipaddr": [ "192.168.99.1" ] + } +} + diff --git a/examples/test-net0.sh b/examples/test-net0.sh index 4492899..fa3a37e 100755 --- a/examples/test-net0.sh +++ b/examples/test-net0.sh @@ -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 e7a8e83..71fc840 100644 --- a/host.c +++ b/host.c @@ -3,9 +3,11 @@ * Copyright (C) 2022 Felix Fietkau */ #include +#include #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 2f8f881..75fc949 100644 --- 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); diff --git a/network.c b/network.c index beb0230..48b0716 100644 --- 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; diff --git a/network.h b/network.h index 933921a..38bb8e2 100644 --- 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 5234e10..c15982d 100644 --- 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, -- 2.30.2