1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016-2017 Jörg Thalheim <joerg@thalheim.io>
6 Copyright 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
12 #include "alloc-util.h"
13 #include "parse-util.h"
16 #include "hexdecoct.h"
17 #include "string-util.h"
18 #include "wireguard.h"
19 #include "networkd-link.h"
20 #include "networkd-util.h"
21 #include "networkd-manager.h"
22 #include "wireguard-netlink.h"
24 static void resolve_endpoints(NetDev
*netdev
);
26 static WireguardPeer
*wireguard_peer_new(Wireguard
*w
, unsigned section
) {
31 if (w
->last_peer_section
== section
&& w
->peers
)
34 peer
= new0(WireguardPeer
, 1);
37 peer
->flags
= WGPEER_F_REPLACE_ALLOWEDIPS
;
39 LIST_PREPEND(peers
, w
->peers
, peer
);
40 w
->last_peer_section
= section
;
45 static int set_wireguard_interface(NetDev
*netdev
) {
48 WireguardPeer
*peer
, *peer_start
;
49 WireguardIPmask
*mask
, *mask_start
= NULL
;
50 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*message
= NULL
;
55 w
= WIREGUARD(netdev
);
58 peer_start
= w
->peers
;
61 message
= sd_netlink_message_unref(message
);
63 r
= sd_genl_message_new(netdev
->manager
->genl
, SD_GENL_WIREGUARD
, WG_CMD_SET_DEVICE
, &message
);
65 return log_netdev_error_errno(netdev
, r
, "Failed to allocate generic netlink message: %m");
67 r
= sd_netlink_message_append_string(message
, WGDEVICE_A_IFNAME
, netdev
->ifname
);
69 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard interface name: %m");
71 if (peer_start
== w
->peers
) {
72 r
= sd_netlink_message_append_data(message
, WGDEVICE_A_PRIVATE_KEY
, &w
->private_key
, WG_KEY_LEN
);
74 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard private key: %m");
76 r
= sd_netlink_message_append_u16(message
, WGDEVICE_A_LISTEN_PORT
, w
->port
);
78 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard port: %m");
80 r
= sd_netlink_message_append_u32(message
, WGDEVICE_A_FWMARK
, w
->fwmark
);
82 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard fwmark: %m");
84 r
= sd_netlink_message_append_u32(message
, WGDEVICE_A_FLAGS
, w
->flags
);
86 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard flags: %m");
89 r
= sd_netlink_message_open_container(message
, WGDEVICE_A_PEERS
);
91 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard peer attributes: %m");
95 LIST_FOREACH(peers
, peer
, peer_start
) {
96 r
= sd_netlink_message_open_array(message
, ++i
);
100 r
= sd_netlink_message_append_data(message
, WGPEER_A_PUBLIC_KEY
, &peer
->public_key
, sizeof(peer
->public_key
));
105 r
= sd_netlink_message_append_data(message
, WGPEER_A_PRESHARED_KEY
, &peer
->preshared_key
, WG_KEY_LEN
);
109 r
= sd_netlink_message_append_u32(message
, WGPEER_A_FLAGS
, peer
->flags
);
113 r
= sd_netlink_message_append_u32(message
, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL
, peer
->persistent_keepalive_interval
);
117 if (peer
->endpoint
.sa
.sa_family
== AF_INET
) {
118 r
= sd_netlink_message_append_data(message
, WGPEER_A_ENDPOINT
, &peer
->endpoint
.in
, sizeof(peer
->endpoint
.in
));
121 } else if (peer
->endpoint
.sa
.sa_family
== AF_INET6
) {
122 r
= sd_netlink_message_append_data(message
, WGPEER_A_ENDPOINT
, &peer
->endpoint
.in6
, sizeof(peer
->endpoint
.in6
));
127 mask_start
= peer
->ipmasks
;
130 r
= sd_netlink_message_open_container(message
, WGPEER_A_ALLOWEDIPS
);
136 LIST_FOREACH(ipmasks
, mask
, mask_start
) {
137 r
= sd_netlink_message_open_array(message
, ++j
);
141 r
= sd_netlink_message_append_u16(message
, WGALLOWEDIP_A_FAMILY
, mask
->family
);
145 if (mask
->family
== AF_INET
) {
146 r
= sd_netlink_message_append_in_addr(message
, WGALLOWEDIP_A_IPADDR
, &mask
->ip
.in
);
149 } else if (mask
->family
== AF_INET6
) {
150 r
= sd_netlink_message_append_in6_addr(message
, WGALLOWEDIP_A_IPADDR
, &mask
->ip
.in6
);
155 r
= sd_netlink_message_append_u8(message
, WGALLOWEDIP_A_CIDR_MASK
, mask
->cidr
);
159 r
= sd_netlink_message_close_container(message
);
161 return log_netdev_error_errno(netdev
, r
, "Could not add wireguard allowed ip: %m");
165 r
= sd_netlink_message_cancel_array(message
);
167 return log_netdev_error_errno(netdev
, r
, "Could not cancel wireguard allowed ip message attribute: %m");
169 r
= sd_netlink_message_close_container(message
);
171 return log_netdev_error_errno(netdev
, r
, "Could not add wireguard allowed ip: %m");
173 r
= sd_netlink_message_close_container(message
);
175 return log_netdev_error_errno(netdev
, r
, "Could not add wireguard peer: %m");
179 if (peer_start
&& !mask_start
) {
180 r
= sd_netlink_message_cancel_array(message
);
182 return log_netdev_error_errno(netdev
, r
, "Could not cancel wireguard peers: %m");
185 r
= sd_netlink_message_close_container(message
);
187 return log_netdev_error_errno(netdev
, r
, "Could not close wireguard container: %m");
189 r
= sd_netlink_send(netdev
->manager
->genl
, message
, &serial
);
191 return log_netdev_error_errno(netdev
, r
, "Could not set wireguard device: %m");
193 } while (peer
|| mask_start
);
198 static WireguardEndpoint
* wireguard_endpoint_free(WireguardEndpoint
*e
) {
201 netdev_unref(e
->netdev
);
202 e
->host
= mfree(e
->host
);
203 e
->port
= mfree(e
->port
);
207 DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint
*, wireguard_endpoint_free
);
209 static int on_resolve_retry(sd_event_source
*s
, usec_t usec
, void *userdata
) {
210 NetDev
*netdev
= userdata
;
214 w
= WIREGUARD(netdev
);
217 w
->resolve_retry_event_source
= sd_event_source_unref(w
->resolve_retry_event_source
);
219 w
->unresolved_endpoints
= TAKE_PTR(w
->failed_endpoints
);
221 resolve_endpoints(netdev
);
227 * Given the number of retries this function will return will an exponential
228 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
230 static int exponential_backoff_milliseconds(unsigned n_retries
) {
231 return (2 << MAX(n_retries
, 7U)) * 100 * USEC_PER_MSEC
;
234 static int wireguard_resolve_handler(sd_resolve_query
*q
,
236 const struct addrinfo
*ai
,
240 _cleanup_(wireguard_endpoint_freep
) WireguardEndpoint
*e
;
248 w
= WIREGUARD(netdev
);
251 w
->resolve_query
= sd_resolve_query_unref(w
->resolve_query
);
254 log_netdev_error(netdev
, "Failed to resolve host '%s:%s': %s", e
->host
, e
->port
, gai_strerror(ret
));
255 LIST_PREPEND(endpoints
, w
->failed_endpoints
, e
);
257 } else if ((ai
->ai_family
== AF_INET
&& ai
->ai_addrlen
== sizeof(struct sockaddr_in
)) ||
258 (ai
->ai_family
== AF_INET6
&& ai
->ai_addrlen
== sizeof(struct sockaddr_in6
)))
259 memcpy(&e
->peer
->endpoint
, ai
->ai_addr
, ai
->ai_addrlen
);
261 log_netdev_error(netdev
, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e
->host
, e
->port
);
263 if (w
->unresolved_endpoints
) {
264 resolve_endpoints(netdev
);
268 set_wireguard_interface(netdev
);
269 if (w
->failed_endpoints
) {
271 r
= sd_event_add_time(netdev
->manager
->event
,
272 &w
->resolve_retry_event_source
,
274 now(CLOCK_MONOTONIC
) + exponential_backoff_milliseconds(w
->n_retries
),
279 log_netdev_warning_errno(netdev
, r
, "Could not arm resolve retry handler: %m");
285 static void resolve_endpoints(NetDev
*netdev
) {
288 WireguardEndpoint
*endpoint
;
289 static const struct addrinfo hints
= {
290 .ai_family
= AF_UNSPEC
,
291 .ai_socktype
= SOCK_DGRAM
,
292 .ai_protocol
= IPPROTO_UDP
296 w
= WIREGUARD(netdev
);
299 LIST_FOREACH(endpoints
, endpoint
, w
->unresolved_endpoints
) {
300 r
= sd_resolve_getaddrinfo(netdev
->manager
->resolve
,
305 wireguard_resolve_handler
,
311 LIST_REMOVE(endpoints
, w
->unresolved_endpoints
, endpoint
);
314 log_netdev_error_errno(netdev
, r
, "Failed create resolver: %m");
319 static int netdev_wireguard_post_create(NetDev
*netdev
, Link
*link
, sd_netlink_message
*m
) {
323 w
= WIREGUARD(netdev
);
326 set_wireguard_interface(netdev
);
327 resolve_endpoints(netdev
);
331 int config_parse_wireguard_listen_port(const char *unit
,
332 const char *filename
,
335 unsigned section_line
,
348 if (!streq(rvalue
, "auto")) {
349 r
= parse_ip_port(rvalue
, &port
);
351 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid port specification, ignoring assignment: %s", rvalue
);
359 static int parse_wireguard_key(const char *unit
,
360 const char *filename
,
363 unsigned section_line
,
369 _cleanup_free_
void *key
= NULL
;
377 r
= unbase64mem(rvalue
, strlen(rvalue
), &key
, &len
);
379 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue
);
382 if (len
!= WG_KEY_LEN
) {
383 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
384 "Wireguard key is too short, ignoring assignment: %s", rvalue
);
388 memcpy(userdata
, key
, WG_KEY_LEN
);
392 int config_parse_wireguard_private_key(const char *unit
,
393 const char *filename
,
396 unsigned section_line
,
410 return parse_wireguard_key(unit
,
423 int config_parse_wireguard_preshared_key(const char *unit
,
424 const char *filename
,
427 unsigned section_line
,
442 peer
= wireguard_peer_new(w
, section_line
);
446 return parse_wireguard_key(unit
,
455 peer
->preshared_key
);
459 int config_parse_wireguard_public_key(const char *unit
,
460 const char *filename
,
463 unsigned section_line
,
478 peer
= wireguard_peer_new(w
, section_line
);
482 return parse_wireguard_key(unit
,
494 int config_parse_wireguard_allowed_ips(const char *unit
,
495 const char *filename
,
498 unsigned section_line
,
504 union in_addr_union addr
;
505 unsigned char prefixlen
;
509 WireguardIPmask
*ipmask
;
516 peer
= wireguard_peer_new(w
, section_line
);
521 _cleanup_free_
char *word
= NULL
;
523 r
= extract_first_word(&rvalue
, &word
, "," WHITESPACE
, 0);
529 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split allowed ips \"%s\" option: %m", rvalue
);
533 r
= in_addr_prefix_from_string_auto(word
, &family
, &addr
, &prefixlen
);
535 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Network address is invalid, ignoring assignment: %s", word
);
539 ipmask
= new0(WireguardIPmask
, 1);
542 ipmask
->family
= family
;
543 ipmask
->ip
.in6
= addr
.in6
;
544 ipmask
->cidr
= prefixlen
;
546 LIST_PREPEND(ipmasks
, peer
->ipmasks
, ipmask
);
552 int config_parse_wireguard_endpoint(const char *unit
,
553 const char *filename
,
556 unsigned section_line
,
565 const char *begin
, *end
= NULL
;
566 _cleanup_free_
char *host
= NULL
, *port
= NULL
;
567 _cleanup_(wireguard_endpoint_freep
) WireguardEndpoint
*endpoint
= NULL
;
576 peer
= wireguard_peer_new(w
, section_line
);
580 endpoint
= new0(WireguardEndpoint
, 1);
584 if (rvalue
[0] == '[') {
586 end
= strchr(rvalue
, ']');
588 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue
);
593 if (*end
!= ':' || !*(end
+ 1)) {
594 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unable to find port of endpoint: %s", rvalue
);
600 end
= strrchr(rvalue
, ':');
601 if (!end
|| !*(end
+ 1)) {
602 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unable to find port of endpoint: %s", rvalue
);
609 host
= strndup(begin
, len
);
617 endpoint
->peer
= TAKE_PTR(peer
);
618 endpoint
->host
= TAKE_PTR(host
);
619 endpoint
->port
= TAKE_PTR(port
);
620 endpoint
->netdev
= netdev_ref(data
);
621 LIST_PREPEND(endpoints
, w
->unresolved_endpoints
, endpoint
);
627 int config_parse_wireguard_keepalive(const char *unit
,
628 const char *filename
,
631 unsigned section_line
,
638 uint16_t keepalive
= 0;
649 peer
= wireguard_peer_new(w
, section_line
);
653 if (streq(rvalue
, "off"))
656 r
= safe_atou16(rvalue
, &keepalive
);
658 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue
);
661 peer
->persistent_keepalive_interval
= keepalive
;
665 static void wireguard_init(NetDev
*netdev
) {
670 w
= WIREGUARD(netdev
);
674 w
->flags
= WGDEVICE_F_REPLACE_PEERS
;
677 static void wireguard_done(NetDev
*netdev
) {
680 WireguardIPmask
*mask
;
683 w
= WIREGUARD(netdev
);
684 assert(!w
->unresolved_endpoints
);
685 w
->resolve_retry_event_source
= sd_event_source_unref(w
->resolve_retry_event_source
);
687 while ((peer
= w
->peers
)) {
688 LIST_REMOVE(peers
, w
->peers
, peer
);
689 while ((mask
= peer
->ipmasks
)) {
690 LIST_REMOVE(ipmasks
, peer
->ipmasks
, mask
);
697 const NetDevVTable wireguard_vtable
= {
698 .object_size
= sizeof(Wireguard
),
699 .sections
= "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
700 .post_create
= netdev_wireguard_post_create
,
701 .init
= wireguard_init
,
702 .done
= wireguard_done
,
703 .create_type
= NETDEV_CREATE_INDEPENDENT
,