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