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