vxlan: fix endian of the configured port
[project/unetd.git] / vxlan.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #define _GNU_SOURCE
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netlink/msg.h>
9 #include <netlink/attr.h>
10 #include <netlink/socket.h>
11 #include <netinet/if_ether.h>
12 #include <net/if.h>
13 #include <linux/rtnetlink.h>
14 #include "unetd.h"
15
16 struct vxlan_tunnel {
17 struct network *net;
18 struct network_service *s;
19 char ifname[IFNAMSIZ + 1];
20 int ifindex;
21 uint16_t mtu;
22 uint16_t port;
23 uint32_t vni;
24 uint32_t *forward_ports;
25 uint32_t *cur_forward_ports;
26 bool active;
27 };
28
29 static struct nl_sock *rtnl;
30 static bool ignore_errors;
31
32 static int
33 unetd_nl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err,
34 void *arg)
35 {
36 struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
37 struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
38 struct nlattr *attrs;
39 int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
40 int len = nlh->nlmsg_len;
41 const char *errstr = "(unknown)";
42
43 if (ignore_errors)
44 return NL_STOP;
45
46 if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
47 return NL_STOP;
48
49 if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
50 ack_len += err->msg.nlmsg_len - sizeof(*nlh);
51
52 attrs = (void *) ((unsigned char *) nlh + ack_len);
53 len -= ack_len;
54
55 nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
56 if (tb[NLMSGERR_ATTR_MSG])
57 errstr = nla_data(tb[NLMSGERR_ATTR_MSG]);
58
59 D("Netlink error(%d): %s\n", err->error, errstr);
60
61 return NL_STOP;
62 }
63
64 static struct nl_msg *vxlan_rtnl_msg(const char *ifname, int type, int flags)
65 {
66 struct ifinfomsg iim = {
67 .ifi_family = AF_UNSPEC,
68 };
69 struct nl_msg *msg;
70
71 msg = nlmsg_alloc_simple(type, flags | NLM_F_REQUEST);
72 if (!msg)
73 return NULL;
74
75 nlmsg_append(msg, &iim, sizeof(iim), 0);
76 nla_put_string(msg, IFLA_IFNAME, ifname);
77
78 return msg;
79 }
80
81 static int vxlan_rtnl_call(struct nl_msg *msg)
82 {
83 int ret;
84
85 ret = nl_send_auto_complete(rtnl, msg);
86 nlmsg_free(msg);
87
88 if (ret < 0)
89 return ret;
90
91 return nl_wait_for_ack(rtnl);
92 }
93
94 static int
95 vxlan_rtnl_init(void)
96 {
97 int fd, opt;
98
99 if (rtnl)
100 return 0;
101
102 rtnl = nl_socket_alloc();
103 if (!rtnl)
104 return -1;
105
106 if (nl_connect(rtnl, NETLINK_ROUTE))
107 goto free;
108
109 nl_socket_disable_seq_check(rtnl);
110 nl_socket_set_buffer_size(rtnl, 65536, 0);
111 nl_cb_err(nl_socket_get_cb(rtnl), NL_CB_CUSTOM, unetd_nl_error_cb, NULL);
112
113 fd = nl_socket_get_fd(rtnl);
114
115 opt = 1;
116 setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt));
117
118 opt = 1;
119 setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &opt, sizeof(opt));
120
121 return 0;
122
123 free:
124 nl_socket_free(rtnl);
125 rtnl = NULL;
126 return -1;
127 }
128
129 static uint32_t
130 vxlan_tunnel_id(struct vxlan_tunnel *vt)
131 {
132 siphash_key_t key = {};
133 const char *name = network_service_name(vt->s);
134 uint64_t val;
135
136 if (vt->vni != ~0)
137 return vt->vni;
138
139 siphash_to_le64(&val, name, strlen(name), &key);
140
141 return val & 0x00ffffff;
142 }
143
144 static int
145 vxlan_update_host_fdb_entry(struct vxlan_tunnel *vt, struct network_host *host, bool add)
146 {
147 struct ndmsg ndmsg = {
148 .ndm_family = PF_BRIDGE,
149 .ndm_state = NUD_NOARP | NUD_PERMANENT,
150 .ndm_flags = NTF_SELF,
151 .ndm_ifindex = vt->ifindex,
152 };
153 unsigned int flags = NLM_F_REQUEST;
154 uint8_t lladdr[ETH_ALEN] = {};
155 struct nl_msg *msg;
156
157 if (add)
158 flags |= NLM_F_CREATE | NLM_F_APPEND;
159
160 msg = nlmsg_alloc_simple(add ? RTM_NEWNEIGH : RTM_DELNEIGH, flags);
161 nlmsg_append(msg, &ndmsg, sizeof(ndmsg), 0);
162 nla_put(msg, NDA_LLADDR, ETH_ALEN, lladdr);
163 nla_put(msg, NDA_DST, sizeof(struct in6_addr), &host->peer.local_addr);
164 nla_put_u32(msg, NDA_IFINDEX, vt->net->ifindex);
165
166 return vxlan_rtnl_call(msg);
167 }
168
169 static void
170 vxlan_update_fdb_hosts(struct vxlan_tunnel *vt)
171 {
172 struct network_service *s = vt->s;
173 bool active;
174 int i;
175
176 if (!vt->active)
177 return;
178
179 for (i = 0; i < s->n_members; i++) {
180 if (s->members[i] == vt->net->net_config.local_host)
181 continue;
182
183 if (vt->forward_ports && !bitmask_test(vt->forward_ports, i))
184 continue;
185
186 active = s->members[i]->peer.state.connected;
187 if (active == bitmask_test(vt->cur_forward_ports, i))
188 continue;
189
190 if (!vxlan_update_host_fdb_entry(vt, s->members[i], active))
191 bitmask_set_val(vt->cur_forward_ports, i, active);
192 }
193 }
194
195 static void
196 vxlan_peer_update(struct network *net, struct network_service *s, struct network_peer *peer)
197 {
198 if (!s->vxlan)
199 return;
200
201 vxlan_update_fdb_hosts(s->vxlan);
202 }
203
204 static void
205 vxlan_tunnel_init(struct vxlan_tunnel *vt)
206 {
207 struct network_peer *local = &vt->net->net_config.local_host->peer;
208 struct nlattr *linkinfo, *data;
209 struct nl_msg *msg;
210 struct in6_addr group_addr;
211
212 if (vxlan_rtnl_init())
213 return;
214
215 memset(&group_addr, 0xff, sizeof(group_addr));
216 msg = vxlan_rtnl_msg(vt->ifname, RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
217
218 linkinfo = nla_nest_start(msg, IFLA_LINKINFO);
219 nla_put_string(msg, IFLA_INFO_KIND, "vxlan");
220 nla_put_u32(msg, IFLA_MTU, vt->mtu);
221
222 data = nla_nest_start(msg, IFLA_INFO_DATA);
223 nla_put_u32(msg, IFLA_VXLAN_ID, vxlan_tunnel_id(vt));
224 nla_put(msg, IFLA_VXLAN_LOCAL6, sizeof(struct in6_addr), &local->local_addr);
225 nla_put(msg, IFLA_VXLAN_GROUP6, sizeof(struct in6_addr), &group_addr);
226 nla_put_u16(msg, IFLA_VXLAN_PORT, htons(vt->port));
227 nla_put_u8(msg, IFLA_VXLAN_LEARNING, 1);
228 nla_put_u32(msg, IFLA_VXLAN_LINK, vt->net->ifindex);
229 nla_nest_end(msg, data);
230
231 nla_nest_end(msg, linkinfo);
232
233 if (vxlan_rtnl_call(msg) < 0)
234 return;
235
236 vt->ifindex = if_nametoindex(vt->ifname);
237 if (!vt->ifindex) {
238 D_SERVICE(vt->net, vt->s, "failed to get ifindex for device %s", vt->ifname);
239 return;
240 }
241
242 vt->active = true;
243 vxlan_update_fdb_hosts(vt);
244 }
245
246 static void
247 vxlan_tunnel_teardown(struct vxlan_tunnel *vt)
248 {
249 struct nl_msg *msg;
250
251 if (!rtnl)
252 return;
253
254 vt->active = false;
255 msg = vxlan_rtnl_msg(vt->ifname, RTM_DELLINK, 0);
256 vxlan_rtnl_call(msg);
257 }
258
259 static const char *
260 vxlan_find_ifname(struct network *net, const char *service)
261 {
262 struct blob_attr *cur;
263 int rem;
264
265 if (!net->config.tunnels)
266 return NULL;
267
268 blobmsg_for_each_attr(cur, net->config.tunnels, rem) {
269 const char *name;
270
271 if (!blobmsg_check_attr(cur, true) ||
272 blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
273 continue;
274
275 if (strcmp(blobmsg_get_string(cur), service) != 0)
276 continue;
277
278 name = blobmsg_name(cur);
279 if (strlen(name) > IFNAMSIZ)
280 break;
281
282 return name;
283 }
284
285 return NULL;
286 }
287
288 static void
289 __vxlan_mark_forward_host(struct vxlan_tunnel *vt, struct network_host *host)
290 {
291 struct network_service *s = vt->s;
292 unsigned int i;
293
294 for (i = 0; i < s->n_members; i++) {
295 if (s->members[i] != host)
296 continue;
297
298 bitmask_set(vt->forward_ports, i);
299 break;
300 }
301 }
302
303 static void
304 vxlan_mark_forward_host(struct vxlan_tunnel *vt, const char *name)
305 {
306 struct network *net = vt->net;
307 struct network_host *host;
308
309 host = avl_find_element(&net->hosts, name, host, node);
310 if (!host)
311 return;
312
313 __vxlan_mark_forward_host(vt, host);
314 }
315
316 static void
317 vxlan_mark_forward_group(struct vxlan_tunnel *vt, const char *name)
318 {
319 struct network *net = vt->net;
320 struct network_group *group;
321 int i;
322
323 group = avl_find_element(&net->groups, name, group, node);
324 if (!group)
325 return;
326
327 for (i = 0; i < group->n_members; i++)
328 __vxlan_mark_forward_host(vt, group->members[i]);
329 }
330
331 static void
332 vxlan_init_forward_ports(struct vxlan_tunnel *vt, struct blob_attr *data)
333 {
334 unsigned int len = bitmask_size(vt->s->n_members);
335 struct blob_attr *cur;
336 int rem;
337
338 vt->cur_forward_ports = realloc(vt->cur_forward_ports, len);
339 memset(vt->cur_forward_ports, 0, len);
340
341 if (!data || blobmsg_check_array(data, BLOBMSG_TYPE_STRING) <= 0) {
342 free(vt->forward_ports);
343 vt->forward_ports = NULL;
344 return;
345 }
346
347 vt->forward_ports = realloc(vt->forward_ports, len);
348 memset(vt->forward_ports, 0, len);
349 blobmsg_for_each_attr(cur, data, rem) {
350 const char *name = blobmsg_get_string(cur);
351
352 if (name[0] == '@')
353 vxlan_mark_forward_group(vt, name + 1);
354 else
355 vxlan_mark_forward_host(vt, name);
356 }
357 }
358
359 static bool
360 vxlan_config_equal(struct network_service *s1, struct network_service *s2)
361 {
362 int i;
363
364 if (!blob_attr_equal(s1->config, s2->config))
365 return false;
366
367 if (s1->n_members != s2->n_members)
368 return false;
369
370 for (i = 0; i < s1->n_members; i++)
371 if (memcmp(s1->members[i]->peer.key, s2->members[i]->peer.key,
372 CURVE25519_KEY_SIZE) != 0)
373 return false;
374
375 return true;
376 }
377
378 static void
379 vxlan_init(struct network *net, struct network_service *s,
380 struct network_service *s_old)
381 {
382 enum {
383 VXCFG_ATTR_FWD_PORTS,
384 VXCFG_ATTR_ID,
385 VXCFG_ATTR_PORT,
386 VXCFG_ATTR_MTU,
387 __VXCFG_ATTR_MAX
388 };
389 static const struct blobmsg_policy policy[__VXCFG_ATTR_MAX] = {
390 [VXCFG_ATTR_FWD_PORTS] = { "forward_ports", BLOBMSG_TYPE_ARRAY },
391 [VXCFG_ATTR_ID] = { "id", BLOBMSG_TYPE_INT32 },
392 [VXCFG_ATTR_PORT] = { "port", BLOBMSG_TYPE_INT32 },
393 [VXCFG_ATTR_MTU] = { "mtu", BLOBMSG_TYPE_INT32 },
394 };
395 struct blob_attr *tb[__VXCFG_ATTR_MAX] = {};
396 struct blob_attr *cur;
397 struct vxlan_tunnel *vt = s->vxlan;
398 const char *name;
399
400 if (s_old) {
401 vt = s_old->vxlan;
402 s_old->vxlan = NULL;
403 if (!vt)
404 return;
405
406 if (vxlan_config_equal(s, s_old)) {
407 s->vxlan = vt;
408 vt->s = s;
409 return;
410 }
411
412 vxlan_tunnel_teardown(vt);
413 goto init;
414 }
415
416 name = vxlan_find_ifname(net, network_service_name(s));
417 if (!name) {
418 D_SERVICE(net, s, "no configured tunnel ifname");
419 return;
420 }
421
422 vt = calloc(1, sizeof(*s->vxlan));
423 snprintf(vt->ifname, sizeof(vt->ifname), "%s", name);
424 vt->net = net;
425
426 init:
427 s->vxlan = vt;
428 vt->s = s;
429 if (s->config)
430 blobmsg_parse(policy, __VXCFG_ATTR_MAX, tb, blobmsg_data(s->config),
431 blobmsg_len(s->config));
432
433 vxlan_init_forward_ports(vt, tb[VXCFG_ATTR_FWD_PORTS]);
434 if ((cur = tb[VXCFG_ATTR_ID]) != NULL)
435 vt->vni = blobmsg_get_u32(cur) & 0x00ffffff;
436 else
437 vt->vni = ~0;
438
439 if ((cur = tb[VXCFG_ATTR_PORT]) != NULL)
440 vt->port = blobmsg_get_u32(cur);
441 else
442 vt->port = 4789;
443
444 if ((cur = tb[VXCFG_ATTR_MTU]) != NULL)
445 vt->mtu = blobmsg_get_u32(cur);
446 else
447 vt->mtu = 1500;
448
449 vxlan_tunnel_init(vt);
450 }
451
452 static void
453 vxlan_free(struct network *net, struct network_service *s)
454 {
455 struct vxlan_tunnel *vt = s->vxlan;
456
457 if (!vt)
458 return;
459
460 vxlan_tunnel_teardown(vt);
461 s->vxlan = NULL;
462 free(vt->forward_ports);
463 free(vt);
464 }
465
466 const struct service_ops vxlan_ops = {
467 .init = vxlan_init,
468 .free = vxlan_free,
469 .peer_update = vxlan_peer_update,
470 };