]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/wireguard.c
Merge pull request #29339 from bluca/mount_namespace_new_api
[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 bool sent_once = false;
229 uint32_t serial;
230 Wireguard *w = WIREGUARD(netdev);
231 int r;
232
233 for (WireguardPeer *peer_start = w->peers; peer_start || !sent_once; ) {
234 uint16_t i = 0;
235
236 message = sd_netlink_message_unref(message);
237
238 r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
239 if (r < 0)
240 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
241
242 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
243 if (r < 0)
244 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
245
246 if (peer_start == w->peers) {
247 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
248 if (r < 0)
249 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
250
251 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
252 if (r < 0)
253 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
254
255 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
256 if (r < 0)
257 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
258
259 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
260 if (r < 0)
261 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
262 }
263
264 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
265 if (r < 0)
266 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
267
268 WireguardPeer *peer_last = NULL;
269 LIST_FOREACH(peers, peer, peer_start) {
270 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
271 if (r < 0)
272 return r;
273 if (r == 0) {
274 peer_last = peer;
275 break;
276 }
277 }
278 peer_start = peer_last; /* Start next cycle from this peer. */
279
280 r = sd_netlink_message_close_container(message);
281 if (r < 0)
282 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
283
284 r = sd_netlink_send(netdev->manager->genl, message, &serial);
285 if (r < 0)
286 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
287
288 sent_once = true;
289 }
290
291 return 0;
292 }
293
294 static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
295 WireguardPeer *peer = ASSERT_PTR(userdata);
296 NetDev *netdev;
297
298 assert(peer->wireguard);
299
300 netdev = NETDEV(peer->wireguard);
301
302 if (!netdev_is_managed(netdev))
303 return 0;
304
305 peer->resolve_query = sd_resolve_query_unref(peer->resolve_query);
306
307 (void) peer_resolve_endpoint(peer);
308 return 0;
309 }
310
311 static usec_t peer_next_resolve_usec(WireguardPeer *peer) {
312 usec_t usec;
313
314 /* Given the number of retries this function will return an exponential increasing amount of
315 * milliseconds to wait starting at 200ms and capped at 25 seconds. */
316
317 assert(peer);
318
319 usec = (2 << MIN(peer->n_retries, 7U)) * 100 * USEC_PER_MSEC;
320
321 return random_u64_range(usec / 10) + usec * 9 / 10;
322 }
323
324 static int wireguard_peer_resolve_handler(
325 sd_resolve_query *q,
326 int ret,
327 const struct addrinfo *ai,
328 void *userdata) {
329
330 WireguardPeer *peer = ASSERT_PTR(userdata);
331 NetDev *netdev;
332 int r;
333
334 assert(peer->wireguard);
335
336 netdev = NETDEV(peer->wireguard);
337
338 if (!netdev_is_managed(netdev))
339 return 0;
340
341 if (ret != 0) {
342 log_netdev_warning(netdev, "Failed to resolve host '%s:%s', ignoring: %s",
343 peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
344 peer->n_retries++;
345
346 } else {
347 bool found = false;
348 for (; ai; ai = ai->ai_next) {
349 if (!IN_SET(ai->ai_family, AF_INET, AF_INET6))
350 continue;
351
352 if (ai->ai_addrlen != (ai->ai_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))
353 continue;
354
355 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
356 (void) wireguard_set_interface(netdev);
357 peer->n_retries = 0;
358 found = true;
359 break;
360 }
361
362 if (!found) {
363 log_netdev_warning(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the endpoint.",
364 peer->endpoint_host, peer->endpoint_port);
365 peer->n_retries++;
366 }
367 }
368
369 if (peer->n_retries > 0) {
370 r = event_reset_time_relative(netdev->manager->event,
371 &peer->resolve_retry_event_source,
372 CLOCK_BOOTTIME,
373 peer_next_resolve_usec(peer), 0,
374 on_resolve_retry, peer, 0, "wireguard-resolve-retry", true);
375 if (r < 0)
376 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler for endpoint %s:%s, ignoring: %m",
377 peer->endpoint_host, peer->endpoint_port);
378 }
379
380 wireguard_resolve_endpoints(netdev);
381 return 0;
382 }
383
384 static int peer_resolve_endpoint(WireguardPeer *peer) {
385 static const struct addrinfo hints = {
386 .ai_family = AF_UNSPEC,
387 .ai_socktype = SOCK_DGRAM,
388 .ai_protocol = IPPROTO_UDP
389 };
390 NetDev *netdev;
391 int r;
392
393 assert(peer);
394 assert(peer->wireguard);
395
396 netdev = NETDEV(peer->wireguard);
397
398 if (!peer->endpoint_host || !peer->endpoint_port)
399 /* Not necessary to resolve the endpoint. */
400 return 0;
401
402 if (sd_event_source_get_enabled(peer->resolve_retry_event_source, NULL) > 0)
403 /* Timer event source is enabled. The endpoint will be resolved later. */
404 return 0;
405
406 if (peer->resolve_query)
407 /* Being resolved, or already resolved. */
408 return 0;
409
410 r = sd_resolve_getaddrinfo(netdev->manager->resolve,
411 &peer->resolve_query,
412 peer->endpoint_host,
413 peer->endpoint_port,
414 &hints,
415 wireguard_peer_resolve_handler,
416 peer);
417 if (r < 0)
418 return log_netdev_full_errno(netdev, r == -ENOBUFS ? LOG_DEBUG : LOG_WARNING, r,
419 "Failed to create endpoint resolver for %s:%s, ignoring: %m",
420 peer->endpoint_host, peer->endpoint_port);
421
422 return 0;
423 }
424
425 static void wireguard_resolve_endpoints(NetDev *netdev) {
426 Wireguard *w = WIREGUARD(netdev);
427
428 LIST_FOREACH(peers, peer, w->peers)
429 if (peer_resolve_endpoint(peer) == -ENOBUFS)
430 /* Too many requests. Let's resolve remaining endpoints later. */
431 break;
432 }
433
434 static int netdev_wireguard_post_create(NetDev *netdev, Link *link) {
435 assert(WIREGUARD(netdev));
436
437 (void) wireguard_set_interface(netdev);
438 wireguard_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 = ASSERT_PTR(data);
455 int r;
456
457 assert(rvalue);
458
459 if (isempty(rvalue) || streq(rvalue, "auto")) {
460 *s = 0;
461 return 0;
462 }
463
464 r = parse_ip_port(rvalue, s);
465 if (r < 0) {
466 log_syntax(unit, LOG_WARNING, filename, line, r,
467 "Invalid port specification, ignoring assignment: %s", rvalue);
468 return 0;
469 }
470
471 return 0;
472 }
473
474 static int wireguard_decode_key_and_warn(
475 const char *rvalue,
476 uint8_t ret[static WG_KEY_LEN],
477 const char *unit,
478 const char *filename,
479 unsigned line,
480 const char *lvalue) {
481
482 _cleanup_(erase_and_freep) void *key = NULL;
483 size_t len;
484 int r;
485
486 assert(rvalue);
487 assert(ret);
488 assert(filename);
489 assert(lvalue);
490
491 if (isempty(rvalue)) {
492 memzero(ret, WG_KEY_LEN);
493 return 0;
494 }
495
496 if (!streq(lvalue, "PublicKey"))
497 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
498
499 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
500 if (r == -ENOMEM)
501 return log_oom();
502 if (r < 0) {
503 log_syntax(unit, LOG_WARNING, filename, line, r,
504 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
505 return 0;
506 }
507 if (len != WG_KEY_LEN) {
508 log_syntax(unit, LOG_WARNING, filename, line, 0,
509 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
510 lvalue, len);
511 return 0;
512 }
513
514 memcpy(ret, key, WG_KEY_LEN);
515 return 0;
516 }
517
518 int config_parse_wireguard_private_key(
519 const char *unit,
520 const char *filename,
521 unsigned line,
522 const char *section,
523 unsigned section_line,
524 const char *lvalue,
525 int ltype,
526 const char *rvalue,
527 void *data,
528 void *userdata) {
529
530 Wireguard *w = WIREGUARD(data);
531
532 return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
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 Wireguard *w = WIREGUARD(data);
548 _cleanup_free_ char *path = NULL;
549
550 if (isempty(rvalue)) {
551 w->private_key_file = mfree(w->private_key_file);
552 return 0;
553 }
554
555 path = strdup(rvalue);
556 if (!path)
557 return log_oom();
558
559 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
560 return 0;
561
562 return free_and_replace(w->private_key_file, path);
563 }
564
565 int config_parse_wireguard_peer_key(
566 const char *unit,
567 const char *filename,
568 unsigned line,
569 const char *section,
570 unsigned section_line,
571 const char *lvalue,
572 int ltype,
573 const char *rvalue,
574 void *data,
575 void *userdata) {
576
577 Wireguard *w = WIREGUARD(data);
578 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
579 int r;
580
581 r = wireguard_peer_new_static(w, filename, section_line, &peer);
582 if (r < 0)
583 return log_oom();
584
585 r = wireguard_decode_key_and_warn(rvalue,
586 streq(lvalue, "PublicKey") ? peer->public_key : peer->preshared_key,
587 unit, filename, line, lvalue);
588 if (r < 0)
589 return r;
590
591 TAKE_PTR(peer);
592 return 0;
593 }
594
595 int config_parse_wireguard_preshared_key_file(
596 const char *unit,
597 const char *filename,
598 unsigned line,
599 const char *section,
600 unsigned section_line,
601 const char *lvalue,
602 int ltype,
603 const char *rvalue,
604 void *data,
605 void *userdata) {
606
607 Wireguard *w = WIREGUARD(data);
608 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
609 _cleanup_free_ char *path = NULL;
610 int r;
611
612 r = wireguard_peer_new_static(w, filename, section_line, &peer);
613 if (r < 0)
614 return log_oom();
615
616 if (isempty(rvalue)) {
617 peer->preshared_key_file = mfree(peer->preshared_key_file);
618 TAKE_PTR(peer);
619 return 0;
620 }
621
622 path = strdup(rvalue);
623 if (!path)
624 return log_oom();
625
626 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
627 return 0;
628
629 free_and_replace(peer->preshared_key_file, path);
630 TAKE_PTR(peer);
631 return 0;
632 }
633
634 int config_parse_wireguard_allowed_ips(
635 const char *unit,
636 const char *filename,
637 unsigned line,
638 const char *section,
639 unsigned section_line,
640 const char *lvalue,
641 int ltype,
642 const char *rvalue,
643 void *data,
644 void *userdata) {
645
646 assert(rvalue);
647
648 Wireguard *w = WIREGUARD(data);
649 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
650 union in_addr_union addr;
651 unsigned char prefixlen;
652 int r, family;
653 WireguardIPmask *ipmask;
654
655 r = wireguard_peer_new_static(w, filename, section_line, &peer);
656 if (r < 0)
657 return log_oom();
658
659 if (isempty(rvalue)) {
660 wireguard_peer_clear_ipmasks(peer);
661 TAKE_PTR(peer);
662 return 0;
663 }
664
665 for (const char *p = rvalue;;) {
666 _cleanup_free_ char *word = NULL;
667 union in_addr_union masked;
668
669 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
670 if (r == 0)
671 break;
672 if (r == -ENOMEM)
673 return log_oom();
674 if (r < 0) {
675 log_syntax(unit, LOG_WARNING, filename, line, r,
676 "Failed to split allowed ips \"%s\" option: %m", rvalue);
677 break;
678 }
679
680 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
681 if (r < 0) {
682 log_syntax(unit, LOG_WARNING, filename, line, r,
683 "Network address is invalid, ignoring assignment: %s", word);
684 continue;
685 }
686
687 masked = addr;
688 assert_se(in_addr_mask(family, &masked, prefixlen) >= 0);
689 if (!in_addr_equal(family, &masked, &addr))
690 log_syntax(unit, LOG_WARNING, filename, line, 0,
691 "Specified address '%s' is not properly masked, assuming '%s'.",
692 word,
693 IN_ADDR_PREFIX_TO_STRING(family, &masked, prefixlen));
694
695 ipmask = new(WireguardIPmask, 1);
696 if (!ipmask)
697 return log_oom();
698
699 *ipmask = (WireguardIPmask) {
700 .family = family,
701 .ip = masked,
702 .cidr = prefixlen,
703 };
704
705 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
706 }
707
708 TAKE_PTR(peer);
709 return 0;
710 }
711
712 int config_parse_wireguard_endpoint(
713 const char *unit,
714 const char *filename,
715 unsigned line,
716 const char *section,
717 unsigned section_line,
718 const char *lvalue,
719 int ltype,
720 const char *rvalue,
721 void *data,
722 void *userdata) {
723
724 assert(filename);
725 assert(rvalue);
726 assert(userdata);
727
728 Wireguard *w = WIREGUARD(userdata);
729 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
730 _cleanup_free_ char *host = NULL;
731 union in_addr_union addr;
732 const char *p;
733 uint16_t port;
734 int family, r;
735
736 r = wireguard_peer_new_static(w, filename, section_line, &peer);
737 if (r < 0)
738 return log_oom();
739
740 r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
741 if (r >= 0) {
742 if (family == AF_INET)
743 peer->endpoint.in = (struct sockaddr_in) {
744 .sin_family = AF_INET,
745 .sin_addr = addr.in,
746 .sin_port = htobe16(port),
747 };
748 else if (family == AF_INET6)
749 peer->endpoint.in6 = (struct sockaddr_in6) {
750 .sin6_family = AF_INET6,
751 .sin6_addr = addr.in6,
752 .sin6_port = htobe16(port),
753 };
754 else
755 assert_not_reached();
756
757 peer->endpoint_host = mfree(peer->endpoint_host);
758 peer->endpoint_port = mfree(peer->endpoint_port);
759
760 TAKE_PTR(peer);
761 return 0;
762 }
763
764 p = strrchr(rvalue, ':');
765 if (!p) {
766 log_syntax(unit, LOG_WARNING, filename, line, 0,
767 "Unable to find port of endpoint, ignoring assignment: %s",
768 rvalue);
769 return 0;
770 }
771
772 host = strndup(rvalue, p - rvalue);
773 if (!host)
774 return log_oom();
775
776 if (!dns_name_is_valid(host)) {
777 log_syntax(unit, LOG_WARNING, filename, line, 0,
778 "Invalid domain name of endpoint, ignoring assignment: %s",
779 rvalue);
780 return 0;
781 }
782
783 p++;
784 r = parse_ip_port(p, &port);
785 if (r < 0) {
786 log_syntax(unit, LOG_WARNING, filename, line, r,
787 "Invalid port of endpoint, ignoring assignment: %s",
788 rvalue);
789 return 0;
790 }
791
792 peer->endpoint = (union sockaddr_union) {};
793
794 free_and_replace(peer->endpoint_host, host);
795
796 r = free_and_strdup(&peer->endpoint_port, p);
797 if (r < 0)
798 return log_oom();
799
800 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
801 return 0;
802 }
803
804 int config_parse_wireguard_keepalive(
805 const char *unit,
806 const char *filename,
807 unsigned line,
808 const char *section,
809 unsigned section_line,
810 const char *lvalue,
811 int ltype,
812 const char *rvalue,
813 void *data,
814 void *userdata) {
815
816 assert(rvalue);
817
818 Wireguard *w = WIREGUARD(data);
819 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
820 uint16_t keepalive = 0;
821 int r;
822
823 r = wireguard_peer_new_static(w, filename, section_line, &peer);
824 if (r < 0)
825 return log_oom();
826
827 if (streq(rvalue, "off"))
828 keepalive = 0;
829 else {
830 r = safe_atou16(rvalue, &keepalive);
831 if (r < 0) {
832 log_syntax(unit, LOG_WARNING, filename, line, r,
833 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
834 rvalue);
835 return 0;
836 }
837 }
838
839 peer->persistent_keepalive_interval = keepalive;
840
841 TAKE_PTR(peer);
842 return 0;
843 }
844
845 int config_parse_wireguard_route_table(
846 const char *unit,
847 const char *filename,
848 unsigned line,
849 const char *section,
850 unsigned section_line,
851 const char *lvalue,
852 int ltype,
853 const char *rvalue,
854 void *data,
855 void *userdata) {
856
857 NetDev *netdev = ASSERT_PTR(userdata);
858 uint32_t *table = ASSERT_PTR(data);
859 int r;
860
861 assert(filename);
862 assert(lvalue);
863 assert(rvalue);
864
865 if (isempty(rvalue) || parse_boolean(rvalue) == 0) {
866 *table = 0; /* Disabled. */
867 return 0;
868 }
869
870 r = manager_get_route_table_from_string(netdev->manager, rvalue, table);
871 if (r < 0) {
872 log_syntax(unit, LOG_WARNING, filename, line, r,
873 "Failed to parse %s=, ignoring assignment: %s",
874 lvalue, rvalue);
875 return 0;
876 }
877
878 return 0;
879 }
880
881 int config_parse_wireguard_peer_route_table(
882 const char *unit,
883 const char *filename,
884 unsigned line,
885 const char *section,
886 unsigned section_line,
887 const char *lvalue,
888 int ltype,
889 const char *rvalue,
890 void *data,
891 void *userdata) {
892
893 Wireguard *w = WIREGUARD(userdata);
894 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
895 int r;
896
897 assert(filename);
898 assert(lvalue);
899 assert(rvalue);
900 assert(NETDEV(w)->manager);
901
902 r = wireguard_peer_new_static(w, filename, section_line, &peer);
903 if (r < 0)
904 return log_oom();
905
906 if (isempty(rvalue)) {
907 peer->route_table_set = false; /* Use the table specified in [WireGuard] section. */
908 TAKE_PTR(peer);
909 return 0;
910 }
911
912 if (parse_boolean(rvalue) == 0) {
913 peer->route_table = 0; /* Disabled. */
914 peer->route_table_set = true;
915 TAKE_PTR(peer);
916 return 0;
917 }
918
919 r = manager_get_route_table_from_string(NETDEV(w)->manager, rvalue, &peer->route_table);
920 if (r < 0) {
921 log_syntax(unit, LOG_WARNING, filename, line, r,
922 "Failed to parse %s=, ignoring assignment: %s",
923 lvalue, rvalue);
924 return 0;
925 }
926
927 peer->route_table_set = true;
928 TAKE_PTR(peer);
929 return 0;
930 }
931
932 int config_parse_wireguard_route_priority(
933 const char *unit,
934 const char *filename,
935 unsigned line,
936 const char *section,
937 unsigned section_line,
938 const char *lvalue,
939 int ltype,
940 const char *rvalue,
941 void *data,
942 void *userdata) {
943
944 uint32_t *priority = ASSERT_PTR(data);
945 int r;
946
947 assert(filename);
948 assert(lvalue);
949 assert(rvalue);
950
951 if (isempty(rvalue)) {
952 *priority = 0;
953 return 0;
954 }
955
956 r = safe_atou32(rvalue, priority);
957 if (r < 0) {
958 log_syntax(unit, LOG_WARNING, filename, line, r,
959 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
960 return 0;
961 }
962
963 return 0;
964 }
965
966 int config_parse_wireguard_peer_route_priority(
967 const char *unit,
968 const char *filename,
969 unsigned line,
970 const char *section,
971 unsigned section_line,
972 const char *lvalue,
973 int ltype,
974 const char *rvalue,
975 void *data,
976 void *userdata) {
977
978 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
979 Wireguard *w;
980 int r;
981
982 assert(filename);
983 assert(lvalue);
984 assert(rvalue);
985 assert(userdata);
986
987 w = WIREGUARD(userdata);
988 assert(w);
989
990 r = wireguard_peer_new_static(w, filename, section_line, &peer);
991 if (r < 0)
992 return log_oom();
993
994 if (isempty(rvalue)) {
995 peer->route_priority_set = false; /* Use the priority specified in [WireGuard] section. */
996 TAKE_PTR(peer);
997 return 0;
998 }
999
1000 r = safe_atou32(rvalue, &peer->route_priority);
1001 if (r < 0) {
1002 log_syntax(unit, LOG_WARNING, filename, line, r,
1003 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1004 return 0;
1005 }
1006
1007 peer->route_priority_set = true;
1008 TAKE_PTR(peer);
1009 return 0;
1010 }
1011
1012 static void wireguard_init(NetDev *netdev) {
1013 Wireguard *w = WIREGUARD(netdev);
1014
1015 w->flags = WGDEVICE_F_REPLACE_PEERS;
1016 }
1017
1018 static void wireguard_done(NetDev *netdev) {
1019 Wireguard *w = WIREGUARD(netdev);
1020
1021 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
1022 free(w->private_key_file);
1023
1024 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
1025
1026 set_free(w->routes);
1027 }
1028
1029 static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
1030 _cleanup_(erase_and_freep) char *key = NULL;
1031 size_t key_len;
1032 int r;
1033
1034 if (!filename)
1035 return 0;
1036
1037 assert(dest);
1038
1039 r = read_full_file_full(
1040 AT_FDCWD, filename, UINT64_MAX, WG_KEY_LEN,
1041 READ_FULL_FILE_SECURE |
1042 READ_FULL_FILE_UNBASE64 |
1043 READ_FULL_FILE_WARN_WORLD_READABLE |
1044 READ_FULL_FILE_CONNECT_SOCKET |
1045 READ_FULL_FILE_FAIL_WHEN_LARGER,
1046 NULL, &key, &key_len);
1047 if (r < 0)
1048 return r;
1049
1050 if (key_len != WG_KEY_LEN)
1051 return -EINVAL;
1052
1053 memcpy(dest, key, WG_KEY_LEN);
1054 return 0;
1055 }
1056
1057 static int wireguard_peer_verify(WireguardPeer *peer) {
1058 NetDev *netdev = NETDEV(peer->wireguard);
1059 int r;
1060
1061 if (section_is_invalid(peer->section))
1062 return -EINVAL;
1063
1064 if (eqzero(peer->public_key))
1065 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1066 "%s: WireGuardPeer section without PublicKey= configured. "
1067 "Ignoring [WireGuardPeer] section from line %u.",
1068 peer->section->filename, peer->section->line);
1069
1070 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
1071 if (r < 0)
1072 return log_netdev_error_errno(netdev, r,
1073 "%s: Failed to read preshared key from '%s'. "
1074 "Ignoring [WireGuardPeer] section from line %u.",
1075 peer->section->filename, peer->preshared_key_file,
1076 peer->section->line);
1077
1078 return 0;
1079 }
1080
1081 static int wireguard_verify(NetDev *netdev, const char *filename) {
1082 Wireguard *w = WIREGUARD(netdev);
1083 int r;
1084
1085 r = wireguard_read_key_file(w->private_key_file, w->private_key);
1086 if (r < 0)
1087 return log_netdev_error_errno(netdev, r,
1088 "Failed to read private key from %s. Ignoring network device.",
1089 w->private_key_file);
1090
1091 if (eqzero(w->private_key))
1092 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1093 "%s: Missing PrivateKey= or PrivateKeyFile=, "
1094 "Ignoring network device.", filename);
1095
1096 LIST_FOREACH(peers, peer, w->peers) {
1097 if (wireguard_peer_verify(peer) < 0) {
1098 wireguard_peer_free(peer);
1099 continue;
1100 }
1101
1102 if ((peer->route_table_set ? peer->route_table : w->route_table) == 0)
1103 continue;
1104
1105 LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
1106 _cleanup_(route_freep) Route *route = NULL;
1107
1108 r = route_new(&route);
1109 if (r < 0)
1110 return log_oom();
1111
1112 route->family = ipmask->family;
1113 route->dst = ipmask->ip;
1114 route->dst_prefixlen = ipmask->cidr;
1115 route->scope = RT_SCOPE_UNIVERSE;
1116 route->protocol = RTPROT_STATIC;
1117 route->table = peer->route_table_set ? peer->route_table : w->route_table;
1118 route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
1119 if (route->priority == 0 && route->family == AF_INET6)
1120 route->priority = IP6_RT_PRIO_USER;
1121 route->source = NETWORK_CONFIG_SOURCE_STATIC;
1122
1123 r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
1124 if (r < 0)
1125 return log_oom();
1126 }
1127 }
1128
1129 return 0;
1130 }
1131
1132 const NetDevVTable wireguard_vtable = {
1133 .object_size = sizeof(Wireguard),
1134 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
1135 .post_create = netdev_wireguard_post_create,
1136 .init = wireguard_init,
1137 .done = wireguard_done,
1138 .create_type = NETDEV_CREATE_INDEPENDENT,
1139 .config_verify = wireguard_verify,
1140 .iftype = ARPHRD_NONE,
1141 };