]> git.proxmox.com Git - systemd.git/blame - src/network/netdev/wireguard.c
New upstream version 245.7
[systemd.git] / src / network / netdev / wireguard.c
CommitLineData
b012e921 1/* SPDX-License-Identifier: LGPL-2.1+ */
1d42b86d 2/***
b012e921 3 Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
1d42b86d
MB
4***/
5
6#include <sys/ioctl.h>
7#include <net/if.h>
8
6e866b33
MB
9#include "sd-resolve.h"
10
1d42b86d 11#include "alloc-util.h"
bb4f798a 12#include "event-util.h"
1d42b86d 13#include "fd-util.h"
bb4f798a 14#include "fileio.h"
1d42b86d 15#include "hexdecoct.h"
bb4f798a
MB
16#include "memory-util.h"
17#include "netlink-util.h"
1d42b86d 18#include "networkd-manager.h"
6e866b33
MB
19#include "networkd-util.h"
20#include "parse-util.h"
bb4f798a 21#include "path-util.h"
6e866b33
MB
22#include "resolve-private.h"
23#include "string-util.h"
24#include "strv.h"
bb4f798a 25#include "wireguard.h"
1d42b86d
MB
26
27static void resolve_endpoints(NetDev *netdev);
28
bb4f798a
MB
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);
54 free(peer->preshared_key_file);
55 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
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;
1d42b86d
MB
66
67 assert(w);
bb4f798a
MB
68 assert(ret);
69 assert(filename);
70 assert(section_line > 0);
71
72 r = network_config_section_new(filename, section_line, &n);
73 if (r < 0)
74 return r;
1d42b86d 75
bb4f798a
MB
76 peer = hashmap_get(w->peers_by_section, n);
77 if (peer) {
78 *ret = TAKE_PTR(peer);
79 return 0;
80 }
1d42b86d 81
6e866b33 82 peer = new(WireguardPeer, 1);
1d42b86d 83 if (!peer)
bb4f798a 84 return -ENOMEM;
6e866b33
MB
85
86 *peer = (WireguardPeer) {
87 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
bb4f798a
MB
88 .wireguard = w,
89 .section = TAKE_PTR(n),
6e866b33 90 };
1d42b86d
MB
91
92 LIST_PREPEND(peers, w->peers, peer);
1d42b86d 93
bb4f798a
MB
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;
1d42b86d
MB
104}
105
7c20daf6 106static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
1d42b86d 107 int r;
7c20daf6
FS
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
bb4f798a 123 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
7c20daf6
FS
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
167 if (!*mask_start) {
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
bb4f798a
MB
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 }
7c20daf6
FS
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) {
1d42b86d 219 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
7c20daf6
FS
220 WireguardIPmask *mask_start = NULL;
221 WireguardPeer *peer, *peer_start;
478ed938 222 bool sent_once = false;
1d42b86d 223 uint32_t serial;
7c20daf6
FS
224 Wireguard *w;
225 int r;
1d42b86d
MB
226
227 assert(netdev);
228 w = WIREGUARD(netdev);
229 assert(w);
230
478ed938 231 for (peer_start = w->peers; peer_start || !sent_once; ) {
7c20daf6 232 uint16_t i = 0;
1d42b86d 233
1d42b86d
MB
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
1d42b86d 266 LIST_FOREACH(peers, peer, peer_start) {
7c20daf6 267 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
1d42b86d 268 if (r < 0)
7c20daf6
FS
269 return r;
270 if (r == 0)
1d42b86d 271 break;
1d42b86d 272 }
7c20daf6 273 peer_start = peer; /* Start next cycle from this peer. */
1d42b86d
MB
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");
478ed938
MB
282
283 sent_once = true;
7c20daf6 284 }
1d42b86d
MB
285
286 return 0;
287}
288
bb4f798a
MB
289static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
290 NetDev *netdev;
1d42b86d 291
bb4f798a
MB
292 assert(peer);
293 assert(peer->wireguard);
6e866b33 294
bb4f798a 295 netdev = NETDEV(peer->wireguard);
6e866b33 296
bb4f798a
MB
297 if (section_is_invalid(peer->section))
298 wireguard_peer_free(peer);
299
300 netdev_unref(netdev);
301}
1d42b86d
MB
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
7c20daf6 311 if (!netdev_is_managed(netdev))
6e866b33 312 return 0;
1d42b86d 313
bb4f798a
MB
314 assert(set_isempty(w->peers_with_unresolved_endpoint));
315
316 SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
1d42b86d
MB
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) {
bb4f798a 328 return (2 << MIN(n_retries, 7U)) * 100 * USEC_PER_MSEC;
1d42b86d
MB
329}
330
331static int wireguard_resolve_handler(sd_resolve_query *q,
332 int ret,
333 const struct addrinfo *ai,
bb4f798a 334 WireguardPeer *peer) {
1d42b86d
MB
335 NetDev *netdev;
336 Wireguard *w;
1d42b86d
MB
337 int r;
338
bb4f798a
MB
339 assert(peer);
340 assert(peer->wireguard);
1d42b86d 341
bb4f798a
MB
342 w = peer->wireguard;
343 netdev = NETDEV(w);
1d42b86d 344
7c20daf6 345 if (!netdev_is_managed(netdev))
6e866b33 346 return 0;
1d42b86d
MB
347
348 if (ret != 0) {
bb4f798a
MB
349 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
350
351 r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL);
352 if (r < 0) {
353 log_oom();
354 peer->section->invalid = true;
355 goto resolve_next;
356 }
357
358 r = set_put(w->peers_with_failed_endpoint, peer);
359 if (r < 0) {
360 log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
361 peer->section->invalid = true;
362 goto resolve_next;
363 }
364
1d42b86d 365 } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
6e866b33 366 (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
bb4f798a 367 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
1d42b86d 368 else
bb4f798a
MB
369 log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
370 peer->endpoint_host, peer->endpoint_port);
1d42b86d 371
bb4f798a
MB
372resolve_next:
373 if (!set_isempty(w->peers_with_unresolved_endpoint)) {
1d42b86d
MB
374 resolve_endpoints(netdev);
375 return 0;
376 }
377
7c20daf6 378 (void) wireguard_set_interface(netdev);
bb4f798a
MB
379
380 if (!set_isempty(w->peers_with_failed_endpoint)) {
381 usec_t usec;
6e866b33 382
1d42b86d 383 w->n_retries++;
bb4f798a
MB
384 usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
385 r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
386 CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
387 0, "wireguard-resolve-retry", true);
6e866b33 388 if (r < 0) {
1d42b86d 389 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
6e866b33
MB
390 return 0;
391 }
1d42b86d
MB
392 }
393
394 return 0;
395}
396
397static void resolve_endpoints(NetDev *netdev) {
1d42b86d
MB
398 static const struct addrinfo hints = {
399 .ai_family = AF_UNSPEC,
400 .ai_socktype = SOCK_DGRAM,
401 .ai_protocol = IPPROTO_UDP
402 };
bb4f798a 403 WireguardPeer *peer;
6e866b33 404 Wireguard *w;
bb4f798a 405 Iterator i;
f2dec872 406 int r;
1d42b86d
MB
407
408 assert(netdev);
409 w = WIREGUARD(netdev);
410 assert(w);
411
bb4f798a 412 SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) {
6e866b33
MB
413 r = resolve_getaddrinfo(netdev->manager->resolve,
414 NULL,
bb4f798a
MB
415 peer->endpoint_host,
416 peer->endpoint_port,
6e866b33
MB
417 &hints,
418 wireguard_resolve_handler,
bb4f798a
MB
419 wireguard_peer_destroy_callback,
420 peer);
1d42b86d
MB
421 if (r == -ENOBUFS)
422 break;
6e866b33
MB
423 if (r < 0) {
424 log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
425 continue;
426 }
1d42b86d 427
6e866b33
MB
428 /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
429 netdev_ref(netdev);
1d42b86d 430
bb4f798a 431 (void) set_remove(w->peers_with_unresolved_endpoint, peer);
1d42b86d
MB
432 }
433}
434
1d42b86d 435static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
1d42b86d 436 assert(netdev);
bb4f798a 437 assert(WIREGUARD(netdev));
1d42b86d 438
7c20daf6 439 (void) wireguard_set_interface(netdev);
1d42b86d
MB
440 resolve_endpoints(netdev);
441 return 0;
442}
443
bb4f798a
MB
444int config_parse_wireguard_listen_port(
445 const char *unit,
446 const char *filename,
447 unsigned line,
448 const char *section,
449 unsigned section_line,
450 const char *lvalue,
451 int ltype,
452 const char *rvalue,
453 void *data,
454 void *userdata) {
455
1d42b86d 456 uint16_t *s = data;
1d42b86d
MB
457 int r;
458
459 assert(rvalue);
460 assert(data);
461
f2dec872
BR
462 if (isempty(rvalue) || streq(rvalue, "auto")) {
463 *s = 0;
464 return 0;
465 }
466
467 r = parse_ip_port(rvalue, s);
468 if (r < 0) {
469 log_syntax(unit, LOG_ERR, filename, line, r,
470 "Invalid port specification, ignoring assignment: %s", rvalue);
471 return 0;
1d42b86d
MB
472 }
473
1d42b86d
MB
474 return 0;
475}
476
bb4f798a
MB
477static int wireguard_decode_key_and_warn(
478 const char *rvalue,
479 uint8_t ret[static WG_KEY_LEN],
480 const char *unit,
481 const char *filename,
482 unsigned line,
483 const char *lvalue) {
484
f2dec872 485 _cleanup_(erase_and_freep) void *key = NULL;
1d42b86d
MB
486 size_t len;
487 int r;
488
1d42b86d 489 assert(rvalue);
bb4f798a
MB
490 assert(ret);
491 assert(filename);
492 assert(lvalue);
1d42b86d 493
bb4f798a
MB
494 if (isempty(rvalue)) {
495 memzero(ret, WG_KEY_LEN);
1d42b86d
MB
496 return 0;
497 }
bb4f798a
MB
498
499 if (!streq(lvalue, "PublicKey"))
500 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
501
502 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
503 if (r < 0)
504 return log_syntax(unit, LOG_ERR, filename, line, r,
505 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
f2dec872 506 if (len != WG_KEY_LEN)
bb4f798a
MB
507 return log_syntax(unit, LOG_ERR, filename, line, 0,
508 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
509 lvalue, len);
1d42b86d 510
bb4f798a
MB
511 memcpy(ret, key, WG_KEY_LEN);
512 return 0;
1d42b86d
MB
513}
514
bb4f798a
MB
515int config_parse_wireguard_private_key(
516 const char *unit,
517 const char *filename,
518 unsigned line,
519 const char *section,
520 unsigned section_line,
521 const char *lvalue,
522 int ltype,
523 const char *rvalue,
524 void *data,
525 void *userdata) {
526
1d42b86d
MB
527 Wireguard *w;
528
529 assert(data);
1d42b86d 530 w = WIREGUARD(data);
bb4f798a
MB
531 assert(w);
532
533 (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
534 return 0;
535}
536
537int config_parse_wireguard_private_key_file(
538 const char *unit,
539 const char *filename,
540 unsigned line,
541 const char *section,
542 unsigned section_line,
543 const char *lvalue,
544 int ltype,
545 const char *rvalue,
546 void *data,
547 void *userdata) {
548
549 _cleanup_free_ char *path = NULL;
550 Wireguard *w;
1d42b86d 551
bb4f798a
MB
552 assert(data);
553 w = WIREGUARD(data);
1d42b86d
MB
554 assert(w);
555
bb4f798a
MB
556 if (isempty(rvalue)) {
557 w->private_key_file = mfree(w->private_key_file);
558 return 0;
559 }
560
561 path = strdup(rvalue);
562 if (!path)
563 return log_oom();
564
565 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
566 return 0;
1d42b86d 567
bb4f798a 568 return free_and_replace(w->private_key_file, path);
1d42b86d
MB
569}
570
bb4f798a
MB
571int config_parse_wireguard_preshared_key(
572 const char *unit,
573 const char *filename,
574 unsigned line,
575 const char *section,
576 unsigned section_line,
577 const char *lvalue,
578 int ltype,
579 const char *rvalue,
580 void *data,
581 void *userdata) {
582
583 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1d42b86d 584 Wireguard *w;
bb4f798a 585 int r;
1d42b86d
MB
586
587 assert(data);
1d42b86d 588 w = WIREGUARD(data);
bb4f798a
MB
589 assert(w);
590
591 r = wireguard_peer_new_static(w, filename, section_line, &peer);
592 if (r < 0)
593 return r;
1d42b86d 594
bb4f798a
MB
595 r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
596 if (r < 0)
597 return r;
598
599 TAKE_PTR(peer);
600 return 0;
601}
602
603int config_parse_wireguard_preshared_key_file(
604 const char *unit,
605 const char *filename,
606 unsigned line,
607 const char *section,
608 unsigned section_line,
609 const char *lvalue,
610 int ltype,
611 const char *rvalue,
612 void *data,
613 void *userdata) {
614
615 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
616 _cleanup_free_ char *path = NULL;
617 Wireguard *w;
618 int r;
619
620 assert(data);
621 w = WIREGUARD(data);
1d42b86d
MB
622 assert(w);
623
bb4f798a
MB
624 r = wireguard_peer_new_static(w, filename, section_line, &peer);
625 if (r < 0)
626 return r;
627
628 if (isempty(rvalue)) {
629 peer->preshared_key_file = mfree(peer->preshared_key_file);
630 TAKE_PTR(peer);
631 return 0;
632 }
633
634 path = strdup(rvalue);
635 if (!path)
1d42b86d
MB
636 return log_oom();
637
bb4f798a
MB
638 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
639 return 0;
640
641 free_and_replace(peer->preshared_key_file, path);
642 TAKE_PTR(peer);
643 return 0;
1d42b86d
MB
644}
645
bb4f798a
MB
646int config_parse_wireguard_public_key(
647 const char *unit,
648 const char *filename,
649 unsigned line,
650 const char *section,
651 unsigned section_line,
652 const char *lvalue,
653 int ltype,
654 const char *rvalue,
655 void *data,
656 void *userdata) {
657
658 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1d42b86d 659 Wireguard *w;
bb4f798a 660 int r;
1d42b86d
MB
661
662 assert(data);
1d42b86d 663 w = WIREGUARD(data);
1d42b86d
MB
664 assert(w);
665
bb4f798a
MB
666 r = wireguard_peer_new_static(w, filename, section_line, &peer);
667 if (r < 0)
668 return r;
669
670 r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
671 if (r < 0)
672 return r;
1d42b86d 673
bb4f798a
MB
674 TAKE_PTR(peer);
675 return 0;
1d42b86d
MB
676}
677
bb4f798a
MB
678int config_parse_wireguard_allowed_ips(
679 const char *unit,
680 const char *filename,
681 unsigned line,
682 const char *section,
683 unsigned section_line,
684 const char *lvalue,
685 int ltype,
686 const char *rvalue,
687 void *data,
688 void *userdata) {
689
690 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1d42b86d
MB
691 union in_addr_union addr;
692 unsigned char prefixlen;
693 int r, family;
694 Wireguard *w;
1d42b86d
MB
695 WireguardIPmask *ipmask;
696
697 assert(rvalue);
698 assert(data);
699
700 w = WIREGUARD(data);
bb4f798a 701 assert(w);
1d42b86d 702
bb4f798a
MB
703 r = wireguard_peer_new_static(w, filename, section_line, &peer);
704 if (r < 0)
705 return r;
1d42b86d
MB
706
707 for (;;) {
708 _cleanup_free_ char *word = NULL;
709
710 r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
711 if (r == 0)
712 break;
713 if (r == -ENOMEM)
714 return log_oom();
715 if (r < 0) {
bb4f798a
MB
716 log_syntax(unit, LOG_ERR, filename, line, r,
717 "Failed to split allowed ips \"%s\" option: %m", rvalue);
1d42b86d
MB
718 break;
719 }
720
721 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
722 if (r < 0) {
bb4f798a
MB
723 log_syntax(unit, LOG_ERR, filename, line, r,
724 "Network address is invalid, ignoring assignment: %s", word);
725 continue;
1d42b86d
MB
726 }
727
6e866b33 728 ipmask = new(WireguardIPmask, 1);
1d42b86d
MB
729 if (!ipmask)
730 return log_oom();
6e866b33
MB
731
732 *ipmask = (WireguardIPmask) {
733 .family = family,
734 .ip.in6 = addr.in6,
735 .cidr = prefixlen,
736 };
1d42b86d
MB
737
738 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
739 }
740
bb4f798a 741 TAKE_PTR(peer);
1d42b86d
MB
742 return 0;
743}
744
bb4f798a
MB
745int config_parse_wireguard_endpoint(
746 const char *unit,
747 const char *filename,
748 unsigned line,
749 const char *section,
750 unsigned section_line,
751 const char *lvalue,
752 int ltype,
753 const char *rvalue,
754 void *data,
755 void *userdata) {
756
757 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
758 const char *begin, *end;
1d42b86d 759 Wireguard *w;
1d42b86d 760 size_t len;
bb4f798a 761 int r;
1d42b86d
MB
762
763 assert(data);
764 assert(rvalue);
765
766 w = WIREGUARD(data);
1d42b86d
MB
767 assert(w);
768
bb4f798a
MB
769 r = wireguard_peer_new_static(w, filename, section_line, &peer);
770 if (r < 0)
771 return r;
1d42b86d 772
1d42b86d
MB
773 if (rvalue[0] == '[') {
774 begin = &rvalue[1];
775 end = strchr(rvalue, ']');
776 if (!end) {
bb4f798a
MB
777 log_syntax(unit, LOG_ERR, filename, line, 0,
778 "Unable to find matching brace of endpoint, ignoring assignment: %s",
779 rvalue);
1d42b86d
MB
780 return 0;
781 }
782 len = end - begin;
783 ++end;
784 if (*end != ':' || !*(end + 1)) {
bb4f798a
MB
785 log_syntax(unit, LOG_ERR, filename, line, 0,
786 "Unable to find port of endpoint, ignoring assignment: %s",
787 rvalue);
1d42b86d
MB
788 return 0;
789 }
790 ++end;
791 } else {
792 begin = rvalue;
793 end = strrchr(rvalue, ':');
794 if (!end || !*(end + 1)) {
bb4f798a
MB
795 log_syntax(unit, LOG_ERR, filename, line, 0,
796 "Unable to find port of endpoint, ignoring assignment: %s",
797 rvalue);
1d42b86d
MB
798 return 0;
799 }
800 len = end - begin;
801 ++end;
802 }
803
bb4f798a
MB
804 r = free_and_strndup(&peer->endpoint_host, begin, len);
805 if (r < 0)
1d42b86d
MB
806 return log_oom();
807
bb4f798a
MB
808 r = free_and_strdup(&peer->endpoint_port, end);
809 if (r < 0)
1d42b86d
MB
810 return log_oom();
811
bb4f798a
MB
812 r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL);
813 if (r < 0)
6e866b33
MB
814 return log_oom();
815
bb4f798a
MB
816 r = set_put(w->peers_with_unresolved_endpoint, peer);
817 if (r < 0)
818 return r;
1d42b86d 819
bb4f798a 820 TAKE_PTR(peer);
1d42b86d
MB
821 return 0;
822}
823
bb4f798a
MB
824int config_parse_wireguard_keepalive(
825 const char *unit,
826 const char *filename,
827 unsigned line,
828 const char *section,
829 unsigned section_line,
830 const char *lvalue,
831 int ltype,
832 const char *rvalue,
833 void *data,
834 void *userdata) {
835
836 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1d42b86d
MB
837 uint16_t keepalive = 0;
838 Wireguard *w;
bb4f798a 839 int r;
1d42b86d
MB
840
841 assert(rvalue);
842 assert(data);
843
844 w = WIREGUARD(data);
1d42b86d
MB
845 assert(w);
846
bb4f798a
MB
847 r = wireguard_peer_new_static(w, filename, section_line, &peer);
848 if (r < 0)
849 return r;
1d42b86d
MB
850
851 if (streq(rvalue, "off"))
852 keepalive = 0;
853 else {
854 r = safe_atou16(rvalue, &keepalive);
bb4f798a
MB
855 if (r < 0) {
856 log_syntax(unit, LOG_ERR, filename, line, r,
857 "The persistent keepalive interval must be 0-65535. Ignore assignment: %s",
858 rvalue);
859 return 0;
860 }
1d42b86d
MB
861 }
862
863 peer->persistent_keepalive_interval = keepalive;
bb4f798a
MB
864
865 TAKE_PTR(peer);
1d42b86d
MB
866 return 0;
867}
868
869static void wireguard_init(NetDev *netdev) {
870 Wireguard *w;
871
872 assert(netdev);
1d42b86d 873 w = WIREGUARD(netdev);
1d42b86d
MB
874 assert(w);
875
876 w->flags = WGDEVICE_F_REPLACE_PEERS;
877}
878
879static void wireguard_done(NetDev *netdev) {
880 Wireguard *w;
1d42b86d
MB
881
882 assert(netdev);
883 w = WIREGUARD(netdev);
6e866b33 884 assert(w);
1d42b86d 885
bb4f798a 886 sd_event_source_unref(w->resolve_retry_event_source);
6e866b33 887
bb4f798a
MB
888 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
889 free(w->private_key_file);
890
891 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
892 set_free(w->peers_with_unresolved_endpoint);
893 set_free(w->peers_with_failed_endpoint);
894}
895
896static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
f2dec872 897 _cleanup_(erase_and_freep) char *key = NULL;
bb4f798a
MB
898 size_t key_len;
899 int r;
900
901 if (!filename)
902 return 0;
6e866b33 903
f2dec872
BR
904 assert(dest);
905
e1f67bc7
MB
906 (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
907
46cdbd49 908 r = read_full_file_full(AT_FDCWD, filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
bb4f798a
MB
909 if (r < 0)
910 return r;
911
f2dec872
BR
912 if (key_len != WG_KEY_LEN)
913 return -EINVAL;
bb4f798a
MB
914
915 memcpy(dest, key, WG_KEY_LEN);
f2dec872 916 return 0;
bb4f798a
MB
917}
918
919static int wireguard_peer_verify(WireguardPeer *peer) {
920 NetDev *netdev = NETDEV(peer->wireguard);
921 int r;
922
923 if (section_is_invalid(peer->section))
924 return -EINVAL;
925
926 if (eqzero(peer->public_key))
927 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
928 "%s: WireGuardPeer section without PublicKey= configured. "
929 "Ignoring [WireGuardPeer] section from line %u.",
930 peer->section->filename, peer->section->line);
931
932 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
933 if (r < 0)
934 return log_netdev_error_errno(netdev, r,
935 "%s: Failed to read preshared key from '%s'. "
936 "Ignoring [WireGuardPeer] section from line %u.",
937 peer->section->filename, peer->preshared_key_file,
938 peer->section->line);
939
940 return 0;
941}
942
943static int wireguard_verify(NetDev *netdev, const char *filename) {
944 WireguardPeer *peer, *peer_next;
945 Wireguard *w;
946 int r;
947
948 assert(netdev);
949 w = WIREGUARD(netdev);
950 assert(w);
951
952 r = wireguard_read_key_file(w->private_key_file, w->private_key);
953 if (r < 0)
954 return log_netdev_error_errno(netdev, r,
955 "Failed to read private key from %s. Dropping network device %s.",
956 w->private_key_file, netdev->ifname);
957
958 if (eqzero(w->private_key))
959 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
960 "%s: Missing PrivateKey= or PrivateKeyFile=, "
961 "Dropping network device %s.",
962 filename, netdev->ifname);
963
964 LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
965 if (wireguard_peer_verify(peer) < 0)
966 wireguard_peer_free(peer);
967
968 return 0;
1d42b86d
MB
969}
970
971const NetDevVTable wireguard_vtable = {
972 .object_size = sizeof(Wireguard),
46cdbd49 973 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
1d42b86d
MB
974 .post_create = netdev_wireguard_post_create,
975 .init = wireguard_init,
976 .done = wireguard_done,
977 .create_type = NETDEV_CREATE_INDEPENDENT,
bb4f798a 978 .config_verify = wireguard_verify,
f2dec872 979 .generate_mac = true,
1d42b86d 980};