unet-cli: strip initial newline in usage message
[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 int ret;
48
49 if (unl.sock)
50 return 0;
51
52 ret = unl_genl_init(&unl, "wireguard");
53 if (ret)
54 return ret;
55
56 nl_socket_set_buffer_size(unl.sock, 32768, 32768);
57 nlmsg_set_default_size(32768);
58
59 return 0;
60 }
61
62 static struct nl_msg *
63 wg_genl_msg(struct network *net, bool set)
64 {
65 struct nl_msg *msg;
66
67 msg = unl_genl_msg(&unl, set ? WG_CMD_SET_DEVICE : WG_CMD_GET_DEVICE, !set);
68 nla_put_string(msg, WGDEVICE_A_IFNAME, network_name(net));
69
70 return msg;
71 }
72
73 static int
74 wg_genl_call(struct nl_msg *msg)
75 {
76 return unl_request(&unl, msg, NULL, NULL);
77 }
78
79 static int
80 __wg_linux_init(struct network *net, void *key)
81 {
82 struct nl_msg *msg;
83
84 msg = wg_genl_msg(net, true);
85 nla_put(msg, WGDEVICE_A_PRIVATE_KEY, WG_KEY_LEN, key);
86 nla_put_u32(msg, WGDEVICE_A_FLAGS, WGDEVICE_F_REPLACE_PEERS);
87
88 return wg_genl_call(msg);
89 }
90
91 static void
92 wg_linux_cleanup(struct network *net)
93 {
94 uint8_t key[WG_KEY_LEN] = {};
95
96 __wg_linux_init(net, key);
97 }
98
99 static int
100 wg_linux_init(struct network *net)
101 {
102 if (wg_nl_init())
103 return -1;
104
105 return __wg_linux_init(net, net->config.key);
106 }
107
108 static int
109 wg_linux_init_local(struct network *net, struct network_peer *peer)
110 {
111 struct nl_msg *msg;
112
113 msg = wg_genl_msg(net, true);
114 nla_put_u16(msg, WGDEVICE_A_LISTEN_PORT, peer ? peer->port : 0);
115
116 return wg_genl_call(msg);
117 }
118
119 static void
120 wg_linux_msg_add_ip(struct nl_msg *msg, int af, void *addr, int mask)
121 {
122 struct nlattr *ip;
123 int len;
124
125 if (af == AF_INET6)
126 len = sizeof(struct in6_addr);
127 else
128 len = sizeof(struct in_addr);
129
130 ip = nla_nest_start(msg, 0);
131 nla_put_u16(msg, WGALLOWEDIP_A_FAMILY, af);
132 nla_put(msg, WGALLOWEDIP_A_IPADDR, len, addr);
133 nla_put_u8(msg, WGALLOWEDIP_A_CIDR_MASK, mask);
134 nla_nest_end(msg, ip);
135 }
136
137 static struct nl_msg *
138 wg_linux_peer_req_init(struct network *net, struct network_peer *peer,
139 struct wg_linux_peer_req *req)
140 {
141 req->msg = wg_genl_msg(net, true);
142
143 req->peers = nla_nest_start(req->msg, WGDEVICE_A_PEERS);
144 req->entry = nla_nest_start(req->msg, 0);
145 nla_put(req->msg, WGPEER_A_PUBLIC_KEY, WG_KEY_LEN, peer->key);
146
147 return req->msg;
148 }
149
150 static int
151 wg_linux_peer_req_done(struct wg_linux_peer_req *req)
152 {
153 nla_nest_end(req->msg, req->entry);
154 nla_nest_end(req->msg, req->peers);
155
156 return wg_genl_call(req->msg);
157 }
158
159 static struct nl_msg *
160 wg_linux_peer_msg_size_check(struct wg_linux_peer_req *req, struct network *net,
161 struct network_peer *peer)
162 {
163 if (nlmsg_get_max_size(req->msg) >
164 nlmsg_total_size(nlmsg_hdr(req->msg)->nlmsg_len) + 256)
165 return req->msg;
166
167 nla_nest_end(req->msg, req->ips);
168 wg_linux_peer_req_done(req);
169
170 wg_linux_peer_req_init(net, peer, req);
171 req->ips = nla_nest_start(req->msg, WGPEER_A_ALLOWEDIPS);
172
173 return req->msg;
174 }
175
176 static void
177 wg_linux_peer_msg_add_allowed_ip(struct wg_linux_peer_req *req, struct network *net,
178 struct network_peer *peer)
179 {
180 struct nl_msg *msg = req->msg;
181 struct blob_attr *cur;
182 int rem;
183
184 wg_linux_msg_add_ip(msg, AF_INET6, &peer->local_addr.in6, 128);
185 msg = wg_linux_peer_msg_size_check(req, net, peer);
186
187 blobmsg_for_each_attr(cur, peer->ipaddr, rem) {
188 const char *str = blobmsg_get_string(cur);
189 struct in6_addr in6;
190 int af, mask;
191
192 if (strchr(str, ':')) {
193 af = AF_INET6;
194 mask = 128;
195 } else {
196 af = AF_INET;
197 mask = 32;
198 }
199
200 if (inet_pton(af, str, &in6) != 1)
201 continue;
202
203 wg_linux_msg_add_ip(msg, af, &in6, mask);
204 msg = wg_linux_peer_msg_size_check(req, net, peer);
205 }
206
207 blobmsg_for_each_attr(cur, peer->subnet, rem) {
208 const char *str = blobmsg_get_string(cur);
209 union network_addr addr;
210 int mask;
211 int af;
212
213 af = strchr(str, ':') ? AF_INET6 : AF_INET;
214 if (network_get_subnet(af, &addr, &mask, str))
215 continue;
216
217 wg_linux_msg_add_ip(msg, af, &addr, mask);
218 msg = wg_linux_peer_msg_size_check(req, net, peer);
219 }
220 }
221
222 static int
223 wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd)
224 {
225 struct wg_linux_peer_req req;
226 struct network_host *host;
227
228 wg_linux_peer_req_init(net, peer, &req);
229
230 if (cmd == WG_PEER_DELETE) {
231 nla_put_u32(req.msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME);
232 goto out;
233 }
234
235 nla_put_u32(req.msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS);
236
237 req.ips = nla_nest_start(req.msg, WGPEER_A_ALLOWEDIPS);
238
239 wg_linux_peer_msg_add_allowed_ip(&req, net, peer);
240
241 for_each_routed_host(host, net, peer)
242 wg_linux_peer_msg_add_allowed_ip(&req, net, &host->peer);
243
244 nla_nest_end(req.msg, req.ips);
245
246 out:
247 return wg_linux_peer_req_done(&req);
248 }
249
250 static void
251 wg_linux_parse_peer(struct network *net, struct nlattr *data, time_t now)
252 {
253 struct network_peer *peer = NULL;
254 struct nlattr *tb[__WGPEER_A_LAST];
255 struct nlattr *cur;
256
257 nla_parse_nested(tb, WGPEER_A_MAX, data, NULL);
258
259 cur = tb[WGPEER_A_PUBLIC_KEY];
260 if (!cur)
261 return;
262
263 peer = wg_peer_update_start(net, nla_data(cur));
264 if (!peer)
265 return;
266
267 if ((cur = tb[WGPEER_A_LAST_HANDSHAKE_TIME]) != NULL) {
268 struct timespec64 *tv = nla_data(cur);
269
270 wg_peer_set_last_handshake(net, peer, now, tv->tv_sec);
271 }
272
273 if ((cur = tb[WGPEER_A_RX_BYTES]) != NULL)
274 wg_peer_set_rx_bytes(net, peer, nla_get_u64(cur));
275
276 if ((cur = tb[WGPEER_A_ENDPOINT]) != NULL)
277 wg_peer_set_endpoint(net, peer, nla_data(cur), nla_len(cur));
278
279 wg_peer_update_done(net, peer);
280 }
281
282 static void
283 wg_linux_parse_peer_list(struct network *net, struct nlattr *data, time_t now)
284 {
285 struct nlattr *cur;
286 int rem;
287
288 if (!data)
289 return;
290
291 nla_for_each_nested(cur, data, rem)
292 wg_linux_parse_peer(net, cur, now);
293 }
294
295 static int
296 wg_linux_get_cb(struct nl_msg *msg, void *arg)
297 {
298 struct nlmsghdr *nh = nlmsg_hdr(msg);
299 struct network *net = arg;
300 struct nlattr *tb[__WGDEVICE_A_LAST];
301 time_t now = time(NULL);
302
303 nlmsg_parse(nh, sizeof(struct genlmsghdr), tb, __WGDEVICE_A_LAST, NULL);
304 wg_linux_parse_peer_list(net, tb[WGDEVICE_A_PEERS], now);
305
306 return NL_SKIP;
307 }
308
309 static int
310 wg_linux_peer_refresh(struct network *net)
311 {
312 struct nl_msg *msg = wg_genl_msg(net, false);
313
314 return unl_request(&unl, msg, wg_linux_get_cb, net);
315 }
316
317 static int
318 wg_linux_peer_connect(struct network *net, struct network_peer *peer,
319 union network_endpoint *ep)
320 {
321 struct wg_linux_peer_req req;
322 struct nl_msg *msg;
323 int len;
324
325 msg = wg_linux_peer_req_init(net, peer, &req);
326
327 if (net->net_config.keepalive) {
328 nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, 0);
329 wg_linux_peer_req_done(&req);
330
331 msg = wg_linux_peer_req_init(net, peer, &req);
332 nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
333 net->net_config.keepalive);
334 }
335
336 if (ep->in.sin_family == AF_INET6)
337 len = sizeof(ep->in6);
338 else
339 len = sizeof(ep->in);
340 nla_put(msg, WGPEER_A_ENDPOINT, len, &ep->in6);
341
342 return wg_linux_peer_req_done(&req);
343 }
344
345 const struct wg_ops wg_linux_ops = {
346 .name = "user",
347 .init = wg_linux_init,
348 .cleanup = wg_linux_cleanup,
349 .init_local = wg_linux_init_local,
350 .peer_update = wg_linux_peer_update,
351 .peer_refresh = wg_linux_peer_refresh,
352 .peer_connect = wg_linux_peer_connect,
353 };