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