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