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