]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/netdev/wireguard.c
util: split out memcmp()/memset() related calls into memory-util.[ch]
[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 11#include "alloc-util.h"
85c987a8 12#include "event-util.h"
e5719363 13#include "fd-util.h"
76df7779 14#include "fileio.h"
e5719363 15#include "hexdecoct.h"
0a970718 16#include "memory-util.h"
43409486 17#include "netlink-util.h"
e5719363 18#include "networkd-link.h"
e5719363 19#include "networkd-manager.h"
a4c9ae40
YW
20#include "networkd-util.h"
21#include "parse-util.h"
76df7779 22#include "path-util.h"
1061dab1 23#include "resolve-private.h"
a4c9ae40
YW
24#include "string-util.h"
25#include "strv.h"
e5719363 26#include "wireguard-netlink.h"
0a970718 27#include "wireguard.h"
e5719363
JT
28
29static void resolve_endpoints(NetDev *netdev);
30
f1368a33
YW
31static void wireguard_peer_free(WireguardPeer *peer) {
32 WireguardIPmask *mask;
33
34 if (!peer)
35 return;
36
37 if (peer->wireguard) {
38 LIST_REMOVE(peers, peer->wireguard->peers, peer);
39
40 set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer);
41 set_remove(peer->wireguard->peers_with_failed_endpoint, peer);
42
43 if (peer->section)
44 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
45 }
46
47 network_config_section_free(peer->section);
48
49 while ((mask = peer->ipmasks)) {
50 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
51 free(mask);
52 }
53
54 free(peer->endpoint_host);
55 free(peer->endpoint_port);
56
57 free(peer);
58}
59
60DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
61
62static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
63 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
64 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
65 int r;
e5719363
JT
66
67 assert(w);
f1368a33
YW
68 assert(ret);
69 assert(filename);
70 assert(section_line > 0);
e5719363 71
f1368a33
YW
72 r = network_config_section_new(filename, section_line, &n);
73 if (r < 0)
74 return r;
75
76 peer = hashmap_get(w->peers_by_section, n);
77 if (peer) {
78 *ret = TAKE_PTR(peer);
79 return 0;
80 }
e5719363 81
fc721553 82 peer = new(WireguardPeer, 1);
e5719363 83 if (!peer)
f1368a33 84 return -ENOMEM;
fc721553
YW
85
86 *peer = (WireguardPeer) {
87 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
f1368a33
YW
88 .wireguard = w,
89 .section = TAKE_PTR(n),
fc721553 90 };
e5719363
JT
91
92 LIST_PREPEND(peers, w->peers, peer);
e5719363 93
f1368a33
YW
94 r = hashmap_ensure_allocated(&w->peers_by_section, &network_config_hash_ops);
95 if (r < 0)
96 return r;
97
98 r = hashmap_put(w->peers_by_section, peer->section, peer);
99 if (r < 0)
100 return r;
101
102 *ret = TAKE_PTR(peer);
103 return 0;
e5719363
JT
104}
105
e1f717d4 106static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
e5719363 107 int r;
e1f717d4
YW
108
109 assert(message);
110 assert(mask);
111 assert(index > 0);
112
113 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
114
115 r = sd_netlink_message_open_array(message, index);
116 if (r < 0)
117 return 0;
118
119 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
120 if (r < 0)
121 goto cancel;
122
43409486 123 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
e1f717d4
YW
124 if (r < 0)
125 goto cancel;
126
127 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
128 if (r < 0)
129 goto cancel;
130
131 r = sd_netlink_message_close_container(message);
132 if (r < 0)
133 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
134
135 return 1;
136
137cancel:
138 r = sd_netlink_message_cancel_array(message);
139 if (r < 0)
140 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
141
142 return 0;
143}
144
145static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
146 WireguardIPmask *mask, *start;
147 uint16_t j = 0;
148 int r;
149
150 assert(message);
151 assert(peer);
152 assert(index > 0);
153 assert(mask_start);
154
155 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
156
157 start = *mask_start ?: peer->ipmasks;
158
159 r = sd_netlink_message_open_array(message, index);
160 if (r < 0)
161 return 0;
162
163 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
164 if (r < 0)
165 goto cancel;
166
2301c54f 167 if (!*mask_start) {
e1f717d4
YW
168 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
169 if (r < 0)
170 goto cancel;
171
172 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
173 if (r < 0)
174 goto cancel;
175
176 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
177 if (r < 0)
178 goto cancel;
179
43409486
YW
180 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
181 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
182 if (r < 0)
183 goto cancel;
184 }
e1f717d4
YW
185 }
186
187 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
188 if (r < 0)
189 goto cancel;
190
191 LIST_FOREACH(ipmasks, mask, start) {
192 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
193 if (r < 0)
194 return r;
195 if (r == 0)
196 break;
197 }
198
199 r = sd_netlink_message_close_container(message);
200 if (r < 0)
201 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
202
203 r = sd_netlink_message_close_container(message);
204 if (r < 0)
205 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
206
207 *mask_start = mask; /* Start next cycle from this mask. */
208 return !mask;
209
210cancel:
211 r = sd_netlink_message_cancel_array(message);
212 if (r < 0)
213 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
214
215 return 0;
216}
217
218static int wireguard_set_interface(NetDev *netdev) {
e5719363 219 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
e1f717d4
YW
220 WireguardIPmask *mask_start = NULL;
221 WireguardPeer *peer, *peer_start;
e5719363 222 uint32_t serial;
e1f717d4
YW
223 Wireguard *w;
224 int r;
e5719363
JT
225
226 assert(netdev);
227 w = WIREGUARD(netdev);
228 assert(w);
229
e1f717d4
YW
230 for (peer_start = w->peers; peer_start; ) {
231 uint16_t i = 0;
e5719363 232
e5719363
JT
233 message = sd_netlink_message_unref(message);
234
235 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
236 if (r < 0)
237 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
238
239 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
240 if (r < 0)
241 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
242
243 if (peer_start == w->peers) {
244 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
245 if (r < 0)
246 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
247
248 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
249 if (r < 0)
250 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
251
252 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
253 if (r < 0)
254 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
255
256 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
257 if (r < 0)
258 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
259 }
260
261 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
262 if (r < 0)
263 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
264
e5719363 265 LIST_FOREACH(peers, peer, peer_start) {
e1f717d4 266 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
e5719363 267 if (r < 0)
e1f717d4
YW
268 return r;
269 if (r == 0)
e5719363 270 break;
e5719363 271 }
e1f717d4 272 peer_start = peer; /* Start next cycle from this peer. */
e5719363
JT
273
274 r = sd_netlink_message_close_container(message);
275 if (r < 0)
276 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
277
278 r = sd_netlink_send(netdev->manager->genl, message, &serial);
279 if (r < 0)
280 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
e1f717d4 281 }
e5719363
JT
282
283 return 0;
284}
285
f1368a33
YW
286static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
287 NetDev *netdev;
e5719363 288
f1368a33
YW
289 assert(peer);
290 assert(peer->wireguard);
8173d1d0 291
f1368a33 292 netdev = NETDEV(peer->wireguard);
8173d1d0 293
f1368a33
YW
294 if (section_is_invalid(peer->section))
295 wireguard_peer_free(peer);
296
297 netdev_unref(netdev);
298}
e5719363
JT
299
300static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
301 NetDev *netdev = userdata;
302 Wireguard *w;
303
304 assert(netdev);
305 w = WIREGUARD(netdev);
306 assert(w);
307
9e2bbf99 308 if (!netdev_is_managed(netdev))
56ba90c2 309 return 0;
e5719363 310
f1368a33
YW
311 assert(set_isempty(w->peers_with_unresolved_endpoint));
312
313 SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
e5719363
JT
314
315 resolve_endpoints(netdev);
316
317 return 0;
318}
319
320/*
321 * Given the number of retries this function will return will an exponential
322 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
323 */
324static int exponential_backoff_milliseconds(unsigned n_retries) {
325 return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC;
326}
327
328static int wireguard_resolve_handler(sd_resolve_query *q,
329 int ret,
330 const struct addrinfo *ai,
f1368a33 331 WireguardPeer *peer) {
1061dab1 332 NetDev *netdev;
e5719363 333 Wireguard *w;
e5719363
JT
334 int r;
335
f1368a33
YW
336 assert(peer);
337 assert(peer->wireguard);
e5719363 338
f1368a33
YW
339 w = peer->wireguard;
340 netdev = NETDEV(w);
e5719363 341
9e2bbf99 342 if (!netdev_is_managed(netdev))
8173d1d0 343 return 0;
e5719363
JT
344
345 if (ret != 0) {
f1368a33
YW
346 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
347
348 r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL);
349 if (r < 0) {
350 log_oom();
351 peer->section->invalid = true;
352 goto resolve_next;
353 }
354
355 r = set_put(w->peers_with_failed_endpoint, peer);
356 if (r < 0) {
357 log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
358 peer->section->invalid = true;
359 goto resolve_next;
360 }
361
e5719363 362 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
8173d1d0 363 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
f1368a33 364 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
e5719363 365 else
f1368a33
YW
366 log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
367 peer->endpoint_host, peer->endpoint_port);
e5719363 368
f1368a33
YW
369resolve_next:
370 if (!set_isempty(w->peers_with_unresolved_endpoint)) {
e5719363
JT
371 resolve_endpoints(netdev);
372 return 0;
373 }
374
e1f717d4 375 (void) wireguard_set_interface(netdev);
f1368a33
YW
376
377 if (!set_isempty(w->peers_with_failed_endpoint)) {
85c987a8 378 usec_t usec;
56ba90c2 379
e5719363 380 w->n_retries++;
85c987a8
YW
381 usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
382 r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
383 CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
384 0, "wireguard-resolve-retry", true);
56ba90c2 385 if (r < 0) {
e5719363 386 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
56ba90c2
YW
387 return 0;
388 }
e5719363
JT
389 }
390
391 return 0;
392}
393
394static void resolve_endpoints(NetDev *netdev) {
e5719363
JT
395 static const struct addrinfo hints = {
396 .ai_family = AF_UNSPEC,
397 .ai_socktype = SOCK_DGRAM,
398 .ai_protocol = IPPROTO_UDP
399 };
f1368a33 400 WireguardPeer *peer;
8173d1d0 401 Wireguard *w;
f1368a33 402 Iterator i;
8173d1d0 403 int r = 0;
e5719363
JT
404
405 assert(netdev);
406 w = WIREGUARD(netdev);
407 assert(w);
408
f1368a33 409 SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) {
1061dab1
YW
410 r = resolve_getaddrinfo(netdev->manager->resolve,
411 NULL,
f1368a33
YW
412 peer->endpoint_host,
413 peer->endpoint_port,
1061dab1
YW
414 &hints,
415 wireguard_resolve_handler,
f1368a33
YW
416 wireguard_peer_destroy_callback,
417 peer);
e5719363
JT
418 if (r == -ENOBUFS)
419 break;
8173d1d0
YW
420 if (r < 0) {
421 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
422 continue;
423 }
e5719363 424
8173d1d0
YW
425 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
426 netdev_ref(netdev);
427
f1368a33 428 (void) set_remove(w->peers_with_unresolved_endpoint, peer);
e5719363
JT
429 }
430}
431
e5719363
JT
432static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
433 Wireguard *w;
434
435 assert(netdev);
436 w = WIREGUARD(netdev);
437 assert(w);
438
e1f717d4 439 (void) wireguard_set_interface(netdev);
e5719363
JT
440 resolve_endpoints(netdev);
441 return 0;
442}
443
444int config_parse_wireguard_listen_port(const char *unit,
445 const char *filename,
446 unsigned line,
447 const char *section,
448 unsigned section_line,
449 const char *lvalue,
450 int ltype,
451 const char *rvalue,
452 void *data,
453 void *userdata) {
454 uint16_t *s = data;
455 uint16_t port = 0;
456 int r;
457
458 assert(rvalue);
459 assert(data);
460
461 if (!streq(rvalue, "auto")) {
f1368a33
YW
462 r = parse_ip_port(rvalue, s);
463 if (r < 0) {
464 log_syntax(unit, LOG_ERR, filename, line, r,
465 "Invalid port specification, ignoring assignment: %s", rvalue);
466 return 0;
467 }
e5719363
JT
468 }
469
470 *s = port;
471
472 return 0;
473}
474
fedcb4c3
YW
475static int wireguard_decode_key_and_warn(
476 const char *rvalue,
477 uint8_t *ret,
478 const char *unit,
479 const char *filename,
480 unsigned line,
481 const char *lvalue) {
e5719363
JT
482 _cleanup_free_ void *key = NULL;
483 size_t len;
484 int r;
485
e5719363 486 assert(rvalue);
fedcb4c3
YW
487 assert(ret);
488 assert(filename);
489 assert(lvalue);
490
491 if (isempty(rvalue)) {
492 memzero(ret, WG_KEY_LEN);
493 return 0;
494 }
e5719363
JT
495
496 r = unbase64mem(rvalue, strlen(rvalue), &key, &len);
497 if (r < 0) {
f1368a33 498 log_syntax(unit, LOG_ERR, filename, line, r,
583eb170 499 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
e5719363
JT
500 return 0;
501 }
502 if (len != WG_KEY_LEN) {
583eb170
YW
503 log_syntax(unit, LOG_ERR, filename, line, 0,
504 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
505 lvalue, len);
e5719363
JT
506 return 0;
507 }
508
fedcb4c3 509 memcpy(ret, key, WG_KEY_LEN);
e5719363
JT
510 return true;
511}
512
513int config_parse_wireguard_private_key(const char *unit,
514 const char *filename,
515 unsigned line,
516 const char *section,
517 unsigned section_line,
518 const char *lvalue,
519 int ltype,
520 const char *rvalue,
521 void *data,
522 void *userdata) {
523 Wireguard *w;
524
525 assert(data);
526
527 w = WIREGUARD(data);
528
529 assert(w);
530
fedcb4c3 531 return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
e5719363
JT
532
533}
534
76df7779
YW
535int config_parse_wireguard_private_key_file(
536 const char *unit,
537 const char *filename,
538 unsigned line,
539 const char *section,
540 unsigned section_line,
541 const char *lvalue,
542 int ltype,
543 const char *rvalue,
544 void *data,
545 void *userdata) {
546
547 _cleanup_free_ char *path = NULL;
548 Wireguard *w;
549
550 assert(data);
551 w = WIREGUARD(data);
552 assert(w);
553
554 if (isempty(rvalue)) {
555 w->private_key_file = mfree(w->private_key_file);
556 return 0;
557 }
558
559 path = strdup(rvalue);
560 if (!path)
561 return log_oom();
562
563 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
564 return 0;
565
566 return free_and_replace(w->private_key_file, path);
567}
568
e5719363
JT
569int config_parse_wireguard_preshared_key(const char *unit,
570 const char *filename,
571 unsigned line,
572 const char *section,
573 unsigned section_line,
574 const char *lvalue,
575 int ltype,
576 const char *rvalue,
577 void *data,
578 void *userdata) {
f1368a33
YW
579
580 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363 581 Wireguard *w;
f1368a33 582 int r;
e5719363
JT
583
584 assert(data);
585
586 w = WIREGUARD(data);
587
588 assert(w);
589
f1368a33
YW
590 r = wireguard_peer_new_static(w, filename, section_line, &peer);
591 if (r < 0)
592 return r;
e5719363 593
fedcb4c3 594 r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
f1368a33
YW
595 if (r < 0)
596 return r;
597
598 TAKE_PTR(peer);
599 return 0;
e5719363
JT
600}
601
e5719363
JT
602int config_parse_wireguard_public_key(const char *unit,
603 const char *filename,
604 unsigned line,
605 const char *section,
606 unsigned section_line,
607 const char *lvalue,
608 int ltype,
609 const char *rvalue,
610 void *data,
611 void *userdata) {
f1368a33
YW
612
613 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363 614 Wireguard *w;
f1368a33 615 int r;
e5719363
JT
616
617 assert(data);
618
619 w = WIREGUARD(data);
620
621 assert(w);
622
f1368a33
YW
623 r = wireguard_peer_new_static(w, filename, section_line, &peer);
624 if (r < 0)
625 return r;
626
fedcb4c3 627 r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
f1368a33
YW
628 if (r < 0)
629 return r;
e5719363 630
f1368a33
YW
631 TAKE_PTR(peer);
632 return 0;
e5719363
JT
633}
634
635int config_parse_wireguard_allowed_ips(const char *unit,
636 const char *filename,
637 unsigned line,
638 const char *section,
639 unsigned section_line,
640 const char *lvalue,
641 int ltype,
642 const char *rvalue,
643 void *data,
644 void *userdata) {
f1368a33
YW
645
646 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
647 union in_addr_union addr;
648 unsigned char prefixlen;
649 int r, family;
650 Wireguard *w;
e5719363
JT
651 WireguardIPmask *ipmask;
652
653 assert(rvalue);
654 assert(data);
655
656 w = WIREGUARD(data);
657
f1368a33
YW
658 assert(w);
659
660 r = wireguard_peer_new_static(w, filename, section_line, &peer);
661 if (r < 0)
662 return r;
e5719363
JT
663
664 for (;;) {
665 _cleanup_free_ char *word = NULL;
666
667 r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
668 if (r == 0)
669 break;
670 if (r == -ENOMEM)
671 return log_oom();
672 if (r < 0) {
f1368a33
YW
673 log_syntax(unit, LOG_ERR, filename, line, r,
674 "Failed to split allowed ips \"%s\" option: %m", rvalue);
e5719363
JT
675 break;
676 }
677
678 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
679 if (r < 0) {
f1368a33
YW
680 log_syntax(unit, LOG_ERR, filename, line, r,
681 "Network address is invalid, ignoring assignment: %s", word);
682 continue;
e5719363
JT
683 }
684
fc721553 685 ipmask = new(WireguardIPmask, 1);
e5719363
JT
686 if (!ipmask)
687 return log_oom();
fc721553
YW
688
689 *ipmask = (WireguardIPmask) {
690 .family = family,
691 .ip.in6 = addr.in6,
692 .cidr = prefixlen,
693 };
e5719363
JT
694
695 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
696 }
697
f1368a33 698 TAKE_PTR(peer);
e5719363
JT
699 return 0;
700}
701
702int config_parse_wireguard_endpoint(const char *unit,
703 const char *filename,
704 unsigned line,
705 const char *section,
706 unsigned section_line,
707 const char *lvalue,
708 int ltype,
709 const char *rvalue,
710 void *data,
711 void *userdata) {
f1368a33
YW
712
713 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
714 const char *begin, *end;
e5719363 715 Wireguard *w;
e5719363 716 size_t len;
f1368a33 717 int r;
e5719363
JT
718
719 assert(data);
720 assert(rvalue);
721
722 w = WIREGUARD(data);
723
724 assert(w);
725
f1368a33
YW
726 r = wireguard_peer_new_static(w, filename, section_line, &peer);
727 if (r < 0)
728 return r;
e5719363 729
e5719363
JT
730 if (rvalue[0] == '[') {
731 begin = &rvalue[1];
732 end = strchr(rvalue, ']');
733 if (!end) {
f1368a33
YW
734 log_syntax(unit, LOG_ERR, filename, line, 0,
735 "Unable to find matching brace of endpoint, ignoring assignment: %s",
736 rvalue);
e5719363
JT
737 return 0;
738 }
739 len = end - begin;
740 ++end;
741 if (*end != ':' || !*(end + 1)) {
f1368a33
YW
742 log_syntax(unit, LOG_ERR, filename, line, 0,
743 "Unable to find port of endpoint, ignoring assignment: %s",
744 rvalue);
e5719363
JT
745 return 0;
746 }
747 ++end;
748 } else {
749 begin = rvalue;
750 end = strrchr(rvalue, ':');
751 if (!end || !*(end + 1)) {
f1368a33
YW
752 log_syntax(unit, LOG_ERR, filename, line, 0,
753 "Unable to find port of endpoint, ignoring assignment: %s",
754 rvalue);
e5719363
JT
755 return 0;
756 }
757 len = end - begin;
758 ++end;
759 }
760
f1368a33
YW
761 peer->endpoint_host = strndup(begin, len);
762 if (!peer->endpoint_host)
e5719363
JT
763 return log_oom();
764
f1368a33
YW
765 peer->endpoint_port = strdup(end);
766 if (!peer->endpoint_port)
e5719363
JT
767 return log_oom();
768
f1368a33
YW
769 r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL);
770 if (r < 0)
fc721553
YW
771 return log_oom();
772
f1368a33
YW
773 r = set_put(w->peers_with_unresolved_endpoint, peer);
774 if (r < 0)
775 return r;
e5719363 776
f1368a33 777 TAKE_PTR(peer);
e5719363
JT
778 return 0;
779}
780
781int config_parse_wireguard_keepalive(const char *unit,
782 const char *filename,
783 unsigned line,
784 const char *section,
785 unsigned section_line,
786 const char *lvalue,
787 int ltype,
788 const char *rvalue,
789 void *data,
790 void *userdata) {
f1368a33
YW
791
792 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
793 uint16_t keepalive = 0;
794 Wireguard *w;
f1368a33 795 int r;
e5719363
JT
796
797 assert(rvalue);
798 assert(data);
799
800 w = WIREGUARD(data);
801
802 assert(w);
803
f1368a33
YW
804 r = wireguard_peer_new_static(w, filename, section_line, &peer);
805 if (r < 0)
806 return r;
e5719363
JT
807
808 if (streq(rvalue, "off"))
809 keepalive = 0;
810 else {
811 r = safe_atou16(rvalue, &keepalive);
f1368a33
YW
812 if (r < 0) {
813 log_syntax(unit, LOG_ERR, filename, line, r,
814 "The persistent keepalive interval must be 0-65535. Ignore assignment: %s",
815 rvalue);
816 return 0;
817 }
e5719363
JT
818 }
819
820 peer->persistent_keepalive_interval = keepalive;
f1368a33
YW
821
822 TAKE_PTR(peer);
e5719363
JT
823 return 0;
824}
825
826static void wireguard_init(NetDev *netdev) {
827 Wireguard *w;
828
829 assert(netdev);
830
831 w = WIREGUARD(netdev);
832
833 assert(w);
834
835 w->flags = WGDEVICE_F_REPLACE_PEERS;
836}
837
838static void wireguard_done(NetDev *netdev) {
839 Wireguard *w;
e5719363
JT
840
841 assert(netdev);
842 w = WIREGUARD(netdev);
c195364d 843 assert(w);
e5719363 844
85c987a8
YW
845 sd_event_source_unref(w->resolve_retry_event_source);
846
76df7779
YW
847 free(w->private_key_file);
848
f1368a33
YW
849 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
850 set_free(w->peers_with_unresolved_endpoint);
851 set_free(w->peers_with_failed_endpoint);
852}
c195364d 853
76df7779
YW
854static int wireguard_read_private_key_file(Wireguard *w, bool fatal) {
855 _cleanup_free_ char *contents = NULL;
856 _cleanup_free_ void *key = NULL;
857 size_t size, key_len;
858 NetDev *netdev;
859 int level, r;
860
861 assert(w);
862
863 netdev = NETDEV(w);
864
865 if (!w->private_key_file)
866 return 0;
867
868 level = fatal ? LOG_ERR : LOG_INFO;
869
870 r = read_full_file(w->private_key_file, &contents, &size);
871 if (r < 0)
872 return log_netdev_full(netdev, level, r,
873 "Failed to read private key from '%s'%s: %m",
874 w->private_key_file, fatal ? "" : ", ignoring");
875
876 r = unbase64mem(contents, size, &key, &key_len);
877 if (r < 0)
878 return log_netdev_full(netdev, level, r,
879 "Failed to decode private key%s: %m",
880 fatal ? "" : ", ignoring");
881
882 if (key_len != WG_KEY_LEN)
883 return log_netdev_full(netdev, level, SYNTHETIC_ERRNO(EINVAL),
884 "Wireguard private key has invalid length (%zu bytes)%s: %m",
885 key_len, fatal ? "" : ", ignoring");
886
887 memcpy(w->private_key, key, WG_KEY_LEN);
888 return 0;
889}
890
9cc9021a
YW
891static int wireguard_peer_verify(WireguardPeer *peer) {
892 NetDev *netdev = NETDEV(peer->wireguard);
893
894 if (section_is_invalid(peer->section))
895 return -EINVAL;
896
897 if (eqzero(peer->public_key))
898 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
899 "%s: WireGuardPeer section without PublicKey= configured. "
900 "Ignoring [WireGuardPeer] section from line %u.",
901 peer->section->filename, peer->section->line);
902
903 return 0;
904}
905
f1368a33
YW
906static int wireguard_verify(NetDev *netdev, const char *filename) {
907 WireguardPeer *peer, *peer_next;
908 Wireguard *w;
76df7779
YW
909 bool empty;
910 int r;
c195364d 911
f1368a33
YW
912 assert(netdev);
913 w = WIREGUARD(netdev);
914 assert(w);
915
76df7779
YW
916 empty = eqzero(w->private_key);
917 if (empty && !w->private_key_file)
9cc9021a
YW
918 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
919 "%s: Missing PrivateKey= or PrivateKeyFile=, ignoring.",
920 filename);
921
76df7779
YW
922 r = wireguard_read_private_key_file(w, empty);
923 if (r < 0 && empty)
924 return r;
925
f1368a33 926 LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
9cc9021a 927 if (wireguard_peer_verify(peer) < 0)
f1368a33
YW
928 wireguard_peer_free(peer);
929
930 return 0;
e5719363
JT
931}
932
933const NetDevVTable wireguard_vtable = {
934 .object_size = sizeof(Wireguard),
935 .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
936 .post_create = netdev_wireguard_post_create,
937 .init = wireguard_init,
938 .done = wireguard_done,
939 .create_type = NETDEV_CREATE_INDEPENDENT,
f1368a33 940 .config_verify = wireguard_verify,
e5719363 941};