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