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