]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/netdev/wireguard.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / network / netdev / wireguard.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
e5719363 2/***
96b2fb93 3 Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
e5719363
JT
4***/
5
6#include <sys/ioctl.h>
7#include <net/if.h>
9f0cf80d
YW
8#include <netinet/in.h>
9#include <linux/if_arp.h>
e9084344 10#include <linux/ipv6_route.h>
e5719363 11
8173d1d0
YW
12#include "sd-resolve.h"
13
e5719363 14#include "alloc-util.h"
4a897d29 15#include "dns-domain.h"
85c987a8 16#include "event-util.h"
e5719363 17#include "fd-util.h"
76df7779 18#include "fileio.h"
e5719363 19#include "hexdecoct.h"
0a970718 20#include "memory-util.h"
43409486 21#include "netlink-util.h"
e5719363 22#include "networkd-manager.h"
e9084344
YW
23#include "networkd-route-util.h"
24#include "networkd-route.h"
a4c9ae40 25#include "networkd-util.h"
c3eaba2d 26#include "parse-helpers.h"
a4c9ae40 27#include "parse-util.h"
8bf7e3b6 28#include "random-util.h"
1061dab1 29#include "resolve-private.h"
a4c9ae40
YW
30#include "string-util.h"
31#include "strv.h"
0a970718 32#include "wireguard.h"
e5719363 33
8bf7e3b6
YW
34static void wireguard_resolve_endpoints(NetDev *netdev);
35static int peer_resolve_endpoint(WireguardPeer *peer);
e5719363 36
75db809a 37static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
f1368a33
YW
38 WireguardIPmask *mask;
39
40 if (!peer)
75db809a 41 return NULL;
f1368a33
YW
42
43 if (peer->wireguard) {
44 LIST_REMOVE(peers, peer->wireguard->peers, peer);
45
f1368a33
YW
46 if (peer->section)
47 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
48 }
49
307fe3cd 50 config_section_free(peer->section);
f1368a33
YW
51
52 while ((mask = peer->ipmasks)) {
53 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
54 free(mask);
55 }
56
57 free(peer->endpoint_host);
58 free(peer->endpoint_port);
a3945c63 59 free(peer->preshared_key_file);
6ef5c881 60 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
f1368a33 61
8bf7e3b6
YW
62 sd_event_source_disable_unref(peer->resolve_retry_event_source);
63 sd_resolve_query_unref(peer->resolve_query);
64
75db809a 65 return mfree(peer);
f1368a33
YW
66}
67
307fe3cd 68DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free);
f1368a33
YW
69
70static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
307fe3cd 71 _cleanup_(config_section_freep) ConfigSection *n = NULL;
f1368a33
YW
72 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
73 int r;
e5719363
JT
74
75 assert(w);
f1368a33
YW
76 assert(ret);
77 assert(filename);
78 assert(section_line > 0);
e5719363 79
307fe3cd 80 r = config_section_new(filename, section_line, &n);
f1368a33
YW
81 if (r < 0)
82 return r;
83
84 peer = hashmap_get(w->peers_by_section, n);
85 if (peer) {
86 *ret = TAKE_PTR(peer);
87 return 0;
88 }
e5719363 89
fc721553 90 peer = new(WireguardPeer, 1);
e5719363 91 if (!peer)
f1368a33 92 return -ENOMEM;
fc721553
YW
93
94 *peer = (WireguardPeer) {
95 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
f1368a33
YW
96 .wireguard = w,
97 .section = TAKE_PTR(n),
fc721553 98 };
e5719363
JT
99
100 LIST_PREPEND(peers, w->peers, peer);
e5719363 101
307fe3cd 102 r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer);
f1368a33
YW
103 if (r < 0)
104 return r;
105
106 *ret = TAKE_PTR(peer);
107 return 0;
e5719363
JT
108}
109
e1f717d4 110static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
e5719363 111 int r;
e1f717d4
YW
112
113 assert(message);
114 assert(mask);
115 assert(index > 0);
116
117 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
118
119 r = sd_netlink_message_open_array(message, index);
120 if (r < 0)
121 return 0;
122
123 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
124 if (r < 0)
125 goto cancel;
126
43409486 127 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
e1f717d4
YW
128 if (r < 0)
129 goto cancel;
130
131 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
132 if (r < 0)
133 goto cancel;
134
135 r = sd_netlink_message_close_container(message);
136 if (r < 0)
137 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
138
139 return 1;
140
141cancel:
142 r = sd_netlink_message_cancel_array(message);
143 if (r < 0)
144 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
145
146 return 0;
147}
148
149static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
03677889 150 WireguardIPmask *start, *last = NULL;
e1f717d4
YW
151 uint16_t j = 0;
152 int r;
153
154 assert(message);
155 assert(peer);
156 assert(index > 0);
157 assert(mask_start);
158
159 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
160
161 start = *mask_start ?: peer->ipmasks;
162
163 r = sd_netlink_message_open_array(message, index);
164 if (r < 0)
165 return 0;
166
167 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
168 if (r < 0)
169 goto cancel;
170
2301c54f 171 if (!*mask_start) {
e1f717d4
YW
172 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
173 if (r < 0)
174 goto cancel;
175
176 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
177 if (r < 0)
178 goto cancel;
179
180 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
181 if (r < 0)
182 goto cancel;
183
43409486
YW
184 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
185 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
186 if (r < 0)
187 goto cancel;
188 }
e1f717d4
YW
189 }
190
191 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
192 if (r < 0)
193 goto cancel;
194
195 LIST_FOREACH(ipmasks, mask, start) {
196 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
197 if (r < 0)
198 return r;
03677889
YW
199 if (r == 0) {
200 last = mask;
e1f717d4 201 break;
03677889 202 }
e1f717d4
YW
203 }
204
205 r = sd_netlink_message_close_container(message);
206 if (r < 0)
207 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
208
209 r = sd_netlink_message_close_container(message);
210 if (r < 0)
211 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
212
03677889
YW
213 *mask_start = last; /* Start next cycle from this mask. */
214 return !last;
e1f717d4
YW
215
216cancel:
217 r = sd_netlink_message_cancel_array(message);
218 if (r < 0)
219 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
220
221 return 0;
222}
223
224static int wireguard_set_interface(NetDev *netdev) {
e5719363 225 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
e1f717d4 226 WireguardIPmask *mask_start = NULL;
03677889 227 WireguardPeer *peer_start;
50254f55 228 bool sent_once = false;
e5719363 229 uint32_t serial;
e1f717d4
YW
230 Wireguard *w;
231 int r;
e5719363
JT
232
233 assert(netdev);
234 w = WIREGUARD(netdev);
235 assert(w);
236
50254f55 237 for (peer_start = w->peers; peer_start || !sent_once; ) {
e1f717d4 238 uint16_t i = 0;
e5719363 239
e5719363
JT
240 message = sd_netlink_message_unref(message);
241
56fdc16d 242 r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
e5719363
JT
243 if (r < 0)
244 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
245
246 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
247 if (r < 0)
248 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
249
250 if (peer_start == w->peers) {
251 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
252 if (r < 0)
253 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
254
255 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
256 if (r < 0)
257 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
258
259 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
260 if (r < 0)
261 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
262
263 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
264 if (r < 0)
265 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
266 }
267
268 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
269 if (r < 0)
270 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
271
03677889 272 WireguardPeer *peer_last = NULL;
e5719363 273 LIST_FOREACH(peers, peer, peer_start) {
e1f717d4 274 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
e5719363 275 if (r < 0)
e1f717d4 276 return r;
03677889
YW
277 if (r == 0) {
278 peer_last = peer;
e5719363 279 break;
03677889 280 }
e5719363 281 }
03677889 282 peer_start = peer_last; /* Start next cycle from this peer. */
e5719363
JT
283
284 r = sd_netlink_message_close_container(message);
285 if (r < 0)
286 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
287
288 r = sd_netlink_send(netdev->manager->genl, message, &serial);
289 if (r < 0)
290 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
50254f55
YW
291
292 sent_once = true;
e1f717d4 293 }
e5719363
JT
294
295 return 0;
296}
297
8bf7e3b6 298static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
99534007 299 WireguardPeer *peer = ASSERT_PTR(userdata);
f1368a33 300 NetDev *netdev;
e5719363 301
f1368a33 302 assert(peer->wireguard);
8173d1d0 303
f1368a33 304 netdev = NETDEV(peer->wireguard);
8173d1d0 305
8bf7e3b6
YW
306 if (!netdev_is_managed(netdev))
307 return 0;
e5719363 308
8bf7e3b6 309 peer->resolve_query = sd_resolve_query_unref(peer->resolve_query);
e5719363 310
8bf7e3b6
YW
311 (void) peer_resolve_endpoint(peer);
312 return 0;
313}
e5719363 314
8bf7e3b6
YW
315static usec_t peer_next_resolve_usec(WireguardPeer *peer) {
316 usec_t usec;
e5719363 317
8bf7e3b6
YW
318 /* Given the number of retries this function will return an exponential increasing amount of
319 * milliseconds to wait starting at 200ms and capped at 25 seconds. */
f1368a33 320
8bf7e3b6 321 assert(peer);
e5719363 322
8bf7e3b6 323 usec = (2 << MIN(peer->n_retries, 7U)) * 100 * USEC_PER_MSEC;
e5719363 324
8bf7e3b6 325 return random_u64_range(usec / 10) + usec * 9 / 10;
e5719363
JT
326}
327
8bf7e3b6
YW
328static int wireguard_peer_resolve_handler(
329 sd_resolve_query *q,
330 int ret,
331 const struct addrinfo *ai,
332 void *userdata) {
e5719363 333
99534007 334 WireguardPeer *peer = ASSERT_PTR(userdata);
1061dab1 335 NetDev *netdev;
e5719363
JT
336 int r;
337
f1368a33 338 assert(peer->wireguard);
e5719363 339
8bf7e3b6 340 netdev = NETDEV(peer->wireguard);
e5719363 341
9e2bbf99 342 if (!netdev_is_managed(netdev))
8173d1d0 343 return 0;
e5719363
JT
344
345 if (ret != 0) {
8bf7e3b6
YW
346 log_netdev_warning(netdev, "Failed to resolve host '%s:%s', ignoring: %s",
347 peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
348 peer->n_retries++;
f1368a33 349
8bf7e3b6 350 } else {
38ef464e
YW
351 bool found = false;
352 for (; ai; ai = ai->ai_next) {
353 if (!IN_SET(ai->ai_family, AF_INET, AF_INET6))
354 continue;
355
356 if (ai->ai_addrlen != (ai->ai_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))
357 continue;
358
359 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
360 (void) wireguard_set_interface(netdev);
361 peer->n_retries = 0;
362 found = true;
363 break;
364 }
365
366 if (!found) {
367 log_netdev_warning(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the endpoint.",
368 peer->endpoint_host, peer->endpoint_port);
369 peer->n_retries++;
370 }
e5719363
JT
371 }
372
8bf7e3b6
YW
373 if (peer->n_retries > 0) {
374 r = event_reset_time_relative(netdev->manager->event,
375 &peer->resolve_retry_event_source,
ba4e0427 376 CLOCK_BOOTTIME,
8bf7e3b6
YW
377 peer_next_resolve_usec(peer), 0,
378 on_resolve_retry, peer, 0, "wireguard-resolve-retry", true);
379 if (r < 0)
380 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler for endpoint %s:%s, ignoring: %m",
381 peer->endpoint_host, peer->endpoint_port);
e5719363
JT
382 }
383
8bf7e3b6 384 wireguard_resolve_endpoints(netdev);
e5719363
JT
385 return 0;
386}
387
8bf7e3b6 388static int peer_resolve_endpoint(WireguardPeer *peer) {
e5719363
JT
389 static const struct addrinfo hints = {
390 .ai_family = AF_UNSPEC,
391 .ai_socktype = SOCK_DGRAM,
392 .ai_protocol = IPPROTO_UDP
393 };
8bf7e3b6
YW
394 NetDev *netdev;
395 int r;
396
397 assert(peer);
398 assert(peer->wireguard);
399
400 netdev = NETDEV(peer->wireguard);
401
402 if (!peer->endpoint_host || !peer->endpoint_port)
403 /* Not necessary to resolve the endpoint. */
404 return 0;
405
71193c0b 406 if (sd_event_source_get_enabled(peer->resolve_retry_event_source, NULL) > 0)
8bf7e3b6
YW
407 /* Timer event source is enabled. The endpoint will be resolved later. */
408 return 0;
409
410 if (peer->resolve_query)
411 /* Being resolved, or already resolved. */
412 return 0;
413
414 r = sd_resolve_getaddrinfo(netdev->manager->resolve,
415 &peer->resolve_query,
416 peer->endpoint_host,
417 peer->endpoint_port,
418 &hints,
419 wireguard_peer_resolve_handler,
420 peer);
421 if (r < 0)
422 return log_netdev_full_errno(netdev, r == -ENOBUFS ? LOG_DEBUG : LOG_WARNING, r,
423 "Failed to create endpoint resolver for %s:%s, ignoring: %m",
424 peer->endpoint_host, peer->endpoint_port);
425
426 return 0;
427}
428
429static void wireguard_resolve_endpoints(NetDev *netdev) {
8173d1d0 430 Wireguard *w;
e5719363
JT
431
432 assert(netdev);
433 w = WIREGUARD(netdev);
434 assert(w);
435
8bf7e3b6
YW
436 LIST_FOREACH(peers, peer, w->peers)
437 if (peer_resolve_endpoint(peer) == -ENOBUFS)
438 /* Too many requests. Let's resolve remaining endpoints later. */
e5719363 439 break;
e5719363
JT
440}
441
c2b19b8f 442static int netdev_wireguard_post_create(NetDev *netdev, Link *link) {
e5719363 443 assert(netdev);
10c353e1 444 assert(WIREGUARD(netdev));
e5719363 445
e1f717d4 446 (void) wireguard_set_interface(netdev);
8bf7e3b6 447 wireguard_resolve_endpoints(netdev);
e5719363
JT
448 return 0;
449}
450
03fec543
YW
451int config_parse_wireguard_listen_port(
452 const char *unit,
453 const char *filename,
454 unsigned line,
455 const char *section,
456 unsigned section_line,
457 const char *lvalue,
458 int ltype,
459 const char *rvalue,
460 void *data,
461 void *userdata) {
462
99534007 463 uint16_t *s = ASSERT_PTR(data);
e5719363
JT
464 int r;
465
466 assert(rvalue);
e5719363 467
a62b7bb7
YW
468 if (isempty(rvalue) || streq(rvalue, "auto")) {
469 *s = 0;
470 return 0;
471 }
472
473 r = parse_ip_port(rvalue, s);
474 if (r < 0) {
d96edb2c 475 log_syntax(unit, LOG_WARNING, filename, line, r,
a62b7bb7
YW
476 "Invalid port specification, ignoring assignment: %s", rvalue);
477 return 0;
e5719363
JT
478 }
479
e5719363
JT
480 return 0;
481}
482
fedcb4c3
YW
483static int wireguard_decode_key_and_warn(
484 const char *rvalue,
2b942a92 485 uint8_t ret[static WG_KEY_LEN],
fedcb4c3
YW
486 const char *unit,
487 const char *filename,
488 unsigned line,
489 const char *lvalue) {
03fec543 490
e693a932 491 _cleanup_(erase_and_freep) void *key = NULL;
e5719363
JT
492 size_t len;
493 int r;
494
e5719363 495 assert(rvalue);
fedcb4c3
YW
496 assert(ret);
497 assert(filename);
498 assert(lvalue);
499
500 if (isempty(rvalue)) {
501 memzero(ret, WG_KEY_LEN);
502 return 0;
503 }
e5719363 504
26f86d50
YW
505 if (!streq(lvalue, "PublicKey"))
506 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
507
6ef5c881 508 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
a8a50f4f
YW
509 if (r == -ENOMEM)
510 return log_oom();
e5f1b999
LP
511 if (r < 0) {
512 log_syntax(unit, LOG_WARNING, filename, line, r,
583eb170 513 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
e5f1b999
LP
514 return 0;
515 }
516 if (len != WG_KEY_LEN) {
517 log_syntax(unit, LOG_WARNING, filename, line, 0,
583eb170
YW
518 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
519 lvalue, len);
e5f1b999
LP
520 return 0;
521 }
e5719363 522
fedcb4c3 523 memcpy(ret, key, WG_KEY_LEN);
86a3d44d 524 return 0;
e5719363
JT
525}
526
03fec543
YW
527int config_parse_wireguard_private_key(
528 const char *unit,
529 const char *filename,
530 unsigned line,
531 const char *section,
532 unsigned section_line,
533 const char *lvalue,
534 int ltype,
535 const char *rvalue,
536 void *data,
537 void *userdata) {
538
e5719363
JT
539 Wireguard *w;
540
541 assert(data);
e5719363 542 w = WIREGUARD(data);
e5719363
JT
543 assert(w);
544
a8a50f4f 545 return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
e5719363
JT
546}
547
76df7779
YW
548int config_parse_wireguard_private_key_file(
549 const char *unit,
550 const char *filename,
551 unsigned line,
552 const char *section,
553 unsigned section_line,
554 const char *lvalue,
555 int ltype,
556 const char *rvalue,
557 void *data,
558 void *userdata) {
559
560 _cleanup_free_ char *path = NULL;
561 Wireguard *w;
562
563 assert(data);
564 w = WIREGUARD(data);
565 assert(w);
566
567 if (isempty(rvalue)) {
568 w->private_key_file = mfree(w->private_key_file);
569 return 0;
570 }
571
572 path = strdup(rvalue);
573 if (!path)
574 return log_oom();
575
576 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
577 return 0;
578
579 return free_and_replace(w->private_key_file, path);
580}
581
02241e43 582int config_parse_wireguard_peer_key(
03fec543
YW
583 const char *unit,
584 const char *filename,
585 unsigned line,
586 const char *section,
587 unsigned section_line,
588 const char *lvalue,
589 int ltype,
590 const char *rvalue,
591 void *data,
592 void *userdata) {
f1368a33 593
02241e43 594 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363 595 Wireguard *w;
f1368a33 596 int r;
e5719363
JT
597
598 assert(data);
e5719363 599 w = WIREGUARD(data);
e5719363
JT
600 assert(w);
601
f1368a33
YW
602 r = wireguard_peer_new_static(w, filename, section_line, &peer);
603 if (r < 0)
d96edb2c 604 return log_oom();
e5719363 605
02241e43
YW
606 r = wireguard_decode_key_and_warn(rvalue,
607 streq(lvalue, "PublicKey") ? peer->public_key : peer->preshared_key,
608 unit, filename, line, lvalue);
609 if (r < 0)
610 return r;
611
612 TAKE_PTR(peer);
d96edb2c 613 return 0;
e5719363
JT
614}
615
a3945c63
YW
616int config_parse_wireguard_preshared_key_file(
617 const char *unit,
618 const char *filename,
619 unsigned line,
620 const char *section,
621 unsigned section_line,
622 const char *lvalue,
623 int ltype,
624 const char *rvalue,
625 void *data,
626 void *userdata) {
627
628 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
629 _cleanup_free_ char *path = NULL;
630 Wireguard *w;
631 int r;
632
633 assert(data);
634 w = WIREGUARD(data);
635 assert(w);
636
637 r = wireguard_peer_new_static(w, filename, section_line, &peer);
638 if (r < 0)
d96edb2c 639 return log_oom();
a3945c63
YW
640
641 if (isempty(rvalue)) {
642 peer->preshared_key_file = mfree(peer->preshared_key_file);
643 TAKE_PTR(peer);
644 return 0;
645 }
646
647 path = strdup(rvalue);
648 if (!path)
649 return log_oom();
650
651 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
652 return 0;
653
654 free_and_replace(peer->preshared_key_file, path);
655 TAKE_PTR(peer);
656 return 0;
657}
658
03fec543
YW
659int config_parse_wireguard_allowed_ips(
660 const char *unit,
661 const char *filename,
662 unsigned line,
663 const char *section,
664 unsigned section_line,
665 const char *lvalue,
666 int ltype,
667 const char *rvalue,
668 void *data,
669 void *userdata) {
f1368a33
YW
670
671 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
672 union in_addr_union addr;
673 unsigned char prefixlen;
674 int r, family;
675 Wireguard *w;
e5719363
JT
676 WireguardIPmask *ipmask;
677
678 assert(rvalue);
679 assert(data);
680
681 w = WIREGUARD(data);
f1368a33
YW
682 assert(w);
683
684 r = wireguard_peer_new_static(w, filename, section_line, &peer);
685 if (r < 0)
d96edb2c 686 return log_oom();
e5719363 687
d96edb2c 688 for (const char *p = rvalue;;) {
e5719363 689 _cleanup_free_ char *word = NULL;
af670fc6 690 union in_addr_union masked;
e5719363 691
d96edb2c 692 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
e5719363
JT
693 if (r == 0)
694 break;
695 if (r == -ENOMEM)
696 return log_oom();
697 if (r < 0) {
d96edb2c 698 log_syntax(unit, LOG_WARNING, filename, line, r,
f1368a33 699 "Failed to split allowed ips \"%s\" option: %m", rvalue);
e5719363
JT
700 break;
701 }
702
703 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
704 if (r < 0) {
d96edb2c 705 log_syntax(unit, LOG_WARNING, filename, line, r,
f1368a33
YW
706 "Network address is invalid, ignoring assignment: %s", word);
707 continue;
e5719363
JT
708 }
709
af670fc6
YW
710 masked = addr;
711 assert_se(in_addr_mask(family, &masked, prefixlen) >= 0);
c71384a9 712 if (!in_addr_equal(family, &masked, &addr))
af670fc6 713 log_syntax(unit, LOG_WARNING, filename, line, 0,
c71384a9
ZJS
714 "Specified address '%s' is not properly masked, assuming '%s'.",
715 word,
716 IN_ADDR_PREFIX_TO_STRING(family, &masked, prefixlen));
af670fc6 717
fc721553 718 ipmask = new(WireguardIPmask, 1);
e5719363
JT
719 if (!ipmask)
720 return log_oom();
fc721553
YW
721
722 *ipmask = (WireguardIPmask) {
723 .family = family,
af670fc6 724 .ip = masked,
fc721553
YW
725 .cidr = prefixlen,
726 };
e5719363
JT
727
728 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
729 }
730
f1368a33 731 TAKE_PTR(peer);
e5719363
JT
732 return 0;
733}
734
03fec543
YW
735int config_parse_wireguard_endpoint(
736 const char *unit,
737 const char *filename,
738 unsigned line,
739 const char *section,
740 unsigned section_line,
741 const char *lvalue,
742 int ltype,
743 const char *rvalue,
744 void *data,
745 void *userdata) {
f1368a33
YW
746
747 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
4a897d29
YW
748 _cleanup_free_ char *host = NULL;
749 union in_addr_union addr;
750 const char *p;
751 uint16_t port;
e5719363 752 Wireguard *w;
4a897d29 753 int family, r;
e5719363 754
4a897d29 755 assert(filename);
e5719363 756 assert(rvalue);
4a897d29 757 assert(userdata);
e5719363 758
4a897d29 759 w = WIREGUARD(userdata);
e5719363
JT
760 assert(w);
761
44e93420
ZJS
762 r = wireguard_peer_new_static(w, filename, section_line, &peer);
763 if (r < 0)
d96edb2c 764 return log_oom();
44e93420 765
4a897d29
YW
766 r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
767 if (r >= 0) {
768 if (family == AF_INET)
769 peer->endpoint.in = (struct sockaddr_in) {
770 .sin_family = AF_INET,
771 .sin_addr = addr.in,
772 .sin_port = htobe16(port),
773 };
774 else if (family == AF_INET6)
775 peer->endpoint.in6 = (struct sockaddr_in6) {
776 .sin6_family = AF_INET6,
777 .sin6_addr = addr.in6,
778 .sin6_port = htobe16(port),
779 };
780 else
781 assert_not_reached();
782
783 peer->endpoint_host = mfree(peer->endpoint_host);
784 peer->endpoint_port = mfree(peer->endpoint_port);
4a897d29
YW
785
786 TAKE_PTR(peer);
787 return 0;
788 }
789
790 p = strrchr(rvalue, ':');
791 if (!p) {
792 log_syntax(unit, LOG_WARNING, filename, line, 0,
793 "Unable to find port of endpoint, ignoring assignment: %s",
794 rvalue);
795 return 0;
796 }
797
798 host = strndup(rvalue, p - rvalue);
799 if (!host)
e5719363
JT
800 return log_oom();
801
4a897d29
YW
802 if (!dns_name_is_valid(host)) {
803 log_syntax(unit, LOG_WARNING, filename, line, 0,
804 "Invalid domain name of endpoint, ignoring assignment: %s",
805 rvalue);
806 return 0;
807 }
808
809 p++;
810 r = parse_ip_port(p, &port);
811 if (r < 0) {
812 log_syntax(unit, LOG_WARNING, filename, line, r,
813 "Invalid port of endpoint, ignoring assignment: %s",
814 rvalue);
815 return 0;
816 }
817
818 peer->endpoint = (union sockaddr_union) {};
819
820 free_and_replace(peer->endpoint_host, host);
821
822 r = free_and_strdup(&peer->endpoint_port, p);
5f07d640 823 if (r < 0)
e5719363
JT
824 return log_oom();
825
4a897d29 826 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
e5719363
JT
827 return 0;
828}
829
03fec543
YW
830int config_parse_wireguard_keepalive(
831 const char *unit,
832 const char *filename,
833 unsigned line,
834 const char *section,
835 unsigned section_line,
836 const char *lvalue,
837 int ltype,
838 const char *rvalue,
839 void *data,
840 void *userdata) {
f1368a33 841
696c0832 842 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
843 uint16_t keepalive = 0;
844 Wireguard *w;
f1368a33 845 int r;
e5719363
JT
846
847 assert(rvalue);
848 assert(data);
849
850 w = WIREGUARD(data);
e5719363
JT
851 assert(w);
852
f1368a33
YW
853 r = wireguard_peer_new_static(w, filename, section_line, &peer);
854 if (r < 0)
d96edb2c 855 return log_oom();
e5719363
JT
856
857 if (streq(rvalue, "off"))
858 keepalive = 0;
859 else {
860 r = safe_atou16(rvalue, &keepalive);
f1368a33 861 if (r < 0) {
d96edb2c 862 log_syntax(unit, LOG_WARNING, filename, line, r,
44e93420 863 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
f1368a33
YW
864 rvalue);
865 return 0;
866 }
e5719363
JT
867 }
868
869 peer->persistent_keepalive_interval = keepalive;
696c0832
YW
870
871 TAKE_PTR(peer);
e5719363
JT
872 return 0;
873}
874
e9084344
YW
875int config_parse_wireguard_route_table(
876 const char *unit,
877 const char *filename,
878 unsigned line,
879 const char *section,
880 unsigned section_line,
881 const char *lvalue,
882 int ltype,
883 const char *rvalue,
884 void *data,
885 void *userdata) {
886
99534007
DT
887 NetDev *netdev = ASSERT_PTR(userdata);
888 uint32_t *table = ASSERT_PTR(data);
e9084344
YW
889 int r;
890
891 assert(filename);
892 assert(lvalue);
893 assert(rvalue);
e9084344 894
e135559d 895 if (isempty(rvalue) || parse_boolean(rvalue) == 0) {
cfe1237f 896 *table = 0; /* Disabled. */
e9084344
YW
897 return 0;
898 }
899
900 r = manager_get_route_table_from_string(netdev->manager, rvalue, table);
901 if (r < 0) {
902 log_syntax(unit, LOG_WARNING, filename, line, r,
903 "Failed to parse %s=, ignoring assignment: %s",
904 lvalue, rvalue);
905 return 0;
906 }
907
908 return 0;
909}
910
911int config_parse_wireguard_peer_route_table(
912 const char *unit,
913 const char *filename,
914 unsigned line,
915 const char *section,
916 unsigned section_line,
917 const char *lvalue,
918 int ltype,
919 const char *rvalue,
920 void *data,
921 void *userdata) {
922
923 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
99534007 924 NetDev *netdev = ASSERT_PTR(userdata);
e9084344
YW
925 Wireguard *w;
926 int r;
927
928 assert(filename);
929 assert(lvalue);
930 assert(rvalue);
e9084344
YW
931 assert(netdev->manager);
932
933 w = WIREGUARD(netdev);
934 assert(w);
935
936 r = wireguard_peer_new_static(w, filename, section_line, &peer);
937 if (r < 0)
938 return log_oom();
939
940 if (isempty(rvalue)) {
941 peer->route_table_set = false; /* Use the table specified in [WireGuard] section. */
942 TAKE_PTR(peer);
943 return 0;
944 }
945
e135559d 946 if (parse_boolean(rvalue) == 0) {
e9084344
YW
947 peer->route_table = 0; /* Disabled. */
948 peer->route_table_set = true;
949 TAKE_PTR(peer);
950 return 0;
951 }
952
953 r = manager_get_route_table_from_string(netdev->manager, rvalue, &peer->route_table);
954 if (r < 0) {
955 log_syntax(unit, LOG_WARNING, filename, line, r,
956 "Failed to parse %s=, ignoring assignment: %s",
957 lvalue, rvalue);
958 return 0;
959 }
960
961 peer->route_table_set = true;
962 TAKE_PTR(peer);
963 return 0;
964}
965
966int config_parse_wireguard_route_priority(
967 const char *unit,
968 const char *filename,
969 unsigned line,
970 const char *section,
971 unsigned section_line,
972 const char *lvalue,
973 int ltype,
974 const char *rvalue,
975 void *data,
976 void *userdata) {
977
99534007 978 uint32_t *priority = ASSERT_PTR(data);
e9084344
YW
979 int r;
980
981 assert(filename);
982 assert(lvalue);
983 assert(rvalue);
e9084344
YW
984
985 if (isempty(rvalue)) {
986 *priority = 0;
987 return 0;
988 }
989
990 r = safe_atou32(rvalue, priority);
991 if (r < 0) {
992 log_syntax(unit, LOG_WARNING, filename, line, r,
993 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
994 return 0;
995 }
996
997 return 0;
998}
999
1000int config_parse_wireguard_peer_route_priority(
1001 const char *unit,
1002 const char *filename,
1003 unsigned line,
1004 const char *section,
1005 unsigned section_line,
1006 const char *lvalue,
1007 int ltype,
1008 const char *rvalue,
1009 void *data,
1010 void *userdata) {
1011
1012 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1013 Wireguard *w;
1014 int r;
1015
1016 assert(filename);
1017 assert(lvalue);
1018 assert(rvalue);
1019 assert(userdata);
1020
1021 w = WIREGUARD(userdata);
1022 assert(w);
1023
1024 r = wireguard_peer_new_static(w, filename, section_line, &peer);
1025 if (r < 0)
1026 return log_oom();
1027
1028 if (isempty(rvalue)) {
1029 peer->route_priority_set = false; /* Use the priority specified in [WireGuard] section. */
1030 TAKE_PTR(peer);
1031 return 0;
1032 }
1033
1034 r = safe_atou32(rvalue, &peer->route_priority);
1035 if (r < 0) {
1036 log_syntax(unit, LOG_WARNING, filename, line, r,
1037 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1038 return 0;
1039 }
1040
1041 peer->route_priority_set = true;
1042 TAKE_PTR(peer);
1043 return 0;
1044}
1045
e5719363
JT
1046static void wireguard_init(NetDev *netdev) {
1047 Wireguard *w;
1048
1049 assert(netdev);
e5719363 1050 w = WIREGUARD(netdev);
e5719363
JT
1051 assert(w);
1052
1053 w->flags = WGDEVICE_F_REPLACE_PEERS;
1054}
1055
1056static void wireguard_done(NetDev *netdev) {
1057 Wireguard *w;
e5719363
JT
1058
1059 assert(netdev);
1060 w = WIREGUARD(netdev);
c195364d 1061 assert(w);
e5719363 1062
6ef5c881 1063 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
76df7779
YW
1064 free(w->private_key_file);
1065
f1368a33 1066 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
e9084344
YW
1067
1068 set_free(w->routes);
f1368a33 1069}
c195364d 1070
cb31e7c8 1071static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
e693a932 1072 _cleanup_(erase_and_freep) char *key = NULL;
cb31e7c8
YW
1073 size_t key_len;
1074 int r;
76df7779 1075
cb31e7c8 1076 if (!filename)
76df7779
YW
1077 return 0;
1078
2caa38e9
LP
1079 assert(dest);
1080
49f16281 1081 r = read_full_file_full(
986311c2 1082 AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
49f16281 1083 READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
d3dcf4e3 1084 NULL, &key, &key_len);
76df7779 1085 if (r < 0)
cb31e7c8 1086 return r;
76df7779 1087
e693a932
ZJS
1088 if (key_len != WG_KEY_LEN)
1089 return -EINVAL;
76df7779 1090
cb31e7c8 1091 memcpy(dest, key, WG_KEY_LEN);
e693a932 1092 return 0;
76df7779
YW
1093}
1094
9cc9021a
YW
1095static int wireguard_peer_verify(WireguardPeer *peer) {
1096 NetDev *netdev = NETDEV(peer->wireguard);
a3945c63 1097 int r;
9cc9021a
YW
1098
1099 if (section_is_invalid(peer->section))
1100 return -EINVAL;
1101
1102 if (eqzero(peer->public_key))
1103 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1104 "%s: WireGuardPeer section without PublicKey= configured. "
1105 "Ignoring [WireGuardPeer] section from line %u.",
1106 peer->section->filename, peer->section->line);
1107
a3945c63
YW
1108 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
1109 if (r < 0)
1110 return log_netdev_error_errno(netdev, r,
1111 "%s: Failed to read preshared key from '%s'. "
1112 "Ignoring [WireGuardPeer] section from line %u.",
1113 peer->section->filename, peer->preshared_key_file,
1114 peer->section->line);
1115
9cc9021a
YW
1116 return 0;
1117}
1118
f1368a33 1119static int wireguard_verify(NetDev *netdev, const char *filename) {
f1368a33 1120 Wireguard *w;
76df7779 1121 int r;
c195364d 1122
f1368a33
YW
1123 assert(netdev);
1124 w = WIREGUARD(netdev);
1125 assert(w);
1126
cb31e7c8
YW
1127 r = wireguard_read_key_file(w->private_key_file, w->private_key);
1128 if (r < 0)
1129 return log_netdev_error_errno(netdev, r,
74bd6ad0
YW
1130 "Failed to read private key from %s. Ignoring network device.",
1131 w->private_key_file);
9cc9021a 1132
cb31e7c8
YW
1133 if (eqzero(w->private_key))
1134 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1135 "%s: Missing PrivateKey= or PrivateKeyFile=, "
74bd6ad0 1136 "Ignoring network device.", filename);
76df7779 1137
80a226b2 1138 LIST_FOREACH(peers, peer, w->peers) {
e9084344 1139 if (wireguard_peer_verify(peer) < 0) {
f1368a33 1140 wireguard_peer_free(peer);
e9084344
YW
1141 continue;
1142 }
1143
1144 if ((peer->route_table_set ? peer->route_table : w->route_table) == 0)
1145 continue;
1146
1147 LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
1148 _cleanup_(route_freep) Route *route = NULL;
1149
1150 r = route_new(&route);
1151 if (r < 0)
1152 return log_oom();
1153
1154 route->family = ipmask->family;
1155 route->dst = ipmask->ip;
1156 route->dst_prefixlen = ipmask->cidr;
1157 route->scope = RT_SCOPE_UNIVERSE;
1158 route->protocol = RTPROT_STATIC;
1159 route->table = peer->route_table_set ? peer->route_table : w->route_table;
1160 route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
1161 if (route->priority == 0 && route->family == AF_INET6)
1162 route->priority = IP6_RT_PRIO_USER;
1163 route->source = NETWORK_CONFIG_SOURCE_STATIC;
1164
1165 r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
1166 if (r < 0)
1167 return log_oom();
1168 }
1169 }
f1368a33
YW
1170
1171 return 0;
e5719363
JT
1172}
1173
1174const NetDevVTable wireguard_vtable = {
1175 .object_size = sizeof(Wireguard),
130b812f 1176 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
e5719363
JT
1177 .post_create = netdev_wireguard_post_create,
1178 .init = wireguard_init,
1179 .done = wireguard_done,
1180 .create_type = NETDEV_CREATE_INDEPENDENT,
f1368a33 1181 .config_verify = wireguard_verify,
9f0cf80d 1182 .iftype = ARPHRD_NONE,
e5719363 1183};