]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/netdev/wireguard.c
network: use destroy callback to unref netdev attached to event source
[thirdparty/systemd.git] / src / network / netdev / wireguard.c
CommitLineData
d506b89c 1/* SPDX-License-Identifier: LGPL-2.1+ */
e5719363 2/***
96b2fb93 3 Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
e5719363
JT
4***/
5
6#include <sys/ioctl.h>
7#include <net/if.h>
8
8173d1d0
YW
9#include "sd-resolve.h"
10
e5719363
JT
11#include "alloc-util.h"
12#include "parse-util.h"
13#include "fd-util.h"
14#include "strv.h"
15#include "hexdecoct.h"
16#include "string-util.h"
17#include "wireguard.h"
18#include "networkd-link.h"
19#include "networkd-util.h"
20#include "networkd-manager.h"
21#include "wireguard-netlink.h"
22
23static void resolve_endpoints(NetDev *netdev);
24
25static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) {
26 WireguardPeer *peer;
27
28 assert(w);
29
30 if (w->last_peer_section == section && w->peers)
31 return w->peers;
32
33 peer = new0(WireguardPeer, 1);
34 if (!peer)
35 return NULL;
36 peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS;
37
38 LIST_PREPEND(peers, w->peers, peer);
39 w->last_peer_section = section;
40
41 return peer;
42}
43
44static int set_wireguard_interface(NetDev *netdev) {
45 int r;
14cb109d 46 unsigned i, j;
e5719363
JT
47 WireguardPeer *peer, *peer_start;
48 WireguardIPmask *mask, *mask_start = NULL;
49 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
50 Wireguard *w;
51 uint32_t serial;
52
53 assert(netdev);
54 w = WIREGUARD(netdev);
55 assert(w);
56
57 peer_start = w->peers;
58
59 do {
60 message = sd_netlink_message_unref(message);
61
62 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
63 if (r < 0)
64 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
65
66 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
67 if (r < 0)
68 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
69
70 if (peer_start == w->peers) {
71 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
72 if (r < 0)
73 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
74
75 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
76 if (r < 0)
77 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
78
79 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
80 if (r < 0)
81 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
82
83 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
84 if (r < 0)
85 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
86 }
87
88 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
89 if (r < 0)
90 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
91
92 i = 0;
93
94 LIST_FOREACH(peers, peer, peer_start) {
95 r = sd_netlink_message_open_array(message, ++i);
96 if (r < 0)
97 break;
98
99 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
100 if (r < 0)
101 break;
102
103 if (!mask_start) {
104 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
105 if (r < 0)
106 break;
107
108 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
109 if (r < 0)
110 break;
111
7d0b26a0 112 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
e5719363
JT
113 if (r < 0)
114 break;
115
116 if (peer->endpoint.sa.sa_family == AF_INET) {
117 r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in));
118 if (r < 0)
119 break;
120 } else if (peer->endpoint.sa.sa_family == AF_INET6) {
121 r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6));
122 if (r < 0)
123 break;
124 }
125
126 mask_start = peer->ipmasks;
127 }
128
129 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
130 if (r < 0) {
131 mask_start = NULL;
132 break;
133 }
134 j = 0;
135 LIST_FOREACH(ipmasks, mask, mask_start) {
136 r = sd_netlink_message_open_array(message, ++j);
137 if (r < 0)
138 break;
139
140 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
141 if (r < 0)
142 break;
143
144 if (mask->family == AF_INET) {
145 r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in);
146 if (r < 0)
147 break;
148 } else if (mask->family == AF_INET6) {
149 r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6);
150 if (r < 0)
151 break;
152 }
153
154 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
155 if (r < 0)
156 break;
157
158 r = sd_netlink_message_close_container(message);
159 if (r < 0)
160 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
161 }
162 mask_start = mask;
163 if (mask_start) {
164 r = sd_netlink_message_cancel_array(message);
165 if (r < 0)
166 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
167 }
168 r = sd_netlink_message_close_container(message);
169 if (r < 0)
170 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
171
172 r = sd_netlink_message_close_container(message);
173 if (r < 0)
174 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
175 }
176
177 peer_start = peer;
178 if (peer_start && !mask_start) {
179 r = sd_netlink_message_cancel_array(message);
180 if (r < 0)
181 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
182 }
183
184 r = sd_netlink_message_close_container(message);
185 if (r < 0)
186 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
187
188 r = sd_netlink_send(netdev->manager->genl, message, &serial);
189 if (r < 0)
190 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
191
192 } while (peer || mask_start);
193
194 return 0;
195}
196
197static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) {
198 if (!e)
199 return NULL;
e5719363
JT
200 e->host = mfree(e->host);
201 e->port = mfree(e->port);
202 return mfree(e);
203}
204
8173d1d0
YW
205static void wireguard_endpoint_destroy_callback(void *userdata) {
206 WireguardEndpoint *e = userdata;
207
208 assert(e);
209 assert(e->netdev);
210
211 netdev_unref(e->netdev);
212 wireguard_endpoint_free(e);
213}
214
e5719363
JT
215DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free);
216
217static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
218 NetDev *netdev = userdata;
219 Wireguard *w;
220
221 assert(netdev);
222 w = WIREGUARD(netdev);
223 assert(w);
224
56ba90c2
YW
225 if (!netdev->manager)
226 /* The netdev is detached. */
227 return 0;
e5719363 228
56ba90c2 229 assert(!w->unresolved_endpoints);
ae2a15bc 230 w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints);
e5719363
JT
231
232 resolve_endpoints(netdev);
233
234 return 0;
235}
236
237/*
238 * Given the number of retries this function will return will an exponential
239 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
240 */
241static int exponential_backoff_milliseconds(unsigned n_retries) {
242 return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC;
243}
244
245static int wireguard_resolve_handler(sd_resolve_query *q,
246 int ret,
247 const struct addrinfo *ai,
248 void *userdata) {
8173d1d0
YW
249 _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL;
250 NetDev *netdev = NULL;
251 WireguardEndpoint *e;
e5719363 252 Wireguard *w;
e5719363
JT
253 int r;
254
255 assert(userdata);
256 e = userdata;
257 netdev = e->netdev;
258
259 assert(netdev);
260 w = WIREGUARD(netdev);
261 assert(w);
262
8173d1d0
YW
263 if (!netdev->manager)
264 /* The netdev is detached. */
265 return 0;
e5719363
JT
266
267 if (ret != 0) {
268 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret));
269 LIST_PREPEND(endpoints, w->failed_endpoints, e);
8173d1d0
YW
270 (void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */
271 netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */
e5719363 272 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
8173d1d0 273 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
e5719363
JT
274 memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen);
275 else
276 log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port);
277
278 if (w->unresolved_endpoints) {
279 resolve_endpoints(netdev);
280 return 0;
281 }
282
283 set_wireguard_interface(netdev);
284 if (w->failed_endpoints) {
56ba90c2
YW
285 _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
286
e5719363
JT
287 w->n_retries++;
288 r = sd_event_add_time(netdev->manager->event,
56ba90c2 289 &s,
e5719363
JT
290 CLOCK_MONOTONIC,
291 now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries),
292 0,
293 on_resolve_retry,
294 netdev);
56ba90c2 295 if (r < 0) {
e5719363 296 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
56ba90c2
YW
297 return 0;
298 }
299
300 r = sd_event_source_set_destroy_callback(s, netdev_destroy_callback);
301 if (r < 0) {
302 log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m");
303 return 0;
304 }
305
306 (void) sd_event_source_set_floating(s, true);
307 netdev_ref(netdev);
e5719363
JT
308 }
309
310 return 0;
311}
312
313static void resolve_endpoints(NetDev *netdev) {
e5719363
JT
314 static const struct addrinfo hints = {
315 .ai_family = AF_UNSPEC,
316 .ai_socktype = SOCK_DGRAM,
317 .ai_protocol = IPPROTO_UDP
318 };
8173d1d0
YW
319 WireguardEndpoint *endpoint;
320 Wireguard *w;
321 int r = 0;
e5719363
JT
322
323 assert(netdev);
324 w = WIREGUARD(netdev);
325 assert(w);
326
327 LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) {
8173d1d0
YW
328 _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL;
329
e5719363 330 r = sd_resolve_getaddrinfo(netdev->manager->resolve,
8173d1d0 331 &q,
e5719363
JT
332 endpoint->host,
333 endpoint->port,
334 &hints,
335 wireguard_resolve_handler,
336 endpoint);
337
338 if (r == -ENOBUFS)
339 break;
8173d1d0
YW
340 if (r < 0) {
341 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
342 continue;
343 }
e5719363 344
8173d1d0
YW
345 r = sd_resolve_query_set_destroy_callback(q, wireguard_endpoint_destroy_callback);
346 if (r < 0) {
347 log_netdev_error_errno(netdev, r, "Failed to set destroy callback to resolving query: %m");
348 continue;
349 }
e5719363 350
8173d1d0
YW
351 (void) sd_resolve_query_set_floating(q, true);
352
353 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
354 netdev_ref(netdev);
355
356 LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint);
e5719363
JT
357 }
358}
359
e5719363
JT
360static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
361 Wireguard *w;
362
363 assert(netdev);
364 w = WIREGUARD(netdev);
365 assert(w);
366
367 set_wireguard_interface(netdev);
368 resolve_endpoints(netdev);
369 return 0;
370}
371
372int config_parse_wireguard_listen_port(const char *unit,
373 const char *filename,
374 unsigned line,
375 const char *section,
376 unsigned section_line,
377 const char *lvalue,
378 int ltype,
379 const char *rvalue,
380 void *data,
381 void *userdata) {
382 uint16_t *s = data;
383 uint16_t port = 0;
384 int r;
385
386 assert(rvalue);
387 assert(data);
388
389 if (!streq(rvalue, "auto")) {
390 r = parse_ip_port(rvalue, &port);
391 if (r < 0)
392 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue);
393 }
394
395 *s = port;
396
397 return 0;
398}
399
400static int parse_wireguard_key(const char *unit,
401 const char *filename,
402 unsigned line,
403 const char *section,
404 unsigned section_line,
405 const char *lvalue,
406 int ltype,
407 const char *rvalue,
408 void *data,
409 void *userdata) {
410 _cleanup_free_ void *key = NULL;
411 size_t len;
412 int r;
413
414 assert(filename);
415 assert(rvalue);
416 assert(userdata);
417
418 r = unbase64mem(rvalue, strlen(rvalue), &key, &len);
419 if (r < 0) {
420 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue);
421 return 0;
422 }
423 if (len != WG_KEY_LEN) {
424 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
425 "Wireguard key is too short, ignoring assignment: %s", rvalue);
426 return 0;
427 }
428
429 memcpy(userdata, key, WG_KEY_LEN);
430 return true;
431}
432
433int config_parse_wireguard_private_key(const char *unit,
434 const char *filename,
435 unsigned line,
436 const char *section,
437 unsigned section_line,
438 const char *lvalue,
439 int ltype,
440 const char *rvalue,
441 void *data,
442 void *userdata) {
443 Wireguard *w;
444
445 assert(data);
446
447 w = WIREGUARD(data);
448
449 assert(w);
450
451 return parse_wireguard_key(unit,
452 filename,
453 line,
454 section,
455 section_line,
456 lvalue,
457 ltype,
458 rvalue,
459 data,
460 &w->private_key);
461
462}
463
464int config_parse_wireguard_preshared_key(const char *unit,
465 const char *filename,
466 unsigned line,
467 const char *section,
468 unsigned section_line,
469 const char *lvalue,
470 int ltype,
471 const char *rvalue,
472 void *data,
473 void *userdata) {
474 Wireguard *w;
475 WireguardPeer *peer;
476
477 assert(data);
478
479 w = WIREGUARD(data);
480
481 assert(w);
482
483 peer = wireguard_peer_new(w, section_line);
484 if (!peer)
485 return log_oom();
486
487 return parse_wireguard_key(unit,
488 filename,
489 line,
490 section,
491 section_line,
492 lvalue,
493 ltype,
494 rvalue,
495 data,
496 peer->preshared_key);
497}
498
e5719363
JT
499int config_parse_wireguard_public_key(const char *unit,
500 const char *filename,
501 unsigned line,
502 const char *section,
503 unsigned section_line,
504 const char *lvalue,
505 int ltype,
506 const char *rvalue,
507 void *data,
508 void *userdata) {
509 Wireguard *w;
510 WireguardPeer *peer;
511
512 assert(data);
513
514 w = WIREGUARD(data);
515
516 assert(w);
517
518 peer = wireguard_peer_new(w, section_line);
519 if (!peer)
520 return log_oom();
521
522 return parse_wireguard_key(unit,
523 filename,
524 line,
525 section,
526 section_line,
527 lvalue,
528 ltype,
529 rvalue,
530 data,
531 peer->public_key);
532}
533
534int config_parse_wireguard_allowed_ips(const char *unit,
535 const char *filename,
536 unsigned line,
537 const char *section,
538 unsigned section_line,
539 const char *lvalue,
540 int ltype,
541 const char *rvalue,
542 void *data,
543 void *userdata) {
544 union in_addr_union addr;
545 unsigned char prefixlen;
546 int r, family;
547 Wireguard *w;
548 WireguardPeer *peer;
549 WireguardIPmask *ipmask;
550
551 assert(rvalue);
552 assert(data);
553
554 w = WIREGUARD(data);
555
556 peer = wireguard_peer_new(w, section_line);
557 if (!peer)
558 return log_oom();
559
560 for (;;) {
561 _cleanup_free_ char *word = NULL;
562
563 r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
564 if (r == 0)
565 break;
566 if (r == -ENOMEM)
567 return log_oom();
568 if (r < 0) {
569 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue);
570 break;
571 }
572
573 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
574 if (r < 0) {
575 log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word);
576 return 0;
577 }
578
579 ipmask = new0(WireguardIPmask, 1);
580 if (!ipmask)
581 return log_oom();
582 ipmask->family = family;
583 ipmask->ip.in6 = addr.in6;
584 ipmask->cidr = prefixlen;
585
586 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
587 }
588
589 return 0;
590}
591
592int config_parse_wireguard_endpoint(const char *unit,
593 const char *filename,
594 unsigned line,
595 const char *section,
596 unsigned section_line,
597 const char *lvalue,
598 int ltype,
599 const char *rvalue,
600 void *data,
601 void *userdata) {
602 Wireguard *w;
603 WireguardPeer *peer;
604 size_t len;
605 const char *begin, *end = NULL;
606 _cleanup_free_ char *host = NULL, *port = NULL;
607 _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL;
608
609 assert(data);
610 assert(rvalue);
611
612 w = WIREGUARD(data);
613
614 assert(w);
615
616 peer = wireguard_peer_new(w, section_line);
617 if (!peer)
618 return log_oom();
619
620 endpoint = new0(WireguardEndpoint, 1);
621 if (!endpoint)
622 return log_oom();
623
624 if (rvalue[0] == '[') {
625 begin = &rvalue[1];
626 end = strchr(rvalue, ']');
627 if (!end) {
628 log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue);
629 return 0;
630 }
631 len = end - begin;
632 ++end;
633 if (*end != ':' || !*(end + 1)) {
634 log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
635 return 0;
636 }
637 ++end;
638 } else {
639 begin = rvalue;
640 end = strrchr(rvalue, ':');
641 if (!end || !*(end + 1)) {
642 log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
643 return 0;
644 }
645 len = end - begin;
646 ++end;
647 }
648
649 host = strndup(begin, len);
650 if (!host)
651 return log_oom();
652
653 port = strdup(end);
654 if (!port)
655 return log_oom();
656
1cc6c93a
YW
657 endpoint->peer = TAKE_PTR(peer);
658 endpoint->host = TAKE_PTR(host);
659 endpoint->port = TAKE_PTR(port);
8173d1d0
YW
660 endpoint->netdev = data;
661 LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint));
e5719363
JT
662
663 return 0;
664}
665
666int config_parse_wireguard_keepalive(const char *unit,
667 const char *filename,
668 unsigned line,
669 const char *section,
670 unsigned section_line,
671 const char *lvalue,
672 int ltype,
673 const char *rvalue,
674 void *data,
675 void *userdata) {
676 int r;
677 uint16_t keepalive = 0;
678 Wireguard *w;
679 WireguardPeer *peer;
680
681 assert(rvalue);
682 assert(data);
683
684 w = WIREGUARD(data);
685
686 assert(w);
687
688 peer = wireguard_peer_new(w, section_line);
689 if (!peer)
690 return log_oom();
691
692 if (streq(rvalue, "off"))
693 keepalive = 0;
694 else {
695 r = safe_atou16(rvalue, &keepalive);
696 if (r < 0)
697 log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue);
698 }
699
700 peer->persistent_keepalive_interval = keepalive;
701 return 0;
702}
703
704static void wireguard_init(NetDev *netdev) {
705 Wireguard *w;
706
707 assert(netdev);
708
709 w = WIREGUARD(netdev);
710
711 assert(w);
712
713 w->flags = WGDEVICE_F_REPLACE_PEERS;
714}
715
716static void wireguard_done(NetDev *netdev) {
717 Wireguard *w;
718 WireguardPeer *peer;
719 WireguardIPmask *mask;
720
721 assert(netdev);
722 w = WIREGUARD(netdev);
723 assert(!w->unresolved_endpoints);
e5719363
JT
724
725 while ((peer = w->peers)) {
726 LIST_REMOVE(peers, w->peers, peer);
727 while ((mask = peer->ipmasks)) {
728 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
729 free(mask);
730 }
731 free(peer);
732 }
733}
734
735const NetDevVTable wireguard_vtable = {
736 .object_size = sizeof(Wireguard),
737 .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
738 .post_create = netdev_wireguard_post_create,
739 .init = wireguard_init,
740 .done = wireguard_done,
741 .create_type = NETDEV_CREATE_INDEPENDENT,
742};