35365e5539b707b9aa84cd91ca8eb4966ffec072
[project/unetd.git] / wg-linux.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 *
5 * Based on wireguard-tools:
6 * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
7 */
8 #define _GNU_SOURCE
9 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <dirent.h>
15 #include <errno.h>
16 #include <time.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/un.h>
21 #include <arpa/inet.h>
22 #include <linux/rtnetlink.h>
23 #include <netlink/msg.h>
24 #include <netlink/attr.h>
25 #include <netlink/socket.h>
26 #include <unl.h>
27
28 #include "linux/wireguard.h"
29 #include "unetd.h"
30
31 struct timespec64 {
32 int64_t tv_sec;
33 int64_t tv_nsec;
34 };
35
36 struct wg_linux_peer_req {
37 struct nl_msg *msg;
38
39 struct nlattr *peers, *entry, *ips;
40 };
41
42 static struct unl unl;
43
44 static int
45 wg_nl_init(void)
46 {
47 if (unl.sock)
48 return 0;
49
50 return unl_genl_init(&unl, "wireguard");
51 }
52
53 static struct nl_msg *
54 wg_genl_msg(struct network *net, bool set)
55 {
56 struct nl_msg *msg;
57
58 msg = unl_genl_msg(&unl, set ? WG_CMD_SET_DEVICE : WG_CMD_GET_DEVICE, !set);
59 nla_put_string(msg, WGDEVICE_A_IFNAME, network_name(net));
60
61 return msg;
62 }
63
64 static int
65 wg_genl_call(struct nl_msg *msg)
66 {
67 return unl_request(&unl, msg, NULL, NULL);
68 }
69
70 static int
71 __wg_linux_init(struct network *net, void *key)
72 {
73 struct nl_msg *msg;
74
75 msg = wg_genl_msg(net, true);
76 nla_put(msg, WGDEVICE_A_PRIVATE_KEY, WG_KEY_LEN, key);
77 nla_put_u32(msg, WGDEVICE_A_FLAGS, WGDEVICE_F_REPLACE_PEERS);
78
79 return wg_genl_call(msg);
80 }
81
82 static void
83 wg_linux_cleanup(struct network *net)
84 {
85 uint8_t key[WG_KEY_LEN] = {};
86
87 __wg_linux_init(net, key);
88 }
89
90 static int
91 wg_linux_init(struct network *net)
92 {
93 if (wg_nl_init())
94 return -1;
95
96 return __wg_linux_init(net, net->config.key);
97 }
98
99 static int
100 wg_linux_init_local(struct network *net, struct network_peer *peer)
101 {
102 struct nl_msg *msg;
103
104 msg = wg_genl_msg(net, true);
105 nla_put_u16(msg, WGDEVICE_A_LISTEN_PORT, peer ? peer->port : 0);
106
107 return wg_genl_call(msg);
108 }
109
110 static void
111 wg_linux_msg_add_ip(struct nl_msg *msg, int af, void *addr, int mask)
112 {
113 struct nlattr *ip;
114 int len;
115
116 if (af == AF_INET6)
117 len = sizeof(struct in6_addr);
118 else
119 len = sizeof(struct in_addr);
120
121 ip = nla_nest_start(msg, 0);
122 nla_put_u16(msg, WGALLOWEDIP_A_FAMILY, af);
123 nla_put(msg, WGALLOWEDIP_A_IPADDR, len, addr);
124 nla_put_u8(msg, WGALLOWEDIP_A_CIDR_MASK, mask);
125 nla_nest_end(msg, ip);
126 }
127
128 static struct nl_msg *
129 wg_linux_peer_req_init(struct network *net, struct network_peer *peer,
130 struct wg_linux_peer_req *req)
131 {
132 req->msg = wg_genl_msg(net, true);
133
134 req->peers = nla_nest_start(req->msg, WGDEVICE_A_PEERS);
135 req->entry = nla_nest_start(req->msg, 0);
136 nla_put(req->msg, WGPEER_A_PUBLIC_KEY, WG_KEY_LEN, peer->key);
137
138 return req->msg;
139 }
140
141 static int
142 wg_linux_peer_req_done(struct wg_linux_peer_req *req)
143 {
144 nla_nest_end(req->msg, req->entry);
145 nla_nest_end(req->msg, req->peers);
146
147 return wg_genl_call(req->msg);
148 }
149
150 static struct nl_msg *
151 wg_linux_peer_msg_size_check(struct wg_linux_peer_req *req, struct network *net,
152 struct network_peer *peer)
153 {
154 if (nlmsg_get_max_size(req->msg) >
155 nlmsg_total_size(nlmsg_hdr(req->msg)->nlmsg_len) + 256)
156 return req->msg;
157
158 nla_nest_end(req->msg, req->ips);
159 wg_linux_peer_req_done(req);
160
161 wg_linux_peer_req_init(net, peer, req);
162 req->ips = nla_nest_start(req->msg, WGPEER_A_ALLOWEDIPS);
163
164 return req->msg;
165 }
166
167 static void
168 wg_linux_peer_msg_add_allowed_ip(struct wg_linux_peer_req *req, struct network *net,
169 struct network_peer *peer)
170 {
171 struct nl_msg *msg = req->msg;
172 struct blob_attr *cur;
173 int rem;
174
175 wg_linux_msg_add_ip(msg, AF_INET6, &peer->local_addr.in6, 128);
176 msg = wg_linux_peer_msg_size_check(req, net, peer);
177
178 blobmsg_for_each_attr(cur, peer->ipaddr, rem) {
179 const char *str = blobmsg_get_string(cur);
180 struct in6_addr in6;
181 int af, mask;
182
183 if (strchr(str, ':')) {
184 af = AF_INET6;
185 mask = 128;
186 } else {
187 af = AF_INET;
188 mask = 32;
189 }
190
191 if (inet_pton(af, str, &in6) != 1)
192 continue;
193
194 wg_linux_msg_add_ip(msg, af, &in6, mask);
195 msg = wg_linux_peer_msg_size_check(req, net, peer);
196 }
197
198 blobmsg_for_each_attr(cur, peer->subnet, rem) {
199 const char *str = blobmsg_get_string(cur);
200 union network_addr addr;
201 int mask;
202 int af;
203
204 af = strchr(str, ':') ? AF_INET6 : AF_INET;
205 if (network_get_subnet(af, &addr, &mask, str))
206 continue;
207
208 wg_linux_msg_add_ip(msg, af, &addr, mask);
209 msg = wg_linux_peer_msg_size_check(req, net, peer);
210 }
211 }
212
213 static int
214 wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd)
215 {
216 struct wg_linux_peer_req req;
217 struct network_host *host;
218
219 wg_linux_peer_req_init(net, peer, &req);
220
221 if (cmd == WG_PEER_DELETE) {
222 nla_put_u32(req.msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME);
223 goto out;
224 }
225
226 nla_put_u32(req.msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS);
227
228 req.ips = nla_nest_start(req.msg, WGPEER_A_ALLOWEDIPS);
229
230 wg_linux_peer_msg_add_allowed_ip(&req, net, peer);
231
232 for_each_routed_host(host, net, peer)
233 wg_linux_peer_msg_add_allowed_ip(&req, net, &host->peer);
234
235 nla_nest_end(req.msg, req.ips);
236
237 out:
238 return wg_linux_peer_req_done(&req);
239 }
240
241 static void
242 wg_linux_parse_peer(struct network *net, struct nlattr *data, time_t now)
243 {
244 struct network_peer *peer = NULL;
245 struct nlattr *tb[__WGPEER_A_LAST];
246 struct nlattr *cur;
247
248 nla_parse_nested(tb, WGPEER_A_MAX, data, NULL);
249
250 cur = tb[WGPEER_A_PUBLIC_KEY];
251 if (!cur)
252 return;
253
254 peer = wg_peer_update_start(net, nla_data(cur));
255 if (!peer)
256 return;
257
258 if ((cur = tb[WGPEER_A_LAST_HANDSHAKE_TIME]) != NULL) {
259 struct timespec64 *tv = nla_data(cur);
260
261 wg_peer_set_last_handshake(net, peer, now, tv->tv_sec);
262 }
263
264 if ((cur = tb[WGPEER_A_RX_BYTES]) != NULL)
265 wg_peer_set_rx_bytes(net, peer, nla_get_u64(cur));
266
267 if ((cur = tb[WGPEER_A_ENDPOINT]) != NULL)
268 wg_peer_set_endpoint(net, peer, nla_data(cur), nla_len(cur));
269
270 wg_peer_update_done(net, peer);
271 }
272
273 static void
274 wg_linux_parse_peer_list(struct network *net, struct nlattr *data, time_t now)
275 {
276 struct nlattr *cur;
277 int rem;
278
279 if (!data)
280 return;
281
282 nla_for_each_nested(cur, data, rem)
283 wg_linux_parse_peer(net, cur, now);
284 }
285
286 static int
287 wg_linux_get_cb(struct nl_msg *msg, void *arg)
288 {
289 struct nlmsghdr *nh = nlmsg_hdr(msg);
290 struct network *net = arg;
291 struct nlattr *tb[__WGDEVICE_A_LAST];
292 time_t now = time(NULL);
293
294 nlmsg_parse(nh, sizeof(struct genlmsghdr), tb, __WGDEVICE_A_LAST, NULL);
295 wg_linux_parse_peer_list(net, tb[WGDEVICE_A_PEERS], now);
296
297 return NL_SKIP;
298 }
299
300 static int
301 wg_linux_peer_refresh(struct network *net)
302 {
303 struct nl_msg *msg = wg_genl_msg(net, false);
304
305 return unl_request(&unl, msg, wg_linux_get_cb, net);
306 }
307
308 static int
309 wg_linux_peer_connect(struct network *net, struct network_peer *peer,
310 union network_endpoint *ep)
311 {
312 struct wg_linux_peer_req req;
313 struct nl_msg *msg;
314 int len;
315
316 msg = wg_linux_peer_req_init(net, peer, &req);
317
318 if (net->net_config.keepalive) {
319 nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, 0);
320 wg_linux_peer_req_done(&req);
321
322 msg = wg_linux_peer_req_init(net, peer, &req);
323 nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
324 net->net_config.keepalive);
325 }
326
327 if (ep->in.sin_family == AF_INET6)
328 len = sizeof(ep->in6);
329 else
330 len = sizeof(ep->in);
331 nla_put(msg, WGPEER_A_ENDPOINT, len, &ep->in6);
332
333 return wg_linux_peer_req_done(&req);
334 }
335
336 const struct wg_ops wg_linux_ops = {
337 .name = "user",
338 .init = wg_linux_init,
339 .cleanup = wg_linux_cleanup,
340 .init_local = wg_linux_init_local,
341 .peer_update = wg_linux_peer_update,
342 .peer_refresh = wg_linux_peer_refresh,
343 .peer_connect = wg_linux_peer_connect,
344 };