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