network: add support for configuring extra peers via a separate json file
[project/unetd.git] / network.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #define _GNU_SOURCE
6 #include <arpa/inet.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/wait.h>
10 #include <net/if.h>
11 #include <libubox/avl-cmp.h>
12 #include <libubox/utils.h>
13 #include <libubox/blobmsg_json.h>
14 #include "unetd.h"
15
16 enum {
17 NETDATA_ATTR_CONFIG,
18 NETDATA_ATTR_HOSTS,
19 NETDATA_ATTR_GROUPS,
20 NETDATA_ATTR_SERVICES,
21 __NETDATA_ATTR_MAX,
22 };
23
24 static const struct blobmsg_policy netdata_policy[__NETDATA_ATTR_MAX] = {
25 [NETDATA_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE },
26 [NETDATA_ATTR_HOSTS] = { "hosts", BLOBMSG_TYPE_TABLE },
27 [NETDATA_ATTR_SERVICES] = { "services", BLOBMSG_TYPE_TABLE },
28 };
29
30 enum {
31 NETCONF_ATTR_ID,
32 NETCONF_ATTR_PORT,
33 NETCONF_ATTR_PEX_PORT,
34 NETCONF_ATTR_KEEPALIVE,
35 __NETCONF_ATTR_MAX
36 };
37
38 static const struct blobmsg_policy netconf_policy[__NETCONF_ATTR_MAX] = {
39 [NETCONF_ATTR_ID] = { "id", BLOBMSG_TYPE_STRING },
40 [NETCONF_ATTR_PORT] = { "port", BLOBMSG_TYPE_INT32 },
41 [NETCONF_ATTR_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 },
42 [NETCONF_ATTR_KEEPALIVE] = { "keepalive", BLOBMSG_TYPE_INT32 },
43 };
44
45 const struct blobmsg_policy network_policy[__NETWORK_ATTR_MAX] = {
46 [NETWORK_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
47 [NETWORK_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
48 [NETWORK_ATTR_AUTH_KEY] = { "auth_key", BLOBMSG_TYPE_STRING },
49 [NETWORK_ATTR_KEY] = { "key", BLOBMSG_TYPE_STRING },
50 [NETWORK_ATTR_FILE] = { "file", BLOBMSG_TYPE_STRING },
51 [NETWORK_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE },
52 [NETWORK_ATTR_INTERFACE] = { "interface", BLOBMSG_TYPE_STRING },
53 [NETWORK_ATTR_KEEPALIVE] = { "keepalive", BLOBMSG_TYPE_INT32 },
54 [NETWORK_ATTR_DOMAIN] = { "domain", BLOBMSG_TYPE_STRING },
55 [NETWORK_ATTR_UPDATE_CMD] = { "update-cmd", BLOBMSG_TYPE_STRING },
56 [NETWORK_ATTR_TUNNELS] = { "tunnels", BLOBMSG_TYPE_TABLE },
57 [NETWORK_ATTR_AUTH_CONNECT] = { "auth_connect", BLOBMSG_TYPE_ARRAY },
58 [NETWORK_ATTR_PEER_DATA] = { "peer_data", BLOBMSG_TYPE_ARRAY },
59 };
60
61 AVL_TREE(networks, avl_strcmp, false, NULL);
62 static struct blob_buf b;
63
64 static void network_load_config_data(struct network *net, struct blob_attr *data)
65 {
66 struct blob_attr *tb[__NETCONF_ATTR_MAX];
67 struct blob_attr *cur;
68 siphash_key_t key = {};
69
70 blobmsg_parse(netconf_policy, __NETCONF_ATTR_MAX, tb,
71 blobmsg_data(data), blobmsg_len(data));
72
73 if ((cur = tb[NETCONF_ATTR_PORT]) != NULL)
74 net->net_config.port = blobmsg_get_u32(cur);
75 else
76 net->net_config.port = 51820;
77
78 if ((cur = tb[NETCONF_ATTR_PEX_PORT]) != NULL)
79 net->net_config.pex_port = blobmsg_get_u32(cur);
80
81 if ((cur = tb[NETCONF_ATTR_ID]) != NULL) {
82 const char *id = blobmsg_get_string(cur);
83 siphash_to_le64(&net->net_config.addr.network_id, id, strlen(id), &key);
84 } else {
85 siphash_to_le64(&net->net_config.addr.network_id, &net->net_config.port,
86 sizeof(net->net_config.port), &key);
87 }
88
89 net->net_config.addr.network_id[0] = 0xfd;
90 network_fill_host_addr(&net->net_config.addr, net->config.pubkey);
91
92 if (net->config.keepalive >= 0)
93 net->net_config.keepalive = net->config.keepalive;
94 else if ((cur = tb[NETCONF_ATTR_KEEPALIVE]) != NULL)
95 net->net_config.keepalive = blobmsg_get_u32(cur);
96 else
97 net->net_config.keepalive = 0;
98 }
99
100 static int network_load_data(struct network *net, struct blob_attr *data)
101 {
102 struct blob_attr *tb[__NETDATA_ATTR_MAX];
103
104 blobmsg_parse(netdata_policy, __NETDATA_ATTR_MAX, tb,
105 blobmsg_data(data), blobmsg_len(data));
106
107 network_load_config_data(net, tb[NETDATA_ATTR_CONFIG]);
108 network_hosts_add(net, tb[NETDATA_ATTR_HOSTS]);
109 network_services_add(net, tb[NETDATA_ATTR_SERVICES]);
110
111 return 0;
112 }
113
114 static int network_load_file(struct network *net)
115 {
116 blob_buf_init(&b, 0);
117
118 if (!blobmsg_add_json_from_file(&b, net->config.file))
119 return -1;
120
121 return network_load_data(net, b.head);
122 }
123
124 static int network_load_dynamic(struct network *net)
125 {
126 const char *json = NULL;
127 char *fname = NULL;
128 struct stat st;
129 FILE *f = NULL;
130 int ret = -1;
131
132 if (asprintf(&fname, "%s/%s.bin", data_dir, network_name(net)) < 0)
133 return -1;
134
135 f = fopen(fname, "r");
136 free(fname);
137
138 if (!f) {
139 D_NET(net, "failed to open %s/%s.bin\n", data_dir, network_name(net));
140 return -1;
141 }
142
143 if (fstat(fileno(f), &st) < 0)
144 goto out;
145
146 net->net_data_len = st.st_size;
147 net->net_data = realloc(net->net_data, net->net_data_len + 1);
148 memset(net->net_data + net->net_data_len, 0, 1);
149 if (fread(net->net_data, 1, net->net_data_len, f) != net->net_data_len ||
150 unet_auth_data_validate(net->config.auth_key, net->net_data,
151 net->net_data_len, &net->net_data_version, &json)) {
152 net->net_data_len = 0;
153 goto out;
154 }
155
156 fclose(f);
157 blob_buf_init(&b, 0);
158 if (!blobmsg_add_json_from_string(&b, json)) {
159 net->net_data_len = 0;
160 return -1;
161 }
162
163 return network_load_data(net, b.head);
164
165 out:
166 fclose(f);
167 return ret;
168 }
169
170 int network_save_dynamic(struct network *net)
171 {
172 char *fname = NULL, *fname2;
173 size_t len;
174 FILE *f;
175 int fd, ret;
176
177 if (net->config.type != NETWORK_TYPE_DYNAMIC ||
178 !net->net_data_len)
179 return -1;
180
181 if (asprintf(&fname, "%s/%s.bin.XXXXXXXX", data_dir, network_name(net)) < 0)
182 return -1;
183
184 fd = mkstemp(fname);
185 if (fd < 0)
186 goto error;
187
188 f = fdopen(fd, "w");
189 if (!f) {
190 close(fd);
191 goto error;
192 }
193
194 len = fwrite(net->net_data, 1, net->net_data_len, f);
195 fflush(f);
196 fdatasync(fd);
197 fclose(f);
198
199 if (len != net->net_data_len)
200 goto error;
201
202 fname2 = strdup(fname);
203 *strrchr(fname2, '.') = 0;
204 ret = rename(fname, fname2);
205 free(fname2);
206
207 if (ret)
208 unlink(fname);
209 free(fname);
210
211 return ret;
212
213 error:
214 free(fname);
215 return -1;
216 }
217
218
219 static void
220 network_fill_ip(struct blob_buf *buf, int af, union network_addr *addr, int mask)
221 {
222 char *str;
223 void *c;
224
225 c = blobmsg_open_table(buf, NULL);
226
227 blobmsg_printf(buf, "mask", "%d", mask);
228
229 str = blobmsg_alloc_string_buffer(buf, "ipaddr", INET6_ADDRSTRLEN);
230 inet_ntop(af, addr, str, INET6_ADDRSTRLEN);
231 blobmsg_add_string_buffer(buf);
232
233 blobmsg_close_table(buf, c);
234 }
235
236 static void
237 network_fill_ipaddr_list(struct network_host *host, struct blob_buf *b, bool ipv6)
238 {
239 union network_addr addr = {};
240 struct blob_attr *cur;
241 void *c;
242 int rem;
243 int af;
244
245 af = ipv6 ? AF_INET6 : AF_INET;
246 blobmsg_for_each_attr(cur, host->peer.ipaddr, rem) {
247 const char *str = blobmsg_get_string(cur);
248
249 if (!!strchr(str, ':') != ipv6)
250 continue;
251
252 if (inet_pton(af, str, &addr) != 1)
253 continue;
254
255 c = blobmsg_open_table(b, NULL);
256 blobmsg_add_string(b, "ipaddr", str);
257 blobmsg_add_string(b, "mask", ipv6 ? "128" : "32");
258 blobmsg_close_table(b, c);
259 }
260 }
261
262 static void
263 network_fill_ip_settings(struct network *net, struct blob_buf *buf)
264 {
265 struct network_host *host = net->net_config.local_host;
266 void *c;
267
268 c = blobmsg_open_array(buf, "ipaddr");
269 network_fill_ipaddr_list(host, buf, false);
270 blobmsg_close_array(buf, c);
271
272 c = blobmsg_open_array(buf, "ip6addr");
273 network_fill_ip(buf, AF_INET6, &host->peer.local_addr, 64);
274 network_fill_ipaddr_list(host, buf, true);
275 blobmsg_close_array(buf, c);
276 }
277
278 static void
279 __network_fill_host_subnets(struct network_host *host, struct blob_buf *b, bool ipv6)
280 {
281 union network_addr addr = {};
282 struct blob_attr *cur;
283 void *c;
284 int af;
285 int mask;
286 int rem;
287
288 af = ipv6 ? AF_INET6 : AF_INET;
289 blobmsg_for_each_attr(cur, host->peer.subnet, rem) {
290 const char *str = blobmsg_get_string(cur);
291 char *buf;
292
293 if (!!strchr(str, ':') != ipv6)
294 continue;
295
296 if (network_get_subnet(af, &addr, &mask, str))
297 continue;
298
299 c = blobmsg_open_table(b, NULL);
300
301 buf = blobmsg_alloc_string_buffer(b, "target", INET6_ADDRSTRLEN);
302 inet_ntop(af, &addr, buf, INET6_ADDRSTRLEN);
303 blobmsg_add_string_buffer(b);
304
305 blobmsg_printf(b, "netmask", "%d", mask);
306
307 blobmsg_close_table(b, c);
308 }
309
310 blobmsg_for_each_attr(cur, host->peer.ipaddr, rem) {
311 const char *str = blobmsg_get_string(cur);
312
313 if (!!strchr(str, ':') != ipv6)
314 continue;
315
316 if (inet_pton(af, str, &addr) != 1)
317 continue;
318
319 c = blobmsg_open_table(b, NULL);
320 blobmsg_add_string(b, "target", str);
321 blobmsg_add_string(b, "netmask", ipv6 ? "128" : "32");
322 blobmsg_close_table(b, c);
323 }
324 }
325
326 static void
327 __network_fill_subnets(struct network *net, struct blob_buf *buf, bool ipv6)
328 {
329 struct network_host *host;
330 void *c;
331
332 c = blobmsg_open_array(buf, ipv6 ? "routes6": "routes");
333 avl_for_each_element(&net->hosts, host, node) {
334 if (host == net->net_config.local_host)
335 continue;
336 __network_fill_host_subnets(host, buf, ipv6);
337 }
338 blobmsg_close_array(buf, c);
339 }
340
341
342 static void
343 network_fill_subnets(struct network *net, struct blob_buf *buf)
344 {
345 __network_fill_subnets(net, buf, false);
346 __network_fill_subnets(net, buf, true);
347 }
348
349 static void
350 network_do_update(struct network *net, bool up)
351 {
352 if (!net->net_config.local_host)
353 up = false;
354
355 blob_buf_init(&b, 0);
356 blobmsg_add_u32(&b, "action", 0);
357 blobmsg_add_string(&b, "ifname", network_name(net));
358 blobmsg_add_u8(&b, "link-up", up);
359
360 if (up) {
361 network_fill_ip_settings(net, &b);
362 network_fill_subnets(net, &b);
363 }
364
365 if (debug) {
366 char *s = blobmsg_format_json(b.head, true);
367 D_NET(net, "update: %s", s);
368 free(s);
369 }
370
371 if (net->config.update_cmd) {
372 const char *argv[] = { net->config.update_cmd, NULL, NULL };
373 int pid, stat;
374
375 pid = fork();
376 if (pid == 0) {
377 argv[1] = blobmsg_format_json(b.head, true);
378 execvp(argv[0], (char **)argv);
379 exit(1);
380 }
381 waitpid(pid, &stat, 0);
382 }
383
384 if (!net->config.interface)
385 return;
386
387 blobmsg_add_string(&b, "interface", net->config.interface);
388 unetd_ubus_netifd_update(b.head);
389 }
390
391 static void network_reload(struct uloop_timeout *t)
392 {
393 struct network *net = container_of(t, struct network, reload_timer);
394
395 net->prev_local_host = net->net_config.local_host;
396
397 memset(&net->net_config, 0, sizeof(net->net_config));
398
399 network_pex_close(net);
400 network_services_free(net);
401 network_hosts_update_start(net);
402 network_services_update_start(net);
403
404 switch (net->config.type) {
405 case NETWORK_TYPE_FILE:
406 network_load_file(net);
407 break;
408 case NETWORK_TYPE_INLINE:
409 network_load_data(net, net->config.net_data);
410 break;
411 case NETWORK_TYPE_DYNAMIC:
412 network_load_dynamic(net);
413 break;
414 }
415
416 network_services_update_done(net);
417 network_hosts_update_done(net);
418 uloop_timeout_set(&net->connect_timer, 10);
419
420 net->prev_local_host = NULL;
421
422 unetd_write_hosts();
423 network_do_update(net, true);
424 network_pex_open(net);
425 }
426
427 static int network_setup(struct network *net)
428 {
429 if (wg_init_network(net)) {
430 fprintf(stderr, "Setup failed for network %s\n", network_name(net));
431 return -1;
432 }
433
434 net->ifindex = if_nametoindex(network_name(net));
435 if (!net->ifindex) {
436 fprintf(stderr, "Could not get ifindex for network %s\n", network_name(net));
437 return -1;
438 }
439
440 return 0;
441 }
442
443 static void network_teardown(struct network *net)
444 {
445 uloop_timeout_cancel(&net->connect_timer);
446 uloop_timeout_cancel(&net->reload_timer);
447 network_do_update(net, false);
448 network_pex_close(net);
449 network_pex_free(net);
450 network_hosts_free(net);
451 network_services_free(net);
452 wg_cleanup_network(net);
453 }
454
455 static void
456 network_destroy(struct network *net)
457 {
458 network_teardown(net);
459 avl_delete(&networks, &net->node);
460 free(net->net_data);
461 free(net->config.data);
462 free(net);
463 }
464
465 static int
466 network_set_config(struct network *net, struct blob_attr *config)
467 {
468 struct blob_attr *tb[__NETWORK_ATTR_MAX];
469 struct blob_attr *cur;
470
471 if (net->config.data && blob_attr_equal(net->config.data, config))
472 goto reload;
473
474 network_teardown(net);
475
476 free(net->config.data);
477 memset(&net->config, 0, sizeof(net->config));
478
479 net->config.data = blob_memdup(config);
480 blobmsg_parse(network_policy, __NETWORK_ATTR_MAX, tb,
481 blobmsg_data(net->config.data),
482 blobmsg_len(net->config.data));
483
484 if ((cur = tb[NETWORK_ATTR_TYPE]) == NULL ||
485 !strlen(blobmsg_get_string(cur)) ||
486 !strcmp(blobmsg_get_string(cur), "dynamic"))
487 net->config.type = NETWORK_TYPE_DYNAMIC;
488 else if (!strcmp(blobmsg_get_string(cur), "file"))
489 net->config.type = NETWORK_TYPE_FILE;
490 else if (!strcmp(blobmsg_get_string(cur), "inline"))
491 net->config.type = NETWORK_TYPE_INLINE;
492 else
493 goto invalid;
494
495 if ((cur = tb[NETWORK_ATTR_KEEPALIVE]) != NULL)
496 net->config.keepalive = blobmsg_get_u32(cur);
497 else
498 net->config.keepalive = -1;
499
500 switch (net->config.type) {
501 case NETWORK_TYPE_FILE:
502 if ((cur = tb[NETWORK_ATTR_FILE]) != NULL)
503 net->config.file = blobmsg_get_string(cur);
504 else
505 goto invalid;
506 break;
507 case NETWORK_TYPE_INLINE:
508 net->config.net_data = tb[NETWORK_ATTR_DATA];
509 if (!net->config.net_data)
510 goto invalid;
511 break;
512 case NETWORK_TYPE_DYNAMIC:
513 if ((cur = tb[NETWORK_ATTR_AUTH_KEY]) == NULL)
514 goto invalid;
515
516 if (b64_decode(blobmsg_get_string(cur), net->config.auth_key,
517 sizeof(net->config.auth_key)) != sizeof(net->config.auth_key))
518 goto invalid;
519 break;
520 }
521
522 if ((cur = tb[NETWORK_ATTR_INTERFACE]) != NULL &&
523 strlen(blobmsg_get_string(cur)) > 0)
524 net->config.interface = blobmsg_get_string(cur);
525
526 if ((cur = tb[NETWORK_ATTR_UPDATE_CMD]) != NULL &&
527 strlen(blobmsg_get_string(cur)) > 0)
528 net->config.update_cmd = blobmsg_get_string(cur);
529
530 if ((cur = tb[NETWORK_ATTR_DOMAIN]) != NULL &&
531 strlen(blobmsg_get_string(cur)) > 0)
532 net->config.domain = blobmsg_get_string(cur);
533
534 if ((cur = tb[NETWORK_ATTR_TUNNELS]) != NULL)
535 net->config.tunnels = cur;
536
537 if ((cur = tb[NETWORK_ATTR_AUTH_CONNECT]) != NULL &&
538 blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
539 net->config.auth_connect = cur;
540
541 if ((cur = tb[NETWORK_ATTR_PEER_DATA]) != NULL &&
542 blobmsg_check_array(cur, BLOBMSG_TYPE_STRING) > 0)
543 net->config.peer_data = cur;
544
545 if ((cur = tb[NETWORK_ATTR_KEY]) == NULL)
546 goto invalid;
547
548 if (b64_decode(blobmsg_get_string(cur), net->config.key, sizeof(net->config.key)) !=
549 sizeof(net->config.key))
550 goto invalid;
551
552 curve25519_generate_public(net->config.pubkey, net->config.key);
553
554 if (network_setup(net))
555 goto invalid;
556
557 reload:
558 network_reload(&net->reload_timer);
559
560 return 0;
561
562 invalid:
563 network_destroy(net);
564 return -1;
565 }
566
567 static struct network *
568 network_alloc(const char *name)
569 {
570 struct network *net;
571 char *name_buf;
572
573 net = calloc_a(sizeof(*net), &name_buf, strlen(name) + 1);
574 net->node.key = strcpy(name_buf, name);
575 net->reload_timer.cb = network_reload;
576 avl_insert(&networks, &net->node);
577
578 network_pex_init(net);
579 network_hosts_init(net);
580 network_services_init(net);
581
582 return net;
583 }
584
585 void network_fill_host_addr(union network_addr *addr, uint8_t *pubkey)
586 {
587 siphash_key_t key = {
588 .key = {
589 get_unaligned_le64(addr->network_id),
590 get_unaligned_le64(addr->network_id)
591 }
592 };
593
594 siphash_to_le64(&addr->host_addr, pubkey, CURVE25519_KEY_SIZE, &key);
595 }
596
597 int unetd_network_add(const char *name, struct blob_attr *config)
598 {
599 struct network *net;
600
601 if (strchr(name, '/'))
602 return -1;
603
604 net = avl_find_element(&networks, name, net, node);
605 if (!net)
606 net = network_alloc(name);
607
608 return network_set_config(net, config);
609 }
610
611 int unetd_network_remove(const char *name)
612 {
613 struct network *net;
614
615 net = avl_find_element(&networks, name, net, node);
616 if (!net)
617 return -1;
618
619 network_destroy(net);
620
621 return 0;
622 }
623
624 void network_free_all(void)
625 {
626 struct network *net, *tmp;
627
628 avl_for_each_element_safe(&networks, net, node, tmp)
629 network_destroy(net);
630 }