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