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