1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4 ***/
5
6 #include <sys/ioctl.h>
7 #include <net/if.h>
8 #include <netinet/in.h>
9 #include <linux/if_arp.h>
10 #include <linux/ipv6_route.h>
11
12 #include "sd-resolve.h"
13
14 #include "alloc-util.h"
15 #include "dns-domain.h"
16 #include "event-util.h"
17 #include "fd-util.h"
18 #include "fileio.h"
19 #include "hexdecoct.h"
20 #include "memory-util.h"
21 #include "netlink-util.h"
22 #include "networkd-manager.h"
23 #include "networkd-route-util.h"
24 #include "networkd-route.h"
25 #include "networkd-util.h"
26 #include "parse-helpers.h"
27 #include "parse-util.h"
28 #include "random-util.h"
29 #include "resolve-private.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "wireguard.h"
33
34 static void wireguard_resolve_endpoints(NetDev *netdev);
35 static int peer_resolve_endpoint(WireguardPeer *peer);
36
wireguard_peer_free(WireguardPeer * peer)37 static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
38 WireguardIPmask *mask;
39
40 if (!peer)
41 return NULL;
42
43 if (peer->wireguard) {
44 LIST_REMOVE(peers, peer->wireguard->peers, peer);
45
46 if (peer->section)
47 hashmap_remove(peer->wireguard->peers_by_section, peer->section);
48 }
49
50 config_section_free(peer->section);
51
52 while ((mask = peer->ipmasks)) {
53 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
54 free(mask);
55 }
56
57 free(peer->endpoint_host);
58 free(peer->endpoint_port);
59 free(peer->preshared_key_file);
60 explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN);
61
62 sd_event_source_disable_unref(peer->resolve_retry_event_source);
63 sd_resolve_query_unref(peer->resolve_query);
64
65 return mfree(peer);
66 }
67
68 DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free);
69
wireguard_peer_new_static(Wireguard * w,const char * filename,unsigned section_line,WireguardPeer ** ret)70 static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
71 _cleanup_(config_section_freep) ConfigSection *n = NULL;
72 _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
73 int r;
74
75 assert(w);
76 assert(ret);
77 assert(filename);
78 assert(section_line > 0);
79
80 r = config_section_new(filename, section_line, &n);
81 if (r < 0)
82 return r;
83
84 peer = hashmap_get(w->peers_by_section, n);
85 if (peer) {
86 *ret = TAKE_PTR(peer);
87 return 0;
88 }
89
90 peer = new(WireguardPeer, 1);
91 if (!peer)
92 return -ENOMEM;
93
94 *peer = (WireguardPeer) {
95 .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
96 .wireguard = w,
97 .section = TAKE_PTR(n),
98 };
99
100 LIST_PREPEND(peers, w->peers, peer);
101
102 r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer);
103 if (r < 0)
104 return r;
105
106 *ret = TAKE_PTR(peer);
107 return 0;
108 }
109
wireguard_set_ipmask_one(NetDev * netdev,sd_netlink_message * message,const WireguardIPmask * mask,uint16_t index)110 static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
111 int r;
112
113 assert(message);
114 assert(mask);
115 assert(index > 0);
116
117 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
118
119 r = sd_netlink_message_open_array(message, index);
120 if (r < 0)
121 return 0;
122
123 r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
124 if (r < 0)
125 goto cancel;
126
127 r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip);
128 if (r < 0)
129 goto cancel;
130
131 r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
132 if (r < 0)
133 goto cancel;
134
135 r = sd_netlink_message_close_container(message);
136 if (r < 0)
137 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
138
139 return 1;
140
141 cancel:
142 r = sd_netlink_message_cancel_array(message);
143 if (r < 0)
144 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
145
146 return 0;
147 }
148
wireguard_set_peer_one(NetDev * netdev,sd_netlink_message * message,const WireguardPeer * peer,uint16_t index,WireguardIPmask ** mask_start)149 static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
150 WireguardIPmask *start, *last = NULL;
151 uint16_t j = 0;
152 int r;
153
154 assert(message);
155 assert(peer);
156 assert(index > 0);
157 assert(mask_start);
158
159 /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */
160
161 start = *mask_start ?: peer->ipmasks;
162
163 r = sd_netlink_message_open_array(message, index);
164 if (r < 0)
165 return 0;
166
167 r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
168 if (r < 0)
169 goto cancel;
170
171 if (!*mask_start) {
172 r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
173 if (r < 0)
174 goto cancel;
175
176 r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
177 if (r < 0)
178 goto cancel;
179
180 r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
181 if (r < 0)
182 goto cancel;
183
184 if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) {
185 r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint);
186 if (r < 0)
187 goto cancel;
188 }
189 }
190
191 r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
192 if (r < 0)
193 goto cancel;
194
195 LIST_FOREACH(ipmasks, mask, start) {
196 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
197 if (r < 0)
198 return r;
199 if (r == 0) {
200 last = mask;
201 break;
202 }
203 }
204
205 r = sd_netlink_message_close_container(message);
206 if (r < 0)
207 return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
208
209 r = sd_netlink_message_close_container(message);
210 if (r < 0)
211 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
212
213 *mask_start = last; /* Start next cycle from this mask. */
214 return !last;
215
216 cancel:
217 r = sd_netlink_message_cancel_array(message);
218 if (r < 0)
219 return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
220
221 return 0;
222 }
223
wireguard_set_interface(NetDev * netdev)224 static int wireguard_set_interface(NetDev *netdev) {
225 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
226 WireguardIPmask *mask_start = NULL;
227 WireguardPeer *peer_start;
228 bool sent_once = false;
229 uint32_t serial;
230 Wireguard *w;
231 int r;
232
233 assert(netdev);
234 w = WIREGUARD(netdev);
235 assert(w);
236
237 for (peer_start = w->peers; peer_start || !sent_once; ) {
238 uint16_t i = 0;
239
240 message = sd_netlink_message_unref(message);
241
242 r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
243 if (r < 0)
244 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
245
246 r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
247 if (r < 0)
248 return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
249
250 if (peer_start == w->peers) {
251 r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
252 if (r < 0)
253 return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
254
255 r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
256 if (r < 0)
257 return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
258
259 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
260 if (r < 0)
261 return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
262
263 r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
264 if (r < 0)
265 return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
266 }
267
268 r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
269 if (r < 0)
270 return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
271
272 WireguardPeer *peer_last = NULL;
273 LIST_FOREACH(peers, peer, peer_start) {
274 r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
275 if (r < 0)
276 return r;
277 if (r == 0) {
278 peer_last = peer;
279 break;
280 }
281 }
282 peer_start = peer_last; /* Start next cycle from this peer. */
283
284 r = sd_netlink_message_close_container(message);
285 if (r < 0)
286 return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
287
288 r = sd_netlink_send(netdev->manager->genl, message, &serial);
289 if (r < 0)
290 return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
291
292 sent_once = true;
293 }
294
295 return 0;
296 }
297
on_resolve_retry(sd_event_source * s,usec_t usec,void * userdata)298 static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
299 WireguardPeer *peer = userdata;
300 NetDev *netdev;
301
302 assert(peer);
303 assert(peer->wireguard);
304
305 netdev = NETDEV(peer->wireguard);
306
307 if (!netdev_is_managed(netdev))
308 return 0;
309
310 peer->resolve_query = sd_resolve_query_unref(peer->resolve_query);
311
312 (void) peer_resolve_endpoint(peer);
313 return 0;
314 }
315
peer_next_resolve_usec(WireguardPeer * peer)316 static usec_t peer_next_resolve_usec(WireguardPeer *peer) {
317 usec_t usec;
318
319 /* Given the number of retries this function will return an exponential increasing amount of
320 * milliseconds to wait starting at 200ms and capped at 25 seconds. */
321
322 assert(peer);
323
324 usec = (2 << MIN(peer->n_retries, 7U)) * 100 * USEC_PER_MSEC;
325
326 return random_u64_range(usec / 10) + usec * 9 / 10;
327 }
328
wireguard_peer_resolve_handler(sd_resolve_query * q,int ret,const struct addrinfo * ai,void * userdata)329 static int wireguard_peer_resolve_handler(
330 sd_resolve_query *q,
331 int ret,
332 const struct addrinfo *ai,
333 void *userdata) {
334
335 WireguardPeer *peer = userdata;
336 NetDev *netdev;
337 int r;
338
339 assert(peer);
340 assert(peer->wireguard);
341
342 netdev = NETDEV(peer->wireguard);
343
344 if (!netdev_is_managed(netdev))
345 return 0;
346
347 if (ret != 0) {
348 log_netdev_warning(netdev, "Failed to resolve host '%s:%s', ignoring: %s",
349 peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
350 peer->n_retries++;
351
352 } else {
353 bool found = false;
354 for (; ai; ai = ai->ai_next) {
355 if (!IN_SET(ai->ai_family, AF_INET, AF_INET6))
356 continue;
357
358 if (ai->ai_addrlen != (ai->ai_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)))
359 continue;
360
361 memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
362 (void) wireguard_set_interface(netdev);
363 peer->n_retries = 0;
364 found = true;
365 break;
366 }
367
368 if (!found) {
369 log_netdev_warning(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the endpoint.",
370 peer->endpoint_host, peer->endpoint_port);
371 peer->n_retries++;
372 }
373 }
374
375 if (peer->n_retries > 0) {
376 r = event_reset_time_relative(netdev->manager->event,
377 &peer->resolve_retry_event_source,
378 CLOCK_BOOTTIME,
379 peer_next_resolve_usec(peer), 0,
380 on_resolve_retry, peer, 0, "wireguard-resolve-retry", true);
381 if (r < 0)
382 log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler for endpoint %s:%s, ignoring: %m",
383 peer->endpoint_host, peer->endpoint_port);
384 }
385
386 wireguard_resolve_endpoints(netdev);
387 return 0;
388 }
389
peer_resolve_endpoint(WireguardPeer * peer)390 static int peer_resolve_endpoint(WireguardPeer *peer) {
391 static const struct addrinfo hints = {
392 .ai_family = AF_UNSPEC,
393 .ai_socktype = SOCK_DGRAM,
394 .ai_protocol = IPPROTO_UDP
395 };
396 NetDev *netdev;
397 int r;
398
399 assert(peer);
400 assert(peer->wireguard);
401
402 netdev = NETDEV(peer->wireguard);
403
404 if (!peer->endpoint_host || !peer->endpoint_port)
405 /* Not necessary to resolve the endpoint. */
406 return 0;
407
408 if (event_source_is_enabled(peer->resolve_retry_event_source) > 0)
409 /* Timer event source is enabled. The endpoint will be resolved later. */
410 return 0;
411
412 if (peer->resolve_query)
413 /* Being resolved, or already resolved. */
414 return 0;
415
416 r = sd_resolve_getaddrinfo(netdev->manager->resolve,
417 &peer->resolve_query,
418 peer->endpoint_host,
419 peer->endpoint_port,
420 &hints,
421 wireguard_peer_resolve_handler,
422 peer);
423 if (r < 0)
424 return log_netdev_full_errno(netdev, r == -ENOBUFS ? LOG_DEBUG : LOG_WARNING, r,
425 "Failed to create endpoint resolver for %s:%s, ignoring: %m",
426 peer->endpoint_host, peer->endpoint_port);
427
428 return 0;
429 }
430
wireguard_resolve_endpoints(NetDev * netdev)431 static void wireguard_resolve_endpoints(NetDev *netdev) {
432 Wireguard *w;
433
434 assert(netdev);
435 w = WIREGUARD(netdev);
436 assert(w);
437
438 LIST_FOREACH(peers, peer, w->peers)
439 if (peer_resolve_endpoint(peer) == -ENOBUFS)
440 /* Too many requests. Let's resolve remaining endpoints later. */
441 break;
442 }
443
netdev_wireguard_post_create(NetDev * netdev,Link * link)444 static int netdev_wireguard_post_create(NetDev *netdev, Link *link) {
445 assert(netdev);
446 assert(WIREGUARD(netdev));
447
448 (void) wireguard_set_interface(netdev);
449 wireguard_resolve_endpoints(netdev);
450 return 0;
451 }
452
config_parse_wireguard_listen_port(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)453 int config_parse_wireguard_listen_port(
454 const char *unit,
455 const char *filename,
456 unsigned line,
457 const char *section,
458 unsigned section_line,
459 const char *lvalue,
460 int ltype,
461 const char *rvalue,
462 void *data,
463 void *userdata) {
464
465 uint16_t *s = data;
466 int r;
467
468 assert(rvalue);
469 assert(data);
470
471 if (isempty(rvalue) || streq(rvalue, "auto")) {
472 *s = 0;
473 return 0;
474 }
475
476 r = parse_ip_port(rvalue, s);
477 if (r < 0) {
478 log_syntax(unit, LOG_WARNING, filename, line, r,
479 "Invalid port specification, ignoring assignment: %s", rvalue);
480 return 0;
481 }
482
483 return 0;
484 }
485
wireguard_decode_key_and_warn(const char * rvalue,uint8_t ret[static WG_KEY_LEN],const char * unit,const char * filename,unsigned line,const char * lvalue)486 static int wireguard_decode_key_and_warn(
487 const char *rvalue,
488 uint8_t ret[static WG_KEY_LEN],
489 const char *unit,
490 const char *filename,
491 unsigned line,
492 const char *lvalue) {
493
494 _cleanup_(erase_and_freep) void *key = NULL;
495 size_t len;
496 int r;
497
498 assert(rvalue);
499 assert(ret);
500 assert(filename);
501 assert(lvalue);
502
503 if (isempty(rvalue)) {
504 memzero(ret, WG_KEY_LEN);
505 return 0;
506 }
507
508 if (!streq(lvalue, "PublicKey"))
509 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
510
511 r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
512 if (r == -ENOMEM)
513 return log_oom();
514 if (r < 0) {
515 log_syntax(unit, LOG_WARNING, filename, line, r,
516 "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
517 return 0;
518 }
519 if (len != WG_KEY_LEN) {
520 log_syntax(unit, LOG_WARNING, filename, line, 0,
521 "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
522 lvalue, len);
523 return 0;
524 }
525
526 memcpy(ret, key, WG_KEY_LEN);
527 return 0;
528 }
529
config_parse_wireguard_private_key(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)530 int config_parse_wireguard_private_key(
531 const char *unit,
532 const char *filename,
533 unsigned line,
534 const char *section,
535 unsigned section_line,
536 const char *lvalue,
537 int ltype,
538 const char *rvalue,
539 void *data,
540 void *userdata) {
541
542 Wireguard *w;
543
544 assert(data);
545 w = WIREGUARD(data);
546 assert(w);
547
548 return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
549 }
550
config_parse_wireguard_private_key_file(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)551 int config_parse_wireguard_private_key_file(
552 const char *unit,
553 const char *filename,
554 unsigned line,
555 const char *section,
556 unsigned section_line,
557 const char *lvalue,
558 int ltype,
559 const char *rvalue,
560 void *data,
561 void *userdata) {
562
563 _cleanup_free_ char *path = NULL;
564 Wireguard *w;
565
566 assert(data);
567 w = WIREGUARD(data);
568 assert(w);
569
570 if (isempty(rvalue)) {
571 w->private_key_file = mfree(w->private_key_file);
572 return 0;
573 }
574
575 path = strdup(rvalue);
576 if (!path)
577 return log_oom();
578
579 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
580 return 0;
581
582 return free_and_replace(w->private_key_file, path);
583 }
584
config_parse_wireguard_peer_key(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)585 int config_parse_wireguard_peer_key(
586 const char *unit,
587 const char *filename,
588 unsigned line,
589 const char *section,
590 unsigned section_line,
591 const char *lvalue,
592 int ltype,
593 const char *rvalue,
594 void *data,
595 void *userdata) {
596
597 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
598 Wireguard *w;
599 int r;
600
601 assert(data);
602 w = WIREGUARD(data);
603 assert(w);
604
605 r = wireguard_peer_new_static(w, filename, section_line, &peer);
606 if (r < 0)
607 return log_oom();
608
609 r = wireguard_decode_key_and_warn(rvalue,
610 streq(lvalue, "PublicKey") ? peer->public_key : peer->preshared_key,
611 unit, filename, line, lvalue);
612 if (r < 0)
613 return r;
614
615 TAKE_PTR(peer);
616 return 0;
617 }
618
config_parse_wireguard_preshared_key_file(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)619 int config_parse_wireguard_preshared_key_file(
620 const char *unit,
621 const char *filename,
622 unsigned line,
623 const char *section,
624 unsigned section_line,
625 const char *lvalue,
626 int ltype,
627 const char *rvalue,
628 void *data,
629 void *userdata) {
630
631 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
632 _cleanup_free_ char *path = NULL;
633 Wireguard *w;
634 int r;
635
636 assert(data);
637 w = WIREGUARD(data);
638 assert(w);
639
640 r = wireguard_peer_new_static(w, filename, section_line, &peer);
641 if (r < 0)
642 return log_oom();
643
644 if (isempty(rvalue)) {
645 peer->preshared_key_file = mfree(peer->preshared_key_file);
646 TAKE_PTR(peer);
647 return 0;
648 }
649
650 path = strdup(rvalue);
651 if (!path)
652 return log_oom();
653
654 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
655 return 0;
656
657 free_and_replace(peer->preshared_key_file, path);
658 TAKE_PTR(peer);
659 return 0;
660 }
661
config_parse_wireguard_allowed_ips(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)662 int config_parse_wireguard_allowed_ips(
663 const char *unit,
664 const char *filename,
665 unsigned line,
666 const char *section,
667 unsigned section_line,
668 const char *lvalue,
669 int ltype,
670 const char *rvalue,
671 void *data,
672 void *userdata) {
673
674 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
675 union in_addr_union addr;
676 unsigned char prefixlen;
677 int r, family;
678 Wireguard *w;
679 WireguardIPmask *ipmask;
680
681 assert(rvalue);
682 assert(data);
683
684 w = WIREGUARD(data);
685 assert(w);
686
687 r = wireguard_peer_new_static(w, filename, section_line, &peer);
688 if (r < 0)
689 return log_oom();
690
691 for (const char *p = rvalue;;) {
692 _cleanup_free_ char *word = NULL;
693 union in_addr_union masked;
694
695 r = extract_first_word(&p, &word, "," WHITESPACE, 0);
696 if (r == 0)
697 break;
698 if (r == -ENOMEM)
699 return log_oom();
700 if (r < 0) {
701 log_syntax(unit, LOG_WARNING, filename, line, r,
702 "Failed to split allowed ips \"%s\" option: %m", rvalue);
703 break;
704 }
705
706 r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
707 if (r < 0) {
708 log_syntax(unit, LOG_WARNING, filename, line, r,
709 "Network address is invalid, ignoring assignment: %s", word);
710 continue;
711 }
712
713 masked = addr;
714 assert_se(in_addr_mask(family, &masked, prefixlen) >= 0);
715 if (!in_addr_equal(family, &masked, &addr)) {
716 _cleanup_free_ char *buf = NULL;
717
718 (void) in_addr_prefix_to_string(family, &masked, prefixlen, &buf);
719 log_syntax(unit, LOG_WARNING, filename, line, 0,
720 "Specified address '%s' is not properly masked, assuming '%s'.", word, strna(buf));
721 }
722
723 ipmask = new(WireguardIPmask, 1);
724 if (!ipmask)
725 return log_oom();
726
727 *ipmask = (WireguardIPmask) {
728 .family = family,
729 .ip = masked,
730 .cidr = prefixlen,
731 };
732
733 LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
734 }
735
736 TAKE_PTR(peer);
737 return 0;
738 }
739
config_parse_wireguard_endpoint(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)740 int config_parse_wireguard_endpoint(
741 const char *unit,
742 const char *filename,
743 unsigned line,
744 const char *section,
745 unsigned section_line,
746 const char *lvalue,
747 int ltype,
748 const char *rvalue,
749 void *data,
750 void *userdata) {
751
752 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
753 _cleanup_free_ char *host = NULL;
754 union in_addr_union addr;
755 const char *p;
756 uint16_t port;
757 Wireguard *w;
758 int family, r;
759
760 assert(filename);
761 assert(rvalue);
762 assert(userdata);
763
764 w = WIREGUARD(userdata);
765 assert(w);
766
767 r = wireguard_peer_new_static(w, filename, section_line, &peer);
768 if (r < 0)
769 return log_oom();
770
771 r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
772 if (r >= 0) {
773 if (family == AF_INET)
774 peer->endpoint.in = (struct sockaddr_in) {
775 .sin_family = AF_INET,
776 .sin_addr = addr.in,
777 .sin_port = htobe16(port),
778 };
779 else if (family == AF_INET6)
780 peer->endpoint.in6 = (struct sockaddr_in6) {
781 .sin6_family = AF_INET6,
782 .sin6_addr = addr.in6,
783 .sin6_port = htobe16(port),
784 };
785 else
786 assert_not_reached();
787
788 peer->endpoint_host = mfree(peer->endpoint_host);
789 peer->endpoint_port = mfree(peer->endpoint_port);
790
791 TAKE_PTR(peer);
792 return 0;
793 }
794
795 p = strrchr(rvalue, ':');
796 if (!p) {
797 log_syntax(unit, LOG_WARNING, filename, line, 0,
798 "Unable to find port of endpoint, ignoring assignment: %s",
799 rvalue);
800 return 0;
801 }
802
803 host = strndup(rvalue, p - rvalue);
804 if (!host)
805 return log_oom();
806
807 if (!dns_name_is_valid(host)) {
808 log_syntax(unit, LOG_WARNING, filename, line, 0,
809 "Invalid domain name of endpoint, ignoring assignment: %s",
810 rvalue);
811 return 0;
812 }
813
814 p++;
815 r = parse_ip_port(p, &port);
816 if (r < 0) {
817 log_syntax(unit, LOG_WARNING, filename, line, r,
818 "Invalid port of endpoint, ignoring assignment: %s",
819 rvalue);
820 return 0;
821 }
822
823 peer->endpoint = (union sockaddr_union) {};
824
825 free_and_replace(peer->endpoint_host, host);
826
827 r = free_and_strdup(&peer->endpoint_port, p);
828 if (r < 0)
829 return log_oom();
830
831 TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
832 return 0;
833 }
834
config_parse_wireguard_keepalive(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)835 int config_parse_wireguard_keepalive(
836 const char *unit,
837 const char *filename,
838 unsigned line,
839 const char *section,
840 unsigned section_line,
841 const char *lvalue,
842 int ltype,
843 const char *rvalue,
844 void *data,
845 void *userdata) {
846
847 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
848 uint16_t keepalive = 0;
849 Wireguard *w;
850 int r;
851
852 assert(rvalue);
853 assert(data);
854
855 w = WIREGUARD(data);
856 assert(w);
857
858 r = wireguard_peer_new_static(w, filename, section_line, &peer);
859 if (r < 0)
860 return log_oom();
861
862 if (streq(rvalue, "off"))
863 keepalive = 0;
864 else {
865 r = safe_atou16(rvalue, &keepalive);
866 if (r < 0) {
867 log_syntax(unit, LOG_WARNING, filename, line, r,
868 "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
869 rvalue);
870 return 0;
871 }
872 }
873
874 peer->persistent_keepalive_interval = keepalive;
875
876 TAKE_PTR(peer);
877 return 0;
878 }
879
config_parse_wireguard_route_table(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)880 int config_parse_wireguard_route_table(
881 const char *unit,
882 const char *filename,
883 unsigned line,
884 const char *section,
885 unsigned section_line,
886 const char *lvalue,
887 int ltype,
888 const char *rvalue,
889 void *data,
890 void *userdata) {
891
892 NetDev *netdev = userdata;
893 uint32_t *table = data;
894 int r;
895
896 assert(filename);
897 assert(lvalue);
898 assert(rvalue);
899 assert(data);
900 assert(userdata);
901
902 if (isempty(rvalue) || parse_boolean(rvalue) == 0) {
903 *table = 0; /* Disabled. */
904 return 0;
905 }
906
907 r = manager_get_route_table_from_string(netdev->manager, rvalue, table);
908 if (r < 0) {
909 log_syntax(unit, LOG_WARNING, filename, line, r,
910 "Failed to parse %s=, ignoring assignment: %s",
911 lvalue, rvalue);
912 return 0;
913 }
914
915 return 0;
916 }
917
config_parse_wireguard_peer_route_table(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)918 int config_parse_wireguard_peer_route_table(
919 const char *unit,
920 const char *filename,
921 unsigned line,
922 const char *section,
923 unsigned section_line,
924 const char *lvalue,
925 int ltype,
926 const char *rvalue,
927 void *data,
928 void *userdata) {
929
930 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
931 NetDev *netdev = userdata;
932 Wireguard *w;
933 int r;
934
935 assert(filename);
936 assert(lvalue);
937 assert(rvalue);
938 assert(netdev);
939 assert(netdev->manager);
940
941 w = WIREGUARD(netdev);
942 assert(w);
943
944 r = wireguard_peer_new_static(w, filename, section_line, &peer);
945 if (r < 0)
946 return log_oom();
947
948 if (isempty(rvalue)) {
949 peer->route_table_set = false; /* Use the table specified in [WireGuard] section. */
950 TAKE_PTR(peer);
951 return 0;
952 }
953
954 if (parse_boolean(rvalue) == 0) {
955 peer->route_table = 0; /* Disabled. */
956 peer->route_table_set = true;
957 TAKE_PTR(peer);
958 return 0;
959 }
960
961 r = manager_get_route_table_from_string(netdev->manager, rvalue, &peer->route_table);
962 if (r < 0) {
963 log_syntax(unit, LOG_WARNING, filename, line, r,
964 "Failed to parse %s=, ignoring assignment: %s",
965 lvalue, rvalue);
966 return 0;
967 }
968
969 peer->route_table_set = true;
970 TAKE_PTR(peer);
971 return 0;
972 }
973
config_parse_wireguard_route_priority(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)974 int config_parse_wireguard_route_priority(
975 const char *unit,
976 const char *filename,
977 unsigned line,
978 const char *section,
979 unsigned section_line,
980 const char *lvalue,
981 int ltype,
982 const char *rvalue,
983 void *data,
984 void *userdata) {
985
986 uint32_t *priority = data;
987 int r;
988
989 assert(filename);
990 assert(lvalue);
991 assert(rvalue);
992 assert(data);
993
994 if (isempty(rvalue)) {
995 *priority = 0;
996 return 0;
997 }
998
999 r = safe_atou32(rvalue, priority);
1000 if (r < 0) {
1001 log_syntax(unit, LOG_WARNING, filename, line, r,
1002 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1003 return 0;
1004 }
1005
1006 return 0;
1007 }
1008
config_parse_wireguard_peer_route_priority(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1009 int config_parse_wireguard_peer_route_priority(
1010 const char *unit,
1011 const char *filename,
1012 unsigned line,
1013 const char *section,
1014 unsigned section_line,
1015 const char *lvalue,
1016 int ltype,
1017 const char *rvalue,
1018 void *data,
1019 void *userdata) {
1020
1021 _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
1022 Wireguard *w;
1023 int r;
1024
1025 assert(filename);
1026 assert(lvalue);
1027 assert(rvalue);
1028 assert(userdata);
1029
1030 w = WIREGUARD(userdata);
1031 assert(w);
1032
1033 r = wireguard_peer_new_static(w, filename, section_line, &peer);
1034 if (r < 0)
1035 return log_oom();
1036
1037 if (isempty(rvalue)) {
1038 peer->route_priority_set = false; /* Use the priority specified in [WireGuard] section. */
1039 TAKE_PTR(peer);
1040 return 0;
1041 }
1042
1043 r = safe_atou32(rvalue, &peer->route_priority);
1044 if (r < 0) {
1045 log_syntax(unit, LOG_WARNING, filename, line, r,
1046 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1047 return 0;
1048 }
1049
1050 peer->route_priority_set = true;
1051 TAKE_PTR(peer);
1052 return 0;
1053 }
1054
wireguard_init(NetDev * netdev)1055 static void wireguard_init(NetDev *netdev) {
1056 Wireguard *w;
1057
1058 assert(netdev);
1059 w = WIREGUARD(netdev);
1060 assert(w);
1061
1062 w->flags = WGDEVICE_F_REPLACE_PEERS;
1063 }
1064
wireguard_done(NetDev * netdev)1065 static void wireguard_done(NetDev *netdev) {
1066 Wireguard *w;
1067
1068 assert(netdev);
1069 w = WIREGUARD(netdev);
1070 assert(w);
1071
1072 explicit_bzero_safe(w->private_key, WG_KEY_LEN);
1073 free(w->private_key_file);
1074
1075 hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
1076
1077 set_free(w->routes);
1078 }
1079
wireguard_read_key_file(const char * filename,uint8_t dest[static WG_KEY_LEN])1080 static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
1081 _cleanup_(erase_and_freep) char *key = NULL;
1082 size_t key_len;
1083 int r;
1084
1085 if (!filename)
1086 return 0;
1087
1088 assert(dest);
1089
1090 (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
1091
1092 r = read_full_file_full(
1093 AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
1094 READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
1095 NULL, &key, &key_len);
1096 if (r < 0)
1097 return r;
1098
1099 if (key_len != WG_KEY_LEN)
1100 return -EINVAL;
1101
1102 memcpy(dest, key, WG_KEY_LEN);
1103 return 0;
1104 }
1105
wireguard_peer_verify(WireguardPeer * peer)1106 static int wireguard_peer_verify(WireguardPeer *peer) {
1107 NetDev *netdev = NETDEV(peer->wireguard);
1108 int r;
1109
1110 if (section_is_invalid(peer->section))
1111 return -EINVAL;
1112
1113 if (eqzero(peer->public_key))
1114 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1115 "%s: WireGuardPeer section without PublicKey= configured. "
1116 "Ignoring [WireGuardPeer] section from line %u.",
1117 peer->section->filename, peer->section->line);
1118
1119 r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key);
1120 if (r < 0)
1121 return log_netdev_error_errno(netdev, r,
1122 "%s: Failed to read preshared key from '%s'. "
1123 "Ignoring [WireGuardPeer] section from line %u.",
1124 peer->section->filename, peer->preshared_key_file,
1125 peer->section->line);
1126
1127 return 0;
1128 }
1129
wireguard_verify(NetDev * netdev,const char * filename)1130 static int wireguard_verify(NetDev *netdev, const char *filename) {
1131 Wireguard *w;
1132 int r;
1133
1134 assert(netdev);
1135 w = WIREGUARD(netdev);
1136 assert(w);
1137
1138 r = wireguard_read_key_file(w->private_key_file, w->private_key);
1139 if (r < 0)
1140 return log_netdev_error_errno(netdev, r,
1141 "Failed to read private key from %s. Ignoring network device.",
1142 w->private_key_file);
1143
1144 if (eqzero(w->private_key))
1145 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1146 "%s: Missing PrivateKey= or PrivateKeyFile=, "
1147 "Ignoring network device.", filename);
1148
1149 LIST_FOREACH(peers, peer, w->peers) {
1150 if (wireguard_peer_verify(peer) < 0) {
1151 wireguard_peer_free(peer);
1152 continue;
1153 }
1154
1155 if ((peer->route_table_set ? peer->route_table : w->route_table) == 0)
1156 continue;
1157
1158 LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
1159 _cleanup_(route_freep) Route *route = NULL;
1160
1161 r = route_new(&route);
1162 if (r < 0)
1163 return log_oom();
1164
1165 route->family = ipmask->family;
1166 route->dst = ipmask->ip;
1167 route->dst_prefixlen = ipmask->cidr;
1168 route->scope = RT_SCOPE_UNIVERSE;
1169 route->protocol = RTPROT_STATIC;
1170 route->table = peer->route_table_set ? peer->route_table : w->route_table;
1171 route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
1172 if (route->priority == 0 && route->family == AF_INET6)
1173 route->priority = IP6_RT_PRIO_USER;
1174 route->source = NETWORK_CONFIG_SOURCE_STATIC;
1175
1176 r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
1177 if (r < 0)
1178 return log_oom();
1179 }
1180 }
1181
1182 return 0;
1183 }
1184
1185 const NetDevVTable wireguard_vtable = {
1186 .object_size = sizeof(Wireguard),
1187 .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
1188 .post_create = netdev_wireguard_post_create,
1189 .init = wireguard_init,
1190 .done = wireguard_done,
1191 .create_type = NETDEV_CREATE_INDEPENDENT,
1192 .config_verify = wireguard_verify,
1193 .iftype = ARPHRD_NONE,
1194 };
1195