]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/wireguard.c
Merge pull request #13439 from yuwata/core-support-systemctl-clean-more
[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.h"
27
28 static void resolve_endpoints(NetDev *netdev);
29
30 static 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);
55 free(peer->preshared_key_file);
56 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
57
58 free(peer);
59 }
60
61 DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
62
63 static 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;
67
68 assert(w);
69 assert(ret);
70 assert(filename);
71 assert(section_line > 0);
72
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 }
82
83 peer = new(WireguardPeer, 1);
84 if (!peer)
85 return -ENOMEM;
86
87 *peer = (WireguardPeer) {
88 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
89 .wireguard = w,
90 .section = TAKE_PTR(n),
91 };
92
93 LIST_PREPEND(peers, w->peers, peer);
94
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;
105 }
106
107 static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
108 int r;
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
124 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
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
138 cancel:
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
146 static 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
168 if (!*mask_start) {
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
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 }
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
211 cancel:
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
219 static int wireguard_set_interface(NetDev *netdev) {
220 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
221 WireguardIPmask *mask_start = NULL;
222 WireguardPeer *peer, *peer_start;
223 uint32_t serial;
224 Wireguard *w;
225 int r;
226
227 assert(netdev);
228 w = WIREGUARD(netdev);
229 assert(w);
230
231 for (peer_start = w->peers; peer_start; ) {
232 uint16_t i = 0;
233
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
266 LIST_FOREACH(peers, peer, peer_start) {
267 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
268 if (r < 0)
269 return r;
270 if (r == 0)
271 break;
272 }
273 peer_start = peer; /* Start next cycle from this peer. */
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");
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_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
363 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
364 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
365 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
366 else
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);
369
370 resolve_next:
371 if (!set_isempty(w->peers_with_unresolved_endpoint)) {
372 resolve_endpoints(netdev);
373 return 0;
374 }
375
376 (void) wireguard_set_interface(netdev);
377
378 if (!set_isempty(w->peers_with_failed_endpoint)) {
379 usec_t usec;
380
381 w->n_retries++;
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);
386 if (r < 0) {
387 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
388 return 0;
389 }
390 }
391
392 return 0;
393 }
394
395 static void resolve_endpoints(NetDev *netdev) {
396 static const struct addrinfo hints = {
397 .ai_family = AF_UNSPEC,
398 .ai_socktype = SOCK_DGRAM,
399 .ai_protocol = IPPROTO_UDP
400 };
401 WireguardPeer *peer;
402 Wireguard *w;
403 Iterator i;
404 int r;
405
406 assert(netdev);
407 w = WIREGUARD(netdev);
408 assert(w);
409
410 SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) {
411 r = resolve_getaddrinfo(netdev->manager->resolve,
412 NULL,
413 peer->endpoint_host,
414 peer->endpoint_port,
415 &hints,
416 wireguard_resolve_handler,
417 wireguard_peer_destroy_callback,
418 peer);
419 if (r == -ENOBUFS)
420 break;
421 if (r < 0) {
422 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
423 continue;
424 }
425
426 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
427 netdev_ref(netdev);
428
429 (void) set_remove(w->peers_with_unresolved_endpoint, peer);
430 }
431 }
432
433 static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
434 assert(netdev);
435 assert(WIREGUARD(netdev));
436
437 (void) wireguard_set_interface(netdev);
438 resolve_endpoints(netdev);
439 return 0;
440 }
441
442 int 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
454 uint16_t *s = data;
455 int r;
456
457 assert(rvalue);
458 assert(data);
459
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;
470 }
471
472 return 0;
473 }
474
475 static int wireguard_decode_key_and_warn(
476 const char *rvalue,
477 uint8_t ret[static WG_KEY_LEN],
478 const char *unit,
479 const char *filename,
480 unsigned line,
481 const char *lvalue) {
482
483 _cleanup_(erase_and_freep) void *key = NULL;
484 size_t len;
485 int r;
486
487 assert(rvalue);
488 assert(ret);
489 assert(filename);
490 assert(lvalue);
491
492 if (isempty(rvalue)) {
493 memzero(ret, WG_KEY_LEN);
494 return 0;
495 }
496
497 if (!streq(lvalue, "PublicKey"))
498 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
499
500 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
501 if (r < 0)
502 return log_syntax(unit, LOG_ERR, filename, line, r,
503 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
504 if (len != WG_KEY_LEN)
505 return log_syntax(unit, LOG_ERR, filename, line, 0,
506 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
507 lvalue, len);
508
509 memcpy(ret, key, WG_KEY_LEN);
510 return 0;
511 }
512
513 int config_parse_wireguard_private_key(
514 const char *unit,
515 const char *filename,
516 unsigned line,
517 const char *section,
518 unsigned section_line,
519 const char *lvalue,
520 int ltype,
521 const char *rvalue,
522 void *data,
523 void *userdata) {
524
525 Wireguard *w;
526
527 assert(data);
528 w = WIREGUARD(data);
529 assert(w);
530
531 (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
532 return 0;
533 }
534
535 int config_parse_wireguard_private_key_file(
536 const char *unit,
537 const char *filename,
538 unsigned line,
539 const char *section,
540 unsigned section_line,
541 const char *lvalue,
542 int ltype,
543 const char *rvalue,
544 void *data,
545 void *userdata) {
546
547 _cleanup_free_ char *path = NULL;
548 Wireguard *w;
549
550 assert(data);
551 w = WIREGUARD(data);
552 assert(w);
553
554 if (isempty(rvalue)) {
555 w->private_key_file = mfree(w->private_key_file);
556 return 0;
557 }
558
559 path = strdup(rvalue);
560 if (!path)
561 return log_oom();
562
563 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
564 return 0;
565
566 return free_and_replace(w->private_key_file, path);
567 }
568
569 int config_parse_wireguard_preshared_key(
570 const char *unit,
571 const char *filename,
572 unsigned line,
573 const char *section,
574 unsigned section_line,
575 const char *lvalue,
576 int ltype,
577 const char *rvalue,
578 void *data,
579 void *userdata) {
580
581 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
582 Wireguard *w;
583 int r;
584
585 assert(data);
586 w = WIREGUARD(data);
587 assert(w);
588
589 r = wireguard_peer_new_static(w, filename, section_line, &peer);
590 if (r < 0)
591 return r;
592
593 r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
594 if (r < 0)
595 return r;
596
597 TAKE_PTR(peer);
598 return 0;
599 }
600
601 int config_parse_wireguard_preshared_key_file(
602 const char *unit,
603 const char *filename,
604 unsigned line,
605 const char *section,
606 unsigned section_line,
607 const char *lvalue,
608 int ltype,
609 const char *rvalue,
610 void *data,
611 void *userdata) {
612
613 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
614 _cleanup_free_ char *path = NULL;
615 Wireguard *w;
616 int r;
617
618 assert(data);
619 w = WIREGUARD(data);
620 assert(w);
621
622 r = wireguard_peer_new_static(w, filename, section_line, &peer);
623 if (r < 0)
624 return r;
625
626 if (isempty(rvalue)) {
627 peer->preshared_key_file = mfree(peer->preshared_key_file);
628 TAKE_PTR(peer);
629 return 0;
630 }
631
632 path = strdup(rvalue);
633 if (!path)
634 return log_oom();
635
636 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
637 return 0;
638
639 free_and_replace(peer->preshared_key_file, path);
640 TAKE_PTR(peer);
641 return 0;
642 }
643
644 int config_parse_wireguard_public_key(
645 const char *unit,
646 const char *filename,
647 unsigned line,
648 const char *section,
649 unsigned section_line,
650 const char *lvalue,
651 int ltype,
652 const char *rvalue,
653 void *data,
654 void *userdata) {
655
656 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
657 Wireguard *w;
658 int r;
659
660 assert(data);
661 w = WIREGUARD(data);
662 assert(w);
663
664 r = wireguard_peer_new_static(w, filename, section_line, &peer);
665 if (r < 0)
666 return r;
667
668 r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
669 if (r < 0)
670 return r;
671
672 TAKE_PTR(peer);
673 return 0;
674 }
675
676 int config_parse_wireguard_allowed_ips(
677 const char *unit,
678 const char *filename,
679 unsigned line,
680 const char *section,
681 unsigned section_line,
682 const char *lvalue,
683 int ltype,
684 const char *rvalue,
685 void *data,
686 void *userdata) {
687
688 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
689 union in_addr_union addr;
690 unsigned char prefixlen;
691 int r, family;
692 Wireguard *w;
693 WireguardIPmask *ipmask;
694
695 assert(rvalue);
696 assert(data);
697
698 w = WIREGUARD(data);
699 assert(w);
700
701 r = wireguard_peer_new_static(w, filename, section_line, &peer);
702 if (r < 0)
703 return r;
704
705 for (;;) {
706 _cleanup_free_ char *word = NULL;
707
708 r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
709 if (r == 0)
710 break;
711 if (r == -ENOMEM)
712 return log_oom();
713 if (r < 0) {
714 log_syntax(unit, LOG_ERR, filename, line, r,
715 "Failed to split allowed ips \"%s\" option: %m", rvalue);
716 break;
717 }
718
719 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
720 if (r < 0) {
721 log_syntax(unit, LOG_ERR, filename, line, r,
722 "Network address is invalid, ignoring assignment: %s", word);
723 continue;
724 }
725
726 ipmask = new(WireguardIPmask, 1);
727 if (!ipmask)
728 return log_oom();
729
730 *ipmask = (WireguardIPmask) {
731 .family = family,
732 .ip.in6 = addr.in6,
733 .cidr = prefixlen,
734 };
735
736 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
737 }
738
739 TAKE_PTR(peer);
740 return 0;
741 }
742
743 int config_parse_wireguard_endpoint(
744 const char *unit,
745 const char *filename,
746 unsigned line,
747 const char *section,
748 unsigned section_line,
749 const char *lvalue,
750 int ltype,
751 const char *rvalue,
752 void *data,
753 void *userdata) {
754
755 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
756 const char *begin, *end;
757 Wireguard *w;
758 size_t len;
759 int r;
760
761 assert(data);
762 assert(rvalue);
763
764 w = WIREGUARD(data);
765 assert(w);
766
767 r = wireguard_peer_new_static(w, filename, section_line, &peer);
768 if (r < 0)
769 return r;
770
771 if (rvalue[0] == '[') {
772 begin = &rvalue[1];
773 end = strchr(rvalue, ']');
774 if (!end) {
775 log_syntax(unit, LOG_ERR, filename, line, 0,
776 "Unable to find matching brace of endpoint, ignoring assignment: %s",
777 rvalue);
778 return 0;
779 }
780 len = end - begin;
781 ++end;
782 if (*end != ':' || !*(end + 1)) {
783 log_syntax(unit, LOG_ERR, filename, line, 0,
784 "Unable to find port of endpoint, ignoring assignment: %s",
785 rvalue);
786 return 0;
787 }
788 ++end;
789 } else {
790 begin = rvalue;
791 end = strrchr(rvalue, ':');
792 if (!end || !*(end + 1)) {
793 log_syntax(unit, LOG_ERR, filename, line, 0,
794 "Unable to find port of endpoint, ignoring assignment: %s",
795 rvalue);
796 return 0;
797 }
798 len = end - begin;
799 ++end;
800 }
801
802 r = free_and_strndup(&peer->endpoint_host, begin, len);
803 if (r < 0)
804 return log_oom();
805
806 r = free_and_strdup(&peer->endpoint_port, end);
807 if (r < 0)
808 return log_oom();
809
810 r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL);
811 if (r < 0)
812 return log_oom();
813
814 r = set_put(w->peers_with_unresolved_endpoint, peer);
815 if (r < 0)
816 return r;
817
818 TAKE_PTR(peer);
819 return 0;
820 }
821
822 int config_parse_wireguard_keepalive(
823 const char *unit,
824 const char *filename,
825 unsigned line,
826 const char *section,
827 unsigned section_line,
828 const char *lvalue,
829 int ltype,
830 const char *rvalue,
831 void *data,
832 void *userdata) {
833
834 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
835 uint16_t keepalive = 0;
836 Wireguard *w;
837 int r;
838
839 assert(rvalue);
840 assert(data);
841
842 w = WIREGUARD(data);
843 assert(w);
844
845 r = wireguard_peer_new_static(w, filename, section_line, &peer);
846 if (r < 0)
847 return r;
848
849 if (streq(rvalue, "off"))
850 keepalive = 0;
851 else {
852 r = safe_atou16(rvalue, &keepalive);
853 if (r < 0) {
854 log_syntax(unit, LOG_ERR, filename, line, r,
855 "The persistent keepalive interval must be 0-65535. Ignore assignment: %s",
856 rvalue);
857 return 0;
858 }
859 }
860
861 peer->persistent_keepalive_interval = keepalive;
862
863 TAKE_PTR(peer);
864 return 0;
865 }
866
867 static void wireguard_init(NetDev *netdev) {
868 Wireguard *w;
869
870 assert(netdev);
871 w = WIREGUARD(netdev);
872 assert(w);
873
874 w->flags = WGDEVICE_F_REPLACE_PEERS;
875 }
876
877 static void wireguard_done(NetDev *netdev) {
878 Wireguard *w;
879
880 assert(netdev);
881 w = WIREGUARD(netdev);
882 assert(w);
883
884 sd_event_source_unref(w->resolve_retry_event_source);
885
886 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
887 free(w->private_key_file);
888
889 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
890 set_free(w->peers_with_unresolved_endpoint);
891 set_free(w->peers_with_failed_endpoint);
892 }
893
894 static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
895 _cleanup_(erase_and_freep) char *key = NULL;
896 size_t key_len;
897 int r;
898
899 if (!filename)
900 return 0;
901
902 assert(dest);
903
904 (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
905
906 r = read_full_file_full(filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
907 if (r < 0)
908 return r;
909
910 if (key_len != WG_KEY_LEN)
911 return -EINVAL;
912
913 memcpy(dest, key, WG_KEY_LEN);
914 return 0;
915 }
916
917 static int wireguard_peer_verify(WireguardPeer *peer) {
918 NetDev *netdev = NETDEV(peer->wireguard);
919 int r;
920
921 if (section_is_invalid(peer->section))
922 return -EINVAL;
923
924 if (eqzero(peer->public_key))
925 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
926 "%s: WireGuardPeer section without PublicKey= configured. "
927 "Ignoring [WireGuardPeer] section from line %u.",
928 peer->section->filename, peer->section->line);
929
930 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
931 if (r < 0)
932 return log_netdev_error_errno(netdev, r,
933 "%s: Failed to read preshared key from '%s'. "
934 "Ignoring [WireGuardPeer] section from line %u.",
935 peer->section->filename, peer->preshared_key_file,
936 peer->section->line);
937
938 return 0;
939 }
940
941 static int wireguard_verify(NetDev *netdev, const char *filename) {
942 WireguardPeer *peer, *peer_next;
943 Wireguard *w;
944 int r;
945
946 assert(netdev);
947 w = WIREGUARD(netdev);
948 assert(w);
949
950 r = wireguard_read_key_file(w->private_key_file, w->private_key);
951 if (r < 0)
952 return log_netdev_error_errno(netdev, r,
953 "Failed to read private key from %s. Dropping network device %s.",
954 w->private_key_file, netdev->ifname);
955
956 if (eqzero(w->private_key))
957 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
958 "%s: Missing PrivateKey= or PrivateKeyFile=, "
959 "Dropping network device %s.",
960 filename, netdev->ifname);
961
962 LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
963 if (wireguard_peer_verify(peer) < 0)
964 wireguard_peer_free(peer);
965
966 return 0;
967 }
968
969 const NetDevVTable wireguard_vtable = {
970 .object_size = sizeof(Wireguard),
971 .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
972 .post_create = netdev_wireguard_post_create,
973 .init = wireguard_init,
974 .done = wireguard_done,
975 .create_type = NETDEV_CREATE_INDEPENDENT,
976 .config_verify = wireguard_verify,
977 .generate_mac = true,
978 };