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,