host: keep multiple endpoint candidates, one for each type
authorFelix Fietkau <nbd@nbd.name>
Fri, 16 Sep 2022 18:14:14 +0000 (20:14 +0200)
committerFelix Fietkau <nbd@nbd.name>
Fri, 16 Sep 2022 18:15:02 +0000 (20:15 +0200)
Some discovery methods might be more reliable than others. Avoid having
them overwrite each other's discovery result

Signed-off-by: Felix Fietkau <nbd@nbd.name>
host.c
host.h
pex.c

diff --git a/host.c b/host.c
index e0c5827b5578638c8a1cc7d25398817b10eacd87..aa3c5b2ffa165cf77329236ef9623fbb0d0a064e 100644 (file)
--- a/host.c
+++ b/host.c
@@ -360,6 +360,35 @@ void network_hosts_update_done(struct network *net)
        return __network_hosts_update_done(net, false);
 }
 
+static union network_endpoint *
+network_peer_next_endpoint(struct network_peer *peer)
+{
+       union network_endpoint *ep;
+       int i;
+
+       for (i = 0; i < __ENDPOINT_TYPE_MAX; i++) {
+               int cur = peer->state.next_endpoint_idx;
+
+               if (++peer->state.next_endpoint_idx == __ENDPOINT_TYPE_MAX)
+                       peer->state.next_endpoint_idx = 0;
+
+               ep = &peer->state.next_endpoint[cur];
+               if (cur == ENDPOINT_TYPE_STATIC &&
+                       (!peer->endpoint ||
+                    network_get_endpoint(ep, AF_UNSPEC, peer->endpoint, peer->port,
+                                         peer->state.connect_attempt++)))
+                       continue;
+
+               if (!ep->sa.sa_family)
+                       continue;
+
+               return ep;
+       }
+
+       return NULL;
+}
+
+
 static void
 network_hosts_connect_cb(struct uloop_timeout *t)
 {
@@ -381,13 +410,8 @@ network_hosts_connect_cb(struct uloop_timeout *t)
                if (peer->state.connected)
                        continue;
 
-               ep = &peer->state.next_endpoint;
-               if (peer->endpoint &&
-                   network_get_endpoint(ep, AF_UNSPEC, peer->endpoint, peer->port,
-                                        peer->state.connect_attempt++))
-                       continue;
-
-               if (!ep->sa.sa_family)
+               ep = network_peer_next_endpoint(peer);
+               if (!ep)
                        continue;
 
                if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0)
diff --git a/host.h b/host.h
index 5eed71175784fb8729dd4763ad3046294d909edc..b3fef037e8596196839ed8eb28337ff440b1189a 100644 (file)
--- a/host.h
+++ b/host.h
@@ -5,6 +5,14 @@
 #ifndef __UNETD_HOST_H
 #define __UNETD_HOST_H
 
+enum peer_endpoint_type {
+       ENDPOINT_TYPE_STATIC,
+       ENDPOINT_TYPE_PEX,
+       ENDPOINT_TYPE_ENDPOINT_NOTIFY,
+       ENDPOINT_TYPE_ENDPOINT_PORT_NOTIFY,
+       __ENDPOINT_TYPE_MAX,
+};
+
 struct network_peer {
        struct vlist_node node;
        uint8_t key[CURVE25519_KEY_SIZE];
@@ -25,7 +33,8 @@ struct network_peer {
                union network_addr local_ep_addr;
                union network_endpoint endpoint;
 
-               union network_endpoint next_endpoint;
+               uint8_t next_endpoint_idx;
+               union network_endpoint next_endpoint[__ENDPOINT_TYPE_MAX];
                uint64_t last_ep_update;
 
                uint64_t rx_bytes;
diff --git a/pex.c b/pex.c
index 65cb1e55647d3a6e8929ba3d3ab7fb0187ccc172..b2285767f4f5dcda22d6d290a39b62ec726db447 100644 (file)
--- a/pex.c
+++ b/pex.c
@@ -485,7 +485,7 @@ network_pex_recv_peers(struct network *net, struct network_peer *peer,
                D_PEER(net, peer, "received peer address for %s",
                       network_peer_name(cur));
                flags = ntohs(data->flags);
-               ep = &cur->state.next_endpoint;
+               ep = &cur->state.next_endpoint[ENDPOINT_TYPE_PEX];
                ep->sa.sa_family = (flags & PEER_EP_F_IPV6) ? AF_INET6 : AF_INET;
                addr = network_endpoint_addr(ep, &len);
                memcpy(addr, data->addr, len);
@@ -904,6 +904,7 @@ global_pex_recv(void *msg, size_t msg_len, struct sockaddr_in6 *addr)
        char buf[INET6_ADDRSTRLEN];
        void *data;
        int addr_len;
+       int ep_idx = ENDPOINT_TYPE_ENDPOINT_NOTIFY;
 
        if (stun_msg_is_valid(msg, msg_len)) {
                avl_for_each_element(&networks, net, node)
@@ -949,6 +950,9 @@ global_pex_recv(void *msg, size_t msg_len, struct sockaddr_in6 *addr)
        case PEX_MSG_ENDPOINT_PORT_NOTIFY:
                if (hdr->len < sizeof(struct pex_endpoint_port_notify))
                        break;
+
+               ep_idx = ENDPOINT_TYPE_ENDPOINT_PORT_NOTIFY;
+               fallthrough;
        case PEX_MSG_ENDPOINT_NOTIFY:
                peer = pex_msg_peer(net, hdr->id);
                if (!peer)
@@ -958,14 +962,14 @@ global_pex_recv(void *msg, size_t msg_len, struct sockaddr_in6 *addr)
                  inet_ntop(addr->sin6_family, network_endpoint_addr((void *)addr, &addr_len),
                            buf, sizeof(buf)));
 
-               memcpy(&peer->state.next_endpoint, addr, sizeof(*addr));
+               memcpy(&peer->state.next_endpoint[ep_idx], addr, sizeof(*addr));
                if (hdr->opcode == PEX_MSG_ENDPOINT_PORT_NOTIFY) {
                        struct pex_endpoint_port_notify *port = data;
                        union network_endpoint host_ep = {
                                .in6 = *addr
                        };
 
-                       peer->state.next_endpoint.in.sin_port = port->port;
+                       peer->state.next_endpoint[ep_idx].in.sin_port = port->port;
                        if (net->pex.num_hosts < NETWORK_PEX_HOSTS_LIMIT)
                                network_pex_create_host(net, &host_ep, 120);
                }