]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
Merge pull request #15762 from keszybz/gcc-10-build
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <netinet/in.h>
7 #include <linux/if.h>
8 #include <linux/if_arp.h>
9 #include "sd-radv.h"
10
11 #include "sd-dhcp6-client.h"
12
13 #include "hashmap.h"
14 #include "hostname-util.h"
15 #include "missing_network.h"
16 #include "network-internal.h"
17 #include "networkd-dhcp6.h"
18 #include "networkd-link.h"
19 #include "networkd-manager.h"
20 #include "siphash24.h"
21 #include "string-util.h"
22 #include "radv-internal.h"
23
24 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
25 static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
26 static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
27 static int dhcp6_prefix_remove_all(Manager *m, Link *link);
28
29 static bool dhcp6_get_prefix_delegation(Link *link) {
30 if (!link->network)
31 return false;
32
33 return IN_SET(link->network->router_prefix_delegation,
34 RADV_PREFIX_DELEGATION_DHCP6,
35 RADV_PREFIX_DELEGATION_BOTH);
36 }
37
38 static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
39 Manager *manager;
40 Link *l;
41 Iterator i;
42
43 assert(dhcp6_link);
44
45 manager = dhcp6_link->manager;
46 assert(manager);
47
48 HASHMAP_FOREACH(l, manager->links, i) {
49 if (l == dhcp6_link)
50 continue;
51
52 if (!dhcp6_get_prefix_delegation(l))
53 continue;
54
55 return true;
56 }
57
58 return false;
59 }
60
61 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
62 Link *link) {
63 return 0;
64 }
65
66 static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
67 uint8_t prefix_len,
68 uint32_t lifetime_preferred,
69 uint32_t lifetime_valid) {
70 sd_radv *radv = link->radv;
71 int r;
72 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
73
74 r = sd_radv_prefix_new(&p);
75 if (r < 0)
76 return r;
77
78 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
79 if (r < 0)
80 return r;
81
82 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
83 if (r < 0)
84 return r;
85
86 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
87 if (r < 0)
88 return r;
89
90 r = sd_radv_stop(radv);
91 if (r < 0)
92 return r;
93
94 r = sd_radv_add_prefix(radv, p, true);
95 if (r < 0 && r != -EEXIST)
96 return r;
97
98 r = dhcp6_prefix_add(link->manager, prefix, link);
99 if (r < 0)
100 return r;
101
102 return sd_radv_start(radv);
103 }
104
105 static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
106 int r;
107
108 assert(link);
109
110 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
111 return 1;
112
113 r = sd_netlink_message_get_errno(m);
114 if (r < 0)
115 log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet");
116
117 return 1;
118 }
119
120 int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
121 int r;
122 sd_dhcp6_lease *lease;
123 union in_addr_union pd_prefix;
124 uint8_t pd_prefix_len;
125 uint32_t lifetime_preferred, lifetime_valid;
126
127 r = sd_dhcp6_client_get_lease(client, &lease);
128 if (r < 0)
129 return r;
130
131 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
132
133 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
134 &lifetime_preferred,
135 &lifetime_valid) >= 0) {
136 _cleanup_free_ char *buf = NULL;
137 _cleanup_(route_freep) Route *route = NULL;
138
139 if (pd_prefix_len >= 64)
140 continue;
141
142 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
143
144 r = route_new(&route);
145 if (r < 0)
146 return r;
147
148 route->family = AF_INET6;
149 route->dst = pd_prefix;
150 route->dst_prefixlen = pd_prefix_len;
151 route->type = RTN_UNREACHABLE;
152
153 r = route_remove(route, link, dhcp6_route_remove_handler);
154 if (r < 0) {
155 log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
156 strnull(buf),
157 pd_prefix_len);
158 continue;
159 }
160
161 log_link_debug(link, "Removing unreachable route %s/%u",
162 strnull(buf), pd_prefix_len);
163 }
164
165 return 0;
166 }
167
168 static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
169 struct in6_addr *pd_prefix,
170 uint8_t pd_prefix_len,
171 uint32_t lifetime_preferred,
172 uint32_t lifetime_valid) {
173 Link *link;
174 Manager *manager = dhcp6_link->manager;
175 union in_addr_union prefix;
176 uint64_t n_prefixes, n_used = 0;
177 _cleanup_free_ char *buf = NULL;
178 _cleanup_free_ char *assigned_buf = NULL;
179 int r;
180
181 assert(manager);
182 assert(pd_prefix_len <= 64);
183
184 prefix.in6 = *pd_prefix;
185
186 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
187 if (r < 0)
188 return r;
189
190 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
191
192 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
193 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
194 n_prefixes, strnull(buf), pd_prefix_len);
195
196 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
197 Link *assigned_link;
198
199 if (n_used == n_prefixes) {
200 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
201 n_used, n_prefixes, strnull(buf), pd_prefix_len);
202
203 return -EAGAIN;
204 }
205
206 if (link == dhcp6_link)
207 continue;
208
209 if (!dhcp6_get_prefix_delegation(link))
210 continue;
211
212 assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
213 if (assigned_link && assigned_link != link)
214 continue;
215
216 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
217 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
218 lifetime_preferred, lifetime_valid);
219 if (r < 0) {
220 log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
221 assigned_link ? "update": "assign",
222 strnull(assigned_buf),
223 strnull(buf), pd_prefix_len);
224
225 if (!assigned_link)
226 continue;
227
228 } else
229 log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
230 n_used + 1, n_prefixes,
231 strnull(assigned_buf),
232 strnull(buf), pd_prefix_len);
233
234 n_used++;
235
236 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
237 if (r < 0 && n_used < n_prefixes)
238 return r;
239 }
240
241 return 0;
242 }
243
244 static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
245 int r;
246
247 assert(link);
248
249 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
250 return 1;
251
252 r = sd_netlink_message_get_errno(m);
253 if (r < 0 && r != -EEXIST)
254 log_link_message_warning_errno(link, m, r, "Received error when adding unreachable route for DHCPv6 delegated subnet");
255
256 return 1;
257 }
258
259 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
260 int r;
261 sd_dhcp6_lease *lease;
262 union in_addr_union pd_prefix;
263 uint8_t pd_prefix_len;
264 uint32_t lifetime_preferred, lifetime_valid;
265 Iterator i = ITERATOR_FIRST;
266
267 r = sd_dhcp6_client_get_lease(client, &lease);
268 if (r < 0)
269 return r;
270
271 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
272
273 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
274 &lifetime_preferred,
275 &lifetime_valid) >= 0) {
276
277 _cleanup_free_ char *buf = NULL;
278
279 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
280
281 if (pd_prefix_len > 64) {
282 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
283 strnull(buf), pd_prefix_len);
284 continue;
285 }
286
287 if (pd_prefix_len < 48)
288 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
289 strnull(buf), pd_prefix_len);
290
291 if (pd_prefix_len < 64) {
292 _cleanup_(route_freep) Route *route = NULL;
293
294 r = route_new(&route);
295 if (r < 0)
296 return r;
297
298 route->family = AF_INET6;
299 route->dst = pd_prefix;
300 route->dst_prefixlen = pd_prefix_len;
301 route->table = link_get_dhcp_route_table(link);
302 route->type = RTN_UNREACHABLE;
303
304 r = route_configure(route, link, dhcp6_route_handler);
305 if (r < 0) {
306 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
307 strnull(buf),
308 pd_prefix_len);
309 continue;
310 }
311
312 log_link_debug(link, "Configuring unreachable route for %s/%u",
313 strnull(buf), pd_prefix_len);
314 } else
315 log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
316
317 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
318 pd_prefix_len,
319 lifetime_preferred,
320 lifetime_valid);
321 if (r < 0 && r != -EAGAIN)
322 return r;
323
324 if (r >= 0)
325 i = ITERATOR_FIRST;
326 }
327
328 return 0;
329 }
330
331 int dhcp6_request_prefix_delegation(Link *link) {
332 Link *l;
333 Iterator i;
334
335 assert_return(link, -EINVAL);
336 assert_return(link->manager, -EOPNOTSUPP);
337
338 if (dhcp6_get_prefix_delegation(link) <= 0)
339 return 0;
340
341 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
342
343 HASHMAP_FOREACH(l, link->manager->links, i) {
344 int r, enabled;
345
346 if (l == link)
347 continue;
348
349 if (!l->dhcp6_client)
350 continue;
351
352 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
353 if (r < 0) {
354 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
355 continue;
356 }
357
358 if (enabled == 0) {
359 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
360 if (r < 0) {
361 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
362 continue;
363 }
364 }
365
366 r = sd_dhcp6_client_is_running(l->dhcp6_client);
367 if (r <= 0)
368 continue;
369
370 if (enabled != 0) {
371 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
372 (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
373
374 continue;
375 }
376
377 r = sd_dhcp6_client_stop(l->dhcp6_client);
378 if (r < 0) {
379 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
380 continue;
381 }
382
383 r = sd_dhcp6_client_start(l->dhcp6_client);
384 if (r < 0) {
385 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
386 continue;
387 }
388
389 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
390 }
391
392 return 0;
393 }
394
395 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
396 int r;
397
398 assert(link);
399
400 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
401 return 1;
402
403 r = sd_netlink_message_get_errno(m);
404 if (r < 0 && r != -EEXIST) {
405 log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 address");
406 link_enter_failed(link);
407 return 1;
408 } else if (r >= 0)
409 (void) manager_rtnl_process_address(rtnl, m, link->manager);
410
411 r = link_request_set_routes(link);
412 if (r < 0) {
413 link_enter_failed(link);
414 return 1;
415 }
416
417 return 1;
418 }
419
420 static int dhcp6_address_change(
421 Link *link,
422 struct in6_addr *ip6_addr,
423 uint32_t lifetime_preferred,
424 uint32_t lifetime_valid) {
425
426 _cleanup_(address_freep) Address *addr = NULL;
427 _cleanup_free_ char *buffer = NULL;
428 int r;
429
430 r = address_new(&addr);
431 if (r < 0)
432 return r;
433
434 addr->family = AF_INET6;
435 addr->in_addr.in6 = *ip6_addr;
436 addr->flags = IFA_F_NOPREFIXROUTE;
437 addr->prefixlen = 128;
438 addr->cinfo.ifa_prefered = lifetime_preferred;
439 addr->cinfo.ifa_valid = lifetime_valid;
440
441 (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
442 log_link_info(link,
443 "DHCPv6 address %s/%d timeout preferred %d valid %d",
444 strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
445
446 r = address_configure(addr, link, dhcp6_address_handler, true);
447 if (r < 0)
448 return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
449
450 return 0;
451 }
452
453 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
454 int r;
455 sd_dhcp6_lease *lease;
456 struct in6_addr ip6_addr;
457 uint32_t lifetime_preferred, lifetime_valid;
458
459 r = sd_dhcp6_client_get_lease(client, &lease);
460 if (r < 0)
461 return r;
462
463 sd_dhcp6_lease_reset_address_iter(lease);
464
465 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
466 &lifetime_preferred,
467 &lifetime_valid) >= 0) {
468
469 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
470 if (r < 0)
471 return r;
472 }
473
474 return 0;
475 }
476
477 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
478 int r;
479 Link *link = userdata;
480
481 assert(link);
482 assert(link->network);
483
484 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
485 return;
486
487 switch(event) {
488 case SD_DHCP6_CLIENT_EVENT_STOP:
489 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
490 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
491 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
492 log_link_warning(link, "DHCPv6 lease lost");
493
494 (void) dhcp6_lease_pd_prefix_lost(client, link);
495 (void) dhcp6_prefix_remove_all(link->manager, link);
496
497 link_dirty(link);
498 link->dhcp6_configured = false;
499 break;
500
501 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
502 r = dhcp6_lease_address_acquired(client, link);
503 if (r < 0) {
504 link_enter_failed(link);
505 return;
506 }
507
508 r = dhcp6_lease_pd_prefix_acquired(client, link);
509 if (r < 0)
510 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
511
512 _fallthrough_;
513 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
514 r = dhcp6_lease_information_acquired(client, link);
515 if (r < 0) {
516 link_enter_failed(link);
517 return;
518 }
519
520 link_dirty(link);
521 link->dhcp6_configured = true;
522 break;
523
524 default:
525 if (event < 0)
526 log_link_warning_errno(link, event, "DHCPv6 error: %m");
527 else
528 log_link_warning(link, "DHCPv6 unknown event: %d", event);
529 return;
530 }
531
532 link_check_ready(link);
533 }
534
535 int dhcp6_request_address(Link *link, int ir) {
536 int r, inf_req, pd;
537 bool running;
538
539 assert(link);
540 assert(link->dhcp6_client);
541 assert(link->network);
542 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
543
544 r = sd_dhcp6_client_is_running(link->dhcp6_client);
545 if (r < 0)
546 return r;
547 running = r;
548
549 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
550 if (r < 0)
551 return r;
552
553 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
554 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
555
556 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
557 false);
558 if (r < 0 )
559 return r;
560
561 ir = false;
562 }
563
564 if (running) {
565 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
566 if (r < 0)
567 return r;
568
569 if (inf_req == ir)
570 return 0;
571
572 r = sd_dhcp6_client_stop(link->dhcp6_client);
573 if (r < 0)
574 return r;
575 } else {
576 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
577 if (r < 0)
578 return r;
579 }
580
581 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
582 if (r < 0)
583 return r;
584
585 r = sd_dhcp6_client_start(link->dhcp6_client);
586 if (r < 0)
587 return r;
588
589 return 0;
590 }
591
592 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
593 _cleanup_free_ char *hostname = NULL;
594 const char *hn;
595 int r;
596
597 assert(link);
598
599 if (!link->network->dhcp_send_hostname)
600 hn = NULL;
601 else if (link->network->dhcp_hostname)
602 hn = link->network->dhcp_hostname;
603 else {
604 r = gethostname_strict(&hostname);
605 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
606 return r;
607
608 hn = hostname;
609 }
610
611 r = sd_dhcp6_client_set_fqdn(client, hn);
612 if (r == -EINVAL && hostname)
613 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
614 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
615 else if (r < 0)
616 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
617
618 return 0;
619 }
620
621 int dhcp6_configure(Link *link) {
622 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
623 sd_dhcp6_option *send_option;
624 const DUID *duid;
625 Iterator i;
626 int r;
627
628 assert(link);
629 assert(link->network);
630
631 if (link->dhcp6_client)
632 return 0;
633
634 r = sd_dhcp6_client_new(&client);
635 if (r == -ENOMEM)
636 return log_oom();
637 if (r < 0)
638 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
639
640 r = sd_dhcp6_client_attach_event(client, NULL, 0);
641 if (r < 0)
642 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
643
644 r = sd_dhcp6_client_set_mac(client,
645 (const uint8_t *) &link->mac,
646 sizeof (link->mac), ARPHRD_ETHER);
647 if (r < 0)
648 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
649
650 if (link->network->iaid_set) {
651 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
652 if (r < 0)
653 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
654 }
655
656 duid = link_get_duid(link);
657 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
658 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
659 else
660 r = sd_dhcp6_client_set_duid(client,
661 duid->type,
662 duid->raw_data_len > 0 ? duid->raw_data : NULL,
663 duid->raw_data_len);
664 if (r < 0)
665 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
666
667 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options, i) {
668 r = sd_dhcp6_client_add_option(client, send_option);
669 if (r == -EEXIST)
670 continue;
671 if (r < 0)
672 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m");
673 }
674
675 r = dhcp6_set_hostname(client, link);
676 if (r < 0)
677 return r;
678
679 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
680 if (r < 0)
681 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
682
683 if (link->network->rapid_commit) {
684 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
685 if (r < 0)
686 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
687 }
688
689 if (link->network->dhcp6_mudurl) {
690 r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
691 if (r < 0)
692 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m");
693 }
694
695 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
696 if (r < 0)
697 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
698
699 if (dhcp6_enable_prefix_delegation(link)) {
700 r = sd_dhcp6_client_set_prefix_delegation(client, true);
701 if (r < 0)
702 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
703 }
704
705 if (link->network->dhcp6_pd_length > 0) {
706 r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
707 if (r < 0)
708 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix hint: %m");
709 }
710
711 link->dhcp6_client = TAKE_PTR(client);
712
713 return 0;
714 }
715
716 static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
717 assert_return(m, NULL);
718 assert_return(addr, NULL);
719
720 return hashmap_get(m->dhcp6_prefixes, addr);
721 }
722
723 static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
724 int r;
725
726 assert(link);
727
728 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
729 return 1;
730
731 r = sd_netlink_message_get_errno(m);
732 if (r < 0 && r != -EEXIST) {
733 log_link_message_warning_errno(link, m, r, "Received error adding DHCPv6 Prefix Delegation route");
734 link_enter_failed(link);
735 return 1;
736 }
737
738 return 1;
739 }
740
741 static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
742 _cleanup_(route_freep) Route *route = NULL;
743 _cleanup_free_ struct in6_addr *a = NULL;
744 _cleanup_free_ char *buf = NULL;
745 Link *assigned_link;
746 int r;
747
748 assert_return(m, -EINVAL);
749 assert_return(addr, -EINVAL);
750
751 r = route_new(&route);
752 if (r < 0)
753 return r;
754
755 route->family = AF_INET6;
756 route->dst.in6 = *addr;
757 route->dst_prefixlen = 64;
758
759 r = route_configure(route, link, dhcp6_route_add_handler);
760 if (r < 0)
761 return r;
762
763 (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
764 log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
765
766 assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
767 if (assigned_link) {
768 assert(assigned_link == link);
769 return 0;
770 }
771
772 a = newdup(struct in6_addr, addr, 1);
773 if (!a)
774 return -ENOMEM;
775
776 r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
777 if (r < 0)
778 return r;
779
780 r = hashmap_put(m->dhcp6_prefixes, a, link);
781 if (r < 0)
782 return r;
783
784 TAKE_PTR(a);
785 link_ref(link);
786 return 0;
787 }
788
789 static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
790 int r;
791
792 assert(link);
793
794 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
795 return 1;
796
797 r = sd_netlink_message_get_errno(m);
798 if (r < 0) {
799 log_link_message_warning_errno(link, m, r, "Received error on DHCPv6 Prefix Delegation route removal");
800 link_enter_failed(link);
801 return 1;
802 }
803
804 return 1;
805 }
806
807 int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
808 _cleanup_free_ struct in6_addr *a = NULL;
809 _cleanup_(link_unrefp) Link *l = NULL;
810 _cleanup_(route_freep) Route *route = NULL;
811 _cleanup_free_ char *buf = NULL;
812 int r;
813
814 assert_return(m, -EINVAL);
815 assert_return(addr, -EINVAL);
816
817 l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
818 if (!l)
819 return -EINVAL;
820
821 (void) sd_radv_remove_prefix(l->radv, addr, 64);
822
823 r = route_new(&route);
824 if (r < 0)
825 return r;
826
827 route->family = AF_INET6;
828 route->dst.in6 = *addr;
829 route->dst_prefixlen = 64;
830
831 r = route_remove(route, l, dhcp6_prefix_remove_handler);
832 if (r < 0)
833 return r;
834
835 (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
836 log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
837
838 return 0;
839 }
840
841 static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
842 struct in6_addr *addr;
843 Iterator i;
844 Link *l;
845
846 assert_return(m, -EINVAL);
847 assert_return(link, -EINVAL);
848
849 HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
850 if (l == link)
851 (void) dhcp6_prefix_remove(m, addr);
852
853 return 0;
854 }