unet-cli: strip initial newline in usage message
[project/unetd.git] / vxlan.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
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 <linux/ipv6.h>
15 #include <linux/udp.h>
16 #include "unetd.h"
17
18 struct vxlan_tunnel {
19 struct network *net;
20 struct network_service *s;
21 char ifname[IFNAMSIZ + 1];
22 int ifindex;
23 uint16_t mtu;
24 uint16_t port;
25 uint32_t vni;
26 uint32_t *forward_ports;
27 uint32_t *cur_forward_ports;
28 bool active;
29 };
30
31 static uint32_t
32 vxlan_tunnel_id(struct vxlan_tunnel *vt)
33 {
34 siphash_key_t key = {};
35 const char *name = network_service_name(vt->s);
36 uint64_t val;
37
38 if (vt->vni != ~0)
39 return vt->vni;
40
41 siphash_to_le64(&val, name, strlen(name), &key);
42
43 return val & 0x00ffffff;
44 }
45
46 static struct nl_msg *vxlan_rtnl_msg(const char *ifname, int type, int flags)
47 {
48 struct ifinfomsg iim = {
49 .ifi_family = AF_UNSPEC,
50 };
51 struct nl_msg *msg;
52
53 msg = nlmsg_alloc_simple(type, flags | NLM_F_REQUEST);
54 if (!msg)
55 return NULL;
56
57 nlmsg_append(msg, &iim, sizeof(iim), 0);
58 nla_put_string(msg, IFLA_IFNAME, ifname);
59
60 return msg;
61 }
62
63 static int
64 vxlan_update_host_fdb_entry(struct vxlan_tunnel *vt, struct network_host *host, bool add)
65 {
66 struct ndmsg ndmsg = {
67 .ndm_family = PF_BRIDGE,
68 .ndm_state = NUD_NOARP | NUD_PERMANENT,
69 .ndm_flags = NTF_SELF,
70 .ndm_ifindex = vt->ifindex,
71 };
72 unsigned int flags = NLM_F_REQUEST;
73 uint8_t lladdr[ETH_ALEN] = {};
74 struct nl_msg *msg;
75
76 if (add)
77 flags |= NLM_F_CREATE | NLM_F_APPEND;
78
79 msg = nlmsg_alloc_simple(add ? RTM_NEWNEIGH : RTM_DELNEIGH, flags);
80 nlmsg_append(msg, &ndmsg, sizeof(ndmsg), 0);
81 nla_put(msg, NDA_LLADDR, ETH_ALEN, lladdr);
82 nla_put(msg, NDA_DST, sizeof(struct in6_addr), &host->peer.local_addr);
83 nla_put_u32(msg, NDA_IFINDEX, vt->net->ifindex);
84
85 return rtnl_call(msg);
86 }
87
88 static void
89 vxlan_update_fdb_hosts(struct vxlan_tunnel *vt)
90 {
91 struct network_service *s = vt->s;
92 bool active;
93 int i;
94
95 if (!vt->active)
96 return;
97
98 for (i = 0; i < s->n_members; i++) {
99 if (s->members[i] == vt->net->net_config.local_host)
100 continue;
101
102 if (vt->forward_ports && !bitmask_test(vt->forward_ports, i))
103 continue;
104
105 active = s->members[i]->peer.state.connected;
106 if (active == bitmask_test(vt->cur_forward_ports, i))
107 continue;
108
109 if (!vxlan_update_host_fdb_entry(vt, s->members[i], active))
110 bitmask_set_val(vt->cur_forward_ports, i, active);
111 }
112 }
113
114 static void
115 vxlan_peer_update(struct network *net, struct network_service *s, struct network_peer *peer)
116 {
117 if (!s->vxlan)
118 return;
119
120 vxlan_update_fdb_hosts(s->vxlan);
121 }
122
123 static void
124 vxlan_tunnel_init(struct vxlan_tunnel *vt)
125 {
126 struct network_peer *local = &vt->net->net_config.local_host->peer;
127 struct nlattr *linkinfo, *data;
128 struct nl_msg *msg;
129 struct in6_addr group_addr;
130 int mtu;
131
132 if (rtnl_init())
133 return;
134
135 memset(&group_addr, 0xff, sizeof(group_addr));
136 msg = vxlan_rtnl_msg(vt->ifname, RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
137
138 linkinfo = nla_nest_start(msg, IFLA_LINKINFO);
139 nla_put_string(msg, IFLA_INFO_KIND, "vxlan");
140 nla_put_u32(msg, IFLA_MTU, vt->mtu);
141
142 data = nla_nest_start(msg, IFLA_INFO_DATA);
143 nla_put_u32(msg, IFLA_VXLAN_ID, vxlan_tunnel_id(vt));
144 nla_put(msg, IFLA_VXLAN_LOCAL6, sizeof(struct in6_addr), &local->local_addr);
145 nla_put(msg, IFLA_VXLAN_GROUP6, sizeof(struct in6_addr), &group_addr);
146 nla_put_u16(msg, IFLA_VXLAN_PORT, htons(vt->port));
147 nla_put_u8(msg, IFLA_VXLAN_LEARNING, 1);
148 nla_put_u32(msg, IFLA_VXLAN_LINK, vt->net->ifindex);
149 nla_nest_end(msg, data);
150
151 nla_nest_end(msg, linkinfo);
152
153 if (rtnl_call(msg) < 0)
154 return;
155
156 vt->ifindex = if_nametoindex(vt->ifname);
157 if (!vt->ifindex) {
158 D_SERVICE(vt->net, vt->s, "failed to get ifindex for device %s", vt->ifname);
159 return;
160 }
161
162 vt->active = true;
163 vxlan_update_fdb_hosts(vt);
164
165 mtu = 1420 - sizeof(struct ipv6hdr) - sizeof(struct udphdr) - 8;
166 unetd_attach_mssfix(vt->ifindex, mtu);
167 }
168
169 static void
170 vxlan_tunnel_teardown(struct vxlan_tunnel *vt)
171 {
172 struct nl_msg *msg;
173
174 vt->active = false;
175 msg = vxlan_rtnl_msg(vt->ifname, RTM_DELLINK, 0);
176 rtnl_call(msg);
177 }
178
179 static const char *
180 vxlan_find_ifname(struct network *net, const char *service)
181 {
182 struct blob_attr *cur;
183 int rem;
184
185 if (!net->config.tunnels)
186 return NULL;
187
188 blobmsg_for_each_attr(cur, net->config.tunnels, rem) {
189 const char *name;
190
191 if (!blobmsg_check_attr(cur, true) ||
192 blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
193 continue;
194
195 if (strcmp(blobmsg_get_string(cur), service) != 0)
196 continue;
197
198 name = blobmsg_name(cur);
199 if (strlen(name) > IFNAMSIZ)
200 break;
201
202 return name;
203 }
204
205 return NULL;
206 }
207
208 static void
209 __vxlan_mark_forward_host(struct vxlan_tunnel *vt, struct network_host *host)
210 {
211 struct network_service *s = vt->s;
212 unsigned int i;
213
214 for (i = 0; i < s->n_members; i++) {
215 if (s->members[i] != host)
216 continue;
217
218 bitmask_set(vt->forward_ports, i);
219 break;
220 }
221 }
222
223 static void
224 vxlan_mark_forward_host(struct vxlan_tunnel *vt, const char *name)
225 {
226 struct network *net = vt->net;
227 struct network_host *host;
228
229 host = avl_find_element(&net->hosts, name, host, node);
230 if (!host)
231 return;
232
233 __vxlan_mark_forward_host(vt, host);
234 }
235
236 static void
237 vxlan_mark_forward_group(struct vxlan_tunnel *vt, const char *name)
238 {
239 struct network *net = vt->net;
240 struct network_group *group;
241 int i;
242
243 group = avl_find_element(&net->groups, name, group, node);
244 if (!group)
245 return;
246
247 for (i = 0; i < group->n_members; i++)
248 __vxlan_mark_forward_host(vt, group->members[i]);
249 }
250
251 static void
252 vxlan_init_forward_ports(struct vxlan_tunnel *vt, struct blob_attr *data)
253 {
254 unsigned int len = bitmask_size(vt->s->n_members);
255 struct blob_attr *cur;
256 int rem;
257
258 vt->cur_forward_ports = realloc(vt->cur_forward_ports, len);
259 memset(vt->cur_forward_ports, 0, len);
260
261 if (!data || blobmsg_check_array(data, BLOBMSG_TYPE_STRING) <= 0) {
262 free(vt->forward_ports);
263 vt->forward_ports = NULL;
264 return;
265 }
266
267 vt->forward_ports = realloc(vt->forward_ports, len);
268 memset(vt->forward_ports, 0, len);
269 blobmsg_for_each_attr(cur, data, rem) {
270 const char *name = blobmsg_get_string(cur);
271
272 if (name[0] == '@')
273 vxlan_mark_forward_group(vt, name + 1);
274 else
275 vxlan_mark_forward_host(vt, name);
276 }
277 }
278
279 static bool
280 vxlan_config_equal(struct network_service *s1, struct network_service *s2)
281 {
282 int i;
283
284 if (!blob_attr_equal(s1->config, s2->config))
285 return false;
286
287 if (s1->n_members != s2->n_members)
288 return false;
289
290 for (i = 0; i < s1->n_members; i++)
291 if (memcmp(s1->members[i]->peer.key, s2->members[i]->peer.key,
292 CURVE25519_KEY_SIZE) != 0)
293 return false;
294
295 return true;
296 }
297
298 static void
299 vxlan_init(struct network *net, struct network_service *s,
300 struct network_service *s_old)
301 {
302 enum {
303 VXCFG_ATTR_FWD_PORTS,
304 VXCFG_ATTR_ID,
305 VXCFG_ATTR_PORT,
306 VXCFG_ATTR_MTU,
307 __VXCFG_ATTR_MAX
308 };
309 static const struct blobmsg_policy policy[__VXCFG_ATTR_MAX] = {
310 [VXCFG_ATTR_FWD_PORTS] = { "forward_ports", BLOBMSG_TYPE_ARRAY },
311 [VXCFG_ATTR_ID] = { "id", BLOBMSG_TYPE_INT32 },
312 [VXCFG_ATTR_PORT] = { "port", BLOBMSG_TYPE_INT32 },
313 [VXCFG_ATTR_MTU] = { "mtu", BLOBMSG_TYPE_INT32 },
314 };
315 struct blob_attr *tb[__VXCFG_ATTR_MAX] = {};
316 struct blob_attr *cur;
317 struct vxlan_tunnel *vt = s->vxlan;
318 const char *name;
319
320 if (s_old) {
321 vt = s_old->vxlan;
322 s_old->vxlan = NULL;
323 if (!vt)
324 return;
325
326 if (vxlan_config_equal(s, s_old)) {
327 s->vxlan = vt;
328 vt->s = s;
329 return;
330 }
331
332 vxlan_tunnel_teardown(vt);
333 goto init;
334 }
335
336 name = vxlan_find_ifname(net, network_service_name(s));
337 if (!name) {
338 D_SERVICE(net, s, "no configured tunnel ifname");
339 return;
340 }
341
342 vt = calloc(1, sizeof(*s->vxlan));
343 snprintf(vt->ifname, sizeof(vt->ifname), "%s", name);
344 vt->net = net;
345
346 init:
347 s->vxlan = vt;
348 vt->s = s;
349 if (s->config)
350 blobmsg_parse(policy, __VXCFG_ATTR_MAX, tb, blobmsg_data(s->config),
351 blobmsg_len(s->config));
352
353 vxlan_init_forward_ports(vt, tb[VXCFG_ATTR_FWD_PORTS]);
354 if ((cur = tb[VXCFG_ATTR_ID]) != NULL)
355 vt->vni = blobmsg_get_u32(cur) & 0x00ffffff;
356 else
357 vt->vni = ~0;
358
359 if ((cur = tb[VXCFG_ATTR_PORT]) != NULL)
360 vt->port = blobmsg_get_u32(cur);
361 else
362 vt->port = 4789;
363
364 if ((cur = tb[VXCFG_ATTR_MTU]) != NULL)
365 vt->mtu = blobmsg_get_u32(cur);
366 else
367 vt->mtu = 1500;
368
369 vxlan_tunnel_init(vt);
370 }
371
372 static void
373 vxlan_free(struct network *net, struct network_service *s)
374 {
375 struct vxlan_tunnel *vt = s->vxlan;
376
377 if (!vt)
378 return;
379
380 vxlan_tunnel_teardown(vt);
381 s->vxlan = NULL;
382 free(vt->forward_ports);
383 free(vt);
384 }
385
386 const struct service_ops vxlan_ops = {
387 .init = vxlan_init,
388 .free = vxlan_free,
389 .peer_update = vxlan_peer_update,
390 };