]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/netdev/wireguard.c
tree-wide: define iterator inside of the macro
[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-manager.h"
a4c9ae40
YW
19#include "networkd-util.h"
20#include "parse-util.h"
76df7779 21#include "path-util.h"
1061dab1 22#include "resolve-private.h"
a4c9ae40
YW
23#include "string-util.h"
24#include "strv.h"
0a970718 25#include "wireguard.h"
e5719363
JT
26
27static void resolve_endpoints(NetDev *netdev);
28
f1368a33
YW
29static void wireguard_peer_free(WireguardPeer *peer) {
30 WireguardIPmask *mask;
31
32 if (!peer)
33 return;
34
35 if (peer->wireguard) {
36 LIST_REMOVE(peers, peer->wireguard->peers, peer);
37
38 set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer);
39 set_remove(peer->wireguard->peers_with_failed_endpoint, peer);
40
41 if (peer->section)
42 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
43 }
44
45 network_config_section_free(peer->section);
46
47 while ((mask = peer->ipmasks)) {
48 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
49 free(mask);
50 }
51
52 free(peer->endpoint_host);
53 free(peer->endpoint_port);
a3945c63 54 free(peer->preshared_key_file);
6ef5c881 55 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
f1368a33
YW
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;
50254f55 222 bool sent_once = false;
e5719363 223 uint32_t serial;
e1f717d4
YW
224 Wireguard *w;
225 int r;
e5719363
JT
226
227 assert(netdev);
228 w = WIREGUARD(netdev);
229 assert(w);
230
50254f55 231 for (peer_start = w->peers; peer_start || !sent_once; ) {
e1f717d4 232 uint16_t i = 0;
e5719363 233
e5719363
JT
234 message = sd_netlink_message_unref(message);
235
236 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
237 if (r < 0)
238 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
239
240 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
241 if (r < 0)
242 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
243
244 if (peer_start == w->peers) {
245 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
246 if (r < 0)
247 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
248
249 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
250 if (r < 0)
251 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
252
253 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
254 if (r < 0)
255 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
256
257 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
258 if (r < 0)
259 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
260 }
261
262 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
263 if (r < 0)
264 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
265
e5719363 266 LIST_FOREACH(peers, peer, peer_start) {
e1f717d4 267 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
e5719363 268 if (r < 0)
e1f717d4
YW
269 return r;
270 if (r == 0)
e5719363 271 break;
e5719363 272 }
e1f717d4 273 peer_start = peer; /* Start next cycle from this peer. */
e5719363
JT
274
275 r = sd_netlink_message_close_container(message);
276 if (r < 0)
277 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
278
279 r = sd_netlink_send(netdev->manager->genl, message, &serial);
280 if (r < 0)
281 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
50254f55
YW
282
283 sent_once = true;
e1f717d4 284 }
e5719363
JT
285
286 return 0;
287}
288
f1368a33
YW
289static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
290 NetDev *netdev;
e5719363 291
f1368a33
YW
292 assert(peer);
293 assert(peer->wireguard);
8173d1d0 294
f1368a33 295 netdev = NETDEV(peer->wireguard);
8173d1d0 296
f1368a33
YW
297 if (section_is_invalid(peer->section))
298 wireguard_peer_free(peer);
299
300 netdev_unref(netdev);
301}
e5719363
JT
302
303static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
304 NetDev *netdev = userdata;
305 Wireguard *w;
306
307 assert(netdev);
308 w = WIREGUARD(netdev);
309 assert(w);
310
9e2bbf99 311 if (!netdev_is_managed(netdev))
56ba90c2 312 return 0;
e5719363 313
f1368a33
YW
314 assert(set_isempty(w->peers_with_unresolved_endpoint));
315
316 SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
e5719363
JT
317
318 resolve_endpoints(netdev);
319
320 return 0;
321}
322
323/*
324 * Given the number of retries this function will return will an exponential
325 * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
326 */
327static int exponential_backoff_milliseconds(unsigned n_retries) {
7232c1f9 328 return (2 << MIN(n_retries, 7U)) * 100 * USEC_PER_MSEC;
e5719363
JT
329}
330
331static int wireguard_resolve_handler(sd_resolve_query *q,
332 int ret,
333 const struct addrinfo *ai,
f1368a33 334 WireguardPeer *peer) {
1061dab1 335 NetDev *netdev;
e5719363 336 Wireguard *w;
e5719363
JT
337 int r;
338
f1368a33
YW
339 assert(peer);
340 assert(peer->wireguard);
e5719363 341
f1368a33
YW
342 w = peer->wireguard;
343 netdev = NETDEV(w);
e5719363 344
9e2bbf99 345 if (!netdev_is_managed(netdev))
8173d1d0 346 return 0;
e5719363
JT
347
348 if (ret != 0) {
f1368a33
YW
349 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
350
de7fef4b 351 r = set_ensure_put(&w->peers_with_failed_endpoint, NULL, peer);
f1368a33
YW
352 if (r < 0) {
353 log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
354 peer->section->invalid = true;
355 goto resolve_next;
356 }
357
e5719363 358 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
8173d1d0 359 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
f1368a33 360 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
e5719363 361 else
f1368a33
YW
362 log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
363 peer->endpoint_host, peer->endpoint_port);
e5719363 364
f1368a33
YW
365resolve_next:
366 if (!set_isempty(w->peers_with_unresolved_endpoint)) {
e5719363
JT
367 resolve_endpoints(netdev);
368 return 0;
369 }
370
e1f717d4 371 (void) wireguard_set_interface(netdev);
f1368a33
YW
372
373 if (!set_isempty(w->peers_with_failed_endpoint)) {
85c987a8 374 usec_t usec;
56ba90c2 375
e5719363 376 w->n_retries++;
85c987a8
YW
377 usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
378 r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
379 CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
380 0, "wireguard-resolve-retry", true);
56ba90c2 381 if (r < 0) {
e5719363 382 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
56ba90c2
YW
383 return 0;
384 }
e5719363
JT
385 }
386
387 return 0;
388}
389
390static void resolve_endpoints(NetDev *netdev) {
e5719363
JT
391 static const struct addrinfo hints = {
392 .ai_family = AF_UNSPEC,
393 .ai_socktype = SOCK_DGRAM,
394 .ai_protocol = IPPROTO_UDP
395 };
f1368a33 396 WireguardPeer *peer;
8173d1d0 397 Wireguard *w;
86e2be7b 398 int r;
e5719363
JT
399
400 assert(netdev);
401 w = WIREGUARD(netdev);
402 assert(w);
403
90e74a66 404 SET_FOREACH(peer, w->peers_with_unresolved_endpoint) {
1061dab1
YW
405 r = resolve_getaddrinfo(netdev->manager->resolve,
406 NULL,
f1368a33
YW
407 peer->endpoint_host,
408 peer->endpoint_port,
1061dab1
YW
409 &hints,
410 wireguard_resolve_handler,
f1368a33
YW
411 wireguard_peer_destroy_callback,
412 peer);
e5719363
JT
413 if (r == -ENOBUFS)
414 break;
8173d1d0
YW
415 if (r < 0) {
416 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
417 continue;
418 }
e5719363 419
8173d1d0
YW
420 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
421 netdev_ref(netdev);
422
f1368a33 423 (void) set_remove(w->peers_with_unresolved_endpoint, peer);
e5719363
JT
424 }
425}
426
e5719363 427static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
e5719363 428 assert(netdev);
10c353e1 429 assert(WIREGUARD(netdev));
e5719363 430
e1f717d4 431 (void) wireguard_set_interface(netdev);
e5719363
JT
432 resolve_endpoints(netdev);
433 return 0;
434}
435
03fec543
YW
436int config_parse_wireguard_listen_port(
437 const char *unit,
438 const char *filename,
439 unsigned line,
440 const char *section,
441 unsigned section_line,
442 const char *lvalue,
443 int ltype,
444 const char *rvalue,
445 void *data,
446 void *userdata) {
447
e5719363 448 uint16_t *s = data;
e5719363
JT
449 int r;
450
451 assert(rvalue);
452 assert(data);
453
a62b7bb7
YW
454 if (isempty(rvalue) || streq(rvalue, "auto")) {
455 *s = 0;
456 return 0;
457 }
458
459 r = parse_ip_port(rvalue, s);
460 if (r < 0) {
d96edb2c 461 log_syntax(unit, LOG_WARNING, filename, line, r,
a62b7bb7
YW
462 "Invalid port specification, ignoring assignment: %s", rvalue);
463 return 0;
e5719363
JT
464 }
465
e5719363
JT
466 return 0;
467}
468
fedcb4c3
YW
469static int wireguard_decode_key_and_warn(
470 const char *rvalue,
2b942a92 471 uint8_t ret[static WG_KEY_LEN],
fedcb4c3
YW
472 const char *unit,
473 const char *filename,
474 unsigned line,
475 const char *lvalue) {
03fec543 476
e693a932 477 _cleanup_(erase_and_freep) void *key = NULL;
e5719363
JT
478 size_t len;
479 int r;
480
e5719363 481 assert(rvalue);
fedcb4c3
YW
482 assert(ret);
483 assert(filename);
484 assert(lvalue);
485
486 if (isempty(rvalue)) {
487 memzero(ret, WG_KEY_LEN);
488 return 0;
489 }
e5719363 490
26f86d50
YW
491 if (!streq(lvalue, "PublicKey"))
492 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
493
6ef5c881 494 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
86a3d44d 495 if (r < 0)
d96edb2c 496 return log_syntax(unit, LOG_WARNING, filename, line, r,
583eb170 497 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
e693a932 498 if (len != WG_KEY_LEN)
d96edb2c 499 return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
583eb170
YW
500 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
501 lvalue, len);
e5719363 502
fedcb4c3 503 memcpy(ret, key, WG_KEY_LEN);
86a3d44d 504 return 0;
e5719363
JT
505}
506
03fec543
YW
507int config_parse_wireguard_private_key(
508 const char *unit,
509 const char *filename,
510 unsigned line,
511 const char *section,
512 unsigned section_line,
513 const char *lvalue,
514 int ltype,
515 const char *rvalue,
516 void *data,
517 void *userdata) {
518
e5719363
JT
519 Wireguard *w;
520
521 assert(data);
e5719363 522 w = WIREGUARD(data);
e5719363
JT
523 assert(w);
524
6ef5c881
YW
525 (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
526 return 0;
e5719363
JT
527}
528
76df7779
YW
529int config_parse_wireguard_private_key_file(
530 const char *unit,
531 const char *filename,
532 unsigned line,
533 const char *section,
534 unsigned section_line,
535 const char *lvalue,
536 int ltype,
537 const char *rvalue,
538 void *data,
539 void *userdata) {
540
541 _cleanup_free_ char *path = NULL;
542 Wireguard *w;
543
544 assert(data);
545 w = WIREGUARD(data);
546 assert(w);
547
548 if (isempty(rvalue)) {
549 w->private_key_file = mfree(w->private_key_file);
550 return 0;
551 }
552
553 path = strdup(rvalue);
554 if (!path)
555 return log_oom();
556
557 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
558 return 0;
559
560 return free_and_replace(w->private_key_file, path);
561}
562
03fec543
YW
563int config_parse_wireguard_preshared_key(
564 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) {
f1368a33 574
44e93420 575 WireguardPeer *peer;
e5719363 576 Wireguard *w;
f1368a33 577 int r;
e5719363
JT
578
579 assert(data);
e5719363 580 w = WIREGUARD(data);
e5719363
JT
581 assert(w);
582
f1368a33
YW
583 r = wireguard_peer_new_static(w, filename, section_line, &peer);
584 if (r < 0)
d96edb2c 585 return log_oom();
e5719363 586
d96edb2c
YW
587 (void) wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
588 return 0;
e5719363
JT
589}
590
a3945c63
YW
591int config_parse_wireguard_preshared_key_file(
592 const char *unit,
593 const char *filename,
594 unsigned line,
595 const char *section,
596 unsigned section_line,
597 const char *lvalue,
598 int ltype,
599 const char *rvalue,
600 void *data,
601 void *userdata) {
602
603 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
604 _cleanup_free_ char *path = NULL;
605 Wireguard *w;
606 int r;
607
608 assert(data);
609 w = WIREGUARD(data);
610 assert(w);
611
612 r = wireguard_peer_new_static(w, filename, section_line, &peer);
613 if (r < 0)
d96edb2c 614 return log_oom();
a3945c63
YW
615
616 if (isempty(rvalue)) {
617 peer->preshared_key_file = mfree(peer->preshared_key_file);
618 TAKE_PTR(peer);
619 return 0;
620 }
621
622 path = strdup(rvalue);
623 if (!path)
624 return log_oom();
625
626 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
627 return 0;
628
629 free_and_replace(peer->preshared_key_file, path);
630 TAKE_PTR(peer);
631 return 0;
632}
633
03fec543
YW
634int config_parse_wireguard_public_key(
635 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 647 Wireguard *w;
f1368a33 648 int r;
e5719363
JT
649
650 assert(data);
e5719363 651 w = WIREGUARD(data);
e5719363
JT
652 assert(w);
653
f1368a33
YW
654 r = wireguard_peer_new_static(w, filename, section_line, &peer);
655 if (r < 0)
d96edb2c 656 return log_oom();
f1368a33 657
fedcb4c3 658 r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
f1368a33 659 if (r < 0)
d96edb2c 660 return 0;
e5719363 661
f1368a33
YW
662 TAKE_PTR(peer);
663 return 0;
e5719363
JT
664}
665
03fec543
YW
666int config_parse_wireguard_allowed_ips(
667 const char *unit,
668 const char *filename,
669 unsigned line,
670 const char *section,
671 unsigned section_line,
672 const char *lvalue,
673 int ltype,
674 const char *rvalue,
675 void *data,
676 void *userdata) {
f1368a33
YW
677
678 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
e5719363
JT
679 union in_addr_union addr;
680 unsigned char prefixlen;
681 int r, family;
682 Wireguard *w;
e5719363
JT
683 WireguardIPmask *ipmask;
684
685 assert(rvalue);
686 assert(data);
687
688 w = WIREGUARD(data);
f1368a33
YW
689 assert(w);
690
691 r = wireguard_peer_new_static(w, filename, section_line, &peer);
692 if (r < 0)
d96edb2c 693 return log_oom();
e5719363 694
d96edb2c 695 for (const char *p = rvalue;;) {
e5719363
JT
696 _cleanup_free_ char *word = NULL;
697
d96edb2c 698 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
e5719363
JT
699 if (r == 0)
700 break;
701 if (r == -ENOMEM)
702 return log_oom();
703 if (r < 0) {
d96edb2c 704 log_syntax(unit, LOG_WARNING, filename, line, r,
f1368a33 705 "Failed to split allowed ips \"%s\" option: %m", rvalue);
e5719363
JT
706 break;
707 }
708
709 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
710 if (r < 0) {
d96edb2c 711 log_syntax(unit, LOG_WARNING, filename, line, r,
f1368a33
YW
712 "Network address is invalid, ignoring assignment: %s", word);
713 continue;
e5719363
JT
714 }
715
fc721553 716 ipmask = new(WireguardIPmask, 1);
e5719363
JT
717 if (!ipmask)
718 return log_oom();
fc721553
YW
719
720 *ipmask = (WireguardIPmask) {
721 .family = family,
722 .ip.in6 = addr.in6,
723 .cidr = prefixlen,
724 };
e5719363
JT
725
726 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
727 }
728
f1368a33 729 TAKE_PTR(peer);
e5719363
JT
730 return 0;
731}
732
03fec543
YW
733int config_parse_wireguard_endpoint(
734 const char *unit,
735 const char *filename,
736 unsigned line,
737 const char *section,
738 unsigned section_line,
739 const char *lvalue,
740 int ltype,
741 const char *rvalue,
742 void *data,
743 void *userdata) {
f1368a33
YW
744
745 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
746 const char *begin, *end;
e5719363 747 Wireguard *w;
e5719363 748 size_t len;
f1368a33 749 int r;
e5719363
JT
750
751 assert(data);
752 assert(rvalue);
753
754 w = WIREGUARD(data);
e5719363
JT
755 assert(w);
756
e5719363
JT
757 if (rvalue[0] == '[') {
758 begin = &rvalue[1];
759 end = strchr(rvalue, ']');
760 if (!end) {
d96edb2c 761 log_syntax(unit, LOG_WARNING, filename, line, 0,
f1368a33
YW
762 "Unable to find matching brace of endpoint, ignoring assignment: %s",
763 rvalue);
e5719363
JT
764 return 0;
765 }
766 len = end - begin;
767 ++end;
768 if (*end != ':' || !*(end + 1)) {
d96edb2c 769 log_syntax(unit, LOG_WARNING, filename, line, 0,
f1368a33
YW
770 "Unable to find port of endpoint, ignoring assignment: %s",
771 rvalue);
e5719363
JT
772 return 0;
773 }
774 ++end;
775 } else {
776 begin = rvalue;
777 end = strrchr(rvalue, ':');
778 if (!end || !*(end + 1)) {
d96edb2c 779 log_syntax(unit, LOG_WARNING, filename, line, 0,
f1368a33
YW
780 "Unable to find port of endpoint, ignoring assignment: %s",
781 rvalue);
e5719363
JT
782 return 0;
783 }
784 len = end - begin;
785 ++end;
786 }
787
44e93420
ZJS
788 r = wireguard_peer_new_static(w, filename, section_line, &peer);
789 if (r < 0)
d96edb2c 790 return log_oom();
44e93420 791
5f07d640
YW
792 r = free_and_strndup(&peer->endpoint_host, begin, len);
793 if (r < 0)
e5719363
JT
794 return log_oom();
795
5f07d640
YW
796 r = free_and_strdup(&peer->endpoint_port, end);
797 if (r < 0)
e5719363
JT
798 return log_oom();
799
de7fef4b 800 r = set_ensure_put(&w->peers_with_unresolved_endpoint, NULL, peer);
f1368a33 801 if (r < 0)
fc721553 802 return log_oom();
44e93420 803 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
de7fef4b 804
e5719363
JT
805 return 0;
806}
807
03fec543
YW
808int config_parse_wireguard_keepalive(
809 const char *unit,
810 const char *filename,
811 unsigned line,
812 const char *section,
813 unsigned section_line,
814 const char *lvalue,
815 int ltype,
816 const char *rvalue,
817 void *data,
818 void *userdata) {
f1368a33 819
44e93420 820 WireguardPeer *peer;
e5719363
JT
821 uint16_t keepalive = 0;
822 Wireguard *w;
f1368a33 823 int r;
e5719363
JT
824
825 assert(rvalue);
826 assert(data);
827
828 w = WIREGUARD(data);
e5719363
JT
829 assert(w);
830
f1368a33
YW
831 r = wireguard_peer_new_static(w, filename, section_line, &peer);
832 if (r < 0)
d96edb2c 833 return log_oom();
e5719363
JT
834
835 if (streq(rvalue, "off"))
836 keepalive = 0;
837 else {
838 r = safe_atou16(rvalue, &keepalive);
f1368a33 839 if (r < 0) {
d96edb2c 840 log_syntax(unit, LOG_WARNING, filename, line, r,
44e93420 841 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
f1368a33
YW
842 rvalue);
843 return 0;
844 }
e5719363
JT
845 }
846
847 peer->persistent_keepalive_interval = keepalive;
848 return 0;
849}
850
851static void wireguard_init(NetDev *netdev) {
852 Wireguard *w;
853
854 assert(netdev);
e5719363 855 w = WIREGUARD(netdev);
e5719363
JT
856 assert(w);
857
858 w->flags = WGDEVICE_F_REPLACE_PEERS;
859}
860
861static void wireguard_done(NetDev *netdev) {
862 Wireguard *w;
e5719363
JT
863
864 assert(netdev);
865 w = WIREGUARD(netdev);
c195364d 866 assert(w);
e5719363 867
85c987a8
YW
868 sd_event_source_unref(w->resolve_retry_event_source);
869
6ef5c881 870 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
76df7779
YW
871 free(w->private_key_file);
872
f1368a33
YW
873 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
874 set_free(w->peers_with_unresolved_endpoint);
875 set_free(w->peers_with_failed_endpoint);
876}
c195364d 877
cb31e7c8 878static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
e693a932 879 _cleanup_(erase_and_freep) char *key = NULL;
cb31e7c8
YW
880 size_t key_len;
881 int r;
76df7779 882
cb31e7c8 883 if (!filename)
76df7779
YW
884 return 0;
885
2caa38e9
LP
886 assert(dest);
887
0bae8575
YW
888 (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
889
49f16281
LP
890 r = read_full_file_full(
891 AT_FDCWD, filename,
892 READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
893 &key, &key_len);
76df7779 894 if (r < 0)
cb31e7c8 895 return r;
76df7779 896
e693a932
ZJS
897 if (key_len != WG_KEY_LEN)
898 return -EINVAL;
76df7779 899
cb31e7c8 900 memcpy(dest, key, WG_KEY_LEN);
e693a932 901 return 0;
76df7779
YW
902}
903
9cc9021a
YW
904static int wireguard_peer_verify(WireguardPeer *peer) {
905 NetDev *netdev = NETDEV(peer->wireguard);
a3945c63 906 int r;
9cc9021a
YW
907
908 if (section_is_invalid(peer->section))
909 return -EINVAL;
910
911 if (eqzero(peer->public_key))
912 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
913 "%s: WireGuardPeer section without PublicKey= configured. "
914 "Ignoring [WireGuardPeer] section from line %u.",
915 peer->section->filename, peer->section->line);
916
a3945c63
YW
917 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
918 if (r < 0)
919 return log_netdev_error_errno(netdev, r,
920 "%s: Failed to read preshared key from '%s'. "
921 "Ignoring [WireGuardPeer] section from line %u.",
922 peer->section->filename, peer->preshared_key_file,
923 peer->section->line);
924
9cc9021a
YW
925 return 0;
926}
927
f1368a33
YW
928static int wireguard_verify(NetDev *netdev, const char *filename) {
929 WireguardPeer *peer, *peer_next;
930 Wireguard *w;
76df7779 931 int r;
c195364d 932
f1368a33
YW
933 assert(netdev);
934 w = WIREGUARD(netdev);
935 assert(w);
936
cb31e7c8
YW
937 r = wireguard_read_key_file(w->private_key_file, w->private_key);
938 if (r < 0)
939 return log_netdev_error_errno(netdev, r,
940 "Failed to read private key from %s. Dropping network device %s.",
941 w->private_key_file, netdev->ifname);
9cc9021a 942
cb31e7c8
YW
943 if (eqzero(w->private_key))
944 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
945 "%s: Missing PrivateKey= or PrivateKeyFile=, "
946 "Dropping network device %s.",
947 filename, netdev->ifname);
76df7779 948
f1368a33 949 LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
9cc9021a 950 if (wireguard_peer_verify(peer) < 0)
f1368a33
YW
951 wireguard_peer_free(peer);
952
953 return 0;
e5719363
JT
954}
955
956const NetDevVTable wireguard_vtable = {
957 .object_size = sizeof(Wireguard),
130b812f 958 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
e5719363
JT
959 .post_create = netdev_wireguard_post_create,
960 .init = wireguard_init,
961 .done = wireguard_done,
962 .create_type = NETDEV_CREATE_INDEPENDENT,
f1368a33 963 .config_verify = wireguard_verify,
daf0f8ca 964 .generate_mac = true,
e5719363 965};