services: switch to vlist
[project/unetd.git] / network.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <arpa/inet.h>
6 #include <sys/wait.h>
7 #include <libubox/avl-cmp.h>
8 #include <libubox/utils.h>
9 #include <libubox/blobmsg_json.h>
10 #include "unetd.h"
11
12 enum {
13 NETDATA_ATTR_CONFIG,
14 NETDATA_ATTR_HOSTS,
15 NETDATA_ATTR_GROUPS,
16 NETDATA_ATTR_SERVICES,
17 __NETDATA_ATTR_MAX,
18 };
19
20 static const struct blobmsg_policy netdata_policy[__NETDATA_ATTR_MAX] = {
21 [NETDATA_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE },
22 [NETDATA_ATTR_HOSTS] = { "hosts", BLOBMSG_TYPE_TABLE },
23 [NETDATA_ATTR_SERVICES] = { "services", BLOBMSG_TYPE_TABLE },
24 };
25
26 enum {
27 NETCONF_ATTR_ID,
28 NETCONF_ATTR_PORT,
29 NETCONF_ATTR_PEX_PORT,
30 NETCONF_ATTR_KEEPALIVE,
31 __NETCONF_ATTR_MAX
32 };
33
34 static const struct blobmsg_policy netconf_policy[__NETCONF_ATTR_MAX] = {
35 [NETCONF_ATTR_ID] = { "id", BLOBMSG_TYPE_STRING },
36 [NETCONF_ATTR_PORT] = { "port", BLOBMSG_TYPE_INT32 },
37 [NETCONF_ATTR_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 },
38 [NETCONF_ATTR_KEEPALIVE] = { "keepalive", BLOBMSG_TYPE_INT32 },
39 };
40
41 const struct blobmsg_policy network_policy[__NETWORK_ATTR_MAX] = {
42 [NETWORK_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
43 [NETWORK_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
44 [NETWORK_ATTR_KEY] = { "key", BLOBMSG_TYPE_STRING },
45 [NETWORK_ATTR_FILE] = { "file", BLOBMSG_TYPE_STRING },
46 [NETWORK_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE },
47 [NETWORK_ATTR_INTERFACE] = { "interface", BLOBMSG_TYPE_STRING },
48 [NETWORK_ATTR_KEEPALIVE] = { "keepalive", BLOBMSG_TYPE_INT32 },
49 [NETWORK_ATTR_DOMAIN] = { "domain", BLOBMSG_TYPE_STRING },
50 [NETWORK_ATTR_UPDATE_CMD] = { "update-cmd", BLOBMSG_TYPE_STRING },
51 };
52
53 AVL_TREE(networks, avl_strcmp, false, NULL);
54 static struct blob_buf b;
55
56 static void network_load_config_data(struct network *net, struct blob_attr *data)
57 {
58 struct blob_attr *tb[__NETCONF_ATTR_MAX];
59 struct blob_attr *cur;
60 siphash_key_t key = {};
61
62 blobmsg_parse(netconf_policy, __NETCONF_ATTR_MAX, tb,
63 blobmsg_data(data), blobmsg_len(data));
64
65 if ((cur = tb[NETCONF_ATTR_PORT]) != NULL)
66 net->net_config.port = blobmsg_get_u32(cur);
67 else
68 net->net_config.port = 51820;
69
70 if ((cur = tb[NETCONF_ATTR_PEX_PORT]) != NULL)
71 net->net_config.pex_port = blobmsg_get_u32(cur);
72
73 if ((cur = tb[NETCONF_ATTR_ID]) != NULL) {
74 const char *id = blobmsg_get_string(cur);
75 siphash_to_le64(&net->net_config.addr.network_id, id, strlen(id), &key);
76 } else {
77 siphash_to_le64(&net->net_config.addr.network_id, &net->net_config.port,
78 sizeof(net->net_config.port), &key);
79 }
80
81 net->net_config.addr.network_id[0] = 0xfd;
82 network_fill_host_addr(&net->net_config.addr, net->config.pubkey);
83
84 if (net->config.keepalive >= 0)
85 net->net_config.keepalive = net->config.keepalive;
86 else if ((cur = tb[NETCONF_ATTR_KEEPALIVE]) != NULL)
87 net->net_config.keepalive = blobmsg_get_u32(cur);
88 else
89 net->net_config.keepalive = 0;
90 }
91
92 static int network_load_data(struct network *net, struct blob_attr *data)
93 {
94 struct blob_attr *tb[__NETDATA_ATTR_MAX];
95
96 blobmsg_parse(netdata_policy, __NETDATA_ATTR_MAX, tb,
97 blobmsg_data(data), blobmsg_len(data));
98
99 network_load_config_data(net, tb[NETDATA_ATTR_CONFIG]);
100 network_hosts_add(net, tb[NETDATA_ATTR_HOSTS]);
101 network_services_add(net, tb[NETDATA_ATTR_SERVICES]);
102
103 return 0;
104 }
105
106 static int network_load_file(struct network *net)
107 {
108 blob_buf_init(&b, 0);
109
110 if (!blobmsg_add_json_from_file(&b, net->config.file))
111 return -1;
112
113 return network_load_data(net, b.head);
114 }
115
116 static void
117 network_fill_ip(struct blob_buf *buf, int af, union network_addr *addr, int mask)
118 {
119 char *str;
120 void *c;
121
122 c = blobmsg_open_table(buf, NULL);
123
124 blobmsg_printf(buf, "mask", "%d", mask);
125
126 str = blobmsg_alloc_string_buffer(buf, "ipaddr", INET6_ADDRSTRLEN);
127 inet_ntop(af, addr, str, INET6_ADDRSTRLEN);
128 blobmsg_add_string_buffer(buf);
129
130 blobmsg_close_table(buf, c);
131 }
132
133 static void
134 network_fill_ipaddr_list(struct network_host *host, struct blob_buf *b, bool ipv6)
135 {
136 union network_addr addr = {};
137 struct blob_attr *cur;
138 void *c;
139 int rem;
140 int af;
141
142 af = ipv6 ? AF_INET6 : AF_INET;
143 blobmsg_for_each_attr(cur, host->peer.ipaddr, rem) {
144 const char *str = blobmsg_get_string(cur);
145
146 if (!!strchr(str, ':') != ipv6)
147 continue;
148
149 if (inet_pton(af, str, &addr) != 1)
150 continue;
151
152 c = blobmsg_open_table(b, NULL);
153 blobmsg_add_string(b, "ipaddr", str);
154 blobmsg_add_string(b, "mask", ipv6 ? "128" : "32");
155 blobmsg_close_table(b, c);
156 }
157 }
158
159 static void
160 network_fill_ip_settings(struct network *net, struct blob_buf *buf)
161 {
162 struct network_host *host = net->net_config.local_host;
163 void *c;
164
165 c = blobmsg_open_array(buf, "ipaddr");
166 network_fill_ipaddr_list(host, buf, false);
167 blobmsg_close_array(buf, c);
168
169 c = blobmsg_open_array(buf, "ip6addr");
170 network_fill_ip(buf, AF_INET6, &host->peer.local_addr, 64);
171 network_fill_ipaddr_list(host, buf, true);
172 blobmsg_close_array(buf, c);
173 }
174
175 static void
176 __network_fill_host_subnets(struct network_host *host, struct blob_buf *b, bool ipv6)
177 {
178 union network_addr addr = {};
179 struct blob_attr *cur;
180 void *c;
181 int af;
182 int mask;
183 int rem;
184
185 af = ipv6 ? AF_INET6 : AF_INET;
186 blobmsg_for_each_attr(cur, host->peer.subnet, rem) {
187 const char *str = blobmsg_get_string(cur);
188 char *buf;
189
190 if (!!strchr(str, ':') != ipv6)
191 continue;
192
193 if (network_get_subnet(af, &addr, &mask, str))
194 continue;
195
196 c = blobmsg_open_table(b, NULL);
197
198 buf = blobmsg_alloc_string_buffer(b, "target", INET6_ADDRSTRLEN);
199 inet_ntop(af, &addr, buf, INET6_ADDRSTRLEN);
200 blobmsg_add_string_buffer(b);
201
202 blobmsg_printf(b, "netmask", "%d", mask);
203
204 blobmsg_close_table(b, c);
205 }
206
207 blobmsg_for_each_attr(cur, host->peer.ipaddr, rem) {
208 const char *str = blobmsg_get_string(cur);
209
210 if (!!strchr(str, ':') != ipv6)
211 continue;
212
213 if (inet_pton(af, str, &addr) != 1)
214 continue;
215
216 c = blobmsg_open_table(b, NULL);
217 blobmsg_add_string(b, "target", str);
218 blobmsg_add_string(b, "netmask", ipv6 ? "128" : "32");
219 blobmsg_close_table(b, c);
220 }
221 }
222
223 static void
224 __network_fill_subnets(struct network *net, struct blob_buf *buf, bool ipv6)
225 {
226 struct network_host *host;
227 void *c;
228
229 c = blobmsg_open_array(buf, ipv6 ? "routes6": "routes");
230 avl_for_each_element(&net->hosts, host, node) {
231 if (host == net->net_config.local_host)
232 continue;
233 __network_fill_host_subnets(host, buf, ipv6);
234 }
235 blobmsg_close_array(buf, c);
236 }
237
238
239 static void
240 network_fill_subnets(struct network *net, struct blob_buf *buf)
241 {
242 __network_fill_subnets(net, buf, false);
243 __network_fill_subnets(net, buf, true);
244 }
245
246 static void
247 network_do_update(struct network *net, bool up)
248 {
249 if (!net->net_config.local_host)
250 up = false;
251
252 blob_buf_init(&b, 0);
253 blobmsg_add_u32(&b, "action", 0);
254 blobmsg_add_string(&b, "ifname", network_name(net));
255 blobmsg_add_u8(&b, "link-up", up);
256
257 if (up) {
258 network_fill_ip_settings(net, &b);
259 network_fill_subnets(net, &b);
260 }
261
262 if (debug) {
263 char *s = blobmsg_format_json(b.head, true);
264 D_NET(net, "update: %s", s);
265 free(s);
266 }
267
268 if (net->config.update_cmd) {
269 const char *argv[] = { net->config.update_cmd, NULL, NULL };
270 int pid, stat;
271
272 pid = fork();
273 if (pid == 0) {
274 argv[1] = blobmsg_format_json(b.head, true);
275 execvp(argv[0], (char **)argv);
276 exit(1);
277 }
278 waitpid(pid, &stat, 0);
279 }
280
281 if (!net->config.interface)
282 return;
283
284 blobmsg_add_string(&b, "interface", net->config.interface);
285 unetd_ubus_netifd_update(b.head);
286 }
287
288 static int network_reload(struct network *net)
289 {
290 int ret;
291
292 net->prev_local_host = net->net_config.local_host;
293
294 memset(&net->net_config, 0, sizeof(net->net_config));
295
296 network_pex_close(net);
297 network_services_free(net);
298 network_hosts_update_start(net);
299 network_services_update_start(net);
300
301 switch (net->config.type) {
302 case NETWORK_TYPE_FILE:
303 ret = network_load_file(net);
304 break;
305 case NETWORK_TYPE_INLINE:
306 ret = network_load_data(net, net->config.net_data);
307 break;
308 }
309
310 network_services_update_done(net);
311 network_hosts_update_done(net);
312 uloop_timeout_set(&net->connect_timer, 10);
313
314 net->prev_local_host = NULL;
315
316 unetd_write_hosts();
317 network_do_update(net, true);
318 network_pex_open(net);
319
320 return ret;
321 }
322
323 static int network_setup(struct network *net)
324 {
325 if (wg_init_network(net)) {
326 fprintf(stderr, "Setup failed for network %s\n", network_name(net));
327 return -1;
328 }
329
330 return 0;
331 }
332
333 static void network_teardown(struct network *net)
334 {
335 network_do_update(net, false);
336 network_pex_close(net);
337 network_hosts_free(net);
338 network_services_free(net);
339 wg_cleanup_network(net);
340 }
341
342 static void
343 network_destroy(struct network *net)
344 {
345 network_teardown(net);
346 avl_delete(&networks, &net->node);
347 free(net->config.data);
348 free(net);
349 }
350
351 static int
352 network_set_config(struct network *net, struct blob_attr *config)
353 {
354 struct blob_attr *tb[__NETWORK_ATTR_MAX];
355 struct blob_attr *cur;
356
357 if (net->config.data && blob_attr_equal(net->config.data, config))
358 goto reload;
359
360 network_teardown(net);
361
362 free(net->config.data);
363 memset(&net->config, 0, sizeof(net->config));
364
365 net->config.data = blob_memdup(config);
366 blobmsg_parse(network_policy, __NETWORK_ATTR_MAX, tb,
367 blobmsg_data(net->config.data),
368 blobmsg_len(net->config.data));
369
370 if ((cur = tb[NETWORK_ATTR_TYPE]) == NULL)
371 goto invalid;
372
373 if (!strcmp(blobmsg_get_string(cur), "file"))
374 net->config.type = NETWORK_TYPE_FILE;
375 else if (!strcmp(blobmsg_get_string(cur), "inline"))
376 net->config.type = NETWORK_TYPE_INLINE;
377 else
378 goto invalid;
379
380 if ((cur = tb[NETWORK_ATTR_KEEPALIVE]) != NULL)
381 net->config.keepalive = blobmsg_get_u32(cur);
382 else
383 net->config.keepalive = -1;
384
385 switch (net->config.type) {
386 case NETWORK_TYPE_FILE:
387 if ((cur = tb[NETWORK_ATTR_FILE]) != NULL)
388 net->config.file = blobmsg_get_string(cur);
389 else
390 goto invalid;
391 break;
392 case NETWORK_TYPE_INLINE:
393 net->config.net_data = tb[NETWORK_ATTR_DATA];
394 if (!net->config.net_data)
395 goto invalid;
396 break;
397 }
398
399 if ((cur = tb[NETWORK_ATTR_INTERFACE]) != NULL)
400 net->config.interface = blobmsg_get_string(cur);
401
402 if ((cur = tb[NETWORK_ATTR_UPDATE_CMD]) != NULL)
403 net->config.update_cmd = blobmsg_get_string(cur);
404
405 if ((cur = tb[NETWORK_ATTR_DOMAIN]) != NULL)
406 net->config.domain = blobmsg_get_string(cur);
407
408 if ((cur = tb[NETWORK_ATTR_KEY]) == NULL)
409 goto invalid;
410
411 if (b64_decode(blobmsg_get_string(cur), net->config.key, sizeof(net->config.key)) !=
412 sizeof(net->config.key))
413 goto invalid;
414
415 curve25519_generate_public(net->config.pubkey, net->config.key);
416
417 if (network_setup(net))
418 goto invalid;
419
420 reload:
421 network_reload(net);
422
423 return 0;
424
425 invalid:
426 network_destroy(net);
427 return -1;
428 }
429
430 static struct network *
431 network_alloc(const char *name)
432 {
433 struct network *net;
434 char *name_buf;
435
436 net = calloc_a(sizeof(*net), &name_buf, strlen(name) + 1);
437 net->node.key = strcpy(name_buf, name);
438 avl_insert(&networks, &net->node);
439
440 network_pex_init(net);
441 network_hosts_init(net);
442 network_services_init(net);
443
444 return net;
445 }
446
447 void network_fill_host_addr(union network_addr *addr, uint8_t *pubkey)
448 {
449 siphash_key_t key = {
450 .key = {
451 get_unaligned_le64(addr->network_id),
452 get_unaligned_le64(addr->network_id)
453 }
454 };
455
456 siphash_to_le64(&addr->host_addr, pubkey, CURVE25519_KEY_SIZE, &key);
457 }
458
459 int unetd_network_add(const char *name, struct blob_attr *config)
460 {
461 struct network *net;
462
463 if (strchr(name, '/'))
464 return -1;
465
466 net = avl_find_element(&networks, name, net, node);
467 if (!net)
468 net = network_alloc(name);
469
470 return network_set_config(net, config);
471 }
472
473 int unetd_network_remove(const char *name)
474 {
475 struct network *net;
476
477 net = avl_find_element(&networks, name, net, node);
478 if (!net)
479 return -1;
480
481 network_destroy(net);
482
483 return 0;
484 }
485
486 void network_free_all(void)
487 {
488 struct network *net, *tmp;
489
490 avl_for_each_element_safe(&networks, net, node, tmp)
491 network_destroy(net);
492 }