host: deal with host/peer null pointers in debug messages
[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_MAX
97 };
98 static const struct blobmsg_policy policy[__NETWORK_HOST_MAX] = {
99 [NETWORK_HOST_KEY] = { "key", BLOBMSG_TYPE_STRING },
100 [NETWORK_HOST_GROUPS] = { "groups", BLOBMSG_TYPE_ARRAY },
101 [NETWORK_HOST_IPADDR] = { "ipaddr", BLOBMSG_TYPE_ARRAY },
102 [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY },
103 [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 },
104 [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING },
105 };
106 struct blob_attr *tb[__NETWORK_HOST_MAX];
107 struct blob_attr *cur, *ipaddr, *subnet;
108 uint8_t key[CURVE25519_KEY_SIZE];
109 struct network_host *host;
110 struct network_peer *peer;
111 int ipaddr_len, subnet_len;
112 const char *name, *endpoint;
113 char *name_buf, *endpoint_buf;
114 int rem;
115
116 blobmsg_parse(policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
117
118 if (!tb[NETWORK_HOST_KEY])
119 return;
120
121 ipaddr_len = tb[NETWORK_HOST_IPADDR] ? blob_pad_len(tb[NETWORK_HOST_IPADDR]) : 0;
122 if (ipaddr_len &&
123 blobmsg_check_array(tb[NETWORK_HOST_IPADDR], BLOBMSG_TYPE_STRING) < 0)
124 ipaddr_len = 0;
125
126 subnet_len = tb[NETWORK_HOST_SUBNET] ? blob_pad_len(tb[NETWORK_HOST_SUBNET]) : 0;
127 if (subnet_len &&
128 blobmsg_check_array(tb[NETWORK_HOST_SUBNET], BLOBMSG_TYPE_STRING) < 0)
129 subnet_len = 0;
130
131 if ((cur = tb[NETWORK_HOST_ENDPOINT]) != NULL)
132 endpoint = blobmsg_get_string(cur);
133 else
134 endpoint = NULL;
135
136 if (b64_decode(blobmsg_get_string(tb[NETWORK_HOST_KEY]), key,
137 sizeof(key)) != sizeof(key))
138 return;
139
140 name = blobmsg_name(attr);
141 host = avl_find_element(&net->hosts, name, host, node);
142 if (host)
143 return;
144
145 host = calloc_a(sizeof(*host),
146 &name_buf, strlen(name) + 1,
147 &ipaddr, ipaddr_len,
148 &subnet, subnet_len,
149 &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0);
150 peer = &host->peer;
151 if ((cur = tb[NETWORK_HOST_IPADDR]) != NULL && ipaddr_len)
152 peer->ipaddr = memcpy(ipaddr, cur, ipaddr_len);
153 if ((cur = tb[NETWORK_HOST_SUBNET]) != NULL && subnet_len)
154 peer->subnet = memcpy(subnet, cur, subnet_len);
155 if ((cur = tb[NETWORK_HOST_PORT]) != NULL)
156 peer->port = blobmsg_get_u32(cur);
157 else
158 peer->port = net->net_config.port;
159 if (endpoint)
160 peer->endpoint = strcpy(endpoint_buf, endpoint);
161 memcpy(peer->key, key, sizeof(key));
162 host->node.key = strcpy(name_buf, name);
163
164 memcpy(&peer->local_addr.network_id,
165 &net->net_config.addr.network_id,
166 sizeof(peer->local_addr.network_id));
167 network_fill_host_addr(&peer->local_addr, peer->key);
168
169 blobmsg_for_each_attr(cur, tb[NETWORK_HOST_GROUPS], rem) {
170 if (!blobmsg_check_attr(cur, false) ||
171 blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
172 continue;
173
174 network_host_add_group(net, host, blobmsg_get_string(cur));
175 }
176
177 avl_insert(&net->hosts, &host->node);
178 if (!memcmp(peer->key, net->config.pubkey, sizeof(key))) {
179 if (!net->prev_local_host ||
180 !network_peer_equal(&net->prev_local_host->peer, &host->peer))
181 net->net_config.local_host_changed = true;
182
183 net->net_config.local_host = host;
184 }
185 }
186
187 void network_hosts_update_start(struct network *net)
188 {
189 struct network_host *host, *htmp;
190 struct network_group *group, *gtmp;
191
192 avl_remove_all_elements(&net->hosts, host, node, htmp)
193 list_add_tail(&host->node.list, &old_hosts);
194
195 avl_remove_all_elements(&net->groups, group, node, gtmp) {
196 free(group->members);
197 free(group);
198 }
199
200 vlist_update(&net->peers);
201 }
202
203 void network_hosts_update_done(struct network *net)
204 {
205 struct network_host *host, *tmp;
206
207 if (!net->net_config.local_host)
208 goto out;
209
210 if (net->net_config.local_host_changed)
211 wg_init_local(net, &net->net_config.local_host->peer);
212
213 avl_for_each_element(&net->hosts, host, node)
214 if (host != net->net_config.local_host)
215 vlist_add(&net->peers, &host->peer.node, host->peer.key);
216
217 out:
218 vlist_flush(&net->peers);
219
220 list_for_each_entry_safe(host, tmp, &old_hosts, node.list) {
221 list_del(&host->node.list);
222 free(host);
223 }
224 }
225
226 static void
227 network_hosts_connect_cb(struct uloop_timeout *t)
228 {
229 struct network *net = container_of(t, struct network, connect_timer);
230 struct network_host *host;
231 union network_endpoint *ep;
232
233 avl_for_each_element(&net->hosts, host, node)
234 host->peer.state.num_net_queries = 0;
235 net->num_net_queries = 0;
236
237 if (!net->net_config.keepalive || !net->net_config.local_host)
238 return;
239
240 wg_peer_refresh(net);
241
242 avl_for_each_element(&net->hosts, host, node) {
243 struct network_peer *peer = &host->peer;
244
245 if (host == net->net_config.local_host)
246 continue;
247
248 if (peer->state.connected)
249 continue;
250
251 ep = &peer->state.next_endpoint;
252 if (peer->endpoint &&
253 network_get_endpoint(ep, peer->endpoint, peer->port,
254 peer->state.connect_attempt++))
255 continue;
256
257 if (!ep->sa.sa_family)
258 continue;
259
260 if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0)
261 unetd_ubus_netifd_add_route(net, ep);
262
263 wg_peer_connect(net, peer, ep);
264 }
265
266 network_pex_event(net, NULL, PEX_EV_QUERY);
267
268 uloop_timeout_set(t, 1000);
269 }
270
271 void network_hosts_add(struct network *net, struct blob_attr *hosts)
272 {
273 struct blob_attr *cur;
274 int rem;
275
276 blobmsg_for_each_attr(cur, hosts, rem)
277 network_host_create(net, cur);
278 }
279
280 void network_hosts_init(struct network *net)
281 {
282 avl_init(&net->hosts, avl_strcmp, false, NULL);
283 vlist_init(&net->peers, avl_key_cmp, network_peer_update);
284 avl_init(&net->groups, avl_strcmp, false, NULL);
285 net->connect_timer.cb = network_hosts_connect_cb;
286 }
287
288 void network_hosts_free(struct network *net)
289 {
290 uloop_timeout_cancel(&net->connect_timer);
291 network_hosts_update_start(net);
292 network_hosts_update_done(net);
293 }