unet-cli: pass host object to set_host()
[project/unetd.git] / pex-msg.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 <errno.h>
9 #include <fcntl.h>
10 #include <libubox/list.h>
11 #include <libubox/uloop.h>
12 #include "pex-msg.h"
13 #include "chacha20.h"
14 #include "auth-data.h"
15
16 static char pex_tx_buf[PEX_BUF_SIZE];
17 static FILE *pex_urandom;
18 static struct uloop_fd pex_fd;
19 static LIST_HEAD(requests);
20 static struct uloop_timeout gc_timer;
21
22 static pex_recv_cb_t pex_recv_cb;
23
24 struct pex_msg_update_recv_ctx {
25 struct list_head list;
26
27 union network_endpoint addr;
28
29 uint8_t priv_key[CURVE25519_KEY_SIZE];
30 uint8_t auth_key[CURVE25519_KEY_SIZE];
31 uint8_t e_key[CURVE25519_KEY_SIZE];
32
33 uint64_t req_id;
34
35 void *data;
36 int data_len;
37 int data_ofs;
38
39 int idle;
40 };
41
42 uint64_t pex_network_hash(const uint8_t *auth_key, uint64_t req_id)
43 {
44 siphash_key_t key = {
45 be64_to_cpu(req_id),
46 be64_to_cpu(req_id)
47 };
48 uint64_t hash;
49
50 siphash_to_be64(&hash, auth_key, CURVE25519_KEY_SIZE, &key);
51
52 return hash;
53 }
54
55
56 struct pex_hdr *__pex_msg_init(const uint8_t *pubkey, uint8_t opcode)
57 {
58 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
59
60 hdr->version = 0;
61 hdr->opcode = opcode;
62 hdr->len = 0;
63 memcpy(hdr->id, pubkey, sizeof(hdr->id));
64
65 return hdr;
66 }
67
68 struct pex_hdr *__pex_msg_init_ext(const uint8_t *pubkey, const uint8_t *auth_key,
69 uint8_t opcode, bool ext)
70 {
71 struct pex_hdr *hdr = __pex_msg_init(pubkey, opcode);
72 struct pex_ext_hdr *ehdr = (struct pex_ext_hdr *)(hdr + 1);
73 uint64_t hash;
74
75 if (!ext)
76 return hdr;
77
78 hdr->len = sizeof(*ehdr);
79
80 if (fread(&ehdr->nonce, sizeof(ehdr->nonce), 1, pex_urandom) != 1)
81 return NULL;
82
83 hash = pex_network_hash(auth_key, ehdr->nonce);
84 *(uint64_t *)hdr->id ^= hash;
85 memcpy(ehdr->auth_id, auth_key, sizeof(ehdr->auth_id));
86
87 return hdr;
88 }
89
90 void *pex_msg_append(size_t len)
91 {
92 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
93 int ofs = hdr->len + sizeof(struct pex_hdr);
94 void *buf = &pex_tx_buf[ofs];
95
96 if (sizeof(pex_tx_buf) - ofs < len)
97 return NULL;
98
99 hdr->len += len;
100 memset(buf, 0, len);
101
102 return buf;
103 }
104
105 static void
106 pex_fd_cb(struct uloop_fd *fd, unsigned int events)
107 {
108 struct sockaddr_in6 sin6;
109 static char buf[PEX_BUF_SIZE];
110 struct pex_hdr *hdr = (struct pex_hdr *)buf;
111 ssize_t len;
112
113 while (1) {
114 socklen_t slen = sizeof(sin6);
115
116 len = recvfrom(fd->fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &slen);
117 if (len < 0) {
118 if (errno == EINTR)
119 continue;
120
121 if (errno == EAGAIN)
122 break;
123
124 pex_close();
125 return;
126 }
127
128 if (!len)
129 continue;
130
131 if (len < sizeof(*hdr) + sizeof(struct pex_ext_hdr))
132 continue;
133
134 hdr->len = ntohs(hdr->len);
135 if (len - sizeof(hdr) - sizeof(struct pex_ext_hdr) < hdr->len)
136 continue;
137
138 pex_recv_cb(hdr, &sin6);
139 }
140 }
141
142 int __pex_msg_send(int fd, const void *addr)
143 {
144 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
145 const struct sockaddr *sa = addr;
146 size_t tx_len = sizeof(*hdr) + hdr->len;
147 uint16_t orig_len = hdr->len;
148 size_t addr_len;
149 int ret;
150
151 if (fd < 0) {
152 hdr->len -= sizeof(struct pex_ext_hdr);
153 fd = pex_fd.fd;
154 }
155
156 hdr->len = htons(hdr->len);
157 if (addr) {
158 if (sa->sa_family == AF_INET6)
159 addr_len = sizeof(struct sockaddr_in6);
160 else
161 addr_len = sizeof(struct sockaddr_in);
162 ret = sendto(fd, pex_tx_buf, tx_len, 0, sa, addr_len);
163 } else {
164 ret = send(fd, pex_tx_buf, tx_len, 0);
165 }
166 hdr->len = orig_len;
167
168 return ret;
169 }
170
171 static void
172 pex_msg_update_response_fill(struct pex_msg_update_send_ctx *ctx)
173 {
174 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
175 int ofs = hdr->len + sizeof(struct pex_hdr);
176 int cur_len = ctx->rem;
177
178 if (cur_len > PEX_BUF_SIZE - ofs)
179 cur_len = PEX_BUF_SIZE - ofs;
180
181 memcpy(pex_msg_append(cur_len), ctx->cur, cur_len);
182 ctx->cur += cur_len;
183 ctx->rem -= cur_len;
184 }
185
186 void pex_msg_update_response_init(struct pex_msg_update_send_ctx *ctx,
187 const uint8_t *pubkey, const uint8_t *auth_key,
188 const uint8_t *peer_key, bool ext,
189 struct pex_update_request *req,
190 const void *data, int len)
191 {
192 uint8_t e_key_priv[CURVE25519_KEY_SIZE];
193 uint8_t enc_key[CURVE25519_KEY_SIZE];
194 struct pex_update_response *res;
195
196 ctx->pubkey = pubkey;
197 ctx->auth_key = auth_key;
198 ctx->ext = ext;
199 ctx->req_id = req->req_id;
200
201 if (!__pex_msg_init_ext(pubkey, auth_key, PEX_MSG_UPDATE_RESPONSE, ext))
202 return;
203
204 res = pex_msg_append(sizeof(*res));
205 res->req_id = req->req_id;
206 res->data_len = len;
207
208 if (!fread(e_key_priv, sizeof(e_key_priv), 1, pex_urandom))
209 return;
210
211 curve25519_clamp_secret(e_key_priv);
212 curve25519_generate_public(res->e_key, e_key_priv);
213 curve25519(enc_key, e_key_priv, peer_key);
214
215 ctx->data = ctx->cur = malloc(len);
216 ctx->rem = len;
217
218 memcpy(ctx->data, data, len);
219 chacha20_encrypt_msg(ctx->data, len, &req->req_id, enc_key);
220
221 pex_msg_update_response_fill(ctx);
222 }
223
224 bool pex_msg_update_response_continue(struct pex_msg_update_send_ctx *ctx)
225 {
226 struct pex_update_response_data *res_ext;
227
228 if (ctx->rem <= 0) {
229 free(ctx->data);
230 ctx->data = NULL;
231
232 return false;
233 }
234
235 if (!__pex_msg_init_ext(ctx->pubkey, ctx->auth_key,
236 PEX_MSG_UPDATE_RESPONSE_DATA, ctx->ext))
237 return false;
238
239 res_ext = pex_msg_append(sizeof(*res_ext));
240 res_ext->req_id = ctx->req_id;
241 res_ext->offset = ctx->cur - ctx->data;
242 pex_msg_update_response_fill(ctx);
243
244 return true;
245 }
246
247
248 struct pex_update_request *
249 pex_msg_update_request_init(const uint8_t *pubkey, const uint8_t *priv_key,
250 const uint8_t *auth_key, union network_endpoint *addr,
251 uint64_t cur_version, bool ext)
252 {
253 struct pex_update_request *req;
254 struct pex_msg_update_recv_ctx *ctx;
255
256 list_for_each_entry(ctx, &requests, list) {
257 if (!memcmp(&ctx->addr, addr, sizeof(ctx->addr)))
258 return NULL;
259 }
260
261 ctx = calloc(1, sizeof(*ctx));
262 memcpy(&ctx->addr, addr, sizeof(ctx->addr));
263 memcpy(ctx->auth_key, auth_key, sizeof(ctx->auth_key));
264 memcpy(ctx->priv_key, priv_key, sizeof(ctx->priv_key));
265 if (!fread(&ctx->req_id, sizeof(ctx->req_id), 1, pex_urandom))
266 return NULL;
267 list_add_tail(&ctx->list, &requests);
268 if (!gc_timer.pending)
269 uloop_timeout_set(&gc_timer, 1000);
270
271 if (!__pex_msg_init_ext(pubkey, auth_key, PEX_MSG_UPDATE_REQUEST, ext)) {
272 free(ctx);
273 return NULL;
274 }
275
276 req = pex_msg_append(sizeof(*req));
277 req->cur_version = cpu_to_be64(cur_version);
278 req->req_id = ctx->req_id;
279
280 return req;
281 }
282
283 static struct pex_msg_update_recv_ctx *
284 pex_msg_update_recv_ctx_get(uint64_t req_id)
285 {
286 struct pex_msg_update_recv_ctx *ctx;
287
288 list_for_each_entry(ctx, &requests, list) {
289 if (ctx->req_id == req_id) {
290 ctx->idle = 0;
291 return ctx;
292 }
293 }
294
295 return NULL;
296 }
297
298 static void pex_msg_update_ctx_free(struct pex_msg_update_recv_ctx *ctx)
299 {
300 list_del(&ctx->list);
301 free(ctx->data);
302 free(ctx);
303 }
304
305 void *pex_msg_update_response_recv(const void *data, int len, enum pex_opcode op,
306 int *data_len, uint64_t *timestamp)
307 {
308 struct pex_msg_update_recv_ctx *ctx;
309 uint8_t enc_key[CURVE25519_KEY_SIZE];
310 void *ret;
311
312 *data_len = 0;
313 if (op == PEX_MSG_UPDATE_RESPONSE) {
314 const struct pex_update_response *res = data;
315
316 if (len < sizeof(*res))
317 return NULL;
318
319 ctx = pex_msg_update_recv_ctx_get(res->req_id);
320 if (!ctx || ctx->data_len || !res->data_len ||
321 res->data_len > UNETD_NET_DATA_SIZE_MAX)
322 return NULL;
323
324 data += sizeof(*res);
325 len -= sizeof(*res);
326
327 ctx->data_len = res->data_len;
328 memcpy(ctx->e_key, res->e_key, sizeof(ctx->e_key));
329 ctx->data = malloc(ctx->data_len);
330 } else if (op == PEX_MSG_UPDATE_RESPONSE_DATA) {
331 const struct pex_update_response_data *res = data;
332
333 if (len <= sizeof(*res))
334 return NULL;
335
336 ctx = pex_msg_update_recv_ctx_get(res->req_id);
337 if (!ctx || ctx->data_ofs != res->offset)
338 return NULL;
339
340 data += sizeof(*res);
341 len -= sizeof(*res);
342 } else if (op == PEX_MSG_UPDATE_RESPONSE_NO_DATA) {
343 const struct pex_update_response_no_data *res = data;
344
345 if (len < sizeof(*res))
346 return NULL;
347
348 ctx = pex_msg_update_recv_ctx_get(res->req_id);
349 if (!ctx)
350 return NULL;
351
352 goto error;
353 } else {
354 return NULL;
355 }
356
357 if (ctx->data_ofs + len > ctx->data_len)
358 goto error;
359
360 memcpy(ctx->data + ctx->data_ofs, data, len);
361 ctx->data_ofs += len;
362 if (ctx->data_ofs < ctx->data_len)
363 return NULL;
364
365 curve25519(enc_key, ctx->priv_key, ctx->e_key);
366 chacha20_encrypt_msg(ctx->data, ctx->data_len, &ctx->req_id, enc_key);
367 if (unet_auth_data_validate(ctx->auth_key, ctx->data, ctx->data_len, timestamp, NULL))
368 goto error;
369
370 *data_len = ctx->data_len;
371 ret = ctx->data;
372 ctx->data = NULL;
373 pex_msg_update_ctx_free(ctx);
374
375 return ret;
376
377 error:
378 pex_msg_update_ctx_free(ctx);
379 *data_len = -1;
380 return NULL;
381 }
382
383 static void
384 pex_gc_cb(struct uloop_timeout *t)
385 {
386 struct pex_msg_update_recv_ctx *ctx, *tmp;
387
388 list_for_each_entry_safe(ctx, tmp, &requests, list) {
389 if (++ctx->idle <= 3)
390 continue;
391
392 pex_msg_update_ctx_free(ctx);
393 }
394
395 if (!list_empty(&requests))
396 uloop_timeout_set(t, 1000);
397 }
398
399 int pex_open(void *addr, size_t addr_len, pex_recv_cb_t cb, bool server)
400 {
401 struct sockaddr *sa = addr;
402 int yes = 1, no = 0;
403 int fd;
404
405 pex_recv_cb = cb;
406
407 pex_urandom = fopen("/dev/urandom", "r");
408 if (!pex_urandom)
409 return -1;
410
411 fd = socket(sa->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
412 if (fd < 0)
413 goto close_urandom;
414
415 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
416 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
417
418 if (server) {
419 if (bind(fd, addr, addr_len) < 0) {
420 perror("bind");
421 goto close_socket;
422 }
423
424 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
425 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
426 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
427 } else {
428 if (connect(fd, addr, addr_len) < 0) {
429 perror("connect");
430 goto close_socket;
431 }
432 }
433
434 pex_fd.fd = fd;
435 pex_fd.cb = pex_fd_cb;
436 uloop_fd_add(&pex_fd, ULOOP_READ);
437
438 gc_timer.cb = pex_gc_cb;
439
440 return 0;
441
442 close_socket:
443 close(fd);
444 close_urandom:
445 fclose(pex_urandom);
446 return -1;
447 }
448
449 void pex_close(void)
450 {
451 if (!pex_fd.cb)
452 return;
453
454 fclose(pex_urandom);
455 uloop_fd_delete(&pex_fd);
456 close(pex_fd.fd);
457 pex_fd.cb = NULL;
458 pex_urandom = NULL;
459 }