1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2016-2017 Jörg Thalheim <joerg@thalheim.io>
4 Copyright 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
10 #include "alloc-util.h"
11 #include "parse-util.h"
14 #include "hexdecoct.h"
15 #include "string-util.h"
16 #include "wireguard.h"
17 #include "networkd-link.h"
18 #include "networkd-util.h"
19 #include "networkd-manager.h"
20 #include "wireguard-netlink.h"
22 static void resolve_endpoints(NetDev
*netdev
);
24 static WireguardPeer
*wireguard_peer_new(Wireguard
*w
, unsigned section
) {
29 if (w
->last_peer_section
== section
&& w
->peers
)
32 peer
= new0(WireguardPeer
, 1);
35 peer
->flags
= WGPEER_F_REPLACE_ALLOWEDIPS
;
37 LIST_PREPEND(peers
, w
->peers
, peer
);
38 w
->last_peer_section
= section
;
43 static int set_wireguard_interface(NetDev
*netdev
) {
46 WireguardPeer
*peer
, *peer_start
;
47 WireguardIPmask
*mask
, *mask_start
= NULL
;
48 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*message
= NULL
;
53 w
= WIREGUARD(netdev
);
56 peer_start
= w
->peers
;
59 message
= sd_netlink_message_unref(message
);
61 r
= sd_genl_message_new(netdev
->manager
->genl
, SD_GENL_WIREGUARD
, WG_CMD_SET_DEVICE
, &message
);
63 return log_netdev_error_errno(netdev
, r
, "Failed to allocate generic netlink message: %m");
65 r
= sd_netlink_message_append_string(message
, WGDEVICE_A_IFNAME
, netdev
->ifname
);
67 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard interface name: %m");
69 if (peer_start
== w
->peers
) {
70 r
= sd_netlink_message_append_data(message
, WGDEVICE_A_PRIVATE_KEY
, &w
->private_key
, WG_KEY_LEN
);
72 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard private key: %m");
74 r
= sd_netlink_message_append_u16(message
, WGDEVICE_A_LISTEN_PORT
, w
->port
);
76 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard port: %m");
78 r
= sd_netlink_message_append_u32(message
, WGDEVICE_A_FWMARK
, w
->fwmark
);
80 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard fwmark: %m");
82 r
= sd_netlink_message_append_u32(message
, WGDEVICE_A_FLAGS
, w
->flags
);
84 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard flags: %m");
87 r
= sd_netlink_message_open_container(message
, WGDEVICE_A_PEERS
);
89 return log_netdev_error_errno(netdev
, r
, "Could not append wireguard peer attributes: %m");
93 LIST_FOREACH(peers
, peer
, peer_start
) {
94 r
= sd_netlink_message_open_array(message
, ++i
);
98 r
= sd_netlink_message_append_data(message
, WGPEER_A_PUBLIC_KEY
, &peer
->public_key
, sizeof(peer
->public_key
));
103 r
= sd_netlink_message_append_data(message
, WGPEER_A_PRESHARED_KEY
, &peer
->preshared_key
, WG_KEY_LEN
);
107 r
= sd_netlink_message_append_u32(message
, WGPEER_A_FLAGS
, peer
->flags
);
111 r
= sd_netlink_message_append_u32(message
, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL
, peer
->persistent_keepalive_interval
);
115 if (peer
->endpoint
.sa
.sa_family
== AF_INET
) {
116 r
= sd_netlink_message_append_data(message
, WGPEER_A_ENDPOINT
, &peer
->endpoint
.in
, sizeof(peer
->endpoint
.in
));
119 } else if (peer
->endpoint
.sa
.sa_family
== AF_INET6
) {
120 r
= sd_netlink_message_append_data(message
, WGPEER_A_ENDPOINT
, &peer
->endpoint
.in6
, sizeof(peer
->endpoint
.in6
));
125 mask_start
= peer
->ipmasks
;
128 r
= sd_netlink_message_open_container(message
, WGPEER_A_ALLOWEDIPS
);
134 LIST_FOREACH(ipmasks
, mask
, mask_start
) {
135 r
= sd_netlink_message_open_array(message
, ++j
);
139 r
= sd_netlink_message_append_u16(message
, WGALLOWEDIP_A_FAMILY
, mask
->family
);
143 if (mask
->family
== AF_INET
) {
144 r
= sd_netlink_message_append_in_addr(message
, WGALLOWEDIP_A_IPADDR
, &mask
->ip
.in
);
147 } else if (mask
->family
== AF_INET6
) {
148 r
= sd_netlink_message_append_in6_addr(message
, WGALLOWEDIP_A_IPADDR
, &mask
->ip
.in6
);
153 r
= sd_netlink_message_append_u8(message
, WGALLOWEDIP_A_CIDR_MASK
, mask
->cidr
);
157 r
= sd_netlink_message_close_container(message
);
159 return log_netdev_error_errno(netdev
, r
, "Could not add wireguard allowed ip: %m");
163 r
= sd_netlink_message_cancel_array(message
);
165 return log_netdev_error_errno(netdev
, r
, "Could not cancel wireguard allowed ip message attribute: %m");
167 r
= sd_netlink_message_close_container(message
);
169 return log_netdev_error_errno(netdev
, r
, "Could not add wireguard allowed ip: %m");
171 r
= sd_netlink_message_close_container(message
);
173 return log_netdev_error_errno(netdev
, r
, "Could not add wireguard peer: %m");
177 if (peer_start
&& !mask_start
) {
178 r
= sd_netlink_message_cancel_array(message
);
180 return log_netdev_error_errno(netdev
, r
, "Could not cancel wireguard peers: %m");
183 r
= sd_netlink_message_close_container(message
);
185 return log_netdev_error_errno(netdev
, r
, "Could not close wireguard container: %m");
187 r
= sd_netlink_send(netdev
->manager
->genl
, message
, &serial
);
189 return log_netdev_error_errno(netdev
, r
, "Could not set wireguard device: %m");
191 } while (peer
|| mask_start
);
196 static WireguardEndpoint
* wireguard_endpoint_free(WireguardEndpoint
*e
) {
199 netdev_unref(e
->netdev
);
200 e
->host
= mfree(e
->host
);
201 e
->port
= mfree(e
->port
);
205 DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint
*, wireguard_endpoint_free
);
207 static int on_resolve_retry(sd_event_source
*s
, usec_t usec
, void *userdata
) {
208 NetDev
*netdev
= userdata
;
212 w
= WIREGUARD(netdev
);
215 w
->resolve_retry_event_source
= sd_event_source_unref(w
->resolve_retry_event_source
);
217 w
->unresolved_endpoints
= TAKE_PTR(w
->failed_endpoints
);
219 resolve_endpoints(netdev
);
225 * Given the number of retries this function will return will an exponential
226 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
228 static int exponential_backoff_milliseconds(unsigned n_retries
) {
229 return (2 << MAX(n_retries
, 7U)) * 100 * USEC_PER_MSEC
;
232 static int wireguard_resolve_handler(sd_resolve_query
*q
,
234 const struct addrinfo
*ai
,
238 _cleanup_(wireguard_endpoint_freep
) WireguardEndpoint
*e
;
246 w
= WIREGUARD(netdev
);
249 w
->resolve_query
= sd_resolve_query_unref(w
->resolve_query
);
252 log_netdev_error(netdev
, "Failed to resolve host '%s:%s': %s", e
->host
, e
->port
, gai_strerror(ret
));
253 LIST_PREPEND(endpoints
, w
->failed_endpoints
, e
);
255 } else if ((ai
->ai_family
== AF_INET
&& ai
->ai_addrlen
== sizeof(struct sockaddr_in
)) ||
256 (ai
->ai_family
== AF_INET6
&& ai
->ai_addrlen
== sizeof(struct sockaddr_in6
)))
257 memcpy(&e
->peer
->endpoint
, ai
->ai_addr
, ai
->ai_addrlen
);
259 log_netdev_error(netdev
, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e
->host
, e
->port
);
261 if (w
->unresolved_endpoints
) {
262 resolve_endpoints(netdev
);
266 set_wireguard_interface(netdev
);
267 if (w
->failed_endpoints
) {
269 r
= sd_event_add_time(netdev
->manager
->event
,
270 &w
->resolve_retry_event_source
,
272 now(CLOCK_MONOTONIC
) + exponential_backoff_milliseconds(w
->n_retries
),
277 log_netdev_warning_errno(netdev
, r
, "Could not arm resolve retry handler: %m");
283 static void resolve_endpoints(NetDev
*netdev
) {
286 WireguardEndpoint
*endpoint
;
287 static const struct addrinfo hints
= {
288 .ai_family
= AF_UNSPEC
,
289 .ai_socktype
= SOCK_DGRAM
,
290 .ai_protocol
= IPPROTO_UDP
294 w
= WIREGUARD(netdev
);
297 LIST_FOREACH(endpoints
, endpoint
, w
->unresolved_endpoints
) {
298 r
= sd_resolve_getaddrinfo(netdev
->manager
->resolve
,
303 wireguard_resolve_handler
,
309 LIST_REMOVE(endpoints
, w
->unresolved_endpoints
, endpoint
);
312 log_netdev_error_errno(netdev
, r
, "Failed create resolver: %m");
316 static int netdev_wireguard_post_create(NetDev
*netdev
, Link
*link
, sd_netlink_message
*m
) {
320 w
= WIREGUARD(netdev
);
323 set_wireguard_interface(netdev
);
324 resolve_endpoints(netdev
);
328 int config_parse_wireguard_listen_port(const char *unit
,
329 const char *filename
,
332 unsigned section_line
,
345 if (!streq(rvalue
, "auto")) {
346 r
= parse_ip_port(rvalue
, &port
);
348 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid port specification, ignoring assignment: %s", rvalue
);
356 static int parse_wireguard_key(const char *unit
,
357 const char *filename
,
360 unsigned section_line
,
366 _cleanup_free_
void *key
= NULL
;
374 r
= unbase64mem(rvalue
, strlen(rvalue
), &key
, &len
);
376 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue
);
379 if (len
!= WG_KEY_LEN
) {
380 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
381 "Wireguard key is too short, ignoring assignment: %s", rvalue
);
385 memcpy(userdata
, key
, WG_KEY_LEN
);
389 int config_parse_wireguard_private_key(const char *unit
,
390 const char *filename
,
393 unsigned section_line
,
407 return parse_wireguard_key(unit
,
420 int config_parse_wireguard_preshared_key(const char *unit
,
421 const char *filename
,
424 unsigned section_line
,
439 peer
= wireguard_peer_new(w
, section_line
);
443 return parse_wireguard_key(unit
,
452 peer
->preshared_key
);
455 int config_parse_wireguard_public_key(const char *unit
,
456 const char *filename
,
459 unsigned section_line
,
474 peer
= wireguard_peer_new(w
, section_line
);
478 return parse_wireguard_key(unit
,
490 int config_parse_wireguard_allowed_ips(const char *unit
,
491 const char *filename
,
494 unsigned section_line
,
500 union in_addr_union addr
;
501 unsigned char prefixlen
;
505 WireguardIPmask
*ipmask
;
512 peer
= wireguard_peer_new(w
, section_line
);
517 _cleanup_free_
char *word
= NULL
;
519 r
= extract_first_word(&rvalue
, &word
, "," WHITESPACE
, 0);
525 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split allowed ips \"%s\" option: %m", rvalue
);
529 r
= in_addr_prefix_from_string_auto(word
, &family
, &addr
, &prefixlen
);
531 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Network address is invalid, ignoring assignment: %s", word
);
535 ipmask
= new0(WireguardIPmask
, 1);
538 ipmask
->family
= family
;
539 ipmask
->ip
.in6
= addr
.in6
;
540 ipmask
->cidr
= prefixlen
;
542 LIST_PREPEND(ipmasks
, peer
->ipmasks
, ipmask
);
548 int config_parse_wireguard_endpoint(const char *unit
,
549 const char *filename
,
552 unsigned section_line
,
561 const char *begin
, *end
= NULL
;
562 _cleanup_free_
char *host
= NULL
, *port
= NULL
;
563 _cleanup_(wireguard_endpoint_freep
) WireguardEndpoint
*endpoint
= NULL
;
572 peer
= wireguard_peer_new(w
, section_line
);
576 endpoint
= new0(WireguardEndpoint
, 1);
580 if (rvalue
[0] == '[') {
582 end
= strchr(rvalue
, ']');
584 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue
);
589 if (*end
!= ':' || !*(end
+ 1)) {
590 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unable to find port of endpoint: %s", rvalue
);
596 end
= strrchr(rvalue
, ':');
597 if (!end
|| !*(end
+ 1)) {
598 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unable to find port of endpoint: %s", rvalue
);
605 host
= strndup(begin
, len
);
613 endpoint
->peer
= TAKE_PTR(peer
);
614 endpoint
->host
= TAKE_PTR(host
);
615 endpoint
->port
= TAKE_PTR(port
);
616 endpoint
->netdev
= netdev_ref(data
);
617 LIST_PREPEND(endpoints
, w
->unresolved_endpoints
, endpoint
);
623 int config_parse_wireguard_keepalive(const char *unit
,
624 const char *filename
,
627 unsigned section_line
,
634 uint16_t keepalive
= 0;
645 peer
= wireguard_peer_new(w
, section_line
);
649 if (streq(rvalue
, "off"))
652 r
= safe_atou16(rvalue
, &keepalive
);
654 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue
);
657 peer
->persistent_keepalive_interval
= keepalive
;
661 static void wireguard_init(NetDev
*netdev
) {
666 w
= WIREGUARD(netdev
);
670 w
->flags
= WGDEVICE_F_REPLACE_PEERS
;
673 static void wireguard_done(NetDev
*netdev
) {
676 WireguardIPmask
*mask
;
679 w
= WIREGUARD(netdev
);
680 assert(!w
->unresolved_endpoints
);
681 w
->resolve_retry_event_source
= sd_event_source_unref(w
->resolve_retry_event_source
);
683 while ((peer
= w
->peers
)) {
684 LIST_REMOVE(peers
, w
->peers
, peer
);
685 while ((mask
= peer
->ipmasks
)) {
686 LIST_REMOVE(ipmasks
, peer
->ipmasks
, mask
);
693 const NetDevVTable wireguard_vtable
= {
694 .object_size
= sizeof(Wireguard
),
695 .sections
= "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
696 .post_create
= netdev_wireguard_post_create
,
697 .init
= wireguard_init
,
698 .done
= wireguard_done
,
699 .create_type
= NETDEV_CREATE_INDEPENDENT
,