]>
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 | ||
56ba90c2 YW |
225 | if (!netdev->manager) |
226 | /* The netdev is detached. */ | |
227 | return 0; | |
e5719363 | 228 | |
56ba90c2 | 229 | assert(!w->unresolved_endpoints); |
ae2a15bc | 230 | w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints); |
e5719363 JT |
231 | |
232 | resolve_endpoints(netdev); | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | /* | |
238 | * Given the number of retries this function will return will an exponential | |
239 | * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds. | |
240 | */ | |
241 | static int exponential_backoff_milliseconds(unsigned n_retries) { | |
242 | return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC; | |
243 | } | |
244 | ||
245 | static int wireguard_resolve_handler(sd_resolve_query *q, | |
246 | int ret, | |
247 | const struct addrinfo *ai, | |
248 | void *userdata) { | |
8173d1d0 YW |
249 | _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL; |
250 | NetDev *netdev = NULL; | |
251 | WireguardEndpoint *e; | |
e5719363 | 252 | Wireguard *w; |
e5719363 JT |
253 | int r; |
254 | ||
255 | assert(userdata); | |
256 | e = userdata; | |
257 | netdev = e->netdev; | |
258 | ||
259 | assert(netdev); | |
260 | w = WIREGUARD(netdev); | |
261 | assert(w); | |
262 | ||
8173d1d0 YW |
263 | if (!netdev->manager) |
264 | /* The netdev is detached. */ | |
265 | return 0; | |
e5719363 JT |
266 | |
267 | if (ret != 0) { | |
268 | log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret)); | |
269 | LIST_PREPEND(endpoints, w->failed_endpoints, e); | |
8173d1d0 YW |
270 | (void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */ |
271 | netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */ | |
e5719363 | 272 | } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) || |
8173d1d0 | 273 | (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) |
e5719363 JT |
274 | memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen); |
275 | else | |
276 | log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port); | |
277 | ||
278 | if (w->unresolved_endpoints) { | |
279 | resolve_endpoints(netdev); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | set_wireguard_interface(netdev); | |
284 | if (w->failed_endpoints) { | |
56ba90c2 YW |
285 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
286 | ||
e5719363 JT |
287 | w->n_retries++; |
288 | r = sd_event_add_time(netdev->manager->event, | |
56ba90c2 | 289 | &s, |
e5719363 JT |
290 | CLOCK_MONOTONIC, |
291 | now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries), | |
292 | 0, | |
293 | on_resolve_retry, | |
294 | netdev); | |
56ba90c2 | 295 | if (r < 0) { |
e5719363 | 296 | log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m"); |
56ba90c2 YW |
297 | return 0; |
298 | } | |
299 | ||
300 | r = sd_event_source_set_destroy_callback(s, netdev_destroy_callback); | |
301 | if (r < 0) { | |
302 | log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m"); | |
303 | return 0; | |
304 | } | |
305 | ||
306 | (void) sd_event_source_set_floating(s, true); | |
307 | netdev_ref(netdev); | |
e5719363 JT |
308 | } |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
313 | static void resolve_endpoints(NetDev *netdev) { | |
e5719363 JT |
314 | static const struct addrinfo hints = { |
315 | .ai_family = AF_UNSPEC, | |
316 | .ai_socktype = SOCK_DGRAM, | |
317 | .ai_protocol = IPPROTO_UDP | |
318 | }; | |
8173d1d0 YW |
319 | WireguardEndpoint *endpoint; |
320 | Wireguard *w; | |
321 | int r = 0; | |
e5719363 JT |
322 | |
323 | assert(netdev); | |
324 | w = WIREGUARD(netdev); | |
325 | assert(w); | |
326 | ||
327 | LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) { | |
8173d1d0 YW |
328 | _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; |
329 | ||
e5719363 | 330 | r = sd_resolve_getaddrinfo(netdev->manager->resolve, |
8173d1d0 | 331 | &q, |
e5719363 JT |
332 | endpoint->host, |
333 | endpoint->port, | |
334 | &hints, | |
335 | wireguard_resolve_handler, | |
336 | endpoint); | |
337 | ||
338 | if (r == -ENOBUFS) | |
339 | break; | |
8173d1d0 YW |
340 | if (r < 0) { |
341 | log_netdev_error_errno(netdev, r, "Failed to create resolver: %m"); | |
342 | continue; | |
343 | } | |
e5719363 | 344 | |
8173d1d0 YW |
345 | r = sd_resolve_query_set_destroy_callback(q, wireguard_endpoint_destroy_callback); |
346 | if (r < 0) { | |
347 | log_netdev_error_errno(netdev, r, "Failed to set destroy callback to resolving query: %m"); | |
348 | continue; | |
349 | } | |
e5719363 | 350 | |
8173d1d0 YW |
351 | (void) sd_resolve_query_set_floating(q, true); |
352 | ||
353 | /* Avoid freeing netdev. It will be unrefed by the destroy callback. */ | |
354 | netdev_ref(netdev); | |
355 | ||
356 | LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); | |
e5719363 JT |
357 | } |
358 | } | |
359 | ||
e5719363 JT |
360 | static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { |
361 | Wireguard *w; | |
362 | ||
363 | assert(netdev); | |
364 | w = WIREGUARD(netdev); | |
365 | assert(w); | |
366 | ||
367 | set_wireguard_interface(netdev); | |
368 | resolve_endpoints(netdev); | |
369 | return 0; | |
370 | } | |
371 | ||
372 | int config_parse_wireguard_listen_port(const char *unit, | |
373 | const char *filename, | |
374 | unsigned line, | |
375 | const char *section, | |
376 | unsigned section_line, | |
377 | const char *lvalue, | |
378 | int ltype, | |
379 | const char *rvalue, | |
380 | void *data, | |
381 | void *userdata) { | |
382 | uint16_t *s = data; | |
383 | uint16_t port = 0; | |
384 | int r; | |
385 | ||
386 | assert(rvalue); | |
387 | assert(data); | |
388 | ||
389 | if (!streq(rvalue, "auto")) { | |
390 | r = parse_ip_port(rvalue, &port); | |
391 | if (r < 0) | |
392 | log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue); | |
393 | } | |
394 | ||
395 | *s = port; | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static int parse_wireguard_key(const char *unit, | |
401 | const char *filename, | |
402 | unsigned line, | |
403 | const char *section, | |
404 | unsigned section_line, | |
405 | const char *lvalue, | |
406 | int ltype, | |
407 | const char *rvalue, | |
408 | void *data, | |
409 | void *userdata) { | |
410 | _cleanup_free_ void *key = NULL; | |
411 | size_t len; | |
412 | int r; | |
413 | ||
414 | assert(filename); | |
415 | assert(rvalue); | |
416 | assert(userdata); | |
417 | ||
418 | r = unbase64mem(rvalue, strlen(rvalue), &key, &len); | |
419 | if (r < 0) { | |
420 | log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue); | |
421 | return 0; | |
422 | } | |
423 | if (len != WG_KEY_LEN) { | |
424 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
425 | "Wireguard key is too short, ignoring assignment: %s", rvalue); | |
426 | return 0; | |
427 | } | |
428 | ||
429 | memcpy(userdata, key, WG_KEY_LEN); | |
430 | return true; | |
431 | } | |
432 | ||
433 | int config_parse_wireguard_private_key(const char *unit, | |
434 | const char *filename, | |
435 | unsigned line, | |
436 | const char *section, | |
437 | unsigned section_line, | |
438 | const char *lvalue, | |
439 | int ltype, | |
440 | const char *rvalue, | |
441 | void *data, | |
442 | void *userdata) { | |
443 | Wireguard *w; | |
444 | ||
445 | assert(data); | |
446 | ||
447 | w = WIREGUARD(data); | |
448 | ||
449 | assert(w); | |
450 | ||
451 | return parse_wireguard_key(unit, | |
452 | filename, | |
453 | line, | |
454 | section, | |
455 | section_line, | |
456 | lvalue, | |
457 | ltype, | |
458 | rvalue, | |
459 | data, | |
460 | &w->private_key); | |
461 | ||
462 | } | |
463 | ||
464 | int config_parse_wireguard_preshared_key(const char *unit, | |
465 | const char *filename, | |
466 | unsigned line, | |
467 | const char *section, | |
468 | unsigned section_line, | |
469 | const char *lvalue, | |
470 | int ltype, | |
471 | const char *rvalue, | |
472 | void *data, | |
473 | void *userdata) { | |
474 | Wireguard *w; | |
475 | WireguardPeer *peer; | |
476 | ||
477 | assert(data); | |
478 | ||
479 | w = WIREGUARD(data); | |
480 | ||
481 | assert(w); | |
482 | ||
483 | peer = wireguard_peer_new(w, section_line); | |
484 | if (!peer) | |
485 | return log_oom(); | |
486 | ||
487 | return parse_wireguard_key(unit, | |
488 | filename, | |
489 | line, | |
490 | section, | |
491 | section_line, | |
492 | lvalue, | |
493 | ltype, | |
494 | rvalue, | |
495 | data, | |
496 | peer->preshared_key); | |
497 | } | |
498 | ||
e5719363 JT |
499 | int config_parse_wireguard_public_key(const char *unit, |
500 | const char *filename, | |
501 | unsigned line, | |
502 | const char *section, | |
503 | unsigned section_line, | |
504 | const char *lvalue, | |
505 | int ltype, | |
506 | const char *rvalue, | |
507 | void *data, | |
508 | void *userdata) { | |
509 | Wireguard *w; | |
510 | WireguardPeer *peer; | |
511 | ||
512 | assert(data); | |
513 | ||
514 | w = WIREGUARD(data); | |
515 | ||
516 | assert(w); | |
517 | ||
518 | peer = wireguard_peer_new(w, section_line); | |
519 | if (!peer) | |
520 | return log_oom(); | |
521 | ||
522 | return parse_wireguard_key(unit, | |
523 | filename, | |
524 | line, | |
525 | section, | |
526 | section_line, | |
527 | lvalue, | |
528 | ltype, | |
529 | rvalue, | |
530 | data, | |
531 | peer->public_key); | |
532 | } | |
533 | ||
534 | int config_parse_wireguard_allowed_ips(const char *unit, | |
535 | const char *filename, | |
536 | unsigned line, | |
537 | const char *section, | |
538 | unsigned section_line, | |
539 | const char *lvalue, | |
540 | int ltype, | |
541 | const char *rvalue, | |
542 | void *data, | |
543 | void *userdata) { | |
544 | union in_addr_union addr; | |
545 | unsigned char prefixlen; | |
546 | int r, family; | |
547 | Wireguard *w; | |
548 | WireguardPeer *peer; | |
549 | WireguardIPmask *ipmask; | |
550 | ||
551 | assert(rvalue); | |
552 | assert(data); | |
553 | ||
554 | w = WIREGUARD(data); | |
555 | ||
556 | peer = wireguard_peer_new(w, section_line); | |
557 | if (!peer) | |
558 | return log_oom(); | |
559 | ||
560 | for (;;) { | |
561 | _cleanup_free_ char *word = NULL; | |
562 | ||
563 | r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0); | |
564 | if (r == 0) | |
565 | break; | |
566 | if (r == -ENOMEM) | |
567 | return log_oom(); | |
568 | if (r < 0) { | |
569 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue); | |
570 | break; | |
571 | } | |
572 | ||
573 | r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen); | |
574 | if (r < 0) { | |
575 | log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word); | |
576 | return 0; | |
577 | } | |
578 | ||
579 | ipmask = new0(WireguardIPmask, 1); | |
580 | if (!ipmask) | |
581 | return log_oom(); | |
582 | ipmask->family = family; | |
583 | ipmask->ip.in6 = addr.in6; | |
584 | ipmask->cidr = prefixlen; | |
585 | ||
586 | LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); | |
587 | } | |
588 | ||
589 | return 0; | |
590 | } | |
591 | ||
592 | int config_parse_wireguard_endpoint(const char *unit, | |
593 | const char *filename, | |
594 | unsigned line, | |
595 | const char *section, | |
596 | unsigned section_line, | |
597 | const char *lvalue, | |
598 | int ltype, | |
599 | const char *rvalue, | |
600 | void *data, | |
601 | void *userdata) { | |
602 | Wireguard *w; | |
603 | WireguardPeer *peer; | |
604 | size_t len; | |
605 | const char *begin, *end = NULL; | |
606 | _cleanup_free_ char *host = NULL, *port = NULL; | |
607 | _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL; | |
608 | ||
609 | assert(data); | |
610 | assert(rvalue); | |
611 | ||
612 | w = WIREGUARD(data); | |
613 | ||
614 | assert(w); | |
615 | ||
616 | peer = wireguard_peer_new(w, section_line); | |
617 | if (!peer) | |
618 | return log_oom(); | |
619 | ||
620 | endpoint = new0(WireguardEndpoint, 1); | |
621 | if (!endpoint) | |
622 | return log_oom(); | |
623 | ||
624 | if (rvalue[0] == '[') { | |
625 | begin = &rvalue[1]; | |
626 | end = strchr(rvalue, ']'); | |
627 | if (!end) { | |
628 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue); | |
629 | return 0; | |
630 | } | |
631 | len = end - begin; | |
632 | ++end; | |
633 | if (*end != ':' || !*(end + 1)) { | |
634 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); | |
635 | return 0; | |
636 | } | |
637 | ++end; | |
638 | } else { | |
639 | begin = rvalue; | |
640 | end = strrchr(rvalue, ':'); | |
641 | if (!end || !*(end + 1)) { | |
642 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); | |
643 | return 0; | |
644 | } | |
645 | len = end - begin; | |
646 | ++end; | |
647 | } | |
648 | ||
649 | host = strndup(begin, len); | |
650 | if (!host) | |
651 | return log_oom(); | |
652 | ||
653 | port = strdup(end); | |
654 | if (!port) | |
655 | return log_oom(); | |
656 | ||
1cc6c93a YW |
657 | endpoint->peer = TAKE_PTR(peer); |
658 | endpoint->host = TAKE_PTR(host); | |
659 | endpoint->port = TAKE_PTR(port); | |
8173d1d0 YW |
660 | endpoint->netdev = data; |
661 | LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint)); | |
e5719363 JT |
662 | |
663 | return 0; | |
664 | } | |
665 | ||
666 | int config_parse_wireguard_keepalive(const char *unit, | |
667 | const char *filename, | |
668 | unsigned line, | |
669 | const char *section, | |
670 | unsigned section_line, | |
671 | const char *lvalue, | |
672 | int ltype, | |
673 | const char *rvalue, | |
674 | void *data, | |
675 | void *userdata) { | |
676 | int r; | |
677 | uint16_t keepalive = 0; | |
678 | Wireguard *w; | |
679 | WireguardPeer *peer; | |
680 | ||
681 | assert(rvalue); | |
682 | assert(data); | |
683 | ||
684 | w = WIREGUARD(data); | |
685 | ||
686 | assert(w); | |
687 | ||
688 | peer = wireguard_peer_new(w, section_line); | |
689 | if (!peer) | |
690 | return log_oom(); | |
691 | ||
692 | if (streq(rvalue, "off")) | |
693 | keepalive = 0; | |
694 | else { | |
695 | r = safe_atou16(rvalue, &keepalive); | |
696 | if (r < 0) | |
697 | log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue); | |
698 | } | |
699 | ||
700 | peer->persistent_keepalive_interval = keepalive; | |
701 | return 0; | |
702 | } | |
703 | ||
704 | static void wireguard_init(NetDev *netdev) { | |
705 | Wireguard *w; | |
706 | ||
707 | assert(netdev); | |
708 | ||
709 | w = WIREGUARD(netdev); | |
710 | ||
711 | assert(w); | |
712 | ||
713 | w->flags = WGDEVICE_F_REPLACE_PEERS; | |
714 | } | |
715 | ||
716 | static void wireguard_done(NetDev *netdev) { | |
717 | Wireguard *w; | |
718 | WireguardPeer *peer; | |
719 | WireguardIPmask *mask; | |
720 | ||
721 | assert(netdev); | |
722 | w = WIREGUARD(netdev); | |
723 | assert(!w->unresolved_endpoints); | |
e5719363 JT |
724 | |
725 | while ((peer = w->peers)) { | |
726 | LIST_REMOVE(peers, w->peers, peer); | |
727 | while ((mask = peer->ipmasks)) { | |
728 | LIST_REMOVE(ipmasks, peer->ipmasks, mask); | |
729 | free(mask); | |
730 | } | |
731 | free(peer); | |
732 | } | |
733 | } | |
734 | ||
735 | const NetDevVTable wireguard_vtable = { | |
736 | .object_size = sizeof(Wireguard), | |
737 | .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0", | |
738 | .post_create = netdev_wireguard_post_create, | |
739 | .init = wireguard_init, | |
740 | .done = wireguard_done, | |
741 | .create_type = NETDEV_CREATE_INDEPENDENT, | |
742 | }; |