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