pex: remove extra newline in debug 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 <linux/wireguard.h>
24 #include <netlink/msg.h>
25 #include <netlink/attr.h>
26 #include <netlink/socket.h>
27 #include <unl.h>
28
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;
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->port);
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 int
151 wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd)
152 {
153 struct wg_linux_peer_req req;
154 struct blob_attr *cur;
155 struct nl_msg *msg;
156 struct nlattr *ips;
157 int rem;
158
159 msg = wg_linux_peer_req_init(net, peer, &req);
160
161 if (cmd == WG_PEER_DELETE) {
162 nla_put_u32(msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME);
163 goto out;
164 }
165
166 nla_put_u32(msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS);
167
168 ips = nla_nest_start(msg, WGPEER_A_ALLOWEDIPS);
169 wg_linux_msg_add_ip(msg, AF_INET6, &peer->local_addr.in6, 128);
170
171 blobmsg_for_each_attr(cur, peer->ipaddr, rem) {
172 const char *str = blobmsg_get_string(cur);
173 struct in6_addr in6;
174 int af, mask;
175
176 if (strchr(str, ':')) {
177 af = AF_INET6;
178 mask = 128;
179 } else {
180 af = AF_INET;
181 mask = 32;
182 }
183
184 if (inet_pton(af, str, &in6) != 1)
185 continue;
186
187 wg_linux_msg_add_ip(msg, af, &in6, mask);
188 }
189
190 blobmsg_for_each_attr(cur, peer->subnet, rem) {
191 const char *str = blobmsg_get_string(cur);
192 union network_addr addr;
193 int mask;
194 int af;
195
196 af = strchr(str, ':') ? AF_INET6 : AF_INET;
197 if (network_get_subnet(af, &addr, &mask, str))
198 continue;
199
200 wg_linux_msg_add_ip(msg, af, &addr, mask);
201 }
202
203 nla_nest_end(msg, ips);
204
205 out:
206 return wg_linux_peer_req_done(&req);
207 }
208
209 static void
210 wg_linux_parse_peer(struct network *net, struct nlattr *data, time_t now)
211 {
212 struct network_peer *peer = NULL;
213 struct nlattr *tb[__WGPEER_A_LAST];
214 struct nlattr *cur;
215
216 nla_parse_nested(tb, WGPEER_A_MAX, data, NULL);
217
218 cur = tb[WGPEER_A_PUBLIC_KEY];
219 if (!cur)
220 return;
221
222 peer = wg_peer_update_start(net, nla_data(cur));
223 if (!peer)
224 return;
225
226 if ((cur = tb[WGPEER_A_LAST_HANDSHAKE_TIME]) != NULL) {
227 struct timespec64 *tv = nla_data(cur);
228
229 wg_peer_set_last_handshake(net, peer, now, tv->tv_sec);
230 }
231
232 if ((cur = tb[WGPEER_A_RX_BYTES]) != NULL)
233 wg_peer_set_rx_bytes(net, peer, nla_get_u64(cur));
234
235 if ((cur = tb[WGPEER_A_ENDPOINT]) != NULL)
236 wg_peer_set_endpoint(net, peer, nla_data(cur), nla_len(cur));
237
238 wg_peer_update_done(net, peer);
239 }
240
241 static void
242 wg_linux_parse_peer_list(struct network *net, struct nlattr *data, time_t now)
243 {
244 struct nlattr *cur;
245 int rem;
246
247 if (!data)
248 return;
249
250 nla_for_each_nested(cur, data, rem)
251 wg_linux_parse_peer(net, cur, now);
252 }
253
254 static int
255 wg_linux_get_cb(struct nl_msg *msg, void *arg)
256 {
257 struct nlmsghdr *nh = nlmsg_hdr(msg);
258 struct network *net = arg;
259 struct nlattr *tb[__WGDEVICE_A_LAST];
260 time_t now = time(NULL);
261
262 nlmsg_parse(nh, sizeof(struct genlmsghdr), tb, __WGDEVICE_A_LAST, NULL);
263 wg_linux_parse_peer_list(net, tb[WGDEVICE_A_PEERS], now);
264
265 return NL_SKIP;
266 }
267
268 static int
269 wg_linux_peer_refresh(struct network *net)
270 {
271 struct nl_msg *msg = wg_genl_msg(net, false);
272
273 return unl_request(&unl, msg, wg_linux_get_cb, net);
274 }
275
276 static int
277 wg_linux_peer_connect(struct network *net, struct network_peer *peer,
278 union network_endpoint *ep)
279 {
280 struct wg_linux_peer_req req;
281 struct nl_msg *msg;
282 int len;
283
284 msg = wg_linux_peer_req_init(net, peer, &req);
285
286 if (net->net_config.keepalive) {
287 nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, 0);
288 wg_linux_peer_req_done(&req);
289
290 msg = wg_linux_peer_req_init(net, peer, &req);
291 nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
292 net->net_config.keepalive);
293 }
294
295 if (ep->in.sin_family == AF_INET6)
296 len = sizeof(ep->in6);
297 else
298 len = sizeof(ep->in);
299 nla_put(msg, WGPEER_A_ENDPOINT, len, &ep->in6);
300
301 return wg_linux_peer_req_done(&req);
302 }
303
304 const struct wg_ops wg_linux_ops = {
305 .name = "user",
306 .init = wg_linux_init,
307 .cleanup = wg_linux_cleanup,
308 .init_local = wg_linux_init_local,
309 .peer_update = wg_linux_peer_update,
310 .peer_refresh = wg_linux_peer_refresh,
311 .peer_connect = wg_linux_peer_connect,
312 };