/*
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
*/
+#define _GNU_SOURCE
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
NETCONF_ATTR_PORT,
NETCONF_ATTR_PEX_PORT,
NETCONF_ATTR_KEEPALIVE,
+ NETCONF_ATTR_STUN_SERVERS,
__NETCONF_ATTR_MAX
};
[NETCONF_ATTR_PORT] = { "port", BLOBMSG_TYPE_INT32 },
[NETCONF_ATTR_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 },
[NETCONF_ATTR_KEEPALIVE] = { "keepalive", BLOBMSG_TYPE_INT32 },
+ [NETCONF_ATTR_STUN_SERVERS] = { "stun-servers", BLOBMSG_TYPE_ARRAY },
};
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);
static struct blob_buf b;
+static void network_load_stun_servers(struct network *net, struct blob_attr *data)
+{
+ struct blob_attr *cur;
+ int rem;
+
+ blobmsg_for_each_attr(cur, data, rem)
+ network_stun_server_add(net, blobmsg_get_string(cur));
+}
+
static void network_load_config_data(struct network *net, struct blob_attr *data)
{
struct blob_attr *tb[__NETCONF_ATTR_MAX];
net->net_config.keepalive = blobmsg_get_u32(cur);
else
net->net_config.keepalive = 0;
+
+ if ((cur = tb[NETCONF_ATTR_STUN_SERVERS]) != NULL &&
+ blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
+ network_load_stun_servers(net, cur);
}
static int network_load_data(struct network *net, struct blob_attr *data)
{
struct blob_attr *tb[__NETDATA_ATTR_MAX];
+ siphash_key_t key = {};
+ net->net_config.hash = siphash(data, blob_raw_len(data), &key);
blobmsg_parse(netdata_policy, __NETDATA_ATTR_MAX, tb,
blobmsg_data(data), blobmsg_len(data));
FILE *f = NULL;
int ret = -1;
- asprintf(&fname, "%s/%s.bin", data_dir, network_name(net));
+ if (asprintf(&fname, "%s/%s.bin", data_dir, network_name(net)) < 0)
+ return -1;
+
f = fopen(fname, "r");
free(fname);
!net->net_data_len)
return -1;
- asprintf(&fname, "%s/%s.bin.XXXXXXXX", data_dir, network_name(net));
+ if (asprintf(&fname, "%s/%s.bin.XXXXXXXX", data_dir, network_name(net)) < 0)
+ return -1;
+
fd = mkstemp(fname);
if (fd < 0)
goto error;
__network_fill_subnets(net, buf, true);
}
+static bool
+__network_skip_endpoint_route(struct network *net, struct network_host *host,
+ union network_endpoint *ep)
+{
+ bool ipv6 = ep->sa.sa_family == AF_INET6;
+ uint32_t *subnet32, *addr32, mask32;
+ union network_addr addr = {};
+ struct blob_attr *cur;
+ int mask, rem;
+
+ blobmsg_for_each_attr(cur, host->peer.ipaddr, rem) {
+ const char *str = blobmsg_get_string(cur);
+
+ if (!!strchr(str, ':') != ipv6)
+ continue;
+
+ if (inet_pton(ep->sa.sa_family, str, &addr) != 1)
+ continue;
+
+ if (ipv6) {
+ if (!memcmp(&addr.in6, &ep->in6.sin6_addr, sizeof(addr.in6)))
+ return true;
+ } else {
+ if (!memcmp(&addr.in, &ep->in.sin_addr, sizeof(addr.in)))
+ return true;
+ }
+ }
+
+ if (ipv6)
+ addr32 = (uint32_t *)&ep->in6.sin6_addr;
+ else
+ addr32 = (uint32_t *)&ep->in.sin_addr;
+
+ subnet32 = (uint32_t *)&addr;
+ blobmsg_for_each_attr(cur, host->peer.subnet, rem) {
+ const char *str = blobmsg_get_string(cur);
+ int i;
+
+ if (!!strchr(str, ':') != ipv6)
+ continue;
+
+ if (network_get_subnet(ep->sa.sa_family, &addr, &mask, str))
+ continue;
+
+ if (mask <= 1)
+ continue;
+
+ for (i = 0; i < (ipv6 ? 4 : 1); i++) {
+ int cur_mask = mask > 32 ? 32 : mask;
+
+ if (mask > 32)
+ mask -= 32;
+ else
+ mask = 0;
+
+ mask32 = ~0ULL << (32 - cur_mask);
+ if (ntohl(subnet32[i] ^ addr32[i]) & mask32)
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool network_skip_endpoint_route(struct network *net, union network_endpoint *ep)
+{
+ struct network_host *host;
+
+ avl_for_each_element(&net->hosts, host, node)
+ if (__network_skip_endpoint_route(net, host, ep))
+ return true;
+
+ return false;
+}
+
+
static void
network_do_update(struct network *net, bool up)
{
memset(&net->net_config, 0, sizeof(net->net_config));
+ network_stun_free(net);
network_pex_close(net);
network_services_free(net);
network_hosts_update_start(net);
unetd_write_hosts();
network_do_update(net, true);
network_pex_open(net);
+ network_stun_start(net);
+ unetd_ubus_notify(net);
+}
+
+void network_soft_reload(struct network *net)
+{
+ siphash_key_t key = {};
+ uint64_t hash;
+
+ if (net->config.type == NETWORK_TYPE_FILE) {
+ blob_buf_init(&b, 0);
+
+ if (!blobmsg_add_json_from_file(&b, net->config.file))
+ return;
+
+ hash = siphash(b.head, blob_raw_len(b.head), &key);
+ if (hash != net->net_config.hash) {
+ uloop_timeout_set(&net->reload_timer, 1);
+ return;
+ }
+ }
+
+ network_hosts_reload_dynamic_peers(net);
}
static int network_setup(struct network *net)
static void network_teardown(struct network *net)
{
+ uloop_timeout_cancel(&net->connect_timer);
uloop_timeout_cancel(&net->reload_timer);
network_do_update(net, false);
+ network_stun_free(net);
network_pex_close(net);
+ network_pex_free(net);
network_hosts_free(net);
network_services_free(net);
wg_cleanup_network(net);
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;
avl_insert(&networks, &net->node);
network_pex_init(net);
+ network_stun_init(net);
network_hosts_init(net);
network_services_init(net);