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