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