]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/wireguard.c
Merge pull request #28832 from dtardon/list-clear
[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 #include <linux/ipv6_route.h>
11
12 #include "sd-resolve.h"
13
14 #include "alloc-util.h"
15 #include "dns-domain.h"
16 #include "event-util.h"
17 #include "fd-util.h"
18 #include "fileio.h"
19 #include "hexdecoct.h"
20 #include "memory-util.h"
21 #include "netlink-util.h"
22 #include "networkd-manager.h"
23 #include "networkd-route-util.h"
24 #include "networkd-route.h"
25 #include "networkd-util.h"
26 #include "parse-helpers.h"
27 #include "parse-util.h"
28 #include "random-util.h"
29 #include "resolve-private.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "wireguard.h"
33
34 static void wireguard_resolve_endpoints(NetDev *netdev);
35 static int peer_resolve_endpoint(WireguardPeer *peer);
36
37 static void wireguard_peer_clear_ipmasks(WireguardPeer *peer) {
38 assert(peer);
39
40 LIST_CLEAR(ipmasks, peer->ipmasks, free);
41 }
42
43 static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
44 if (!peer)
45 return NULL;
46
47 if (peer->wireguard) {
48 LIST_REMOVE(peers, peer->wireguard->peers, peer);
49
50 if (peer->section)
51 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
52 }
53
54 config_section_free(peer->section);
55
56 wireguard_peer_clear_ipmasks(peer);
57
58 free(peer->endpoint_host);
59 free(peer->endpoint_port);
60 free(peer->preshared_key_file);
61 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
62
63 sd_event_source_disable_unref(peer->resolve_retry_event_source);
64 sd_resolve_query_unref(peer->resolve_query);
65
66 return mfree(peer);
67 }
68
69 DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free);
70
71 static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
72 _cleanup_(config_section_freep) ConfigSection *n = NULL;
73 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
74 int r;
75
76 assert(w);
77 assert(ret);
78 assert(filename);
79 assert(section_line > 0);
80
81 r = config_section_new(filename, section_line, &n);
82 if (r < 0)
83 return r;
84
85 peer = hashmap_get(w->peers_by_section, n);
86 if (peer) {
87 *ret = TAKE_PTR(peer);
88 return 0;
89 }
90
91 peer = new(WireguardPeer, 1);
92 if (!peer)
93 return -ENOMEM;
94
95 *peer = (WireguardPeer) {
96 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
97 .wireguard = w,
98 .section = TAKE_PTR(n),
99 };
100
101 LIST_PREPEND(peers, w->peers, peer);
102
103 r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer);
104 if (r < 0)
105 return r;
106
107 *ret = TAKE_PTR(peer);
108 return 0;
109 }
110
111 static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
112 int r;
113
114 assert(message);
115 assert(mask);
116 assert(index > 0);
117
118 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
119
120 r = sd_netlink_message_open_array(message, index);
121 if (r < 0)
122 return 0;
123
124 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
125 if (r < 0)
126 goto cancel;
127
128 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
129 if (r < 0)
130 goto cancel;
131
132 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
133 if (r < 0)
134 goto cancel;
135
136 r = sd_netlink_message_close_container(message);
137 if (r < 0)
138 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
139
140 return 1;
141
142 cancel:
143 r = sd_netlink_message_cancel_array(message);
144 if (r < 0)
145 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
146
147 return 0;
148 }
149
150 static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
151 WireguardIPmask *start, *last = NULL;
152 uint16_t j = 0;
153 int r;
154
155 assert(message);
156 assert(peer);
157 assert(index > 0);
158 assert(mask_start);
159
160 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
161
162 start = *mask_start ?: peer->ipmasks;
163
164 r = sd_netlink_message_open_array(message, index);
165 if (r < 0)
166 return 0;
167
168 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
169 if (r < 0)
170 goto cancel;
171
172 if (!*mask_start) {
173 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
174 if (r < 0)
175 goto cancel;
176
177 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
178 if (r < 0)
179 goto cancel;
180
181 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
182 if (r < 0)
183 goto cancel;
184
185 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
186 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
187 if (r < 0)
188 goto cancel;
189 }
190 }
191
192 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
193 if (r < 0)
194 goto cancel;
195
196 LIST_FOREACH(ipmasks, mask, start) {
197 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
198 if (r < 0)
199 return r;
200 if (r == 0) {
201 last = mask;
202 break;
203 }
204 }
205
206 r = sd_netlink_message_close_container(message);
207 if (r < 0)
208 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
209
210 r = sd_netlink_message_close_container(message);
211 if (r < 0)
212 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
213
214 *mask_start = last; /* Start next cycle from this mask. */
215 return !last;
216
217 cancel:
218 r = sd_netlink_message_cancel_array(message);
219 if (r < 0)
220 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
221
222 return 0;
223 }
224
225 static int wireguard_set_interface(NetDev *netdev) {
226 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
227 WireguardIPmask *mask_start = NULL;
228 WireguardPeer *peer_start;
229 bool sent_once = false;
230 uint32_t serial;
231 Wireguard *w;
232 int r;
233
234 assert(netdev);
235 w = WIREGUARD(netdev);
236 assert(w);
237
238 for (peer_start = w->peers; peer_start || !sent_once; ) {
239 uint16_t i = 0;
240
241 message = sd_netlink_message_unref(message);
242
243 r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
244 if (r < 0)
245 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
246
247 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
248 if (r < 0)
249 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
250
251 if (peer_start == w->peers) {
252 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
253 if (r < 0)
254 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
255
256 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
257 if (r < 0)
258 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
259
260 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
261 if (r < 0)
262 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
263
264 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
265 if (r < 0)
266 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
267 }
268
269 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
270 if (r < 0)
271 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
272
273 WireguardPeer *peer_last = NULL;
274 LIST_FOREACH(peers, peer, peer_start) {
275 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
276 if (r < 0)
277 return r;
278 if (r == 0) {
279 peer_last = peer;
280 break;
281 }
282 }
283 peer_start = peer_last; /* Start next cycle from this peer. */
284
285 r = sd_netlink_message_close_container(message);
286 if (r < 0)
287 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
288
289 r = sd_netlink_send(netdev->manager->genl, message, &serial);
290 if (r < 0)
291 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
292
293 sent_once = true;
294 }
295
296 return 0;
297 }
298
299 static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
300 WireguardPeer *peer = ASSERT_PTR(userdata);
301 NetDev *netdev;
302
303 assert(peer->wireguard);
304
305 netdev = NETDEV(peer->wireguard);
306
307 if (!netdev_is_managed(netdev))
308 return 0;
309
310 peer->resolve_query = sd_resolve_query_unref(peer->resolve_query);
311
312 (void) peer_resolve_endpoint(peer);
313 return 0;
314 }
315
316 static usec_t peer_next_resolve_usec(WireguardPeer *peer) {
317 usec_t usec;
318
319 /* Given the number of retries this function will return an exponential increasing amount of
320 * milliseconds to wait starting at 200ms and capped at 25 seconds. */
321
322 assert(peer);
323
324 usec = (2 << MIN(peer->n_retries, 7U)) * 100 * USEC_PER_MSEC;
325
326 return random_u64_range(usec / 10) + usec * 9 / 10;
327 }
328
329 static int wireguard_peer_resolve_handler(
330 sd_resolve_query *q,
331 int ret,
332 const struct addrinfo *ai,
333 void *userdata) {
334
335 WireguardPeer *peer = ASSERT_PTR(userdata);
336 NetDev *netdev;
337 int r;
338
339 assert(peer->wireguard);
340
341 netdev = NETDEV(peer->wireguard);
342
343 if (!netdev_is_managed(netdev))
344 return 0;
345
346 if (ret != 0) {
347 log_netdev_warning(netdev, "Failed to resolve host '%s:%s', ignoring: %s",
348 peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
349 peer->n_retries++;
350
351 } else {
352 bool found = false;
353 for (; ai; ai = ai->ai_next) {
354 if (!IN_SET(ai->ai_family, AF_INET, AF_INET6))
355 continue;
356
357 if (ai->ai_addrlen != (ai->ai_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))
358 continue;
359
360 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
361 (void) wireguard_set_interface(netdev);
362 peer->n_retries = 0;
363 found = true;
364 break;
365 }
366
367 if (!found) {
368 log_netdev_warning(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the endpoint.",
369 peer->endpoint_host, peer->endpoint_port);
370 peer->n_retries++;
371 }
372 }
373
374 if (peer->n_retries > 0) {
375 r = event_reset_time_relative(netdev->manager->event,
376 &peer->resolve_retry_event_source,
377 CLOCK_BOOTTIME,
378 peer_next_resolve_usec(peer), 0,
379 on_resolve_retry, peer, 0, "wireguard-resolve-retry", true);
380 if (r < 0)
381 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler for endpoint %s:%s, ignoring: %m",
382 peer->endpoint_host, peer->endpoint_port);
383 }
384
385 wireguard_resolve_endpoints(netdev);
386 return 0;
387 }
388
389 static int peer_resolve_endpoint(WireguardPeer *peer) {
390 static const struct addrinfo hints = {
391 .ai_family = AF_UNSPEC,
392 .ai_socktype = SOCK_DGRAM,
393 .ai_protocol = IPPROTO_UDP
394 };
395 NetDev *netdev;
396 int r;
397
398 assert(peer);
399 assert(peer->wireguard);
400
401 netdev = NETDEV(peer->wireguard);
402
403 if (!peer->endpoint_host || !peer->endpoint_port)
404 /* Not necessary to resolve the endpoint. */
405 return 0;
406
407 if (sd_event_source_get_enabled(peer->resolve_retry_event_source, NULL) > 0)
408 /* Timer event source is enabled. The endpoint will be resolved later. */
409 return 0;
410
411 if (peer->resolve_query)
412 /* Being resolved, or already resolved. */
413 return 0;
414
415 r = sd_resolve_getaddrinfo(netdev->manager->resolve,
416 &peer->resolve_query,
417 peer->endpoint_host,
418 peer->endpoint_port,
419 &hints,
420 wireguard_peer_resolve_handler,
421 peer);
422 if (r < 0)
423 return log_netdev_full_errno(netdev, r == -ENOBUFS ? LOG_DEBUG : LOG_WARNING, r,
424 "Failed to create endpoint resolver for %s:%s, ignoring: %m",
425 peer->endpoint_host, peer->endpoint_port);
426
427 return 0;
428 }
429
430 static void wireguard_resolve_endpoints(NetDev *netdev) {
431 Wireguard *w;
432
433 assert(netdev);
434 w = WIREGUARD(netdev);
435 assert(w);
436
437 LIST_FOREACH(peers, peer, w->peers)
438 if (peer_resolve_endpoint(peer) == -ENOBUFS)
439 /* Too many requests. Let's resolve remaining endpoints later. */
440 break;
441 }
442
443 static int netdev_wireguard_post_create(NetDev *netdev, Link *link) {
444 assert(netdev);
445 assert(WIREGUARD(netdev));
446
447 (void) wireguard_set_interface(netdev);
448 wireguard_resolve_endpoints(netdev);
449 return 0;
450 }
451
452 int config_parse_wireguard_listen_port(
453 const char *unit,
454 const char *filename,
455 unsigned line,
456 const char *section,
457 unsigned section_line,
458 const char *lvalue,
459 int ltype,
460 const char *rvalue,
461 void *data,
462 void *userdata) {
463
464 uint16_t *s = ASSERT_PTR(data);
465 int r;
466
467 assert(rvalue);
468
469 if (isempty(rvalue) || streq(rvalue, "auto")) {
470 *s = 0;
471 return 0;
472 }
473
474 r = parse_ip_port(rvalue, s);
475 if (r < 0) {
476 log_syntax(unit, LOG_WARNING, filename, line, r,
477 "Invalid port specification, ignoring assignment: %s", rvalue);
478 return 0;
479 }
480
481 return 0;
482 }
483
484 static int wireguard_decode_key_and_warn(
485 const char *rvalue,
486 uint8_t ret[static WG_KEY_LEN],
487 const char *unit,
488 const char *filename,
489 unsigned line,
490 const char *lvalue) {
491
492 _cleanup_(erase_and_freep) void *key = NULL;
493 size_t len;
494 int r;
495
496 assert(rvalue);
497 assert(ret);
498 assert(filename);
499 assert(lvalue);
500
501 if (isempty(rvalue)) {
502 memzero(ret, WG_KEY_LEN);
503 return 0;
504 }
505
506 if (!streq(lvalue, "PublicKey"))
507 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
508
509 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
510 if (r == -ENOMEM)
511 return log_oom();
512 if (r < 0) {
513 log_syntax(unit, LOG_WARNING, filename, line, r,
514 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
515 return 0;
516 }
517 if (len != WG_KEY_LEN) {
518 log_syntax(unit, LOG_WARNING, filename, line, 0,
519 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
520 lvalue, len);
521 return 0;
522 }
523
524 memcpy(ret, key, WG_KEY_LEN);
525 return 0;
526 }
527
528 int config_parse_wireguard_private_key(
529 const char *unit,
530 const char *filename,
531 unsigned line,
532 const char *section,
533 unsigned section_line,
534 const char *lvalue,
535 int ltype,
536 const char *rvalue,
537 void *data,
538 void *userdata) {
539
540 Wireguard *w;
541
542 assert(data);
543 w = WIREGUARD(data);
544 assert(w);
545
546 return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
547 }
548
549 int config_parse_wireguard_private_key_file(
550 const char *unit,
551 const char *filename,
552 unsigned line,
553 const char *section,
554 unsigned section_line,
555 const char *lvalue,
556 int ltype,
557 const char *rvalue,
558 void *data,
559 void *userdata) {
560
561 _cleanup_free_ char *path = NULL;
562 Wireguard *w;
563
564 assert(data);
565 w = WIREGUARD(data);
566 assert(w);
567
568 if (isempty(rvalue)) {
569 w->private_key_file = mfree(w->private_key_file);
570 return 0;
571 }
572
573 path = strdup(rvalue);
574 if (!path)
575 return log_oom();
576
577 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
578 return 0;
579
580 return free_and_replace(w->private_key_file, path);
581 }
582
583 int config_parse_wireguard_peer_key(
584 const char *unit,
585 const char *filename,
586 unsigned line,
587 const char *section,
588 unsigned section_line,
589 const char *lvalue,
590 int ltype,
591 const char *rvalue,
592 void *data,
593 void *userdata) {
594
595 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
596 Wireguard *w;
597 int r;
598
599 assert(data);
600 w = WIREGUARD(data);
601 assert(w);
602
603 r = wireguard_peer_new_static(w, filename, section_line, &peer);
604 if (r < 0)
605 return log_oom();
606
607 r = wireguard_decode_key_and_warn(rvalue,
608 streq(lvalue, "PublicKey") ? peer->public_key : peer->preshared_key,
609 unit, filename, line, lvalue);
610 if (r < 0)
611 return r;
612
613 TAKE_PTR(peer);
614 return 0;
615 }
616
617 int config_parse_wireguard_preshared_key_file(
618 const char *unit,
619 const char *filename,
620 unsigned line,
621 const char *section,
622 unsigned section_line,
623 const char *lvalue,
624 int ltype,
625 const char *rvalue,
626 void *data,
627 void *userdata) {
628
629 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
630 _cleanup_free_ char *path = NULL;
631 Wireguard *w;
632 int r;
633
634 assert(data);
635 w = WIREGUARD(data);
636 assert(w);
637
638 r = wireguard_peer_new_static(w, filename, section_line, &peer);
639 if (r < 0)
640 return log_oom();
641
642 if (isempty(rvalue)) {
643 peer->preshared_key_file = mfree(peer->preshared_key_file);
644 TAKE_PTR(peer);
645 return 0;
646 }
647
648 path = strdup(rvalue);
649 if (!path)
650 return log_oom();
651
652 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
653 return 0;
654
655 free_and_replace(peer->preshared_key_file, path);
656 TAKE_PTR(peer);
657 return 0;
658 }
659
660 int config_parse_wireguard_allowed_ips(
661 const char *unit,
662 const char *filename,
663 unsigned line,
664 const char *section,
665 unsigned section_line,
666 const char *lvalue,
667 int ltype,
668 const char *rvalue,
669 void *data,
670 void *userdata) {
671
672 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
673 union in_addr_union addr;
674 unsigned char prefixlen;
675 int r, family;
676 Wireguard *w;
677 WireguardIPmask *ipmask;
678
679 assert(rvalue);
680 assert(data);
681
682 w = WIREGUARD(data);
683 assert(w);
684
685 r = wireguard_peer_new_static(w, filename, section_line, &peer);
686 if (r < 0)
687 return log_oom();
688
689 if (isempty(rvalue)) {
690 wireguard_peer_clear_ipmasks(peer);
691 TAKE_PTR(peer);
692 return 0;
693 }
694
695 for (const char *p = rvalue;;) {
696 _cleanup_free_ char *word = NULL;
697 union in_addr_union masked;
698
699 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
700 if (r == 0)
701 break;
702 if (r == -ENOMEM)
703 return log_oom();
704 if (r < 0) {
705 log_syntax(unit, LOG_WARNING, filename, line, r,
706 "Failed to split allowed ips \"%s\" option: %m", rvalue);
707 break;
708 }
709
710 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
711 if (r < 0) {
712 log_syntax(unit, LOG_WARNING, filename, line, r,
713 "Network address is invalid, ignoring assignment: %s", word);
714 continue;
715 }
716
717 masked = addr;
718 assert_se(in_addr_mask(family, &masked, prefixlen) >= 0);
719 if (!in_addr_equal(family, &masked, &addr))
720 log_syntax(unit, LOG_WARNING, filename, line, 0,
721 "Specified address '%s' is not properly masked, assuming '%s'.",
722 word,
723 IN_ADDR_PREFIX_TO_STRING(family, &masked, prefixlen));
724
725 ipmask = new(WireguardIPmask, 1);
726 if (!ipmask)
727 return log_oom();
728
729 *ipmask = (WireguardIPmask) {
730 .family = family,
731 .ip = masked,
732 .cidr = prefixlen,
733 };
734
735 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
736 }
737
738 TAKE_PTR(peer);
739 return 0;
740 }
741
742 int config_parse_wireguard_endpoint(
743 const char *unit,
744 const char *filename,
745 unsigned line,
746 const char *section,
747 unsigned section_line,
748 const char *lvalue,
749 int ltype,
750 const char *rvalue,
751 void *data,
752 void *userdata) {
753
754 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
755 _cleanup_free_ char *host = NULL;
756 union in_addr_union addr;
757 const char *p;
758 uint16_t port;
759 Wireguard *w;
760 int family, r;
761
762 assert(filename);
763 assert(rvalue);
764 assert(userdata);
765
766 w = WIREGUARD(userdata);
767 assert(w);
768
769 r = wireguard_peer_new_static(w, filename, section_line, &peer);
770 if (r < 0)
771 return log_oom();
772
773 r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
774 if (r >= 0) {
775 if (family == AF_INET)
776 peer->endpoint.in = (struct sockaddr_in) {
777 .sin_family = AF_INET,
778 .sin_addr = addr.in,
779 .sin_port = htobe16(port),
780 };
781 else if (family == AF_INET6)
782 peer->endpoint.in6 = (struct sockaddr_in6) {
783 .sin6_family = AF_INET6,
784 .sin6_addr = addr.in6,
785 .sin6_port = htobe16(port),
786 };
787 else
788 assert_not_reached();
789
790 peer->endpoint_host = mfree(peer->endpoint_host);
791 peer->endpoint_port = mfree(peer->endpoint_port);
792
793 TAKE_PTR(peer);
794 return 0;
795 }
796
797 p = strrchr(rvalue, ':');
798 if (!p) {
799 log_syntax(unit, LOG_WARNING, filename, line, 0,
800 "Unable to find port of endpoint, ignoring assignment: %s",
801 rvalue);
802 return 0;
803 }
804
805 host = strndup(rvalue, p - rvalue);
806 if (!host)
807 return log_oom();
808
809 if (!dns_name_is_valid(host)) {
810 log_syntax(unit, LOG_WARNING, filename, line, 0,
811 "Invalid domain name of endpoint, ignoring assignment: %s",
812 rvalue);
813 return 0;
814 }
815
816 p++;
817 r = parse_ip_port(p, &port);
818 if (r < 0) {
819 log_syntax(unit, LOG_WARNING, filename, line, r,
820 "Invalid port of endpoint, ignoring assignment: %s",
821 rvalue);
822 return 0;
823 }
824
825 peer->endpoint = (union sockaddr_union) {};
826
827 free_and_replace(peer->endpoint_host, host);
828
829 r = free_and_strdup(&peer->endpoint_port, p);
830 if (r < 0)
831 return log_oom();
832
833 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
834 return 0;
835 }
836
837 int config_parse_wireguard_keepalive(
838 const char *unit,
839 const char *filename,
840 unsigned line,
841 const char *section,
842 unsigned section_line,
843 const char *lvalue,
844 int ltype,
845 const char *rvalue,
846 void *data,
847 void *userdata) {
848
849 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
850 uint16_t keepalive = 0;
851 Wireguard *w;
852 int r;
853
854 assert(rvalue);
855 assert(data);
856
857 w = WIREGUARD(data);
858 assert(w);
859
860 r = wireguard_peer_new_static(w, filename, section_line, &peer);
861 if (r < 0)
862 return log_oom();
863
864 if (streq(rvalue, "off"))
865 keepalive = 0;
866 else {
867 r = safe_atou16(rvalue, &keepalive);
868 if (r < 0) {
869 log_syntax(unit, LOG_WARNING, filename, line, r,
870 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
871 rvalue);
872 return 0;
873 }
874 }
875
876 peer->persistent_keepalive_interval = keepalive;
877
878 TAKE_PTR(peer);
879 return 0;
880 }
881
882 int config_parse_wireguard_route_table(
883 const char *unit,
884 const char *filename,
885 unsigned line,
886 const char *section,
887 unsigned section_line,
888 const char *lvalue,
889 int ltype,
890 const char *rvalue,
891 void *data,
892 void *userdata) {
893
894 NetDev *netdev = ASSERT_PTR(userdata);
895 uint32_t *table = ASSERT_PTR(data);
896 int r;
897
898 assert(filename);
899 assert(lvalue);
900 assert(rvalue);
901
902 if (isempty(rvalue) || parse_boolean(rvalue) == 0) {
903 *table = 0; /* Disabled. */
904 return 0;
905 }
906
907 r = manager_get_route_table_from_string(netdev->manager, rvalue, table);
908 if (r < 0) {
909 log_syntax(unit, LOG_WARNING, filename, line, r,
910 "Failed to parse %s=, ignoring assignment: %s",
911 lvalue, rvalue);
912 return 0;
913 }
914
915 return 0;
916 }
917
918 int config_parse_wireguard_peer_route_table(
919 const char *unit,
920 const char *filename,
921 unsigned line,
922 const char *section,
923 unsigned section_line,
924 const char *lvalue,
925 int ltype,
926 const char *rvalue,
927 void *data,
928 void *userdata) {
929
930 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
931 NetDev *netdev = ASSERT_PTR(userdata);
932 Wireguard *w;
933 int r;
934
935 assert(filename);
936 assert(lvalue);
937 assert(rvalue);
938 assert(netdev->manager);
939
940 w = WIREGUARD(netdev);
941 assert(w);
942
943 r = wireguard_peer_new_static(w, filename, section_line, &peer);
944 if (r < 0)
945 return log_oom();
946
947 if (isempty(rvalue)) {
948 peer->route_table_set = false; /* Use the table specified in [WireGuard] section. */
949 TAKE_PTR(peer);
950 return 0;
951 }
952
953 if (parse_boolean(rvalue) == 0) {
954 peer->route_table = 0; /* Disabled. */
955 peer->route_table_set = true;
956 TAKE_PTR(peer);
957 return 0;
958 }
959
960 r = manager_get_route_table_from_string(netdev->manager, rvalue, &peer->route_table);
961 if (r < 0) {
962 log_syntax(unit, LOG_WARNING, filename, line, r,
963 "Failed to parse %s=, ignoring assignment: %s",
964 lvalue, rvalue);
965 return 0;
966 }
967
968 peer->route_table_set = true;
969 TAKE_PTR(peer);
970 return 0;
971 }
972
973 int config_parse_wireguard_route_priority(
974 const char *unit,
975 const char *filename,
976 unsigned line,
977 const char *section,
978 unsigned section_line,
979 const char *lvalue,
980 int ltype,
981 const char *rvalue,
982 void *data,
983 void *userdata) {
984
985 uint32_t *priority = ASSERT_PTR(data);
986 int r;
987
988 assert(filename);
989 assert(lvalue);
990 assert(rvalue);
991
992 if (isempty(rvalue)) {
993 *priority = 0;
994 return 0;
995 }
996
997 r = safe_atou32(rvalue, priority);
998 if (r < 0) {
999 log_syntax(unit, LOG_WARNING, filename, line, r,
1000 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1001 return 0;
1002 }
1003
1004 return 0;
1005 }
1006
1007 int config_parse_wireguard_peer_route_priority(
1008 const char *unit,
1009 const char *filename,
1010 unsigned line,
1011 const char *section,
1012 unsigned section_line,
1013 const char *lvalue,
1014 int ltype,
1015 const char *rvalue,
1016 void *data,
1017 void *userdata) {
1018
1019 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1020 Wireguard *w;
1021 int r;
1022
1023 assert(filename);
1024 assert(lvalue);
1025 assert(rvalue);
1026 assert(userdata);
1027
1028 w = WIREGUARD(userdata);
1029 assert(w);
1030
1031 r = wireguard_peer_new_static(w, filename, section_line, &peer);
1032 if (r < 0)
1033 return log_oom();
1034
1035 if (isempty(rvalue)) {
1036 peer->route_priority_set = false; /* Use the priority specified in [WireGuard] section. */
1037 TAKE_PTR(peer);
1038 return 0;
1039 }
1040
1041 r = safe_atou32(rvalue, &peer->route_priority);
1042 if (r < 0) {
1043 log_syntax(unit, LOG_WARNING, filename, line, r,
1044 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1045 return 0;
1046 }
1047
1048 peer->route_priority_set = true;
1049 TAKE_PTR(peer);
1050 return 0;
1051 }
1052
1053 static void wireguard_init(NetDev *netdev) {
1054 Wireguard *w;
1055
1056 assert(netdev);
1057 w = WIREGUARD(netdev);
1058 assert(w);
1059
1060 w->flags = WGDEVICE_F_REPLACE_PEERS;
1061 }
1062
1063 static void wireguard_done(NetDev *netdev) {
1064 Wireguard *w;
1065
1066 assert(netdev);
1067 w = WIREGUARD(netdev);
1068 assert(w);
1069
1070 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
1071 free(w->private_key_file);
1072
1073 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
1074
1075 set_free(w->routes);
1076 }
1077
1078 static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
1079 _cleanup_(erase_and_freep) char *key = NULL;
1080 size_t key_len;
1081 int r;
1082
1083 if (!filename)
1084 return 0;
1085
1086 assert(dest);
1087
1088 r = read_full_file_full(
1089 AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
1090 READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
1091 NULL, &key, &key_len);
1092 if (r < 0)
1093 return r;
1094
1095 if (key_len != WG_KEY_LEN)
1096 return -EINVAL;
1097
1098 memcpy(dest, key, WG_KEY_LEN);
1099 return 0;
1100 }
1101
1102 static int wireguard_peer_verify(WireguardPeer *peer) {
1103 NetDev *netdev = NETDEV(peer->wireguard);
1104 int r;
1105
1106 if (section_is_invalid(peer->section))
1107 return -EINVAL;
1108
1109 if (eqzero(peer->public_key))
1110 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1111 "%s: WireGuardPeer section without PublicKey= configured. "
1112 "Ignoring [WireGuardPeer] section from line %u.",
1113 peer->section->filename, peer->section->line);
1114
1115 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
1116 if (r < 0)
1117 return log_netdev_error_errno(netdev, r,
1118 "%s: Failed to read preshared key from '%s'. "
1119 "Ignoring [WireGuardPeer] section from line %u.",
1120 peer->section->filename, peer->preshared_key_file,
1121 peer->section->line);
1122
1123 return 0;
1124 }
1125
1126 static int wireguard_verify(NetDev *netdev, const char *filename) {
1127 Wireguard *w;
1128 int r;
1129
1130 assert(netdev);
1131 w = WIREGUARD(netdev);
1132 assert(w);
1133
1134 r = wireguard_read_key_file(w->private_key_file, w->private_key);
1135 if (r < 0)
1136 return log_netdev_error_errno(netdev, r,
1137 "Failed to read private key from %s. Ignoring network device.",
1138 w->private_key_file);
1139
1140 if (eqzero(w->private_key))
1141 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1142 "%s: Missing PrivateKey= or PrivateKeyFile=, "
1143 "Ignoring network device.", filename);
1144
1145 LIST_FOREACH(peers, peer, w->peers) {
1146 if (wireguard_peer_verify(peer) < 0) {
1147 wireguard_peer_free(peer);
1148 continue;
1149 }
1150
1151 if ((peer->route_table_set ? peer->route_table : w->route_table) == 0)
1152 continue;
1153
1154 LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
1155 _cleanup_(route_freep) Route *route = NULL;
1156
1157 r = route_new(&route);
1158 if (r < 0)
1159 return log_oom();
1160
1161 route->family = ipmask->family;
1162 route->dst = ipmask->ip;
1163 route->dst_prefixlen = ipmask->cidr;
1164 route->scope = RT_SCOPE_UNIVERSE;
1165 route->protocol = RTPROT_STATIC;
1166 route->table = peer->route_table_set ? peer->route_table : w->route_table;
1167 route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
1168 if (route->priority == 0 && route->family == AF_INET6)
1169 route->priority = IP6_RT_PRIO_USER;
1170 route->source = NETWORK_CONFIG_SOURCE_STATIC;
1171
1172 r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
1173 if (r < 0)
1174 return log_oom();
1175 }
1176 }
1177
1178 return 0;
1179 }
1180
1181 const NetDevVTable wireguard_vtable = {
1182 .object_size = sizeof(Wireguard),
1183 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
1184 .post_create = netdev_wireguard_post_create,
1185 .init = wireguard_init,
1186 .done = wireguard_done,
1187 .create_type = NETDEV_CREATE_INDEPENDENT,
1188 .config_verify = wireguard_verify,
1189 .iftype = ARPHRD_NONE,
1190 };