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