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