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