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