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