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