]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/netdev/wireguard.c
network: add WireGuardPeer.PresharedKeyFile= setting
[thirdparty/systemd.git] / src / network / netdev / wireguard.c
CommitLineData
d506b89c 1/* SPDX-License-Identifier: LGPL-2.1+ */
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>
8
8173d1d0
YW
9#include "sd-resolve.h"
10
e5719363 11#include "alloc-util.h"
85c987a8 12#include "event-util.h"
e5719363 13#include "fd-util.h"
76df7779 14#include "fileio.h"
e5719363 15#include "hexdecoct.h"
0a970718 16#include "memory-util.h"
43409486 17#include "netlink-util.h"
e5719363 18#include "networkd-link.h"
e5719363 19#include "networkd-manager.h"
a4c9ae40
YW
20#include "networkd-util.h"
21#include "parse-util.h"
76df7779 22#include "path-util.h"
1061dab1 23#include "resolve-private.h"
a4c9ae40
YW
24#include "string-util.h"
25#include "strv.h"
e5719363 26#include "wireguard-netlink.h"
0a970718 27#include "wireguard.h"
e5719363
JT
28
29static void resolve_endpoints(NetDev *netdev);
30
f1368a33
YW
31static void wireguard_peer_free(WireguardPeer *peer) {
32 WireguardIPmask *mask;
33
34 if (!peer)
35 return;
36
37 if (peer->wireguard) {
38 LIST_REMOVE(peers, peer->wireguard->peers, peer);
39
40 set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer);
41 set_remove(peer->wireguard->peers_with_failed_endpoint, peer);
42
43 if (peer->section)
44 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
45 }
46
47 network_config_section_free(peer->section);
48
49 while ((mask = peer->ipmasks)) {
50 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
51 free(mask);
52 }
53
54 free(peer->endpoint_host);
55 free(peer->endpoint_port);
a3945c63 56 free(peer->preshared_key_file);
6ef5c881 57 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
f1368a33
YW
58
59 free(peer);
60}
61
62DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
63
64static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
65 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
66 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
67 int r;
e5719363
JT
68
69 assert(w);
f1368a33
YW
70 assert(ret);
71 assert(filename);
72 assert(section_line > 0);
e5719363 73
f1368a33
YW
74 r = network_config_section_new(filename, section_line, &n);
75 if (r < 0)
76 return r;
77
78 peer = hashmap_get(w->peers_by_section, n);
79 if (peer) {
80 *ret = TAKE_PTR(peer);
81 return 0;
82 }
e5719363 83
fc721553 84 peer = new(WireguardPeer, 1);
e5719363 85 if (!peer)
f1368a33 86 return -ENOMEM;
fc721553
YW
87
88 *peer = (WireguardPeer) {
89 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
f1368a33
YW
90 .wireguard = w,
91 .section = TAKE_PTR(n),
fc721553 92 };
e5719363
JT
93
94 LIST_PREPEND(peers, w->peers, peer);
e5719363 95
f1368a33
YW
96 r = hashmap_ensure_allocated(&w->peers_by_section, &network_config_hash_ops);
97 if (r < 0)
98 return r;
99
100 r = hashmap_put(w->peers_by_section, peer->section, peer);
101 if (r < 0)
102 return r;
103
104 *ret = TAKE_PTR(peer);
105 return 0;
e5719363
JT
106}
107
e1f717d4 108static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
e5719363 109 int r;
e1f717d4
YW
110
111 assert(message);
112 assert(mask);
113 assert(index > 0);
114
115 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
116
117 r = sd_netlink_message_open_array(message, index);
118 if (r < 0)
119 return 0;
120
121 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
122 if (r < 0)
123 goto cancel;
124
43409486 125 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
e1f717d4
YW
126 if (r < 0)
127 goto cancel;
128
129 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
130 if (r < 0)
131 goto cancel;
132
133 r = sd_netlink_message_close_container(message);
134 if (r < 0)
135 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
136
137 return 1;
138
139cancel:
140 r = sd_netlink_message_cancel_array(message);
141 if (r < 0)
142 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
143
144 return 0;
145}
146
147static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
148 WireguardIPmask *mask, *start;
149 uint16_t j = 0;
150 int r;
151
152 assert(message);
153 assert(peer);
154 assert(index > 0);
155 assert(mask_start);
156
157 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
158
159 start = *mask_start ?: peer->ipmasks;
160
161 r = sd_netlink_message_open_array(message, index);
162 if (r < 0)
163 return 0;
164
165 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
166 if (r < 0)
167 goto cancel;
168
2301c54f 169 if (!*mask_start) {
e1f717d4
YW
170 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
171 if (r < 0)
172 goto cancel;
173
174 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
175 if (r < 0)
176 goto cancel;
177
178 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
179 if (r < 0)
180 goto cancel;
181
43409486
YW
182 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
183 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
184 if (r < 0)
185 goto cancel;
186 }
e1f717d4
YW
187 }
188
189 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
190 if (r < 0)
191 goto cancel;
192
193 LIST_FOREACH(ipmasks, mask, start) {
194 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
195 if (r < 0)
196 return r;
197 if (r == 0)
198 break;
199 }
200
201 r = sd_netlink_message_close_container(message);
202 if (r < 0)
203 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
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 peer: %m");
208
209 *mask_start = mask; /* Start next cycle from this mask. */
210 return !mask;
211
212cancel:
213 r = sd_netlink_message_cancel_array(message);
214 if (r < 0)
215 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
216
217 return 0;
218}
219
220static int wireguard_set_interface(NetDev *netdev) {
e5719363 221 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
e1f717d4
YW
222 WireguardIPmask *mask_start = NULL;
223 WireguardPeer *peer, *peer_start;
e5719363 224 uint32_t serial;
e1f717d4
YW
225 Wireguard *w;
226 int r;
e5719363
JT
227
228 assert(netdev);
229 w = WIREGUARD(netdev);
230 assert(w);
231
e1f717d4
YW
232 for (peer_start = w->peers; peer_start; ) {
233 uint16_t i = 0;
e5719363 234
e5719363
JT
235 message = sd_netlink_message_unref(message);
236
237 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
238 if (r < 0)
239 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
240
241 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
242 if (r < 0)
243 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
244
245 if (peer_start == w->peers) {
246 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
247 if (r < 0)
248 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
249
250 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
251 if (r < 0)
252 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
253
254 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
255 if (r < 0)
256 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
257
258 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
259 if (r < 0)
260 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
261 }
262
263 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
264 if (r < 0)
265 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
266
e5719363 267 LIST_FOREACH(peers, peer, peer_start) {
e1f717d4 268 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
e5719363 269 if (r < 0)
e1f717d4
YW
270 return r;
271 if (r == 0)
e5719363 272 break;
e5719363 273 }
e1f717d4 274 peer_start = peer; /* Start next cycle from this peer. */
e5719363
JT
275
276 r = sd_netlink_message_close_container(message);
277 if (r < 0)
278 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
279
280 r = sd_netlink_send(netdev->manager->genl, message, &serial);
281 if (r < 0)
282 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
e1f717d4 283 }
e5719363
JT
284
285 return 0;
286}
287
f1368a33
YW
288static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
289 NetDev *netdev;
e5719363 290
f1368a33
YW
291 assert(peer);
292 assert(peer->wireguard);
8173d1d0 293
f1368a33 294 netdev = NETDEV(peer->wireguard);
8173d1d0 295
f1368a33
YW
296 if (section_is_invalid(peer->section))
297 wireguard_peer_free(peer);
298
299 netdev_unref(netdev);
300}
e5719363
JT
301
302static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
303 NetDev *netdev = userdata;
304 Wireguard *w;
305
306 assert(netdev);
307 w = WIREGUARD(netdev);
308 assert(w);
309
9e2bbf99 310 if (!netdev_is_managed(netdev))
56ba90c2 311 return 0;
e5719363 312
f1368a33
YW
313 assert(set_isempty(w->peers_with_unresolved_endpoint));
314
315 SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
e5719363
JT
316
317 resolve_endpoints(netdev);
318
319 return 0;
320}
321
322/*
323 * Given the number of retries this function will return will an exponential
324 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
325 */
326static int exponential_backoff_milliseconds(unsigned n_retries) {
7232c1f9 327 return (2 << MIN(n_retries, 7U)) * 100 * USEC_PER_MSEC;
e5719363
JT
328}
329
330static int wireguard_resolve_handler(sd_resolve_query *q,
331 int ret,
332 const struct addrinfo *ai,
f1368a33 333 WireguardPeer *peer) {
1061dab1 334 NetDev *netdev;
e5719363 335 Wireguard *w;
e5719363
JT
336 int r;
337
f1368a33
YW
338 assert(peer);
339 assert(peer->wireguard);
e5719363 340
f1368a33
YW
341 w = peer->wireguard;
342 netdev = NETDEV(w);
e5719363 343
9e2bbf99 344 if (!netdev_is_managed(netdev))
8173d1d0 345 return 0;
e5719363
JT
346
347 if (ret != 0) {
f1368a33
YW
348 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
349
350 r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL);
351 if (r < 0) {
352 log_oom();
353 peer->section->invalid = true;
354 goto resolve_next;
355 }
356
357 r = set_put(w->peers_with_failed_endpoint, peer);
358 if (r < 0) {
359 log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
360 peer->section->invalid = true;
361 goto resolve_next;
362 }
363
e5719363 364 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
8173d1d0 365 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
f1368a33 366 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
e5719363 367 else
f1368a33
YW
368 log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
369 peer->endpoint_host, peer->endpoint_port);
e5719363 370
f1368a33
YW
371resolve_next:
372 if (!set_isempty(w->peers_with_unresolved_endpoint)) {
e5719363
JT
373 resolve_endpoints(netdev);
374 return 0;
375 }
376
e1f717d4 377 (void) wireguard_set_interface(netdev);
f1368a33
YW
378
379 if (!set_isempty(w->peers_with_failed_endpoint)) {
85c987a8 380 usec_t usec;
56ba90c2 381
e5719363 382 w->n_retries++;
85c987a8
YW
383 usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
384 r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
385 CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
386 0, "wireguard-resolve-retry", true);
56ba90c2 387 if (r < 0) {
e5719363 388 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
56ba90c2
YW
389 return 0;
390 }
e5719363
JT
391 }
392
393 return 0;
394}
395
396static void resolve_endpoints(NetDev *netdev) {
e5719363
JT
397 static const struct addrinfo hints = {
398 .ai_family = AF_UNSPEC,
399 .ai_socktype = SOCK_DGRAM,
400 .ai_protocol = IPPROTO_UDP
401 };
f1368a33 402 WireguardPeer *peer;
8173d1d0 403 Wireguard *w;
f1368a33 404 Iterator i;
8173d1d0 405 int r = 0;
e5719363
JT
406
407 assert(netdev);
408 w = WIREGUARD(netdev);
409 assert(w);
410
f1368a33 411 SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) {
1061dab1
YW
412 r = resolve_getaddrinfo(netdev->manager->resolve,
413 NULL,
f1368a33
YW
414 peer->endpoint_host,
415 peer->endpoint_port,
1061dab1
YW
416 &hints,
417 wireguard_resolve_handler,
f1368a33
YW
418 wireguard_peer_destroy_callback,
419 peer);
e5719363
JT
420 if (r == -ENOBUFS)
421 break;
8173d1d0
YW
422 if (r < 0) {
423 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
424 continue;
425 }
e5719363 426
8173d1d0
YW
427 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
428 netdev_ref(netdev);
429
f1368a33 430 (void) set_remove(w->peers_with_unresolved_endpoint, peer);
e5719363
JT
431 }
432}
433
e5719363 434static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
e5719363 435 assert(netdev);
10c353e1 436 assert(WIREGUARD(netdev));
e5719363 437
e1f717d4 438 (void) wireguard_set_interface(netdev);
e5719363
JT
439 resolve_endpoints(netdev);
440 return 0;
441}
442
03fec543
YW
443int config_parse_wireguard_listen_port(
444 const char *unit,
445 const char *filename,
446 unsigned line,
447 const char *section,
448 unsigned section_line,
449 const char *lvalue,
450 int ltype,
451 const char *rvalue,
452 void *data,
453 void *userdata) {
454
e5719363
JT
455 uint16_t *s = data;
456 uint16_t port = 0;
457 int r;
458
459 assert(rvalue);
460 assert(data);
461
462 if (!streq(rvalue, "auto")) {
f1368a33
YW
463 r = parse_ip_port(rvalue, s);
464 if (r < 0) {
465 log_syntax(unit, LOG_ERR, filename, line, r,
466 "Invalid port specification, ignoring assignment: %s", rvalue);
467 return 0;
468 }
e5719363
JT
469 }
470
471 *s = port;
e5719363
JT
472 return 0;
473}
474
fedcb4c3
YW
475static int wireguard_decode_key_and_warn(
476 const char *rvalue,
477 uint8_t *ret,
478 const char *unit,
479 const char *filename,
480 unsigned line,
481 const char *lvalue) {
03fec543 482
e5719363
JT
483 _cleanup_free_ void *key = NULL;
484 size_t len;
485 int r;
486
e5719363 487 assert(rvalue);
fedcb4c3
YW
488 assert(ret);
489 assert(filename);
490 assert(lvalue);
491
492 if (isempty(rvalue)) {
493 memzero(ret, WG_KEY_LEN);
494 return 0;
495 }
e5719363 496
6ef5c881 497 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
e5719363 498 if (r < 0) {
f1368a33 499 log_syntax(unit, LOG_ERR, filename, line, r,
583eb170 500 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
6ef5c881 501 goto finalize;
e5719363
JT
502 }
503 if (len != WG_KEY_LEN) {
583eb170
YW
504 log_syntax(unit, LOG_ERR, filename, line, 0,
505 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
506 lvalue, len);
6ef5c881 507 goto finalize;
e5719363
JT
508 }
509
fedcb4c3 510 memcpy(ret, key, WG_KEY_LEN);
6ef5c881
YW
511 r = 0;
512
513finalize:
514 explicit_bzero_safe(key, len);
515 return r;
e5719363
JT
516}
517
03fec543
YW
518int config_parse_wireguard_private_key(
519 const char *unit,
520 const char *filename,
521 unsigned line,
522 const char *section,
523 unsigned section_line,
524 const char *lvalue,
525 int ltype,
526 const char *rvalue,
527 void *data,
528 void *userdata) {
529
e5719363
JT
530 Wireguard *w;
531
532 assert(data);
e5719363 533 w = WIREGUARD(data);
e5719363
JT
534 assert(w);
535
6ef5c881
YW
536 (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
537 return 0;
e5719363
JT
538}
539
76df7779
YW
540int config_parse_wireguard_private_key_file(
541 const char *unit,
542 const char *filename,
543 unsigned line,
544 const char *section,
545 unsigned section_line,
546 const char *lvalue,
547 int ltype,
548 const char *rvalue,
549 void *data,
550 void *userdata) {
551
552 _cleanup_free_ char *path = NULL;
553 Wireguard *w;
554
555 assert(data);
556 w = WIREGUARD(data);
557 assert(w);
558
559 if (isempty(rvalue)) {
560 w->private_key_file = mfree(w->private_key_file);
561 return 0;
562 }
563
564 path = strdup(rvalue);
565 if (!path)
566 return log_oom();
567
568 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
569 return 0;
570
571 return free_and_replace(w->private_key_file, path);
572}
573
03fec543
YW
574int config_parse_wireguard_preshared_key(
575 const char *unit,
576 const char *filename,
577 unsigned line,
578 const char *section,
579 unsigned section_line,
580 const char *lvalue,
581 int ltype,
582 const char *rvalue,
583 void *data,
584 void *userdata) {
f1368a33
YW
585
586 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363 587 Wireguard *w;
f1368a33 588 int r;
e5719363
JT
589
590 assert(data);
e5719363 591 w = WIREGUARD(data);
e5719363
JT
592 assert(w);
593
f1368a33
YW
594 r = wireguard_peer_new_static(w, filename, section_line, &peer);
595 if (r < 0)
596 return r;
e5719363 597
fedcb4c3 598 r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
f1368a33
YW
599 if (r < 0)
600 return r;
601
602 TAKE_PTR(peer);
603 return 0;
e5719363
JT
604}
605
a3945c63
YW
606int config_parse_wireguard_preshared_key_file(
607 const char *unit,
608 const char *filename,
609 unsigned line,
610 const char *section,
611 unsigned section_line,
612 const char *lvalue,
613 int ltype,
614 const char *rvalue,
615 void *data,
616 void *userdata) {
617
618 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
619 _cleanup_free_ char *path = NULL;
620 Wireguard *w;
621 int r;
622
623 assert(data);
624 w = WIREGUARD(data);
625 assert(w);
626
627 r = wireguard_peer_new_static(w, filename, section_line, &peer);
628 if (r < 0)
629 return r;
630
631 if (isempty(rvalue)) {
632 peer->preshared_key_file = mfree(peer->preshared_key_file);
633 TAKE_PTR(peer);
634 return 0;
635 }
636
637 path = strdup(rvalue);
638 if (!path)
639 return log_oom();
640
641 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
642 return 0;
643
644 free_and_replace(peer->preshared_key_file, path);
645 TAKE_PTR(peer);
646 return 0;
647}
648
03fec543
YW
649int config_parse_wireguard_public_key(
650 const char *unit,
651 const char *filename,
652 unsigned line,
653 const char *section,
654 unsigned section_line,
655 const char *lvalue,
656 int ltype,
657 const char *rvalue,
658 void *data,
659 void *userdata) {
f1368a33
YW
660
661 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363 662 Wireguard *w;
f1368a33 663 int r;
e5719363
JT
664
665 assert(data);
e5719363 666 w = WIREGUARD(data);
e5719363
JT
667 assert(w);
668
f1368a33
YW
669 r = wireguard_peer_new_static(w, filename, section_line, &peer);
670 if (r < 0)
671 return r;
672
fedcb4c3 673 r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
f1368a33
YW
674 if (r < 0)
675 return r;
e5719363 676
f1368a33
YW
677 TAKE_PTR(peer);
678 return 0;
e5719363
JT
679}
680
03fec543
YW
681int config_parse_wireguard_allowed_ips(
682 const char *unit,
683 const char *filename,
684 unsigned line,
685 const char *section,
686 unsigned section_line,
687 const char *lvalue,
688 int ltype,
689 const char *rvalue,
690 void *data,
691 void *userdata) {
f1368a33
YW
692
693 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
694 union in_addr_union addr;
695 unsigned char prefixlen;
696 int r, family;
697 Wireguard *w;
e5719363
JT
698 WireguardIPmask *ipmask;
699
700 assert(rvalue);
701 assert(data);
702
703 w = WIREGUARD(data);
f1368a33
YW
704 assert(w);
705
706 r = wireguard_peer_new_static(w, filename, section_line, &peer);
707 if (r < 0)
708 return r;
e5719363
JT
709
710 for (;;) {
711 _cleanup_free_ char *word = NULL;
712
713 r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
714 if (r == 0)
715 break;
716 if (r == -ENOMEM)
717 return log_oom();
718 if (r < 0) {
f1368a33
YW
719 log_syntax(unit, LOG_ERR, filename, line, r,
720 "Failed to split allowed ips \"%s\" option: %m", rvalue);
e5719363
JT
721 break;
722 }
723
724 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
725 if (r < 0) {
f1368a33
YW
726 log_syntax(unit, LOG_ERR, filename, line, r,
727 "Network address is invalid, ignoring assignment: %s", word);
728 continue;
e5719363
JT
729 }
730
fc721553 731 ipmask = new(WireguardIPmask, 1);
e5719363
JT
732 if (!ipmask)
733 return log_oom();
fc721553
YW
734
735 *ipmask = (WireguardIPmask) {
736 .family = family,
737 .ip.in6 = addr.in6,
738 .cidr = prefixlen,
739 };
e5719363
JT
740
741 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
742 }
743
f1368a33 744 TAKE_PTR(peer);
e5719363
JT
745 return 0;
746}
747
03fec543
YW
748int config_parse_wireguard_endpoint(
749 const char *unit,
750 const char *filename,
751 unsigned line,
752 const char *section,
753 unsigned section_line,
754 const char *lvalue,
755 int ltype,
756 const char *rvalue,
757 void *data,
758 void *userdata) {
f1368a33
YW
759
760 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
761 const char *begin, *end;
e5719363 762 Wireguard *w;
e5719363 763 size_t len;
f1368a33 764 int r;
e5719363
JT
765
766 assert(data);
767 assert(rvalue);
768
769 w = WIREGUARD(data);
e5719363
JT
770 assert(w);
771
f1368a33
YW
772 r = wireguard_peer_new_static(w, filename, section_line, &peer);
773 if (r < 0)
774 return r;
e5719363 775
e5719363
JT
776 if (rvalue[0] == '[') {
777 begin = &rvalue[1];
778 end = strchr(rvalue, ']');
779 if (!end) {
f1368a33
YW
780 log_syntax(unit, LOG_ERR, filename, line, 0,
781 "Unable to find matching brace of endpoint, ignoring assignment: %s",
782 rvalue);
e5719363
JT
783 return 0;
784 }
785 len = end - begin;
786 ++end;
787 if (*end != ':' || !*(end + 1)) {
f1368a33
YW
788 log_syntax(unit, LOG_ERR, filename, line, 0,
789 "Unable to find port of endpoint, ignoring assignment: %s",
790 rvalue);
e5719363
JT
791 return 0;
792 }
793 ++end;
794 } else {
795 begin = rvalue;
796 end = strrchr(rvalue, ':');
797 if (!end || !*(end + 1)) {
f1368a33
YW
798 log_syntax(unit, LOG_ERR, filename, line, 0,
799 "Unable to find port of endpoint, ignoring assignment: %s",
800 rvalue);
e5719363
JT
801 return 0;
802 }
803 len = end - begin;
804 ++end;
805 }
806
5f07d640
YW
807 r = free_and_strndup(&peer->endpoint_host, begin, len);
808 if (r < 0)
e5719363
JT
809 return log_oom();
810
5f07d640
YW
811 r = free_and_strdup(&peer->endpoint_port, end);
812 if (r < 0)
e5719363
JT
813 return log_oom();
814
f1368a33
YW
815 r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL);
816 if (r < 0)
fc721553
YW
817 return log_oom();
818
f1368a33
YW
819 r = set_put(w->peers_with_unresolved_endpoint, peer);
820 if (r < 0)
821 return r;
e5719363 822
f1368a33 823 TAKE_PTR(peer);
e5719363
JT
824 return 0;
825}
826
03fec543
YW
827int config_parse_wireguard_keepalive(
828 const char *unit,
829 const char *filename,
830 unsigned line,
831 const char *section,
832 unsigned section_line,
833 const char *lvalue,
834 int ltype,
835 const char *rvalue,
836 void *data,
837 void *userdata) {
f1368a33
YW
838
839 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
840 uint16_t keepalive = 0;
841 Wireguard *w;
f1368a33 842 int r;
e5719363
JT
843
844 assert(rvalue);
845 assert(data);
846
847 w = WIREGUARD(data);
e5719363
JT
848 assert(w);
849
f1368a33
YW
850 r = wireguard_peer_new_static(w, filename, section_line, &peer);
851 if (r < 0)
852 return r;
e5719363
JT
853
854 if (streq(rvalue, "off"))
855 keepalive = 0;
856 else {
857 r = safe_atou16(rvalue, &keepalive);
f1368a33
YW
858 if (r < 0) {
859 log_syntax(unit, LOG_ERR, filename, line, r,
860 "The persistent keepalive interval must be 0-65535. Ignore assignment: %s",
861 rvalue);
862 return 0;
863 }
e5719363
JT
864 }
865
866 peer->persistent_keepalive_interval = keepalive;
f1368a33
YW
867
868 TAKE_PTR(peer);
e5719363
JT
869 return 0;
870}
871
872static void wireguard_init(NetDev *netdev) {
873 Wireguard *w;
874
875 assert(netdev);
e5719363 876 w = WIREGUARD(netdev);
e5719363
JT
877 assert(w);
878
879 w->flags = WGDEVICE_F_REPLACE_PEERS;
880}
881
882static void wireguard_done(NetDev *netdev) {
883 Wireguard *w;
e5719363
JT
884
885 assert(netdev);
886 w = WIREGUARD(netdev);
c195364d 887 assert(w);
e5719363 888
85c987a8
YW
889 sd_event_source_unref(w->resolve_retry_event_source);
890
6ef5c881 891 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
76df7779
YW
892 free(w->private_key_file);
893
f1368a33
YW
894 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
895 set_free(w->peers_with_unresolved_endpoint);
896 set_free(w->peers_with_failed_endpoint);
897}
c195364d 898
cb31e7c8
YW
899static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
900 _cleanup_free_ char *key = NULL;
901 size_t key_len;
902 int r;
76df7779 903
cb31e7c8 904 if (!filename)
76df7779
YW
905 return 0;
906
cb31e7c8 907 r = read_full_file_full(filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
76df7779 908 if (r < 0)
cb31e7c8 909 return r;
76df7779 910
cb31e7c8
YW
911 if (key_len != WG_KEY_LEN) {
912 r = -EINVAL;
913 goto finalize;
914 }
76df7779 915
cb31e7c8
YW
916 memcpy(dest, key, WG_KEY_LEN);
917 r = 0;
76df7779 918
cb31e7c8
YW
919finalize:
920 explicit_bzero_safe(key, key_len);
921 return r;
76df7779
YW
922}
923
9cc9021a
YW
924static int wireguard_peer_verify(WireguardPeer *peer) {
925 NetDev *netdev = NETDEV(peer->wireguard);
a3945c63 926 int r;
9cc9021a
YW
927
928 if (section_is_invalid(peer->section))
929 return -EINVAL;
930
931 if (eqzero(peer->public_key))
932 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
933 "%s: WireGuardPeer section without PublicKey= configured. "
934 "Ignoring [WireGuardPeer] section from line %u.",
935 peer->section->filename, peer->section->line);
936
a3945c63
YW
937 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
938 if (r < 0)
939 return log_netdev_error_errno(netdev, r,
940 "%s: Failed to read preshared key from '%s'. "
941 "Ignoring [WireGuardPeer] section from line %u.",
942 peer->section->filename, peer->preshared_key_file,
943 peer->section->line);
944
9cc9021a
YW
945 return 0;
946}
947
f1368a33
YW
948static int wireguard_verify(NetDev *netdev, const char *filename) {
949 WireguardPeer *peer, *peer_next;
950 Wireguard *w;
76df7779 951 int r;
c195364d 952
f1368a33
YW
953 assert(netdev);
954 w = WIREGUARD(netdev);
955 assert(w);
956
cb31e7c8
YW
957 r = wireguard_read_key_file(w->private_key_file, w->private_key);
958 if (r < 0)
959 return log_netdev_error_errno(netdev, r,
960 "Failed to read private key from %s. Dropping network device %s.",
961 w->private_key_file, netdev->ifname);
9cc9021a 962
cb31e7c8
YW
963 if (eqzero(w->private_key))
964 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
965 "%s: Missing PrivateKey= or PrivateKeyFile=, "
966 "Dropping network device %s.",
967 filename, netdev->ifname);
76df7779 968
f1368a33 969 LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
9cc9021a 970 if (wireguard_peer_verify(peer) < 0)
f1368a33
YW
971 wireguard_peer_free(peer);
972
973 return 0;
e5719363
JT
974}
975
976const NetDevVTable wireguard_vtable = {
977 .object_size = sizeof(Wireguard),
978 .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
979 .post_create = netdev_wireguard_post_create,
980 .init = wireguard_init,
981 .done = wireguard_done,
982 .create_type = NETDEV_CREATE_INDEPENDENT,
f1368a33 983 .config_verify = wireguard_verify,
e5719363 984};