unet-cli: fix formatting of help text
[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 "unetd.h"
7
8 static LIST_HEAD(old_hosts);
9
10 static int avl_key_cmp(const void *k1, const void *k2, void *ptr)
11 {
12 return memcmp(k1, k2, CURVE25519_KEY_SIZE);
13 }
14
15 static bool
16 network_peer_equal(struct network_peer *p1, struct network_peer *p2)
17 {
18 return !memcmp(&p1->local_addr, &p2->local_addr, sizeof(p1->local_addr)) &&
19 blob_attr_equal(p1->ipaddr, p2->ipaddr) &&
20 blob_attr_equal(p1->subnet, p2->subnet) &&
21 p1->port == p2->port;
22 }
23
24 static void
25 network_peer_update(struct vlist_tree *tree,
26 struct vlist_node *node_new,
27 struct vlist_node *node_old)
28 {
29 struct network *net = container_of(tree, struct network, peers);
30 struct network_peer *h_new = container_of_safe(node_new, struct network_peer, node);
31 struct network_peer *h_old = container_of_safe(node_old, struct network_peer, node);
32 int ret;
33
34 if (h_new && h_old) {
35 memcpy(&h_new->state, &h_old->state, sizeof(h_new->state));
36
37 if (network_peer_equal(h_new, h_old))
38 return;
39 }
40
41 if (h_new)
42 ret = wg_peer_update(net, h_new, h_old ? WG_PEER_UPDATE : WG_PEER_CREATE);
43 else
44 ret = wg_peer_update(net, h_old, WG_PEER_DELETE);
45
46 if (ret)
47 fprintf(stderr, "Failed to %s peer on network %s: %s\n",
48 h_new ? "update" : "delete", network_name(net),
49 strerror(-ret));
50 }
51
52 static struct network_group *
53 network_group_get(struct network *net, const char *name)
54 {
55 struct network_group *group;
56 char *name_buf;
57
58 group = avl_find_element(&net->groups, name, group, node);
59 if (group)
60 return group;
61
62 group = calloc_a(sizeof(*group), &name_buf, strlen(name) + 1);
63 group->node.key = strcpy(name_buf, name);
64 avl_insert(&net->groups, &group->node);
65
66 return group;
67 }
68
69 static void
70 network_host_add_group(struct network *net, struct network_host *host,
71 const char *name)
72 {
73 struct network_group *group;
74 int i;
75
76 group = network_group_get(net, name);
77 for (i = 0; i < group->n_members; i++)
78 if (group->members[i] == host)
79 return;
80
81 group->n_members++;
82 group->members = realloc(group->members, group->n_members * sizeof(*group->members));
83 group->members[group->n_members - 1] = host;
84 }
85
86 static void
87 network_host_create(struct network *net, struct blob_attr *attr)
88 {
89 enum {
90 NETWORK_HOST_KEY,
91 NETWORK_HOST_GROUPS,
92 NETWORK_HOST_IPADDR,
93 NETWORK_HOST_SUBNET,
94 NETWORK_HOST_PORT,
95 NETWORK_HOST_ENDPOINT,
96 NETWORK_HOST_GATEWAY,
97 __NETWORK_HOST_MAX
98 };
99 static const struct blobmsg_policy policy[__NETWORK_HOST_MAX] = {
100 [NETWORK_HOST_KEY] = { "key", BLOBMSG_TYPE_STRING },
101 [NETWORK_HOST_GROUPS] = { "groups", BLOBMSG_TYPE_ARRAY },
102 [NETWORK_HOST_IPADDR] = { "ipaddr", BLOBMSG_TYPE_ARRAY },
103 [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY },
104 [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 },
105 [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING },
106 [NETWORK_HOST_GATEWAY] = { "gateway", BLOBMSG_TYPE_STRING },
107 };
108 struct blob_attr *tb[__NETWORK_HOST_MAX];
109 struct blob_attr *cur, *ipaddr, *subnet;
110 uint8_t key[CURVE25519_KEY_SIZE];
111 struct network_host *host;
112 struct network_peer *peer;
113 int ipaddr_len, subnet_len;
114 const char *name, *endpoint, *gateway;
115 char *name_buf, *endpoint_buf, *gateway_buf;
116 int rem;
117
118 blobmsg_parse(policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
119
120 if (!tb[NETWORK_HOST_KEY])
121 return;
122
123 ipaddr_len = tb[NETWORK_HOST_IPADDR] ? blob_pad_len(tb[NETWORK_HOST_IPADDR]) : 0;
124 if (ipaddr_len &&
125 blobmsg_check_array(tb[NETWORK_HOST_IPADDR], BLOBMSG_TYPE_STRING) < 0)
126 ipaddr_len = 0;
127
128 subnet_len = tb[NETWORK_HOST_SUBNET] ? blob_pad_len(tb[NETWORK_HOST_SUBNET]) : 0;
129 if (subnet_len &&
130 blobmsg_check_array(tb[NETWORK_HOST_SUBNET], BLOBMSG_TYPE_STRING) < 0)
131 subnet_len = 0;
132
133 if ((cur = tb[NETWORK_HOST_ENDPOINT]) != NULL)
134 endpoint = blobmsg_get_string(cur);
135 else
136 endpoint = NULL;
137
138 if ((cur = tb[NETWORK_HOST_GATEWAY]) != NULL)
139 gateway = blobmsg_get_string(cur);
140 else
141 gateway = NULL;
142
143 if (b64_decode(blobmsg_get_string(tb[NETWORK_HOST_KEY]), key,
144 sizeof(key)) != sizeof(key))
145 return;
146
147 name = blobmsg_name(attr);
148 host = avl_find_element(&net->hosts, name, host, node);
149 if (host)
150 return;
151
152 host = calloc_a(sizeof(*host),
153 &name_buf, strlen(name) + 1,
154 &ipaddr, ipaddr_len,
155 &subnet, subnet_len,
156 &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0,
157 &gateway_buf, gateway ? strlen(endpoint) + 1 : 0);
158 peer = &host->peer;
159 if ((cur = tb[NETWORK_HOST_IPADDR]) != NULL && ipaddr_len)
160 peer->ipaddr = memcpy(ipaddr, cur, ipaddr_len);
161 if ((cur = tb[NETWORK_HOST_SUBNET]) != NULL && subnet_len)
162 peer->subnet = memcpy(subnet, cur, subnet_len);
163 if ((cur = tb[NETWORK_HOST_PORT]) != NULL)
164 peer->port = blobmsg_get_u32(cur);
165 else
166 peer->port = net->net_config.port;
167 if (endpoint)
168 peer->endpoint = strcpy(endpoint_buf, endpoint);
169 if (gateway)
170 host->gateway = strcpy(gateway_buf, gateway);
171 memcpy(peer->key, key, sizeof(key));
172 host->node.key = strcpy(name_buf, name);
173
174 memcpy(&peer->local_addr.network_id,
175 &net->net_config.addr.network_id,
176 sizeof(peer->local_addr.network_id));
177 network_fill_host_addr(&peer->local_addr, peer->key);
178
179 blobmsg_for_each_attr(cur, tb[NETWORK_HOST_GROUPS], rem) {
180 if (!blobmsg_check_attr(cur, false) ||
181 blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
182 continue;
183
184 network_host_add_group(net, host, blobmsg_get_string(cur));
185 }
186
187 avl_insert(&net->hosts, &host->node);
188 if (!memcmp(peer->key, net->config.pubkey, sizeof(key))) {
189 if (!net->prev_local_host ||
190 !network_peer_equal(&net->prev_local_host->peer, &host->peer))
191 net->net_config.local_host_changed = true;
192
193 net->net_config.local_host = host;
194 }
195 }
196
197 void network_hosts_update_start(struct network *net)
198 {
199 struct network_host *host, *htmp;
200 struct network_group *group, *gtmp;
201
202 avl_remove_all_elements(&net->hosts, host, node, htmp)
203 list_add_tail(&host->node.list, &old_hosts);
204
205 avl_remove_all_elements(&net->groups, group, node, gtmp) {
206 free(group->members);
207 free(group);
208 }
209
210 vlist_update(&net->peers);
211 }
212
213 void network_hosts_update_done(struct network *net)
214 {
215 struct network_host *local, *host, *tmp;
216 const char *local_name;
217
218 local = net->net_config.local_host;
219 if (!local)
220 goto out;
221
222 local_name = network_host_name(local);
223
224 if (net->net_config.local_host_changed)
225 wg_init_local(net, &local->peer);
226
227 avl_for_each_element(&net->hosts, host, node) {
228 if (host == local)
229 continue;
230 if (host->gateway && strcmp(host->gateway, local_name) != 0)
231 continue;
232 if (local->gateway && strcmp(local->gateway, network_host_name(host)) != 0)
233 continue;
234 vlist_add(&net->peers, &host->peer.node, host->peer.key);
235 }
236
237 out:
238 vlist_flush(&net->peers);
239
240 list_for_each_entry_safe(host, tmp, &old_hosts, node.list) {
241 list_del(&host->node.list);
242 free(host);
243 }
244 }
245
246 static void
247 network_hosts_connect_cb(struct uloop_timeout *t)
248 {
249 struct network *net = container_of(t, struct network, connect_timer);
250 struct network_host *host;
251 union network_endpoint *ep;
252
253 avl_for_each_element(&net->hosts, host, node)
254 host->peer.state.num_net_queries = 0;
255 net->num_net_queries = 0;
256
257 if (!net->net_config.keepalive || !net->net_config.local_host)
258 return;
259
260 wg_peer_refresh(net);
261
262 avl_for_each_element(&net->hosts, host, node) {
263 struct network_peer *peer = &host->peer;
264
265 if (!network_host_is_peer(host))
266 continue;
267
268 if (peer->state.connected)
269 continue;
270
271 ep = &peer->state.next_endpoint;
272 if (peer->endpoint &&
273 network_get_endpoint(ep, peer->endpoint, peer->port,
274 peer->state.connect_attempt++))
275 continue;
276
277 if (!ep->sa.sa_family)
278 continue;
279
280 if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0)
281 unetd_ubus_netifd_add_route(net, ep);
282
283 wg_peer_connect(net, peer, ep);
284 }
285
286 network_pex_event(net, NULL, PEX_EV_QUERY);
287
288 uloop_timeout_set(t, 1000);
289 }
290
291 void network_hosts_add(struct network *net, struct blob_attr *hosts)
292 {
293 struct blob_attr *cur;
294 int rem;
295
296 blobmsg_for_each_attr(cur, hosts, rem)
297 network_host_create(net, cur);
298 }
299
300 void network_hosts_init(struct network *net)
301 {
302 avl_init(&net->hosts, avl_strcmp, false, NULL);
303 vlist_init(&net->peers, avl_key_cmp, network_peer_update);
304 avl_init(&net->groups, avl_strcmp, false, NULL);
305 net->connect_timer.cb = network_hosts_connect_cb;
306 }
307
308 void network_hosts_free(struct network *net)
309 {
310 uloop_timeout_cancel(&net->connect_timer);
311 network_hosts_update_start(net);
312 network_hosts_update_done(net);
313 }