From 412d03012f13ca1de7b16e2b73960df4cbb6279f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 17 Feb 2023 12:32:52 +0100 Subject: [PATCH] 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 --- host.c | 3 ++- network.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ network.h | 1 + 3 files changed, 81 insertions(+), 1 deletion(-) 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); -- 2.30.2