]>
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 JT |
11 | #include "alloc-util.h" |
12 | #include "parse-util.h" | |
13 | #include "fd-util.h" | |
14 | #include "strv.h" | |
15 | #include "hexdecoct.h" | |
16 | #include "string-util.h" | |
17 | #include "wireguard.h" | |
18 | #include "networkd-link.h" | |
19 | #include "networkd-util.h" | |
20 | #include "networkd-manager.h" | |
21 | #include "wireguard-netlink.h" | |
22 | ||
23 | static void resolve_endpoints(NetDev *netdev); | |
24 | ||
25 | static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { | |
26 | WireguardPeer *peer; | |
27 | ||
28 | assert(w); | |
29 | ||
30 | if (w->last_peer_section == section && w->peers) | |
31 | return w->peers; | |
32 | ||
fc721553 | 33 | peer = new(WireguardPeer, 1); |
e5719363 JT |
34 | if (!peer) |
35 | return NULL; | |
fc721553 YW |
36 | |
37 | *peer = (WireguardPeer) { | |
38 | .flags = WGPEER_F_REPLACE_ALLOWEDIPS, | |
39 | }; | |
e5719363 JT |
40 | |
41 | LIST_PREPEND(peers, w->peers, peer); | |
42 | w->last_peer_section = section; | |
43 | ||
44 | return peer; | |
45 | } | |
46 | ||
47 | static int set_wireguard_interface(NetDev *netdev) { | |
48 | int r; | |
14cb109d | 49 | unsigned i, j; |
e5719363 JT |
50 | WireguardPeer *peer, *peer_start; |
51 | WireguardIPmask *mask, *mask_start = NULL; | |
52 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; | |
53 | Wireguard *w; | |
54 | uint32_t serial; | |
55 | ||
56 | assert(netdev); | |
57 | w = WIREGUARD(netdev); | |
58 | assert(w); | |
59 | ||
60 | peer_start = w->peers; | |
61 | ||
62 | do { | |
63 | message = sd_netlink_message_unref(message); | |
64 | ||
65 | r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message); | |
66 | if (r < 0) | |
67 | return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m"); | |
68 | ||
69 | r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname); | |
70 | if (r < 0) | |
71 | return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m"); | |
72 | ||
73 | if (peer_start == w->peers) { | |
74 | r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN); | |
75 | if (r < 0) | |
76 | return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m"); | |
77 | ||
78 | r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port); | |
79 | if (r < 0) | |
80 | return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m"); | |
81 | ||
82 | r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark); | |
83 | if (r < 0) | |
84 | return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m"); | |
85 | ||
86 | r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags); | |
87 | if (r < 0) | |
88 | return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m"); | |
89 | } | |
90 | ||
91 | r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS); | |
92 | if (r < 0) | |
93 | return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m"); | |
94 | ||
95 | i = 0; | |
96 | ||
97 | LIST_FOREACH(peers, peer, peer_start) { | |
98 | r = sd_netlink_message_open_array(message, ++i); | |
99 | if (r < 0) | |
100 | break; | |
101 | ||
102 | r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key)); | |
103 | if (r < 0) | |
104 | break; | |
105 | ||
106 | if (!mask_start) { | |
107 | r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN); | |
108 | if (r < 0) | |
109 | break; | |
110 | ||
111 | r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags); | |
112 | if (r < 0) | |
113 | break; | |
114 | ||
7d0b26a0 | 115 | r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); |
e5719363 JT |
116 | if (r < 0) |
117 | break; | |
118 | ||
119 | if (peer->endpoint.sa.sa_family == AF_INET) { | |
120 | r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in)); | |
121 | if (r < 0) | |
122 | break; | |
123 | } else if (peer->endpoint.sa.sa_family == AF_INET6) { | |
124 | r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6)); | |
125 | if (r < 0) | |
126 | break; | |
127 | } | |
128 | ||
129 | mask_start = peer->ipmasks; | |
130 | } | |
131 | ||
132 | r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS); | |
133 | if (r < 0) { | |
134 | mask_start = NULL; | |
135 | break; | |
136 | } | |
137 | j = 0; | |
138 | LIST_FOREACH(ipmasks, mask, mask_start) { | |
139 | r = sd_netlink_message_open_array(message, ++j); | |
140 | if (r < 0) | |
141 | break; | |
142 | ||
143 | r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family); | |
144 | if (r < 0) | |
145 | break; | |
146 | ||
147 | if (mask->family == AF_INET) { | |
148 | r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in); | |
149 | if (r < 0) | |
150 | break; | |
151 | } else if (mask->family == AF_INET6) { | |
152 | r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6); | |
153 | if (r < 0) | |
154 | break; | |
155 | } | |
156 | ||
157 | r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr); | |
158 | if (r < 0) | |
159 | break; | |
160 | ||
161 | r = sd_netlink_message_close_container(message); | |
162 | if (r < 0) | |
163 | return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); | |
164 | } | |
165 | mask_start = mask; | |
166 | if (mask_start) { | |
167 | r = sd_netlink_message_cancel_array(message); | |
168 | if (r < 0) | |
169 | return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m"); | |
170 | } | |
171 | r = sd_netlink_message_close_container(message); | |
172 | if (r < 0) | |
173 | return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); | |
174 | ||
175 | r = sd_netlink_message_close_container(message); | |
176 | if (r < 0) | |
177 | return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m"); | |
178 | } | |
179 | ||
180 | peer_start = peer; | |
181 | if (peer_start && !mask_start) { | |
182 | r = sd_netlink_message_cancel_array(message); | |
183 | if (r < 0) | |
184 | return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m"); | |
185 | } | |
186 | ||
187 | r = sd_netlink_message_close_container(message); | |
188 | if (r < 0) | |
189 | return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m"); | |
190 | ||
191 | r = sd_netlink_send(netdev->manager->genl, message, &serial); | |
192 | if (r < 0) | |
193 | return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m"); | |
194 | ||
195 | } while (peer || mask_start); | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) { | |
201 | if (!e) | |
202 | return NULL; | |
e5719363 JT |
203 | e->host = mfree(e->host); |
204 | e->port = mfree(e->port); | |
205 | return mfree(e); | |
206 | } | |
207 | ||
8173d1d0 YW |
208 | static void wireguard_endpoint_destroy_callback(void *userdata) { |
209 | WireguardEndpoint *e = userdata; | |
210 | ||
211 | assert(e); | |
212 | assert(e->netdev); | |
213 | ||
214 | netdev_unref(e->netdev); | |
215 | wireguard_endpoint_free(e); | |
216 | } | |
217 | ||
e5719363 JT |
218 | DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free); |
219 | ||
220 | static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { | |
221 | NetDev *netdev = userdata; | |
222 | Wireguard *w; | |
223 | ||
224 | assert(netdev); | |
225 | w = WIREGUARD(netdev); | |
226 | assert(w); | |
227 | ||
56ba90c2 YW |
228 | if (!netdev->manager) |
229 | /* The netdev is detached. */ | |
230 | return 0; | |
e5719363 | 231 | |
56ba90c2 | 232 | assert(!w->unresolved_endpoints); |
ae2a15bc | 233 | w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints); |
e5719363 JT |
234 | |
235 | resolve_endpoints(netdev); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | /* | |
241 | * Given the number of retries this function will return will an exponential | |
242 | * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds. | |
243 | */ | |
244 | static int exponential_backoff_milliseconds(unsigned n_retries) { | |
245 | return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC; | |
246 | } | |
247 | ||
248 | static int wireguard_resolve_handler(sd_resolve_query *q, | |
249 | int ret, | |
250 | const struct addrinfo *ai, | |
251 | void *userdata) { | |
8173d1d0 YW |
252 | _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL; |
253 | NetDev *netdev = NULL; | |
254 | WireguardEndpoint *e; | |
e5719363 | 255 | Wireguard *w; |
e5719363 JT |
256 | int r; |
257 | ||
258 | assert(userdata); | |
259 | e = userdata; | |
260 | netdev = e->netdev; | |
261 | ||
262 | assert(netdev); | |
263 | w = WIREGUARD(netdev); | |
264 | assert(w); | |
265 | ||
8173d1d0 YW |
266 | if (!netdev->manager) |
267 | /* The netdev is detached. */ | |
268 | return 0; | |
e5719363 JT |
269 | |
270 | if (ret != 0) { | |
271 | log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret)); | |
272 | LIST_PREPEND(endpoints, w->failed_endpoints, e); | |
8173d1d0 YW |
273 | (void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */ |
274 | netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */ | |
e5719363 | 275 | } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) || |
8173d1d0 | 276 | (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) |
e5719363 JT |
277 | memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen); |
278 | else | |
279 | log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port); | |
280 | ||
281 | if (w->unresolved_endpoints) { | |
282 | resolve_endpoints(netdev); | |
283 | return 0; | |
284 | } | |
285 | ||
286 | set_wireguard_interface(netdev); | |
287 | if (w->failed_endpoints) { | |
56ba90c2 YW |
288 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
289 | ||
e5719363 JT |
290 | w->n_retries++; |
291 | r = sd_event_add_time(netdev->manager->event, | |
56ba90c2 | 292 | &s, |
e5719363 JT |
293 | CLOCK_MONOTONIC, |
294 | now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries), | |
295 | 0, | |
296 | on_resolve_retry, | |
297 | netdev); | |
56ba90c2 | 298 | if (r < 0) { |
e5719363 | 299 | log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m"); |
56ba90c2 YW |
300 | return 0; |
301 | } | |
302 | ||
303 | r = sd_event_source_set_destroy_callback(s, netdev_destroy_callback); | |
304 | if (r < 0) { | |
305 | log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m"); | |
306 | return 0; | |
307 | } | |
308 | ||
309 | (void) sd_event_source_set_floating(s, true); | |
310 | netdev_ref(netdev); | |
e5719363 JT |
311 | } |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
316 | static void resolve_endpoints(NetDev *netdev) { | |
e5719363 JT |
317 | static const struct addrinfo hints = { |
318 | .ai_family = AF_UNSPEC, | |
319 | .ai_socktype = SOCK_DGRAM, | |
320 | .ai_protocol = IPPROTO_UDP | |
321 | }; | |
8173d1d0 YW |
322 | WireguardEndpoint *endpoint; |
323 | Wireguard *w; | |
324 | int r = 0; | |
e5719363 JT |
325 | |
326 | assert(netdev); | |
327 | w = WIREGUARD(netdev); | |
328 | assert(w); | |
329 | ||
330 | LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) { | |
8173d1d0 YW |
331 | _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; |
332 | ||
e5719363 | 333 | r = sd_resolve_getaddrinfo(netdev->manager->resolve, |
8173d1d0 | 334 | &q, |
e5719363 JT |
335 | endpoint->host, |
336 | endpoint->port, | |
337 | &hints, | |
338 | wireguard_resolve_handler, | |
339 | endpoint); | |
340 | ||
341 | if (r == -ENOBUFS) | |
342 | break; | |
8173d1d0 YW |
343 | if (r < 0) { |
344 | log_netdev_error_errno(netdev, r, "Failed to create resolver: %m"); | |
345 | continue; | |
346 | } | |
e5719363 | 347 | |
8173d1d0 YW |
348 | r = sd_resolve_query_set_destroy_callback(q, wireguard_endpoint_destroy_callback); |
349 | if (r < 0) { | |
350 | log_netdev_error_errno(netdev, r, "Failed to set destroy callback to resolving query: %m"); | |
351 | continue; | |
352 | } | |
e5719363 | 353 | |
8173d1d0 YW |
354 | (void) sd_resolve_query_set_floating(q, true); |
355 | ||
356 | /* Avoid freeing netdev. It will be unrefed by the destroy callback. */ | |
357 | netdev_ref(netdev); | |
358 | ||
359 | LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); | |
e5719363 JT |
360 | } |
361 | } | |
362 | ||
e5719363 JT |
363 | static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { |
364 | Wireguard *w; | |
365 | ||
366 | assert(netdev); | |
367 | w = WIREGUARD(netdev); | |
368 | assert(w); | |
369 | ||
370 | set_wireguard_interface(netdev); | |
371 | resolve_endpoints(netdev); | |
372 | return 0; | |
373 | } | |
374 | ||
375 | int config_parse_wireguard_listen_port(const char *unit, | |
376 | const char *filename, | |
377 | unsigned line, | |
378 | const char *section, | |
379 | unsigned section_line, | |
380 | const char *lvalue, | |
381 | int ltype, | |
382 | const char *rvalue, | |
383 | void *data, | |
384 | void *userdata) { | |
385 | uint16_t *s = data; | |
386 | uint16_t port = 0; | |
387 | int r; | |
388 | ||
389 | assert(rvalue); | |
390 | assert(data); | |
391 | ||
392 | if (!streq(rvalue, "auto")) { | |
393 | r = parse_ip_port(rvalue, &port); | |
394 | if (r < 0) | |
395 | log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue); | |
396 | } | |
397 | ||
398 | *s = port; | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | static int parse_wireguard_key(const char *unit, | |
404 | const char *filename, | |
405 | unsigned line, | |
406 | const char *section, | |
407 | unsigned section_line, | |
408 | const char *lvalue, | |
409 | int ltype, | |
410 | const char *rvalue, | |
411 | void *data, | |
412 | void *userdata) { | |
413 | _cleanup_free_ void *key = NULL; | |
414 | size_t len; | |
415 | int r; | |
416 | ||
417 | assert(filename); | |
418 | assert(rvalue); | |
419 | assert(userdata); | |
420 | ||
421 | r = unbase64mem(rvalue, strlen(rvalue), &key, &len); | |
422 | if (r < 0) { | |
423 | log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue); | |
424 | return 0; | |
425 | } | |
426 | if (len != WG_KEY_LEN) { | |
427 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
428 | "Wireguard key is too short, ignoring assignment: %s", rvalue); | |
429 | return 0; | |
430 | } | |
431 | ||
432 | memcpy(userdata, key, WG_KEY_LEN); | |
433 | return true; | |
434 | } | |
435 | ||
436 | int config_parse_wireguard_private_key(const char *unit, | |
437 | const char *filename, | |
438 | unsigned line, | |
439 | const char *section, | |
440 | unsigned section_line, | |
441 | const char *lvalue, | |
442 | int ltype, | |
443 | const char *rvalue, | |
444 | void *data, | |
445 | void *userdata) { | |
446 | Wireguard *w; | |
447 | ||
448 | assert(data); | |
449 | ||
450 | w = WIREGUARD(data); | |
451 | ||
452 | assert(w); | |
453 | ||
454 | return parse_wireguard_key(unit, | |
455 | filename, | |
456 | line, | |
457 | section, | |
458 | section_line, | |
459 | lvalue, | |
460 | ltype, | |
461 | rvalue, | |
462 | data, | |
463 | &w->private_key); | |
464 | ||
465 | } | |
466 | ||
467 | int config_parse_wireguard_preshared_key(const char *unit, | |
468 | const char *filename, | |
469 | unsigned line, | |
470 | const char *section, | |
471 | unsigned section_line, | |
472 | const char *lvalue, | |
473 | int ltype, | |
474 | const char *rvalue, | |
475 | void *data, | |
476 | void *userdata) { | |
477 | Wireguard *w; | |
478 | WireguardPeer *peer; | |
479 | ||
480 | assert(data); | |
481 | ||
482 | w = WIREGUARD(data); | |
483 | ||
484 | assert(w); | |
485 | ||
486 | peer = wireguard_peer_new(w, section_line); | |
487 | if (!peer) | |
488 | return log_oom(); | |
489 | ||
490 | return parse_wireguard_key(unit, | |
491 | filename, | |
492 | line, | |
493 | section, | |
494 | section_line, | |
495 | lvalue, | |
496 | ltype, | |
497 | rvalue, | |
498 | data, | |
499 | peer->preshared_key); | |
500 | } | |
501 | ||
e5719363 JT |
502 | int config_parse_wireguard_public_key(const char *unit, |
503 | const char *filename, | |
504 | unsigned line, | |
505 | const char *section, | |
506 | unsigned section_line, | |
507 | const char *lvalue, | |
508 | int ltype, | |
509 | const char *rvalue, | |
510 | void *data, | |
511 | void *userdata) { | |
512 | Wireguard *w; | |
513 | WireguardPeer *peer; | |
514 | ||
515 | assert(data); | |
516 | ||
517 | w = WIREGUARD(data); | |
518 | ||
519 | assert(w); | |
520 | ||
521 | peer = wireguard_peer_new(w, section_line); | |
522 | if (!peer) | |
523 | return log_oom(); | |
524 | ||
525 | return parse_wireguard_key(unit, | |
526 | filename, | |
527 | line, | |
528 | section, | |
529 | section_line, | |
530 | lvalue, | |
531 | ltype, | |
532 | rvalue, | |
533 | data, | |
534 | peer->public_key); | |
535 | } | |
536 | ||
537 | int config_parse_wireguard_allowed_ips(const char *unit, | |
538 | const char *filename, | |
539 | unsigned line, | |
540 | const char *section, | |
541 | unsigned section_line, | |
542 | const char *lvalue, | |
543 | int ltype, | |
544 | const char *rvalue, | |
545 | void *data, | |
546 | void *userdata) { | |
547 | union in_addr_union addr; | |
548 | unsigned char prefixlen; | |
549 | int r, family; | |
550 | Wireguard *w; | |
551 | WireguardPeer *peer; | |
552 | WireguardIPmask *ipmask; | |
553 | ||
554 | assert(rvalue); | |
555 | assert(data); | |
556 | ||
557 | w = WIREGUARD(data); | |
558 | ||
559 | peer = wireguard_peer_new(w, section_line); | |
560 | if (!peer) | |
561 | return log_oom(); | |
562 | ||
563 | for (;;) { | |
564 | _cleanup_free_ char *word = NULL; | |
565 | ||
566 | r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0); | |
567 | if (r == 0) | |
568 | break; | |
569 | if (r == -ENOMEM) | |
570 | return log_oom(); | |
571 | if (r < 0) { | |
572 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue); | |
573 | break; | |
574 | } | |
575 | ||
576 | r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen); | |
577 | if (r < 0) { | |
578 | log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word); | |
579 | return 0; | |
580 | } | |
581 | ||
fc721553 | 582 | ipmask = new(WireguardIPmask, 1); |
e5719363 JT |
583 | if (!ipmask) |
584 | return log_oom(); | |
fc721553 YW |
585 | |
586 | *ipmask = (WireguardIPmask) { | |
587 | .family = family, | |
588 | .ip.in6 = addr.in6, | |
589 | .cidr = prefixlen, | |
590 | }; | |
e5719363 JT |
591 | |
592 | LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); | |
593 | } | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
598 | int config_parse_wireguard_endpoint(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) { | |
608 | Wireguard *w; | |
609 | WireguardPeer *peer; | |
610 | size_t len; | |
611 | const char *begin, *end = NULL; | |
612 | _cleanup_free_ char *host = NULL, *port = NULL; | |
613 | _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL; | |
614 | ||
615 | assert(data); | |
616 | assert(rvalue); | |
617 | ||
618 | w = WIREGUARD(data); | |
619 | ||
620 | assert(w); | |
621 | ||
622 | peer = wireguard_peer_new(w, section_line); | |
623 | if (!peer) | |
624 | return log_oom(); | |
625 | ||
e5719363 JT |
626 | if (rvalue[0] == '[') { |
627 | begin = &rvalue[1]; | |
628 | end = strchr(rvalue, ']'); | |
629 | if (!end) { | |
630 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue); | |
631 | return 0; | |
632 | } | |
633 | len = end - begin; | |
634 | ++end; | |
635 | if (*end != ':' || !*(end + 1)) { | |
636 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); | |
637 | return 0; | |
638 | } | |
639 | ++end; | |
640 | } else { | |
641 | begin = rvalue; | |
642 | end = strrchr(rvalue, ':'); | |
643 | if (!end || !*(end + 1)) { | |
644 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); | |
645 | return 0; | |
646 | } | |
647 | len = end - begin; | |
648 | ++end; | |
649 | } | |
650 | ||
651 | host = strndup(begin, len); | |
652 | if (!host) | |
653 | return log_oom(); | |
654 | ||
655 | port = strdup(end); | |
656 | if (!port) | |
657 | return log_oom(); | |
658 | ||
fc721553 YW |
659 | endpoint = new(WireguardEndpoint, 1); |
660 | if (!endpoint) | |
661 | return log_oom(); | |
662 | ||
663 | *endpoint = (WireguardEndpoint) { | |
664 | .peer = TAKE_PTR(peer), | |
665 | .host = TAKE_PTR(host), | |
666 | .port = TAKE_PTR(port), | |
667 | .netdev = data, | |
668 | }; | |
8173d1d0 | 669 | LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint)); |
e5719363 JT |
670 | |
671 | return 0; | |
672 | } | |
673 | ||
674 | int config_parse_wireguard_keepalive(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) { | |
684 | int r; | |
685 | uint16_t keepalive = 0; | |
686 | Wireguard *w; | |
687 | WireguardPeer *peer; | |
688 | ||
689 | assert(rvalue); | |
690 | assert(data); | |
691 | ||
692 | w = WIREGUARD(data); | |
693 | ||
694 | assert(w); | |
695 | ||
696 | peer = wireguard_peer_new(w, section_line); | |
697 | if (!peer) | |
698 | return log_oom(); | |
699 | ||
700 | if (streq(rvalue, "off")) | |
701 | keepalive = 0; | |
702 | else { | |
703 | r = safe_atou16(rvalue, &keepalive); | |
704 | if (r < 0) | |
705 | log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue); | |
706 | } | |
707 | ||
708 | peer->persistent_keepalive_interval = keepalive; | |
709 | return 0; | |
710 | } | |
711 | ||
712 | static void wireguard_init(NetDev *netdev) { | |
713 | Wireguard *w; | |
714 | ||
715 | assert(netdev); | |
716 | ||
717 | w = WIREGUARD(netdev); | |
718 | ||
719 | assert(w); | |
720 | ||
721 | w->flags = WGDEVICE_F_REPLACE_PEERS; | |
722 | } | |
723 | ||
724 | static void wireguard_done(NetDev *netdev) { | |
725 | Wireguard *w; | |
726 | WireguardPeer *peer; | |
727 | WireguardIPmask *mask; | |
c195364d | 728 | WireguardEndpoint *e; |
e5719363 JT |
729 | |
730 | assert(netdev); | |
731 | w = WIREGUARD(netdev); | |
c195364d | 732 | assert(w); |
e5719363 JT |
733 | |
734 | while ((peer = w->peers)) { | |
735 | LIST_REMOVE(peers, w->peers, peer); | |
736 | while ((mask = peer->ipmasks)) { | |
737 | LIST_REMOVE(ipmasks, peer->ipmasks, mask); | |
738 | free(mask); | |
739 | } | |
740 | free(peer); | |
741 | } | |
c195364d YW |
742 | |
743 | while ((e = w->unresolved_endpoints)) { | |
744 | LIST_REMOVE(endpoints, w->unresolved_endpoints, e); | |
745 | wireguard_endpoint_free(e); | |
746 | } | |
747 | ||
748 | while ((e = w->failed_endpoints)) { | |
749 | LIST_REMOVE(endpoints, w->failed_endpoints, e); | |
750 | wireguard_endpoint_free(e); | |
751 | } | |
e5719363 JT |
752 | } |
753 | ||
754 | const NetDevVTable wireguard_vtable = { | |
755 | .object_size = sizeof(Wireguard), | |
756 | .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0", | |
757 | .post_create = netdev_wireguard_post_create, | |
758 | .init = wireguard_init, | |
759 | .done = wireguard_done, | |
760 | .create_type = NETDEV_CREATE_INDEPENDENT, | |
761 | }; |