unet-cli: strip initial newline in usage message
[project/unetd.git] / pex.c
diff --git a/pex.c b/pex.c
index 1f831a0e1da3bc27e71e406045f5d41588b4b762..854837c8285d91f92c57b4a45734764810a54fc1 100644 (file)
--- a/pex.c
+++ b/pex.c
@@ -39,7 +39,7 @@ pex_msg_init_ext(struct network *net, uint8_t opcode, bool ext)
 }
 
 static struct network_peer *
-pex_msg_peer(struct network *net, const uint8_t *id)
+pex_msg_peer(struct network *net, const uint8_t *id, bool allow_indirect)
 {
        struct network_peer *peer;
        uint8_t key[WG_KEY_LEN] = {};
@@ -50,6 +50,8 @@ pex_msg_peer(struct network *net, const uint8_t *id)
                D_NET(net, "can't find peer %s", pex_peer_id_str(id));
                return NULL;
        }
+       if (peer->indirect && !allow_indirect)
+               return NULL;
 
        return peer;
 }
@@ -154,7 +156,7 @@ network_pex_handle_endpoint_change(struct network *net, struct network_peer *pee
        struct network_peer *cur;
 
        vlist_for_each_element(&net->peers, cur, node) {
-               if (cur == peer || !cur->state.connected)
+               if (cur == peer || !cur->state.connected || cur->indirect)
                        continue;
 
                pex_msg_init(net, PEX_MSG_NOTIFY_PEERS);
@@ -166,7 +168,7 @@ network_pex_handle_endpoint_change(struct network *net, struct network_peer *pee
 }
 
 static void
-network_pex_host_request_update(struct network *net, struct network_pex_host *host)
+network_pex_host_send_endpoint_notify(struct network *net, struct network_pex_host *host)
 {
        union {
                struct {
@@ -179,32 +181,10 @@ network_pex_host_request_update(struct network *net, struct network_pex_host *ho
                } ipv6;
        } packet = {};
        struct udphdr *udp;
-       char addrstr[INET6_ADDRSTRLEN];
        union network_endpoint dest_ep;
        union network_addr local_addr = {};
-       uint64_t version = 0;
        int len;
 
-       if (net->net_data_len)
-               version = net->net_data_version;
-
-       D("request network data from host %s",
-         inet_ntop(host->endpoint.sa.sa_family,
-                   (host->endpoint.sa.sa_family == AF_INET6 ?
-                    (const void *)&host->endpoint.in6.sin6_addr :
-                    (const void *)&host->endpoint.in.sin_addr),
-                   addrstr, sizeof(addrstr)));
-
-       if (!pex_msg_update_request_init(net->config.pubkey, net->config.key,
-                                        net->config.auth_key, &host->endpoint,
-                                        version, true))
-               return;
-
-       __pex_msg_send(-1, &host->endpoint, NULL, 0);
-
-       if (!net->net_config.local_host)
-               return;
-
        pex_msg_init_ext(net, PEX_MSG_ENDPOINT_NOTIFY, true);
 
        memcpy(&dest_ep, &host->endpoint, sizeof(dest_ep));
@@ -252,28 +232,90 @@ network_pex_host_request_update(struct network *net, struct network_pex_host *ho
                D_NET(net, "pex_msg_send_raw failed: %s", strerror(errno));
 }
 
+
+static void
+network_pex_host_send_port_notify(struct network *net, struct network_pex_host *host)
+{
+       struct pex_endpoint_port_notify *data;
+
+       if (!net->stun.port_ext)
+               return;
+
+       pex_msg_init_ext(net, PEX_MSG_ENDPOINT_PORT_NOTIFY, true);
+
+       data = pex_msg_append(sizeof(*data));
+       data->port = htons(net->stun.port_ext);
+
+       __pex_msg_send(-1, &host->endpoint, NULL, 0);
+}
+
+static void
+network_pex_host_request_update(struct network *net, struct network_pex_host *host)
+{
+       char addrstr[INET6_ADDRSTRLEN];
+       uint64_t version = 0;
+
+       host->last_ping = unet_gettime();
+
+       if (net->net_data_len)
+               version = net->net_data_version;
+
+       D("request network data from host %s",
+         inet_ntop(host->endpoint.sa.sa_family,
+                   (host->endpoint.sa.sa_family == AF_INET6 ?
+                    (const void *)&host->endpoint.in6.sin6_addr :
+                    (const void *)&host->endpoint.in.sin_addr),
+                   addrstr, sizeof(addrstr)));
+
+       if (!pex_msg_update_request_init(net->config.pubkey, net->config.key,
+                                        net->config.auth_key, &host->endpoint,
+                                        version, true))
+               return;
+
+       __pex_msg_send(-1, &host->endpoint, NULL, 0);
+
+       if (!net->net_config.local_host)
+               return;
+
+       network_pex_host_send_port_notify(net, host);
+       network_pex_host_send_endpoint_notify(net, host);
+}
+
+static void
+network_pex_free_host(struct network *net, struct network_pex_host *host)
+{
+       struct network_pex *pex = &net->pex;
+
+       pex->num_hosts--;
+       list_del(&host->list);
+       free(host);
+}
+
 static void
 network_pex_request_update_cb(struct uloop_timeout *t)
 {
        struct network *net = container_of(t, struct network, pex.request_update_timer);
        struct network_pex *pex = &net->pex;
-       struct network_pex_host *host;
+       struct network_pex_host *host, *tmp;
+       uint64_t now = unet_gettime();
 
-       uloop_timeout_set(t, 5000);
+       uloop_timeout_set(t, 500);
 
-retry:
        if (list_empty(&pex->hosts))
                return;
 
-       host = list_first_entry(&pex->hosts, struct network_pex_host, list);
-       if (host->timeout && host->timeout < unet_gettime()) {
-               list_del(&host->list);
-               free(host);
-               goto retry;
-       }
+       list_for_each_entry_safe(host, tmp, &pex->hosts, list) {
+               if (host->timeout && host->timeout < now) {
+                       network_pex_free_host(net, host);
+                       continue;
+               }
 
-       list_move_tail(&host->list, &pex->hosts);
-       network_pex_host_request_update(net, host);
+               if (host->last_ping + 10 >= now)
+                       continue;
+
+               list_move_tail(&host->list, &pex->hosts);
+               network_pex_host_request_update(net, host);
+       }
 }
 
 void network_pex_init(struct network *net)
@@ -290,6 +332,7 @@ static void
 network_pex_query_hosts(struct network *net)
 {
        struct network_host *host;
+       uint64_t now;
        int rv = rand();
        int hosts = 0;
        int i;
@@ -300,9 +343,8 @@ network_pex_query_hosts(struct network *net)
                struct network_peer *peer = &host->peer;
                void *id;
 
-               if (host == net->net_config.local_host ||
-                   peer->state.connected ||
-                   peer->endpoint)
+               if ((net->stun.port_ext && host == net->net_config.local_host) ||
+                   peer->state.connected || peer->endpoint || host->gateway)
                        continue;
 
                id = pex_msg_append(PEX_ID_LEN);
@@ -316,6 +358,7 @@ network_pex_query_hosts(struct network *net)
        if (!hosts)
                return;
 
+       now = unet_gettime();
        rv %= net->hosts.count;
        for (i = 0; i < 2; i++) {
                avl_for_each_element(&net->hosts, host, node) {
@@ -329,11 +372,13 @@ network_pex_query_hosts(struct network *net)
                        if (host == net->net_config.local_host)
                                continue;
 
-                       if (!peer->state.connected)
+                       if (!peer->state.connected ||
+                           peer->state.last_query_sent + 15 >= now)
                                continue;
 
                        D_PEER(net, peer, "send query for %d hosts", hosts);
                        pex_msg_send(net, peer);
+                       peer->state.last_query_sent = now;
                        return;
                }
        }
@@ -382,6 +427,7 @@ void network_pex_event(struct network *net, struct network_peer *peer,
 
        switch (ev) {
        case PEX_EV_HANDSHAKE:
+               peer->state.last_query_sent = 0;
                pex_send_hello(net, peer);
                if (net->config.type == NETWORK_TYPE_DYNAMIC)
                        network_pex_send_update_request(net, peer, NULL);
@@ -434,17 +480,19 @@ network_pex_recv_peers(struct network *net, struct network_peer *peer,
                void *addr;
                int len;
 
-               cur = pex_msg_peer(net, data->peer_id);
-               if (!cur)
+               if (!memcmp(data->peer_id, &local->key, PEX_ID_LEN)) {
+                       network_stun_update_port(net, false, ntohs(data->port));
                        continue;
+               }
 
-               if (cur == peer || cur == local)
+               cur = pex_msg_peer(net, data->peer_id, false);
+               if (!cur || cur == peer)
                        continue;
 
                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);
@@ -461,10 +509,16 @@ network_pex_recv_query(struct network *net, struct network_peer *peer,
 
        pex_msg_init(net, PEX_MSG_NOTIFY_PEERS);
        for (; len >= 8; data += 8, len -= 8) {
-               cur = pex_msg_peer(net, data);
+               struct network_host *host;
+
+               cur = pex_msg_peer(net, data, false);
                if (!cur || !cur->state.connected)
                        continue;
 
+               host = container_of(peer, struct network_host, peer);
+               if (host->gateway)
+                       continue;
+
                if (!pex_msg_add_peer_endpoint(net, cur, peer))
                        resp++;
        }
@@ -495,6 +549,7 @@ network_pex_recv_update_request(struct network *net, struct network_peer *peer,
                                struct sockaddr_in6 *addr)
 {
        struct pex_update_request *req = (struct pex_update_request *)data;
+       struct pex_endpoint_port_notify *port_data;
        struct pex_msg_update_send_ctx ctx = {};
        uint64_t req_version = be64_to_cpu(req->cur_version);
        int *query_count;
@@ -533,7 +588,7 @@ network_pex_recv_update_request(struct network *net, struct network_peer *peer,
                return;
 
        if (req_version >= net->net_data_version)
-               return;
+               goto out;
 
        pex_msg_update_response_init(&ctx, net->config.pubkey, net->config.auth_key,
                                     peer->key, !!addr, (void *)data,
@@ -542,6 +597,20 @@ network_pex_recv_update_request(struct network *net, struct network_peer *peer,
                pex_msg_send_ext(net, peer, addr);
                done = !pex_msg_update_response_continue(&ctx);
        }
+
+out:
+       if (peer->state.connected || !net->net_config.local_host)
+               return;
+
+       pex_msg_init_ext(net, PEX_MSG_ENDPOINT_PORT_NOTIFY, !!addr);
+
+       port_data = pex_msg_append(sizeof(*port_data));
+       if (net->stun.port_ext)
+               port_data->port = htons(net->stun.port_ext);
+       else
+               port_data->port = htons(net->net_config.local_host->peer.port);
+
+       pex_msg_send_ext(net, peer, addr);
 }
 
 static void
@@ -656,7 +725,7 @@ network_pex_fd_cb(struct uloop_fd *fd, unsigned int events)
                if (!hdr)
                        continue;
 
-               peer = pex_msg_peer(net, hdr->id);
+               peer = pex_msg_peer(net, hdr->id, false);
                if (!peer)
                        continue;
 
@@ -675,13 +744,17 @@ void network_pex_create_host(struct network *net, union network_endpoint *ep,
 {
        struct network_pex *pex = &net->pex;
        struct network_pex_host *host;
+       uint64_t now = unet_gettime();
        bool new_host = false;
 
        list_for_each_entry(host, &pex->hosts, list) {
                if (memcmp(&host->endpoint, ep, sizeof(host->endpoint)) != 0)
                        continue;
 
-               list_move_tail(&host->list, &pex->hosts);
+               if (host->last_ping + 10 < now) {
+                       list_move_tail(&host->list, &pex->hosts);
+                       network_pex_host_request_update(net, host);
+               }
                goto out;
        }
 
@@ -689,11 +762,11 @@ void network_pex_create_host(struct network *net, union network_endpoint *ep,
        new_host = true;
        memcpy(&host->endpoint, ep, sizeof(host->endpoint));
        list_add_tail(&host->list, &pex->hosts);
+       pex->num_hosts++;
 
 out:
        if (timeout && (new_host || host->timeout))
                host->timeout = timeout + unet_gettime();
-       network_pex_host_request_update(net, host);
 }
 
 static void
@@ -715,7 +788,7 @@ network_pex_open_auth_connect(struct network *net)
                if (!peer->endpoint || peer->dynamic)
                        continue;
 
-               if (network_get_endpoint(&ep, peer->endpoint,
+               if (network_get_endpoint(&ep, AF_UNSPEC, peer->endpoint,
                                         UNETD_GLOBAL_PEX_PORT, 0) < 0)
                        continue;
 
@@ -729,7 +802,7 @@ network_pex_open_auth_connect(struct network *net)
        blobmsg_for_each_attr(cur, net->config.auth_connect, rem) {
                union network_endpoint ep = {};
 
-               if (network_get_endpoint(&ep, blobmsg_get_string(cur),
+               if (network_get_endpoint(&ep, AF_UNSPEC, blobmsg_get_string(cur),
                                         UNETD_GLOBAL_PEX_PORT, 0) < 0)
                        continue;
 
@@ -802,8 +875,7 @@ void network_pex_close(struct network *net)
                if (host->last_active + UNETD_PEX_HOST_ACITVE_TIMEOUT >= now)
                        continue;
 
-               list_del(&host->list);
-               free(host);
+               network_pex_free_host(net, host);
        }
 
        if (pex->fd.fd < 0)
@@ -819,10 +891,8 @@ void network_pex_free(struct network *net)
        struct network_pex *pex = &net->pex;
        struct network_pex_host *host, *tmp;
 
-       list_for_each_entry_safe(host, tmp, &pex->hosts, list) {
-               list_del(&host->list);
-               free(host);
-       }
+       list_for_each_entry_safe(host, tmp, &pex->hosts, list)
+               network_pex_free_host(net, host);
 }
 
 static struct network *
@@ -862,6 +932,12 @@ 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)
+                       network_stun_rx_packet(net, msg, msg_len);
+       }
 
        hdr = pex_rx_accept(msg, msg_len, true);
        if (!hdr)
@@ -890,7 +966,7 @@ global_pex_recv(void *msg, size_t msg_len, struct sockaddr_in6 *addr)
        case PEX_MSG_PONG:
                break;
        case PEX_MSG_UPDATE_REQUEST:
-               peer = pex_msg_peer(net, hdr->id);
+               peer = pex_msg_peer(net, hdr->id, true);
                network_pex_recv_update_request(net, peer, data, hdr->len,
                                                addr);
                break;
@@ -899,8 +975,14 @@ global_pex_recv(void *msg, size_t msg_len, struct sockaddr_in6 *addr)
        case PEX_MSG_UPDATE_RESPONSE_NO_DATA:
                network_pex_recv_update_response(net, data, hdr->len, addr, hdr->opcode);
                break;
+       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);
+               peer = pex_msg_peer(net, hdr->id, true);
                if (!peer)
                        break;
 
@@ -908,7 +990,17 @@ 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[ep_idx].in.sin_port = port->port;
+                       if (net->pex.num_hosts < NETWORK_PEX_HOSTS_LIMIT)
+                               network_pex_create_host(net, &host_ep, 120);
+               }
                break;
        }
 }