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