network: prevent adding endpoint routes for addresses on the network
authorFelix Fietkau <nbd@nbd.name>
Fri, 17 Feb 2023 11:32:52 +0000 (12:32 +0100)
committerFelix Fietkau <nbd@nbd.name>
Fri, 17 Feb 2023 12:47:11 +0000 (13:47 +0100)
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>
host.c
network.c
network.h

diff --git a/host.c b/host.c
index aa3c5b2ffa165cf77329236ef9623fbb0d0a064e..7522e8b1fba8ee0140ff9d7ebb0914b98017114c 100644 (file)
--- 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);
index 4a17af62af0a8bfd4d100eebfb388848838e3644..0578ad0c70205c4824a978ed2bdf8746065c7dd8 100644 (file)
--- 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)
 {
index dc53bb1a84a41879855f1597892d04301125506d..bfd4c78d9b900c8811c9ff82ee7f408facd44ab4 100644 (file)
--- 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);