pex: use pubkey directly instead of accessing local_host in pex_msg_init()
[project/unetd.git] / pex.c
1 // SPDX-License-Identifier: GPL-2.0+
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 <time.h>
11 #include "unetd.h"
12
13 #define PEX_BUF_SIZE 1024
14
15 enum pex_opcode {
16 PEX_MSG_HELLO,
17 PEX_MSG_NOTIFY_PEERS,
18 PEX_MSG_QUERY,
19 PEX_MSG_PING,
20 PEX_MSG_PONG,
21 };
22
23 #define PEX_ID_LEN 8
24
25 struct pex_hdr {
26 uint8_t version;
27 uint8_t opcode;
28 uint16_t len;
29 uint8_t id[PEX_ID_LEN];
30 };
31
32 #define PEER_EP_F_IPV6 (1 << 0)
33 #define PEER_EP_F_LOCAL (1 << 1)
34
35 struct pex_peer_endpoint {
36 uint16_t flags;
37 uint16_t port;
38 uint8_t peer_id[PEX_ID_LEN];
39 uint8_t addr[16];
40 };
41
42 struct pex_hello {
43 uint16_t flags;
44 uint8_t local_addr[16];
45 };
46
47 static char tx_buf[PEX_BUF_SIZE];
48
49 static const char *pex_peer_id_str(const uint8_t *key)
50 {
51 static char str[20];
52 int i;
53
54 for (i = 0; i < 8; i++)
55 sprintf(str + i * 2, "%02x", key[i]);
56
57 return str;
58 }
59
60
61 static struct network_peer *
62 pex_msg_peer(struct network *net, const uint8_t *id)
63 {
64 struct network_peer *peer;
65 uint8_t key[WG_KEY_LEN] = {};
66
67 memcpy(key, id, PEX_ID_LEN);
68 peer = avl_find_ge_element(&net->peers.avl, key, peer, node.avl);
69 if (!peer || memcmp(peer->key, key, PEX_ID_LEN) != 0) {
70 D_NET(net, "can't find peer %s", pex_peer_id_str(id));
71 return NULL;
72 }
73
74 return peer;
75 }
76
77 static struct pex_hdr *pex_msg_init(struct network *net, uint8_t opcode)
78 {
79 struct pex_hdr *hdr = (struct pex_hdr *)tx_buf;
80
81 hdr->version = 0;
82 hdr->opcode = opcode;
83 hdr->len = 0;
84 memcpy(hdr->id, net->config.pubkey, sizeof(hdr->id));
85
86 return hdr;
87 }
88
89 static void *pex_msg_append(size_t len)
90 {
91 struct pex_hdr *hdr = (struct pex_hdr *)tx_buf;
92 int ofs = hdr->len + sizeof(struct pex_hdr);
93 void *buf = &tx_buf[ofs];
94
95 if (sizeof(tx_buf) - ofs < len)
96 return NULL;
97
98 hdr->len += len;
99 memset(buf, 0, len);
100
101 return buf;
102 }
103
104 static void pex_msg_send(struct network *net, struct network_peer *peer)
105 {
106 struct sockaddr_in6 sin6 = {};
107 struct pex_hdr *hdr = (struct pex_hdr *)tx_buf;
108 size_t tx_len = sizeof(*hdr) + hdr->len;
109 int ret;
110
111 if (peer == &net->net_config.local_host->peer || !peer->state.connected)
112 return;
113
114 sin6.sin6_family = AF_INET6;
115 memcpy(&sin6.sin6_addr, &peer->local_addr.in6,
116 sizeof(peer->local_addr.in6));
117 sin6.sin6_port = htons(net->net_config.pex_port);
118 hdr->len = htons(hdr->len);
119 ret = sendto(net->pex.fd.fd, tx_buf, tx_len, 0, (struct sockaddr *)&sin6, sizeof(sin6));
120 hdr->len = ntohs(hdr->len);
121 if (ret < 0)
122 D_PEER(net, peer, "pex_msg_send failed: %s", strerror(errno));
123 }
124
125 static void
126 pex_send_hello(struct network *net, struct network_peer *peer)
127 {
128 struct pex_hello *data;
129
130 pex_msg_init(net, PEX_MSG_HELLO);
131 data = pex_msg_append(sizeof(*data));
132 if (peer->state.endpoint.sa.sa_family == AF_INET6)
133 data->flags |= htons(PEER_EP_F_IPV6);
134 if (network_get_local_addr(&data->local_addr, &peer->state.endpoint))
135 return;
136
137 pex_msg_send(net, peer);
138 }
139
140
141 static int
142 pex_msg_add_peer_endpoint(struct network *net, struct network_peer *peer,
143 struct network_peer *receiver)
144 {
145 struct pex_peer_endpoint *data;
146 uint16_t flags = 0;
147 const void *addr;
148 int port;
149 int len;
150
151 addr = network_endpoint_addr(&peer->state.endpoint, &len);
152 port = peer->state.endpoint.in.sin_port;
153 if (len > 4)
154 flags |= PEER_EP_F_IPV6;
155 if (network_endpoint_addr_equal(&peer->state.endpoint,
156 &receiver->state.endpoint)) {
157 if (!peer->state.has_local_ep_addr) {
158 D_PEER(net, peer, "can't send peer to %s, missing local address",
159 network_peer_name(receiver));
160 return -1;
161 }
162
163 addr = &peer->state.local_ep_addr;
164 port = htons(peer->port);
165 flags |= PEER_EP_F_LOCAL;
166 }
167
168 data = pex_msg_append(sizeof(*data));
169 if (!data)
170 return -1;
171
172 memcpy(data->peer_id, peer->key, sizeof(data->peer_id));
173 memcpy(data->addr, addr, len);
174 data->port = port;
175 data->flags = htons(flags);
176 D_PEER(net, peer, "send endpoint to %s", network_peer_name(receiver));
177
178 return 0;
179 }
180
181 static void
182 network_pex_handle_endpoint_change(struct network *net, struct network_peer *peer)
183 {
184 struct network_peer *cur;
185
186 vlist_for_each_element(&net->peers, cur, node) {
187 if (cur == peer || !cur->state.connected)
188 continue;
189
190 pex_msg_init(net, PEX_MSG_NOTIFY_PEERS);
191 if (pex_msg_add_peer_endpoint(net, peer, cur))
192 continue;
193
194 pex_msg_send(net, cur);
195 }
196 }
197
198 void network_pex_init(struct network *net)
199 {
200 struct network_pex *pex = &net->pex;
201
202 memset(pex, 0, sizeof(*pex));
203 pex->fd.fd = -1;
204 }
205
206 static void
207 network_pex_query_hosts(struct network *net)
208 {
209 struct network_host *host;
210 int rv = rand();
211 int hosts = 0;
212 int i;
213
214 pex_msg_init(net, PEX_MSG_QUERY);
215
216 avl_for_each_element(&net->hosts, host, node) {
217 struct network_peer *peer = &host->peer;
218 void *id;
219
220 if (host == net->net_config.local_host ||
221 peer->state.connected ||
222 peer->endpoint)
223 continue;
224
225 id = pex_msg_append(PEX_ID_LEN);
226 if (!id)
227 break;
228
229 memcpy(id, peer->key, PEX_ID_LEN);
230 hosts++;
231 }
232
233 if (!hosts)
234 return;
235
236 rv %= net->hosts.count;
237 for (i = 0; i < 2; i++) {
238 avl_for_each_element(&net->hosts, host, node) {
239 struct network_peer *peer = &host->peer;
240
241 if (rv > 0) {
242 rv--;
243 continue;
244 }
245
246 if (host == net->net_config.local_host)
247 continue;
248
249 if (!peer->state.connected)
250 continue;
251
252 D_PEER(net, peer, "send query for %d hosts", hosts);
253 pex_msg_send(net, peer);
254 return;
255 }
256 }
257
258 }
259
260 static void
261 network_pex_send_ping(struct network *net, struct network_peer *peer)
262 {
263 pex_msg_init(net, PEX_MSG_PING);
264 pex_msg_send(net, peer);
265 }
266
267 void network_pex_event(struct network *net, struct network_peer *peer,
268 enum pex_event ev)
269 {
270 if (!network_pex_active(&net->pex))
271 return;
272
273 if (peer)
274 D_PEER(net, peer, "PEX event type=%d", ev);
275 else
276 D_NET(net, "PEX event type=%d", ev);
277
278 switch (ev) {
279 case PEX_EV_HANDSHAKE:
280 pex_send_hello(net, peer);
281 break;
282 case PEX_EV_ENDPOINT_CHANGE:
283 network_pex_handle_endpoint_change(net, peer);
284 break;
285 case PEX_EV_QUERY:
286 network_pex_query_hosts(net);
287 break;
288 case PEX_EV_PING:
289 network_pex_send_ping(net, peer);
290 break;
291 }
292 }
293
294 static void
295 network_pex_recv_hello(struct network *net, struct network_peer *peer,
296 const struct pex_hello *data, size_t len)
297 {
298 char addrstr[INET6_ADDRSTRLEN];
299 uint16_t flags;
300 int af;
301
302 if (len < sizeof(*data))
303 return;
304
305 if (peer->state.has_local_ep_addr &&
306 !memcmp(&peer->state.local_ep_addr, data->local_addr, sizeof(data->local_addr)))
307 return;
308
309 flags = ntohs(data->flags);
310 af = (flags & PEER_EP_F_IPV6) ? AF_INET6 : AF_INET;
311 D_PEER(net, peer, "set local endpoint address to %s",
312 inet_ntop(af, data->local_addr, addrstr, sizeof(addrstr)));
313 peer->state.has_local_ep_addr = true;
314 memcpy(&peer->state.local_ep_addr, data->local_addr, sizeof(data->local_addr));
315 }
316
317 static void
318 network_pex_recv_peers(struct network *net, struct network_peer *peer,
319 const struct pex_peer_endpoint *data, size_t len)
320 {
321 struct network_peer *local = &net->net_config.local_host->peer;
322 struct network_peer *cur;
323
324 for (; len >= sizeof(*data); len -= sizeof(*data), data++) {
325 union network_endpoint *ep;
326 uint16_t flags;
327 void *addr;
328 int len;
329
330 cur = pex_msg_peer(net, data->peer_id);
331 if (!cur)
332 continue;
333
334 if (cur == peer || cur == local)
335 continue;
336
337 D_PEER(net, peer, "received peer address for %s\n",
338 network_peer_name(cur));
339 flags = ntohs(data->flags);
340 ep = &cur->state.next_endpoint;
341 ep->sa.sa_family = (flags & PEER_EP_F_IPV6) ? AF_INET6 : AF_INET;
342 addr = network_endpoint_addr(ep, &len);
343 memcpy(addr, data->addr, len);
344 ep->in.sin_port = data->port;
345 }
346 }
347
348 static void
349 network_pex_recv_query(struct network *net, struct network_peer *peer,
350 const uint8_t *data, size_t len)
351 {
352 struct network_peer *cur;
353 int resp = 0;
354
355 pex_msg_init(net, PEX_MSG_NOTIFY_PEERS);
356 for (; len >= 8; data += 8, len -= 8) {
357 cur = pex_msg_peer(net, data);
358 if (!cur || !cur->state.connected)
359 continue;
360
361 if (!pex_msg_add_peer_endpoint(net, cur, peer))
362 resp++;
363 }
364
365 if (!resp)
366 return;
367
368 D_PEER(net, peer, "send query response with %d hosts", resp);
369 pex_msg_send(net, peer);
370 }
371
372 static void
373 network_pex_recv_ping(struct network *net, struct network_peer *peer)
374 {
375 time_t now = time(NULL);
376
377 if (peer->state.last_request == now)
378 return;
379
380 peer->state.last_request = now;
381 pex_msg_init(net, PEX_MSG_PONG);
382 pex_msg_send(net, peer);
383 }
384
385 static void
386 network_pex_recv(struct network *net, struct network_peer *peer, struct pex_hdr *hdr)
387 {
388 const void *data = hdr + 1;
389
390 if (hdr->version != 0)
391 return;
392
393 D_PEER(net, peer, "PEX rx op=%d", hdr->opcode);
394 switch (hdr->opcode) {
395 case PEX_MSG_HELLO:
396 network_pex_recv_hello(net, peer, data, hdr->len);
397 break;
398 case PEX_MSG_NOTIFY_PEERS:
399 network_pex_recv_peers(net, peer, data, hdr->len);
400 break;
401 case PEX_MSG_QUERY:
402 network_pex_recv_query(net, peer, data, hdr->len);
403 break;
404 case PEX_MSG_PING:
405 network_pex_recv_ping(net, peer);
406 break;
407 case PEX_MSG_PONG:
408 break;
409 }
410 }
411
412 static void
413 network_pex_fd_cb(struct uloop_fd *fd, unsigned int events)
414 {
415 struct network *net = container_of(fd, struct network, pex.fd);
416 struct network_peer *local = &net->net_config.local_host->peer;
417 struct network_peer *peer;
418 struct sockaddr_in6 sin6;
419 static char buf[PEX_BUF_SIZE];
420 struct pex_hdr *hdr = (struct pex_hdr *)buf;
421 ssize_t len;
422
423 while (1) {
424 socklen_t slen = sizeof(sin6);
425
426 len = recvfrom(fd->fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &slen);
427 if (len < 0) {
428 if (errno == EINTR)
429 continue;
430
431 if (errno == EAGAIN)
432 break;
433
434 D_NET(net, "recvfrom failed: %s", strerror(errno));
435 network_pex_close(net);
436 return;
437 }
438
439 if (!len)
440 continue;
441
442 if (len < sizeof(*hdr))
443 continue;
444
445 hdr->len = ntohs(hdr->len);
446 if (len - sizeof(hdr) < hdr->len)
447 continue;
448
449 peer = pex_msg_peer(net, hdr->id);
450 if (!peer)
451 continue;
452
453 if (memcmp(&sin6.sin6_addr, &peer->local_addr.in6, sizeof(sin6.sin6_addr)) != 0)
454 continue;
455
456 if (peer == local)
457 continue;
458
459 network_pex_recv(net, peer, hdr);
460 }
461 }
462
463 int network_pex_open(struct network *net)
464 {
465 struct network_peer *local = &net->net_config.local_host->peer;
466 struct network_pex *pex = &net->pex;
467 struct sockaddr_in6 sin6 = {};
468 int yes = 1;
469 int fd;
470
471 if (!local || !net->net_config.pex_port)
472 return 0;
473
474 fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
475 if (fd < 0)
476 return -1;
477
478 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
479 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
480
481 sin6.sin6_family = AF_INET6;
482 memcpy(&sin6.sin6_addr, &local->local_addr.in6,
483 sizeof(local->local_addr.in6));
484 sin6.sin6_port = htons(net->net_config.pex_port);
485
486 if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
487 perror("bind");
488 goto close;
489 }
490
491 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
492 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
493 #ifdef linux
494 setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
495 network_name(net), strlen(network_name(net)));
496 #endif
497
498 pex->fd.fd = fd;
499 pex->fd.cb = network_pex_fd_cb;
500 uloop_fd_add(&pex->fd, ULOOP_READ);
501
502 return 0;
503
504 close:
505 close(fd);
506 return -1;
507 }
508
509 void network_pex_close(struct network *net)
510 {
511 struct network_pex *pex = &net->pex;
512
513 if (pex->fd.fd < 0)
514 return;
515
516 uloop_fd_delete(&pex->fd);
517 close(pex->fd.fd);
518 network_pex_init(net);
519 }