add support for disabling VXLAN/eBPF support
[project/unetd.git] / pex.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <arpa/inet.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <inttypes.h>
11 #include "unetd.h"
12 #include "pex-msg.h"
13
14 static const char *pex_peer_id_str(const uint8_t *key)
15 {
16 static char str[20];
17 int i;
18
19 for (i = 0; i < 8; i++)
20 sprintf(str + i * 2, "%02x", key[i]);
21
22 return str;
23 }
24
25 static struct pex_hdr *
26 pex_msg_init(struct network *net, uint8_t opcode)
27 {
28 return __pex_msg_init(net->config.pubkey, opcode);
29 }
30
31 static struct pex_hdr *
32 pex_msg_init_ext(struct network *net, uint8_t opcode, bool ext)
33 {
34 return __pex_msg_init_ext(net->config.pubkey, net->config.auth_key, opcode, ext);
35 }
36
37 static struct network_peer *
38 pex_msg_peer(struct network *net, const uint8_t *id)
39 {
40 struct network_peer *peer;
41 uint8_t key[WG_KEY_LEN] = {};
42
43 memcpy(key, id, PEX_ID_LEN);
44 peer = avl_find_ge_element(&net->peers.avl, key, peer, node.avl);
45 if (!peer || memcmp(peer->key, key, PEX_ID_LEN) != 0) {
46 D_NET(net, "can't find peer %s", pex_peer_id_str(id));
47 return NULL;
48 }
49
50 return peer;
51 }
52
53 static void
54 pex_get_peer_addr(struct sockaddr_in6 *sin6, struct network *net,
55 struct network_peer *peer)
56 {
57 *sin6 = (struct sockaddr_in6){
58 .sin6_family = AF_INET6,
59 .sin6_addr = peer->local_addr.in6,
60 .sin6_port = htons(peer->pex_port),
61 };
62 }
63
64 static void pex_msg_send(struct network *net, struct network_peer *peer)
65 {
66 struct sockaddr_in6 sin6 = {};
67
68 if (!peer || peer == &net->net_config.local_host->peer ||
69 !peer->pex_port)
70 return;
71
72 pex_get_peer_addr(&sin6, net, peer);
73 if (__pex_msg_send(net->pex.fd.fd, &sin6) < 0)
74 D_PEER(net, peer, "pex_msg_send failed: %s", strerror(errno));
75 }
76
77 static void pex_msg_send_ext(struct network *net, struct network_peer *peer,
78 struct sockaddr_in6 *addr)
79 {
80 char addrbuf[INET6_ADDRSTRLEN];
81
82 if (!addr)
83 return pex_msg_send(net, peer);
84
85 if (__pex_msg_send(-1, addr) < 0)
86 D_NET(net, "pex_msg_send_ext(%s) failed: %s",
87 inet_ntop(addr->sin6_family, (const void *)&addr->sin6_addr, addrbuf,
88 sizeof(addrbuf)),
89 strerror(errno));
90 }
91
92 static void
93 pex_send_hello(struct network *net, struct network_peer *peer)
94 {
95 struct pex_hello *data;
96
97 pex_msg_init(net, PEX_MSG_HELLO);
98 data = pex_msg_append(sizeof(*data));
99 if (peer->state.endpoint.sa.sa_family == AF_INET6)
100 data->flags |= htons(PEER_EP_F_IPV6);
101 if (network_get_local_addr(&data->local_addr, &peer->state.endpoint))
102 return;
103
104 pex_msg_send(net, peer);
105 }
106
107 static int
108 pex_msg_add_peer_endpoint(struct network *net, struct network_peer *peer,
109 struct network_peer *receiver)
110 {
111 struct pex_peer_endpoint *data;
112 uint16_t flags = 0;
113 const void *addr;
114 int port;
115 int len;
116
117 addr = network_endpoint_addr(&peer->state.endpoint, &len);
118 port = peer->state.endpoint.in.sin_port;
119 if (len > 4)
120 flags |= PEER_EP_F_IPV6;
121 if (network_endpoint_addr_equal(&peer->state.endpoint,
122 &receiver->state.endpoint)) {
123 if (!peer->state.has_local_ep_addr) {
124 D_PEER(net, peer, "can't send peer to %s, missing local address",
125 network_peer_name(receiver));
126 return -1;
127 }
128
129 addr = &peer->state.local_ep_addr;
130 port = htons(peer->port);
131 flags |= PEER_EP_F_LOCAL;
132 }
133
134 data = pex_msg_append(sizeof(*data));
135 if (!data)
136 return -1;
137
138 memcpy(data->peer_id, peer->key, sizeof(data->peer_id));
139 memcpy(data->addr, addr, len);
140 data->port = port;
141 data->flags = htons(flags);
142 D_PEER(net, peer, "send endpoint to %s", network_peer_name(receiver));
143
144 return 0;
145 }
146
147 static void
148 network_pex_handle_endpoint_change(struct network *net, struct network_peer *peer)
149 {
150 struct network_peer *cur;
151
152 vlist_for_each_element(&net->peers, cur, node) {
153 if (cur == peer || !cur->state.connected)
154 continue;
155
156 pex_msg_init(net, PEX_MSG_NOTIFY_PEERS);
157 if (pex_msg_add_peer_endpoint(net, peer, cur))
158 continue;
159
160 pex_msg_send(net, cur);
161 }
162 }
163
164 static void
165 network_pex_host_request_update(struct network *net, struct network_pex_host *host)
166 {
167 char addrstr[INET6_ADDRSTRLEN];
168 uint64_t version = 0;
169
170 if (net->net_data_len)
171 version = net->net_data_version;
172
173 D("request network data from host %s",
174 inet_ntop(host->endpoint.sa.sa_family,
175 (host->endpoint.sa.sa_family == AF_INET6 ?
176 (const void *)&host->endpoint.in6.sin6_addr :
177 (const void *)&host->endpoint.in.sin_addr),
178 addrstr, sizeof(addrstr)));
179
180 if (!pex_msg_update_request_init(net->config.pubkey, net->config.key,
181 net->config.auth_key, &host->endpoint,
182 version, true))
183 return;
184 __pex_msg_send(-1, &host->endpoint);
185 }
186
187 static void
188 network_pex_request_update_cb(struct uloop_timeout *t)
189 {
190 struct network *net = container_of(t, struct network, pex.request_update_timer);
191 struct network_pex *pex = &net->pex;
192 struct network_pex_host *host;
193
194 uloop_timeout_set(t, 5000);
195
196 if (list_empty(&pex->hosts))
197 return;
198
199 host = list_first_entry(&pex->hosts, struct network_pex_host, list);
200 list_move_tail(&host->list, &pex->hosts);
201 network_pex_host_request_update(net, host);
202 }
203
204 void network_pex_init(struct network *net)
205 {
206 struct network_pex *pex = &net->pex;
207
208 memset(pex, 0, sizeof(*pex));
209 pex->fd.fd = -1;
210 INIT_LIST_HEAD(&pex->hosts);
211 pex->request_update_timer.cb = network_pex_request_update_cb;
212 }
213
214 static void
215 network_pex_query_hosts(struct network *net)
216 {
217 struct network_host *host;
218 int rv = rand();
219 int hosts = 0;
220 int i;
221
222 pex_msg_init(net, PEX_MSG_QUERY);
223
224 avl_for_each_element(&net->hosts, host, node) {
225 struct network_peer *peer = &host->peer;
226 void *id;
227
228 if (host == net->net_config.local_host ||
229 peer->state.connected ||
230 peer->endpoint)
231 continue;
232
233 id = pex_msg_append(PEX_ID_LEN);
234 if (!id)
235 break;
236
237 memcpy(id, peer->key, PEX_ID_LEN);
238 hosts++;
239 }
240
241 if (!hosts)
242 return;
243
244 rv %= net->hosts.count;
245 for (i = 0; i < 2; i++) {
246 avl_for_each_element(&net->hosts, host, node) {
247 struct network_peer *peer = &host->peer;
248
249 if (rv > 0) {
250 rv--;
251 continue;
252 }
253
254 if (host == net->net_config.local_host)
255 continue;
256
257 if (!peer->state.connected)
258 continue;
259
260 D_PEER(net, peer, "send query for %d hosts", hosts);
261 pex_msg_send(net, peer);
262 return;
263 }
264 }
265
266 }
267
268 static void
269 network_pex_send_ping(struct network *net, struct network_peer *peer)
270 {
271 pex_msg_init(net, PEX_MSG_PING);
272 pex_msg_send(net, peer);
273 }
274
275 static void
276 network_pex_send_update_request(struct network *net, struct network_peer *peer,
277 struct sockaddr_in6 *addr)
278 {
279 union network_endpoint ep = {};
280 uint64_t version = 0;
281
282 if (addr)
283 memcpy(&ep.in6, addr, sizeof(ep.in6));
284 else
285 pex_get_peer_addr(&ep.in6, net, peer);
286
287 if (net->net_data_len)
288 version = net->net_data_version;
289
290 if (!pex_msg_update_request_init(net->config.pubkey, net->config.key,
291 net->config.auth_key, &ep,
292 version, !!addr))
293 return;
294
295 pex_msg_send_ext(net, peer, addr);
296 }
297
298 void network_pex_event(struct network *net, struct network_peer *peer,
299 enum pex_event ev)
300 {
301 if (!network_pex_active(&net->pex))
302 return;
303
304 if (peer)
305 D_PEER(net, peer, "PEX event type=%d", ev);
306 else
307 D_NET(net, "PEX event type=%d", ev);
308
309 switch (ev) {
310 case PEX_EV_HANDSHAKE:
311 pex_send_hello(net, peer);
312 if (net->config.type == NETWORK_TYPE_DYNAMIC)
313 network_pex_send_update_request(net, peer, NULL);
314 break;
315 case PEX_EV_ENDPOINT_CHANGE:
316 network_pex_handle_endpoint_change(net, peer);
317 break;
318 case PEX_EV_QUERY:
319 network_pex_query_hosts(net);
320 break;
321 case PEX_EV_PING:
322 network_pex_send_ping(net, peer);
323 break;
324 }
325 }
326
327 static void
328 network_pex_recv_hello(struct network *net, struct network_peer *peer,
329 const struct pex_hello *data, size_t len)
330 {
331 char addrstr[INET6_ADDRSTRLEN];
332 uint16_t flags;
333 int af;
334
335 if (len < sizeof(*data))
336 return;
337
338 if (peer->state.has_local_ep_addr &&
339 !memcmp(&peer->state.local_ep_addr, data->local_addr, sizeof(data->local_addr)))
340 return;
341
342 flags = ntohs(data->flags);
343 af = (flags & PEER_EP_F_IPV6) ? AF_INET6 : AF_INET;
344 D_PEER(net, peer, "set local endpoint address to %s",
345 inet_ntop(af, data->local_addr, addrstr, sizeof(addrstr)));
346 peer->state.has_local_ep_addr = true;
347 memcpy(&peer->state.local_ep_addr, data->local_addr, sizeof(data->local_addr));
348 }
349
350 static void
351 network_pex_recv_peers(struct network *net, struct network_peer *peer,
352 const struct pex_peer_endpoint *data, size_t len)
353 {
354 struct network_peer *local = &net->net_config.local_host->peer;
355 struct network_peer *cur;
356
357 for (; len >= sizeof(*data); len -= sizeof(*data), data++) {
358 union network_endpoint *ep;
359 uint16_t flags;
360 void *addr;
361 int len;
362
363 cur = pex_msg_peer(net, data->peer_id);
364 if (!cur)
365 continue;
366
367 if (cur == peer || cur == local)
368 continue;
369
370 D_PEER(net, peer, "received peer address for %s",
371 network_peer_name(cur));
372 flags = ntohs(data->flags);
373 ep = &cur->state.next_endpoint;
374 ep->sa.sa_family = (flags & PEER_EP_F_IPV6) ? AF_INET6 : AF_INET;
375 addr = network_endpoint_addr(ep, &len);
376 memcpy(addr, data->addr, len);
377 ep->in.sin_port = data->port;
378 }
379 }
380
381 static void
382 network_pex_recv_query(struct network *net, struct network_peer *peer,
383 const uint8_t *data, size_t len)
384 {
385 struct network_peer *cur;
386 int resp = 0;
387
388 pex_msg_init(net, PEX_MSG_NOTIFY_PEERS);
389 for (; len >= 8; data += 8, len -= 8) {
390 cur = pex_msg_peer(net, data);
391 if (!cur || !cur->state.connected)
392 continue;
393
394 if (!pex_msg_add_peer_endpoint(net, cur, peer))
395 resp++;
396 }
397
398 if (!resp)
399 return;
400
401 D_PEER(net, peer, "send query response with %d hosts", resp);
402 pex_msg_send(net, peer);
403 }
404
405 static void
406 network_pex_recv_ping(struct network *net, struct network_peer *peer)
407 {
408 time_t now = time(NULL);
409
410 if (peer->state.last_request == now)
411 return;
412
413 peer->state.last_request = now;
414 pex_msg_init(net, PEX_MSG_PONG);
415 pex_msg_send(net, peer);
416 }
417
418 static void
419 network_pex_recv_update_request(struct network *net, struct network_peer *peer,
420 const uint8_t *data, size_t len,
421 struct sockaddr_in6 *addr)
422 {
423 struct pex_update_request *req = (struct pex_update_request *)data;
424 struct pex_msg_update_send_ctx ctx = {};
425 uint64_t req_version = be64_to_cpu(req->cur_version);
426 int *query_count;
427 bool done = false;
428
429 if (len < sizeof(struct pex_update_request))
430 return;
431
432 if (net->config.type != NETWORK_TYPE_DYNAMIC)
433 return;
434
435 if (peer)
436 query_count = &peer->state.num_net_queries;
437 else
438 query_count = &net->num_net_queries;
439
440 if (++*query_count > 10)
441 return;
442
443 D("receive update request, local version=%"PRIu64", remote version=%"PRIu64, net->net_data_version, req_version);
444
445 if (req_version >= net->net_data_version) {
446 struct pex_update_response_no_data *res;
447
448 pex_msg_init_ext(net, PEX_MSG_UPDATE_RESPONSE_NO_DATA, !!addr);
449 res = pex_msg_append(sizeof(*res));
450 res->req_id = req->req_id;
451 res->cur_version = cpu_to_be64(net->net_data_version);
452 pex_msg_send_ext(net, peer, addr);
453 }
454
455 if (req_version > net->net_data_version)
456 network_pex_send_update_request(net, peer, addr);
457
458 if (!peer || !net->net_data_len)
459 return;
460
461 if (req_version >= net->net_data_version)
462 return;
463
464 pex_msg_update_response_init(&ctx, net->config.pubkey, net->config.auth_key,
465 peer->key, !!addr, (void *)data,
466 net->net_data, net->net_data_len);
467 while (!done) {
468 pex_msg_send_ext(net, peer, addr);
469 done = !pex_msg_update_response_continue(&ctx);
470 }
471 }
472
473 static void
474 network_pex_recv_update_response(struct network *net, const uint8_t *data, size_t len,
475 struct sockaddr_in6 *addr, enum pex_opcode op)
476 {
477 struct network_peer *peer;
478 void *net_data;
479 int net_data_len = 0;
480 uint64_t version = 0;
481 bool no_prev_data = !net->net_data_len;
482
483 if (net->config.type != NETWORK_TYPE_DYNAMIC)
484 return;
485
486 net_data = pex_msg_update_response_recv(data, len, op, &net_data_len, &version);
487 if (!net_data)
488 return;
489
490 if (version <= net->net_data_version) {
491 free(net_data);
492 return;
493 }
494
495 D_NET(net, "received updated network data, len=%d", net_data_len);
496 free(net->net_data);
497
498 net->net_data = net_data;
499 net->net_data_len = net_data_len;
500 net->net_data_version = version;
501 if (network_save_dynamic(net) < 0)
502 return;
503
504 uloop_timeout_set(&net->reload_timer, no_prev_data ? 1 : UNETD_DATA_UPDATE_DELAY);
505 vlist_for_each_element(&net->peers, peer, node) {
506 if (!peer->state.connected)
507 continue;
508 network_pex_send_update_request(net, peer, NULL);
509 }
510 }
511
512 static void
513 network_pex_recv(struct network *net, struct network_peer *peer, struct pex_hdr *hdr)
514 {
515 const void *data = hdr + 1;
516
517 if (hdr->version != 0)
518 return;
519
520 D_PEER(net, peer, "PEX rx op=%d", hdr->opcode);
521 switch (hdr->opcode) {
522 case PEX_MSG_HELLO:
523 network_pex_recv_hello(net, peer, data, hdr->len);
524 break;
525 case PEX_MSG_NOTIFY_PEERS:
526 network_pex_recv_peers(net, peer, data, hdr->len);
527 break;
528 case PEX_MSG_QUERY:
529 network_pex_recv_query(net, peer, data, hdr->len);
530 break;
531 case PEX_MSG_PING:
532 network_pex_recv_ping(net, peer);
533 break;
534 case PEX_MSG_PONG:
535 break;
536 case PEX_MSG_UPDATE_REQUEST:
537 network_pex_recv_update_request(net, peer, data, hdr->len,
538 NULL);
539 break;
540 case PEX_MSG_UPDATE_RESPONSE:
541 case PEX_MSG_UPDATE_RESPONSE_DATA:
542 case PEX_MSG_UPDATE_RESPONSE_NO_DATA:
543 network_pex_recv_update_response(net, data, hdr->len,
544 NULL, hdr->opcode);
545 break;
546 }
547 }
548
549 static void
550 network_pex_fd_cb(struct uloop_fd *fd, unsigned int events)
551 {
552 struct network *net = container_of(fd, struct network, pex.fd);
553 struct network_peer *local = &net->net_config.local_host->peer;
554 struct network_peer *peer;
555 struct sockaddr_in6 sin6;
556 static char buf[PEX_BUF_SIZE];
557 struct pex_hdr *hdr = (struct pex_hdr *)buf;
558 ssize_t len;
559
560 while (1) {
561 socklen_t slen = sizeof(sin6);
562
563 len = recvfrom(fd->fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &slen);
564 if (len < 0) {
565 if (errno == EINTR)
566 continue;
567
568 if (errno == EAGAIN)
569 break;
570
571 D_NET(net, "recvfrom failed: %s", strerror(errno));
572 network_pex_close(net);
573 return;
574 }
575
576 if (!len)
577 continue;
578
579 if (len < sizeof(*hdr))
580 continue;
581
582 hdr->len = ntohs(hdr->len);
583 if (len - sizeof(hdr) < hdr->len)
584 continue;
585
586 peer = pex_msg_peer(net, hdr->id);
587 if (!peer)
588 continue;
589
590 if (memcmp(&sin6.sin6_addr, &peer->local_addr.in6, sizeof(sin6.sin6_addr)) != 0)
591 continue;
592
593 if (peer == local)
594 continue;
595
596 network_pex_recv(net, peer, hdr);
597 }
598 }
599
600 static void
601 network_pex_create_host(struct network *net, union network_endpoint *ep)
602 {
603 struct network_pex *pex = &net->pex;
604 struct network_pex_host *host;
605
606 host = calloc(1, sizeof(*host));
607 memcpy(&host->endpoint, ep, sizeof(host->endpoint));
608 list_add_tail(&host->list, &pex->hosts);
609 network_pex_host_request_update(net, host);
610 }
611
612 static void
613 network_pex_open_auth_connect(struct network *net)
614 {
615 struct network_pex *pex = &net->pex;
616 struct network_peer *peer;
617 struct blob_attr *cur;
618 int rem;
619
620 if (net->config.type != NETWORK_TYPE_DYNAMIC)
621 return;
622
623 uloop_timeout_set(&pex->request_update_timer, 5000);
624
625 vlist_for_each_element(&net->peers, peer, node) {
626 union network_endpoint ep = {};
627
628 if (!peer->endpoint)
629 continue;
630
631 if (network_get_endpoint(&ep, peer->endpoint,
632 UNETD_GLOBAL_PEX_PORT, 0) < 0)
633 continue;
634
635 ep.in.sin_port = htons(UNETD_GLOBAL_PEX_PORT);
636 network_pex_create_host(net, &ep);
637 }
638
639 if (!net->config.auth_connect)
640 return;
641
642 blobmsg_for_each_attr(cur, net->config.auth_connect, rem) {
643 union network_endpoint ep = {};
644
645 if (network_get_endpoint(&ep, blobmsg_get_string(cur),
646 UNETD_GLOBAL_PEX_PORT, 0) < 0)
647 continue;
648
649 network_pex_create_host(net, &ep);
650 }
651 }
652
653
654 int network_pex_open(struct network *net)
655 {
656 struct network_host *local_host = net->net_config.local_host;
657 struct network_peer *local;
658 struct network_pex *pex = &net->pex;
659 struct sockaddr_in6 sin6 = {};
660 int yes = 1;
661 int fd;
662
663 network_pex_open_auth_connect(net);
664
665 if (!local_host || !local_host->peer.pex_port)
666 return 0;
667
668 local = &local_host->peer;
669 fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
670 if (fd < 0)
671 return -1;
672
673 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
674 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
675
676 sin6.sin6_family = AF_INET6;
677 memcpy(&sin6.sin6_addr, &local->local_addr.in6,
678 sizeof(local->local_addr.in6));
679 sin6.sin6_port = htons(local_host->peer.pex_port);
680
681 if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
682 perror("bind");
683 goto close;
684 }
685
686 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
687 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
688 #ifdef linux
689 setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
690 network_name(net), strlen(network_name(net)));
691 #endif
692
693 pex->fd.fd = fd;
694 pex->fd.cb = network_pex_fd_cb;
695 uloop_fd_add(&pex->fd, ULOOP_READ);
696
697 return 0;
698
699 close:
700 close(fd);
701 return -1;
702 }
703
704 void network_pex_close(struct network *net)
705 {
706 struct network_pex *pex = &net->pex;
707 struct network_pex_host *host, *tmp;
708
709 uloop_timeout_cancel(&pex->request_update_timer);
710 list_for_each_entry_safe(host, tmp, &pex->hosts, list) {
711 list_del(&host->list);
712 free(host);
713 }
714
715 if (pex->fd.fd < 0)
716 return;
717
718 uloop_fd_delete(&pex->fd);
719 close(pex->fd.fd);
720 network_pex_init(net);
721 }
722
723 static struct network *
724 global_pex_find_network(const uint8_t *id)
725 {
726 struct network *net;
727
728 avl_for_each_element(&networks, net, node) {
729 if (!memcmp(id, net->config.auth_key, PEX_ID_LEN))
730 return net;
731 }
732
733 return NULL;
734 }
735
736 static void
737 global_pex_recv(struct pex_hdr *hdr, struct sockaddr_in6 *addr)
738 {
739 struct pex_ext_hdr *ehdr = (void *)(hdr + 1);
740 struct network_peer *peer;
741 struct network *net;
742 void *data = (void *)(ehdr + 1);
743
744 if (hdr->version != 0)
745 return;
746
747 net = global_pex_find_network(ehdr->auth_id);
748 if (!net || net->config.type != NETWORK_TYPE_DYNAMIC)
749 return;
750
751 *(uint64_t *)hdr->id ^= pex_network_hash(net->config.auth_key, ehdr->nonce);
752
753 D("PEX global rx op=%d", hdr->opcode);
754 switch (hdr->opcode) {
755 case PEX_MSG_HELLO:
756 case PEX_MSG_NOTIFY_PEERS:
757 case PEX_MSG_QUERY:
758 case PEX_MSG_PING:
759 case PEX_MSG_PONG:
760 break;
761 case PEX_MSG_UPDATE_REQUEST:
762 peer = pex_msg_peer(net, hdr->id);
763 network_pex_recv_update_request(net, peer, data, hdr->len,
764 addr);
765 break;
766 case PEX_MSG_UPDATE_RESPONSE:
767 case PEX_MSG_UPDATE_RESPONSE_DATA:
768 case PEX_MSG_UPDATE_RESPONSE_NO_DATA:
769 network_pex_recv_update_response(net, data, hdr->len, addr, hdr->opcode);
770 break;
771 }
772 }
773
774 int global_pex_open(void)
775 {
776 struct sockaddr_in6 sin6 = {};
777
778 sin6.sin6_family = AF_INET6;
779 sin6.sin6_port = htons(global_pex_port);
780
781 return pex_open(&sin6, sizeof(sin6), global_pex_recv, true);
782 }