pex: add support for figuring out the external data port via STUN servers
[project/unetd.git] / host.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <libubox/avl-cmp.h>
6 #include <libubox/blobmsg_json.h>
7 #include "unetd.h"
8
9 static LIST_HEAD(old_hosts);
10 static struct blob_buf b;
11
12 static int avl_key_cmp(const void *k1, const void *k2, void *ptr)
13 {
14 return memcmp(k1, k2, CURVE25519_KEY_SIZE);
15 }
16
17 static bool
18 network_peer_equal(struct network_peer *p1, struct network_peer *p2)
19 {
20 return !memcmp(&p1->local_addr, &p2->local_addr, sizeof(p1->local_addr)) &&
21 blob_attr_equal(p1->ipaddr, p2->ipaddr) &&
22 blob_attr_equal(p1->subnet, p2->subnet) &&
23 p1->port == p2->port;
24 }
25
26 static void
27 network_peer_update(struct vlist_tree *tree,
28 struct vlist_node *node_new,
29 struct vlist_node *node_old)
30 {
31 struct network *net = container_of(tree, struct network, peers);
32 struct network_peer *h_new = container_of_safe(node_new, struct network_peer, node);
33 struct network_peer *h_old = container_of_safe(node_old, struct network_peer, node);
34 int ret;
35
36 if (h_new && h_old) {
37 memcpy(&h_new->state, &h_old->state, sizeof(h_new->state));
38
39 if (network_peer_equal(h_new, h_old))
40 return;
41 }
42
43 if (h_new)
44 ret = wg_peer_update(net, h_new, h_old ? WG_PEER_UPDATE : WG_PEER_CREATE);
45 else
46 ret = wg_peer_update(net, h_old, WG_PEER_DELETE);
47
48 if (ret)
49 fprintf(stderr, "Failed to %s peer on network %s: %s\n",
50 h_new ? "update" : "delete", network_name(net),
51 strerror(-ret));
52 }
53
54 static struct network_group *
55 network_group_get(struct network *net, const char *name)
56 {
57 struct network_group *group;
58 char *name_buf;
59
60 group = avl_find_element(&net->groups, name, group, node);
61 if (group)
62 return group;
63
64 group = calloc_a(sizeof(*group), &name_buf, strlen(name) + 1);
65 group->node.key = strcpy(name_buf, name);
66 avl_insert(&net->groups, &group->node);
67
68 return group;
69 }
70
71 static void
72 network_host_add_group(struct network *net, struct network_host *host,
73 const char *name)
74 {
75 struct network_group *group;
76 int i;
77
78 group = network_group_get(net, name);
79 for (i = 0; i < group->n_members; i++)
80 if (group->members[i] == host)
81 return;
82
83 group->n_members++;
84 group->members = realloc(group->members, group->n_members * sizeof(*group->members));
85 group->members[group->n_members - 1] = host;
86 }
87
88 enum {
89 NETWORK_HOST_KEY,
90 NETWORK_HOST_GROUPS,
91 NETWORK_HOST_IPADDR,
92 NETWORK_HOST_SUBNET,
93 NETWORK_HOST_PORT,
94 NETWORK_HOST_PEX_PORT,
95 NETWORK_HOST_ENDPOINT,
96 NETWORK_HOST_GATEWAY,
97 __NETWORK_HOST_MAX
98 };
99
100 static const struct blobmsg_policy host_policy[__NETWORK_HOST_MAX] = {
101 [NETWORK_HOST_KEY] = { "key", BLOBMSG_TYPE_STRING },
102 [NETWORK_HOST_GROUPS] = { "groups", BLOBMSG_TYPE_ARRAY },
103 [NETWORK_HOST_IPADDR] = { "ipaddr", BLOBMSG_TYPE_ARRAY },
104 [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY },
105 [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 },
106 [NETWORK_HOST_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 },
107 [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING },
108 [NETWORK_HOST_GATEWAY] = { "gateway", BLOBMSG_TYPE_STRING },
109 };
110
111 static void
112 network_host_create(struct network *net, struct blob_attr *attr, bool dynamic)
113 {
114 struct blob_attr *tb[__NETWORK_HOST_MAX];
115 struct blob_attr *cur, *ipaddr, *subnet;
116 uint8_t key[CURVE25519_KEY_SIZE];
117 struct network_host *host = NULL;
118 struct network_peer *peer;
119 int ipaddr_len, subnet_len;
120 const char *endpoint, *gateway;
121 char *endpoint_buf, *gateway_buf;
122 int rem;
123
124 blobmsg_parse(host_policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
125
126 if (!tb[NETWORK_HOST_KEY])
127 return;
128
129 ipaddr_len = tb[NETWORK_HOST_IPADDR] ? blob_pad_len(tb[NETWORK_HOST_IPADDR]) : 0;
130 if (ipaddr_len &&
131 blobmsg_check_array(tb[NETWORK_HOST_IPADDR], BLOBMSG_TYPE_STRING) < 0)
132 ipaddr_len = 0;
133
134 subnet_len = tb[NETWORK_HOST_SUBNET] ? blob_pad_len(tb[NETWORK_HOST_SUBNET]) : 0;
135 if (subnet_len &&
136 blobmsg_check_array(tb[NETWORK_HOST_SUBNET], BLOBMSG_TYPE_STRING) < 0)
137 subnet_len = 0;
138
139 if ((cur = tb[NETWORK_HOST_ENDPOINT]) != NULL)
140 endpoint = blobmsg_get_string(cur);
141 else
142 endpoint = NULL;
143
144 if (!dynamic && (cur = tb[NETWORK_HOST_GATEWAY]) != NULL)
145 gateway = blobmsg_get_string(cur);
146 else
147 gateway = NULL;
148
149 if (b64_decode(blobmsg_get_string(tb[NETWORK_HOST_KEY]), key,
150 sizeof(key)) != sizeof(key))
151 return;
152
153 if (dynamic) {
154 struct network_dynamic_peer *dyn_peer;
155
156 /* don't override/alter hosts configured via network data */
157 peer = vlist_find(&net->peers, key, peer, node);
158 if (peer && !peer->dynamic &&
159 peer->node.version == net->peers.version)
160 return;
161
162 dyn_peer = calloc_a(sizeof(*dyn_peer),
163 &ipaddr, ipaddr_len,
164 &subnet, subnet_len,
165 &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0);
166 list_add_tail(&dyn_peer->list, &net->dynamic_peers);
167 peer = &dyn_peer->peer;
168 } else {
169 const char *name;
170 char *name_buf;
171
172 name = blobmsg_name(attr);
173 host = avl_find_element(&net->hosts, name, host, node);
174 if (host)
175 return;
176
177 host = calloc_a(sizeof(*host),
178 &name_buf, strlen(name) + 1,
179 &ipaddr, ipaddr_len,
180 &subnet, subnet_len,
181 &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0,
182 &gateway_buf, gateway ? strlen(endpoint) + 1 : 0);
183 host->node.key = strcpy(name_buf, name);
184 peer = &host->peer;
185 }
186
187 peer->dynamic = dynamic;
188 if ((cur = tb[NETWORK_HOST_IPADDR]) != NULL && ipaddr_len)
189 peer->ipaddr = memcpy(ipaddr, cur, ipaddr_len);
190 if ((cur = tb[NETWORK_HOST_SUBNET]) != NULL && subnet_len)
191 peer->subnet = memcpy(subnet, cur, subnet_len);
192 if ((cur = tb[NETWORK_HOST_PORT]) != NULL)
193 peer->port = blobmsg_get_u32(cur);
194 else
195 peer->port = net->net_config.port;
196 if ((cur = tb[NETWORK_HOST_PEX_PORT]) != NULL)
197 peer->pex_port = blobmsg_get_u32(cur);
198 else
199 peer->pex_port = net->net_config.pex_port;
200 if (endpoint)
201 peer->endpoint = strcpy(endpoint_buf, endpoint);
202 memcpy(peer->key, key, sizeof(key));
203
204 memcpy(&peer->local_addr.network_id,
205 &net->net_config.addr.network_id,
206 sizeof(peer->local_addr.network_id));
207 network_fill_host_addr(&peer->local_addr, peer->key);
208
209 if (!host)
210 return;
211
212 if (gateway)
213 host->gateway = strcpy(gateway_buf, gateway);
214
215 blobmsg_for_each_attr(cur, tb[NETWORK_HOST_GROUPS], rem) {
216 if (!blobmsg_check_attr(cur, false) ||
217 blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
218 continue;
219
220 network_host_add_group(net, host, blobmsg_get_string(cur));
221 }
222
223 avl_insert(&net->hosts, &host->node);
224 if (!memcmp(peer->key, net->config.pubkey, sizeof(key))) {
225 if (!net->prev_local_host ||
226 !network_peer_equal(&net->prev_local_host->peer, &host->peer))
227 net->net_config.local_host_changed = true;
228
229 net->net_config.local_host = host;
230 }
231 }
232
233 static void
234 network_hosts_load_dynamic_file(struct network *net, const char *file)
235 {
236 struct blob_attr *cur;
237 int rem;
238
239 blob_buf_init(&b, 0);
240
241 if (!blobmsg_add_json_from_file(&b, file))
242 return;
243
244 blob_for_each_attr(cur, b.head, rem)
245 network_host_create(net, cur, true);
246 }
247
248 static void
249 network_hosts_load_dynamic_peers(struct network *net)
250 {
251 struct network_dynamic_peer *dyn;
252 struct blob_attr *cur;
253 int rem;
254
255 if (!net->config.peer_data)
256 return;
257
258 blobmsg_for_each_attr(cur, net->config.peer_data, rem)
259 network_hosts_load_dynamic_file(net, blobmsg_get_string(cur));
260
261 blob_buf_free(&b);
262
263 list_for_each_entry(dyn, &net->dynamic_peers, list)
264 vlist_add(&net->peers, &dyn->peer.node, &dyn->peer.key);
265 }
266
267 static void
268 network_host_free_dynamic_peers(struct list_head *list)
269 {
270 struct network_dynamic_peer *dyn, *dyn_tmp;
271
272 list_for_each_entry_safe(dyn, dyn_tmp, list, list) {
273 list_del(&dyn->list);
274 free(dyn);
275 }
276 }
277
278 void network_hosts_reload_dynamic_peers(struct network *net)
279 {
280 struct network_peer *peer;
281 LIST_HEAD(old_entries);
282
283 if (!net->config.peer_data)
284 return;
285
286 list_splice_init(&net->dynamic_peers, &old_entries);
287
288 vlist_for_each_element(&net->peers, peer, node)
289 if (peer->dynamic)
290 peer->node.version = net->peers.version - 1;
291
292 network_hosts_load_dynamic_peers(net);
293
294 vlist_flush(&net->peers);
295
296 network_host_free_dynamic_peers(&old_entries);
297 }
298
299 void network_hosts_update_start(struct network *net)
300 {
301 struct network_host *host, *htmp;
302 struct network_group *group, *gtmp;
303
304 avl_remove_all_elements(&net->hosts, host, node, htmp)
305 list_add_tail(&host->node.list, &old_hosts);
306
307 avl_remove_all_elements(&net->groups, group, node, gtmp) {
308 free(group->members);
309 free(group);
310 }
311
312 vlist_update(&net->peers);
313 }
314
315 static void
316 __network_hosts_update_done(struct network *net, bool free_net)
317 {
318 struct network_host *local, *host, *tmp;
319 LIST_HEAD(old_dynamic);
320 const char *local_name;
321
322 list_splice_init(&net->dynamic_peers, &old_dynamic);
323 if (free_net)
324 goto out;
325
326 local = net->net_config.local_host;
327 if (!local)
328 goto out;
329
330 local_name = network_host_name(local);
331
332 if (net->net_config.local_host_changed)
333 wg_init_local(net, &local->peer);
334
335 avl_for_each_element(&net->hosts, host, node) {
336 if (host == local)
337 continue;
338 if (host->gateway && strcmp(host->gateway, local_name) != 0)
339 continue;
340 if (local->gateway && strcmp(local->gateway, network_host_name(host)) != 0)
341 continue;
342 vlist_add(&net->peers, &host->peer.node, host->peer.key);
343 }
344
345 network_hosts_load_dynamic_peers(net);
346
347 out:
348 vlist_flush(&net->peers);
349
350 network_host_free_dynamic_peers(&old_dynamic);
351
352 list_for_each_entry_safe(host, tmp, &old_hosts, node.list) {
353 list_del(&host->node.list);
354 free(host);
355 }
356 }
357
358 void network_hosts_update_done(struct network *net)
359 {
360 return __network_hosts_update_done(net, false);
361 }
362
363 static void
364 network_hosts_connect_cb(struct uloop_timeout *t)
365 {
366 struct network *net = container_of(t, struct network, connect_timer);
367 struct network_host *host;
368 struct network_peer *peer;
369 union network_endpoint *ep;
370
371 avl_for_each_element(&net->hosts, host, node)
372 host->peer.state.num_net_queries = 0;
373 net->num_net_queries = 0;
374
375 if (!net->net_config.keepalive || !net->net_config.local_host)
376 return;
377
378 wg_peer_refresh(net);
379
380 vlist_for_each_element(&net->peers, peer, node) {
381 if (peer->state.connected)
382 continue;
383
384 ep = &peer->state.next_endpoint;
385 if (peer->endpoint &&
386 network_get_endpoint(ep, AF_UNSPEC, peer->endpoint, peer->port,
387 peer->state.connect_attempt++))
388 continue;
389
390 if (!ep->sa.sa_family)
391 continue;
392
393 if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0)
394 unetd_ubus_netifd_add_route(net, ep);
395
396 wg_peer_connect(net, peer, ep);
397 }
398
399 network_pex_event(net, NULL, PEX_EV_QUERY);
400
401 uloop_timeout_set(t, 1000);
402 }
403
404 void network_hosts_add(struct network *net, struct blob_attr *hosts)
405 {
406 struct blob_attr *cur;
407 int rem;
408
409 blobmsg_for_each_attr(cur, hosts, rem)
410 network_host_create(net, cur, false);
411 }
412
413 void network_hosts_init(struct network *net)
414 {
415 INIT_LIST_HEAD(&net->dynamic_peers);
416 avl_init(&net->hosts, avl_strcmp, false, NULL);
417 vlist_init(&net->peers, avl_key_cmp, network_peer_update);
418 avl_init(&net->groups, avl_strcmp, false, NULL);
419 net->connect_timer.cb = network_hosts_connect_cb;
420 }
421
422 void network_hosts_free(struct network *net)
423 {
424 uloop_timeout_cancel(&net->connect_timer);
425 network_hosts_update_start(net);
426 __network_hosts_update_done(net, true);
427 }