summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau2023-02-17 11:32:52 +0000
committerFelix Fietkau2023-02-17 12:47:11 +0000
commit412d03012f13ca1de7b16e2b73960df4cbb6279f (patch)
treedcd011a6bab7b5f0910024654774acf5f1627070
parent6c888f897862b152e2cfae735faafc9cdcb07336 (diff)
downloadunetd-412d03012f13ca1de7b16e2b73960df4cbb6279f.tar.gz
network: prevent adding endpoint routes for addresses on the network
Sometimes a peer might be reachable only over another peer. In that case PEX could announce an endpoint address already covered by the network routes. When connecting, asking netifd to route that address breaks access, since it's only reachable over unet. Detect this case and skip the netifd host route request. Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--host.c3
-rw-r--r--network.c78
-rw-r--r--network.h1
3 files changed, 81 insertions, 1 deletions
diff --git a/host.c b/host.c
index aa3c5b2..7522e8b 100644
--- a/host.c
+++ b/host.c
@@ -414,7 +414,8 @@ network_hosts_connect_cb(struct uloop_timeout *t)
if (!ep)
continue;
- if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0)
+ if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0 &&
+ !network_skip_endpoint_route(net, ep))
unetd_ubus_netifd_add_route(net, ep);
wg_peer_connect(net, peer, ep);
diff --git a/network.c b/network.c
index 4a17af6..0578ad0 100644
--- a/network.c
+++ b/network.c
@@ -363,6 +363,84 @@ network_fill_subnets(struct network *net, struct blob_buf *buf)
__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)
{
diff --git a/network.h b/network.h
index dc53bb1..bfd4c78 100644
--- a/network.h
+++ b/network.h
@@ -100,6 +100,7 @@ static inline const char *network_name(struct network *net)
return net->node.key;
}
+bool network_skip_endpoint_route(struct network *net, union network_endpoint *ep);
void network_fill_host_addr(union network_addr *addr, uint8_t *key);
int network_save_dynamic(struct network *net);
void network_soft_reload(struct network *net);