diff options
| author | Felix Fietkau | 2023-02-17 11:32:52 +0000 |
|---|---|---|
| committer | Felix Fietkau | 2023-02-17 12:47:11 +0000 |
| commit | 412d03012f13ca1de7b16e2b73960df4cbb6279f (patch) | |
| tree | dcd011a6bab7b5f0910024654774acf5f1627070 | |
| parent | 6c888f897862b152e2cfae735faafc9cdcb07336 (diff) | |
| download | unetd-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.c | 3 | ||||
| -rw-r--r-- | network.c | 78 | ||||
| -rw-r--r-- | network.h | 1 |
3 files changed, 81 insertions, 1 deletions
@@ -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); @@ -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) { @@ -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); |