}
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] = {};
D_NET(net, "can't find peer %s", pex_peer_id_str(id));
return NULL;
}
+ if (peer->indirect && !allow_indirect)
+ return NULL;
return peer;
}
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);
char addrstr[INET6_ADDRSTRLEN];
uint64_t version = 0;
+ host->last_ping = unet_gettime();
+
if (net->net_data_len)
version = net->net_data_version;
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)
network_pex_query_hosts(struct network *net)
{
struct network_host *host;
+ uint64_t now;
int rv = rand();
int hosts = 0;
int i;
void *id;
if ((net->stun.port_ext && host == net->net_config.local_host) ||
- peer->state.connected || peer->endpoint)
+ peer->state.connected || peer->endpoint || host->gateway)
continue;
id = pex_msg_append(PEX_ID_LEN);
if (!hosts)
return;
+ now = unet_gettime();
rv %= net->hosts.count;
for (i = 0; i < 2; i++) {
avl_for_each_element(&net->hosts, host, node) {
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;
}
}
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);
continue;
}
- cur = pex_msg_peer(net, data->peer_id);
+ 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);
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++;
}
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;
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,
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
if (!hdr)
continue;
- peer = pex_msg_peer(net, hdr->id);
+ peer = pex_msg_peer(net, hdr->id, false);
if (!peer)
continue;
{
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;
}
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
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)
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 *
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)
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;
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;
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);
}
break;
}