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