]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
Merge pull request #18007 from fw-strlen/ipv6_masq_and_dnat
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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
10 #include "sd-dhcp6-client.h"
11
12 #include "escape.h"
13 #include "hashmap.h"
14 #include "hostname-setup.h"
15 #include "hostname-util.h"
16 #include "missing_network.h"
17 #include "networkd-address.h"
18 #include "networkd-dhcp6.h"
19 #include "networkd-link.h"
20 #include "networkd-manager.h"
21 #include "networkd-radv.h"
22 #include "siphash24.h"
23 #include "string-table.h"
24 #include "string-util.h"
25 #include "radv-internal.h"
26 #include "web-util.h"
27
28 bool link_dhcp6_pd_is_enabled(Link *link) {
29 assert(link);
30
31 if (!link->network)
32 return false;
33
34 return link->network->dhcp6_pd;
35 }
36
37 static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
38 uint32_t lifetime_preferred, lifetime_valid;
39 union in_addr_union pd_prefix;
40 uint8_t pd_prefix_len;
41
42 if (!lease)
43 return false;
44
45 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
46
47 return sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
48 }
49
50 DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) {
51 if (!p)
52 return NULL;
53
54 if (p->link && p->link->manager) {
55 hashmap_remove(p->link->manager->dhcp6_prefixes, &p->prefix);
56 set_remove(p->link->manager->dhcp6_pd_prefixes, p);
57 }
58
59 link_unref(p->link);
60 return mfree(p);
61 }
62
63 static void dhcp6_pd_hash_func(const DHCP6DelegatedPrefix *p, struct siphash *state) {
64 assert(p);
65
66 siphash24_compress(&p->pd_prefix, sizeof(p->pd_prefix), state);
67 siphash24_compress(&p->link, sizeof(p->link), state);
68 }
69
70 static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6DelegatedPrefix *b) {
71 int r;
72
73 r = memcmp(&a->pd_prefix, &b->pd_prefix, sizeof(a->pd_prefix));
74 if (r != 0)
75 return r;
76
77 return CMP(a->link, b->link);
78 }
79
80 DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func);
81
82 static Link *dhcp6_pd_get_link_by_prefix(Link *link, const union in_addr_union *prefix) {
83 DHCP6DelegatedPrefix *pd;
84
85 assert(link);
86 assert(link->manager);
87 assert(prefix);
88
89 pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6);
90 if (!pd)
91 return NULL;
92
93 return pd->link;
94 }
95
96 static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *pd_prefix, union in_addr_union *ret_prefix) {
97 DHCP6DelegatedPrefix *pd, in;
98
99 assert(link);
100 assert(link->manager);
101 assert(pd_prefix);
102 assert(ret_prefix);
103
104 in = (DHCP6DelegatedPrefix) {
105 .pd_prefix = pd_prefix->in6,
106 .link = link,
107 };
108
109 pd = set_get(link->manager->dhcp6_pd_prefixes, &in);
110 if (!pd)
111 return -ENOENT;
112
113 ret_prefix->in6 = pd->prefix;
114 return 0;
115 }
116
117 static int dhcp6_pd_remove_old(Link *link, bool force);
118
119 static int dhcp6_pd_address_callback(Address *address) {
120 Address *a;
121
122 assert(address);
123 assert(address->link);
124
125 /* Make this called only once */
126 SET_FOREACH(a, address->link->dhcp6_pd_addresses)
127 a->callback = NULL;
128
129 return dhcp6_pd_remove_old(address->link, true);
130 }
131
132 static int dhcp6_pd_remove_old(Link *link, bool force) {
133 Address *address;
134 Route *route;
135 int k, r = 0;
136
137 assert(link);
138 assert(link->manager);
139
140 if (!force && (link->dhcp6_pd_address_messages != 0 || link->dhcp6_pd_route_messages != 0))
141 return 0;
142
143 if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old))
144 return 0;
145
146 if (!force) {
147 bool set_callback = !set_isempty(link->dhcp6_pd_addresses);
148
149 SET_FOREACH(address, link->dhcp6_pd_addresses)
150 if (address_is_ready(address)) {
151 set_callback = false;
152 break;
153 }
154
155 if (set_callback) {
156 SET_FOREACH(address, link->dhcp6_pd_addresses)
157 address->callback = dhcp6_pd_address_callback;
158 return 0;
159 }
160 }
161
162 log_link_debug(link, "Removing old DHCPv6 Prefix Delegation addresses and routes.");
163
164 SET_FOREACH(route, link->dhcp6_pd_routes_old) {
165 k = route_remove(route, NULL, link, NULL);
166 if (k < 0)
167 r = k;
168
169 if (link->radv)
170 (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
171 dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6));
172 }
173
174 SET_FOREACH(address, link->dhcp6_pd_addresses_old) {
175 k = address_remove(address, link, NULL);
176 if (k < 0)
177 r = k;
178 }
179
180 return r;
181 }
182
183 int dhcp6_pd_remove(Link *link) {
184 Address *address;
185 Route *route;
186 int k, r = 0;
187
188 assert(link);
189 assert(link->manager);
190
191 if (!link_dhcp6_pd_is_enabled(link))
192 return 0;
193
194 link->dhcp6_pd_address_configured = false;
195 link->dhcp6_pd_route_configured = false;
196
197 k = dhcp6_pd_remove_old(link, true);
198 if (k < 0)
199 r = k;
200
201 if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes))
202 return r;
203
204 log_link_debug(link, "Removing DHCPv6 Prefix Delegation addresses and routes.");
205
206 SET_FOREACH(route, link->dhcp6_pd_routes) {
207 k = route_remove(route, NULL, link, NULL);
208 if (k < 0)
209 r = k;
210
211 if (link->radv)
212 (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
213 dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6));
214 }
215
216 SET_FOREACH(address, link->dhcp6_pd_addresses) {
217 k = address_remove(address, link, NULL);
218 if (k < 0)
219 r = k;
220 }
221
222 return r;
223 }
224
225 static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
226 int r;
227
228 assert(link);
229 assert(link->dhcp6_pd_route_messages > 0);
230
231 link->dhcp6_pd_route_messages--;
232
233 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
234 return 1;
235
236 r = sd_netlink_message_get_errno(m);
237 if (r < 0 && r != -EEXIST) {
238 log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route");
239 link_enter_failed(link);
240 return 1;
241 }
242
243 if (link->dhcp6_pd_route_messages == 0) {
244 log_link_debug(link, "DHCPv6 prefix delegation routes set");
245 if (link->dhcp6_pd_prefixes_assigned)
246 link->dhcp6_pd_route_configured = true;
247
248 r = dhcp6_pd_remove_old(link, false);
249 if (r < 0) {
250 link_enter_failed(link);
251 return 1;
252 }
253
254 link_check_ready(link);
255 }
256
257 return 1;
258 }
259
260 static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix) {
261 _cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL;
262 _cleanup_(route_freep) Route *route = NULL;
263 Link *assigned_link;
264 Route *ret;
265 int r;
266
267 assert(link);
268 assert(link->manager);
269 assert(prefix);
270 assert(pd_prefix);
271
272 r = route_new(&route);
273 if (r < 0)
274 return r;
275
276 route->family = AF_INET6;
277 route->dst = *prefix;
278 route->dst_prefixlen = 64;
279 route->protocol = RTPROT_DHCP;
280
281 r = route_configure(route, link, dhcp6_pd_route_handler, &ret);
282 if (r < 0)
283 return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m");
284 if (r > 0)
285 link->dhcp6_pd_route_configured = false;
286
287 link->dhcp6_pd_route_messages++;
288
289 r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret);
290 if (r < 0)
291 return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m");
292
293 (void) set_remove(link->dhcp6_pd_routes_old, ret);
294
295 assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix);
296 if (assigned_link) {
297 assert(assigned_link == link);
298 return 0;
299 }
300
301 pd = new(DHCP6DelegatedPrefix, 1);
302 if (!pd)
303 return log_oom();
304
305 *pd = (DHCP6DelegatedPrefix) {
306 .prefix = prefix->in6,
307 .pd_prefix = pd_prefix->in6,
308 .link = link_ref(link),
309 };
310
311 r = hashmap_ensure_put(&link->manager->dhcp6_prefixes, &in6_addr_hash_ops, &pd->prefix, pd);
312 if (r == -ENOMEM)
313 return log_oom();
314 if (r < 0)
315 return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m");
316
317 r = set_ensure_put(&link->manager->dhcp6_pd_prefixes, &dhcp6_pd_hash_ops, pd);
318 if (r < 0)
319 return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m");
320
321 TAKE_PTR(pd);
322 return 0;
323 }
324
325 static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
326 int r;
327
328 assert(link);
329 assert(link->dhcp6_pd_address_messages > 0);
330
331 link->dhcp6_pd_address_messages--;
332
333 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
334 return 1;
335
336 r = sd_netlink_message_get_errno(m);
337 if (r < 0 && r != -EEXIST) {
338 log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address");
339 link_enter_failed(link);
340 return 1;
341 } else if (r >= 0)
342 (void) manager_rtnl_process_address(rtnl, m, link->manager);
343
344 if (link->dhcp6_pd_address_messages == 0) {
345 log_link_debug(link, "DHCPv6 delegated prefix addresses set");
346 if (link->dhcp6_pd_prefixes_assigned)
347 link->dhcp6_pd_address_configured = true;
348
349 r = dhcp6_pd_remove_old(link, false);
350 if (r < 0) {
351 link_enter_failed(link);
352 return 1;
353 }
354 }
355
356 return 1;
357 }
358
359 static int dhcp6_set_pd_address(
360 Link *link,
361 const union in_addr_union *prefix,
362 uint32_t lifetime_preferred,
363 uint32_t lifetime_valid) {
364
365 _cleanup_(address_freep) Address *address = NULL;
366 Address *ret;
367 int r;
368
369 assert(link);
370 assert(link->network);
371 assert(prefix);
372
373 if (!link->network->dhcp6_pd_assign)
374 return 0;
375
376 r = address_new(&address);
377 if (r < 0)
378 return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m");
379
380 address->in_addr = *prefix;
381
382 if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_pd_token))
383 memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.in6.s6_addr + 8, 8);
384 else {
385 r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
386 if (r < 0)
387 return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m");
388 }
389
390 address->prefixlen = 64;
391 address->family = AF_INET6;
392 address->cinfo.ifa_prefered = lifetime_preferred;
393 address->cinfo.ifa_valid = lifetime_valid;
394 SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp6_pd_manage_temporary_address);
395
396 r = address_configure(address, link, dhcp6_pd_address_handler, &ret);
397 if (r < 0)
398 return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m");
399 if (r > 0)
400 link->dhcp6_pd_address_configured = false;
401
402 link->dhcp6_pd_address_messages++;
403
404 r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, ret);
405 if (r < 0)
406 return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m");
407
408 (void) set_remove(link->dhcp6_pd_addresses_old, ret);
409
410 return 0;
411 }
412
413 static int dhcp6_pd_assign_prefix(
414 Link *link,
415 const union in_addr_union *prefix,
416 const union in_addr_union *pd_prefix,
417 uint32_t lifetime_preferred,
418 uint32_t lifetime_valid) {
419
420 int r;
421
422 assert(link);
423 assert(link->network);
424 assert(prefix);
425
426 if (link->network->dhcp6_pd_announce) {
427 r = radv_add_prefix(link, &prefix->in6, 64, lifetime_preferred, lifetime_valid);
428 if (r < 0)
429 return r;
430 }
431
432 r = dhcp6_set_pd_route(link, prefix, pd_prefix);
433 if (r < 0)
434 return r;
435
436 r = dhcp6_set_pd_address(link, prefix, lifetime_preferred, lifetime_valid);
437 if (r < 0)
438 return r;
439
440 return 0;
441 }
442
443 static bool link_has_preferred_subnet_id(Link *link) {
444 if (!link->network)
445 return false;
446
447 return link->network->dhcp6_pd_subnet_id >= 0;
448 }
449
450 static int dhcp6_get_preferred_delegated_prefix(
451 Link *link,
452 const union in_addr_union *masked_pd_prefix,
453 uint8_t pd_prefix_len,
454 union in_addr_union *ret) {
455
456 /* We start off with the original PD prefix we have been assigned and iterate from there */
457 union in_addr_union prefix;
458 uint64_t n_prefixes;
459 Link *assigned_link;
460 int r;
461
462 assert(link);
463 assert(link->manager);
464 assert(masked_pd_prefix);
465 assert(pd_prefix_len <= 64);
466
467 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
468 prefix = *masked_pd_prefix;
469
470 if (link_has_preferred_subnet_id(link)) {
471 uint64_t subnet_id = link->network->dhcp6_pd_subnet_id;
472
473 /* If the link has a preference for a particular subnet id try to allocate that */
474 if (subnet_id >= n_prefixes)
475 return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE),
476 "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
477 subnet_id, n_prefixes);
478
479 r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id);
480 if (r < 0)
481 return log_link_warning_errno(link, r,
482 "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
483 subnet_id, n_prefixes);
484
485 /* Verify that the prefix we did calculate fits in the pd prefix.
486 * This should not fail as we checked the prefix size beforehand */
487 assert_se(in_addr_prefix_covers(AF_INET6, masked_pd_prefix, pd_prefix_len, &prefix) > 0);
488
489 assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix);
490 if (assigned_link && assigned_link != link) {
491 _cleanup_free_ char *assigned_buf = NULL;
492
493 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
494 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN),
495 "The requested prefix %s is already assigned to another link.",
496 strna(assigned_buf));
497 }
498
499 *ret = prefix;
500 return 0;
501 }
502
503 for (uint64_t n = 0; n < n_prefixes; n++) {
504 /* If we do not have an allocation preference just iterate
505 * through the address space and return the first free prefix. */
506 assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix);
507 if (!assigned_link || assigned_link == link) {
508 *ret = prefix;
509 return 0;
510 }
511
512 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
513 if (r < 0)
514 return log_link_warning_errno(link, r, "Can't allocate another prefix. Out of address space?: %m");
515 }
516
517 return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space.");
518 }
519
520 static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
521 const union in_addr_union *masked_pd_prefix,
522 uint8_t pd_prefix_len,
523 uint32_t lifetime_preferred,
524 uint32_t lifetime_valid,
525 bool assign_preferred_subnet_id) {
526
527 Link *link;
528 int r;
529
530 assert(dhcp6_link);
531 assert(dhcp6_link->manager);
532 assert(masked_pd_prefix);
533 assert(pd_prefix_len <= 64);
534
535 HASHMAP_FOREACH(link, dhcp6_link->manager->links) {
536 _cleanup_free_ char *assigned_buf = NULL;
537 union in_addr_union assigned_prefix;
538
539 if (link == dhcp6_link)
540 continue;
541
542 if (!link_dhcp6_pd_is_enabled(link))
543 continue;
544
545 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
546 continue;
547
548 if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link))
549 continue;
550
551 r = dhcp6_pd_get_assigned_prefix(link, masked_pd_prefix, &assigned_prefix);
552 if (r < 0) {
553 r = dhcp6_get_preferred_delegated_prefix(link, masked_pd_prefix, pd_prefix_len, &assigned_prefix);
554 if (r < 0) {
555 link->dhcp6_pd_prefixes_assigned = false;
556 continue;
557 }
558 }
559
560 (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf);
561 r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix,
562 lifetime_preferred, lifetime_valid);
563 if (r < 0) {
564 log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m",
565 strna(assigned_buf));
566 link_enter_failed(link);
567 } else
568 log_link_debug(link, "Assigned prefix %s/64", strna(assigned_buf));
569 }
570 }
571
572 static int dhcp6_pd_prepare(Link *link) {
573 Address *address;
574 Route *route;
575 int r;
576
577 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
578 return 0;
579
580 if (!link_dhcp6_pd_is_enabled(link))
581 return 0;
582
583 link->dhcp6_pd_prefixes_assigned = true;
584
585 while ((address = set_steal_first(link->dhcp6_pd_addresses))) {
586 r = set_ensure_put(&link->dhcp6_pd_addresses_old, &address_hash_ops, address);
587 if (r < 0)
588 return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation address: %m");
589 }
590
591 while ((route = set_steal_first(link->dhcp6_pd_routes))) {
592 r = set_ensure_put(&link->dhcp6_pd_routes_old, &route_hash_ops, route);
593 if (r < 0)
594 return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation route: %m");
595 }
596
597 return 0;
598 }
599
600 static int dhcp6_pd_finalize(Link *link) {
601 int r;
602
603 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
604 return 0;
605
606 if (!link_dhcp6_pd_is_enabled(link))
607 return 0;
608
609 if (link->dhcp6_pd_address_messages == 0) {
610 if (link->dhcp6_pd_prefixes_assigned)
611 link->dhcp6_pd_address_configured = true;
612 } else
613 log_link_debug(link, "Setting DHCPv6 PD addresses");
614
615 if (link->dhcp6_pd_route_messages == 0) {
616 if (link->dhcp6_pd_prefixes_assigned)
617 link->dhcp6_pd_route_configured = true;
618 } else
619 log_link_debug(link, "Setting DHCPv6 PD routes");
620
621 r = dhcp6_pd_remove_old(link, false);
622 if (r < 0)
623 return r;
624
625 if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured)
626 link_check_ready(link);
627 else
628 link_set_state(link, LINK_STATE_CONFIGURING);
629
630 return 0;
631 }
632
633 static void dhcp6_pd_prefix_lost(Link *dhcp6_link) {
634 Link *link;
635 int r;
636
637 assert(dhcp6_link);
638 assert(dhcp6_link->manager);
639
640 HASHMAP_FOREACH(link, dhcp6_link->manager->links) {
641 if (link == dhcp6_link)
642 continue;
643
644 r = dhcp6_pd_remove(link);
645 if (r < 0)
646 link_enter_failed(link);
647 }
648 }
649
650 static int dhcp6_remove_old(Link *link, bool force);
651
652 static int dhcp6_address_callback(Address *address) {
653 Address *a;
654
655 assert(address);
656 assert(address->link);
657
658 /* Make this called only once */
659 SET_FOREACH(a, address->link->dhcp6_addresses)
660 a->callback = NULL;
661
662 return dhcp6_remove_old(address->link, true);
663 }
664
665 static int dhcp6_remove_old(Link *link, bool force) {
666 Address *address;
667 Route *route;
668 int k, r = 0;
669
670 assert(link);
671
672 if (!force && (!link->dhcp6_address_configured || !link->dhcp6_route_configured))
673 return 0;
674
675 if (set_isempty(link->dhcp6_addresses_old) && set_isempty(link->dhcp6_routes_old))
676 return 0;
677
678 if (!force) {
679 bool set_callback = !set_isempty(link->dhcp6_addresses);
680
681 SET_FOREACH(address, link->dhcp6_addresses)
682 if (address_is_ready(address)) {
683 set_callback = false;
684 break;
685 }
686
687 if (set_callback) {
688 SET_FOREACH(address, link->dhcp6_addresses)
689 address->callback = dhcp6_address_callback;
690 return 0;
691 }
692 }
693
694 log_link_debug(link, "Removing old DHCPv6 addresses and routes.");
695
696 SET_FOREACH(route, link->dhcp6_routes_old) {
697 k = route_remove(route, NULL, link, NULL);
698 if (k < 0)
699 r = k;
700 }
701
702 SET_FOREACH(address, link->dhcp6_addresses_old) {
703 k = address_remove(address, link, NULL);
704 if (k < 0)
705 r = k;
706 }
707
708 return r;
709 }
710
711 static int dhcp6_remove(Link *link) {
712 Address *address;
713 Route *route;
714 int k, r = 0;
715
716 assert(link);
717
718 link->dhcp6_address_configured = false;
719 link->dhcp6_route_configured = false;
720
721 k = dhcp6_remove_old(link, true);
722 if (k < 0)
723 r = k;
724
725 if (set_isempty(link->dhcp6_addresses) && set_isempty(link->dhcp6_routes))
726 return r;
727
728 log_link_debug(link, "Removing DHCPv6 addresses and routes.");
729
730 SET_FOREACH(route, link->dhcp6_routes) {
731 k = route_remove(route, NULL, link, NULL);
732 if (k < 0)
733 r = k;
734 }
735
736 SET_FOREACH(address, link->dhcp6_addresses) {
737 k = address_remove(address, link, NULL);
738 if (k < 0)
739 r = k;
740 }
741
742 return r;
743 }
744
745 static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
746 int r;
747
748 assert(link);
749 assert(link->dhcp6_route_messages > 0);
750
751 link->dhcp6_route_messages--;
752
753 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
754 return 1;
755
756 r = sd_netlink_message_get_errno(m);
757 if (r < 0 && r != -EEXIST) {
758 log_link_message_warning_errno(link, m, r, "Failed to add unreachable route for DHCPv6 delegated subnet");
759 link_enter_failed(link);
760 return 1;
761 }
762
763 if (link->dhcp6_route_messages == 0) {
764 log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set");
765 link->dhcp6_route_configured = true;
766
767 r = dhcp6_remove_old(link, false);
768 if (r < 0) {
769 link_enter_failed(link);
770 return 1;
771 }
772
773 link_check_ready(link);
774 }
775
776 return 1;
777 }
778
779 static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *addr, uint8_t prefixlen) {
780 _cleanup_(route_freep) Route *route = NULL;
781 _cleanup_free_ char *buf = NULL;
782 Route *ret;
783 int r;
784
785 assert(link);
786 assert(addr);
787
788 (void) in_addr_to_string(AF_INET6, addr, &buf);
789
790 if (prefixlen > 64) {
791 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
792 strna(buf), prefixlen);
793 return 0;
794 }
795
796 if (prefixlen == 64) {
797 log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s/64 since distributed prefix is 64",
798 strna(buf));
799 return 1;
800 }
801
802 if (prefixlen < 48)
803 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
804 strna(buf), prefixlen);
805
806 r = route_new(&route);
807 if (r < 0)
808 return log_oom();
809
810 route->family = AF_INET6;
811 route->dst = *addr;
812 route->dst_prefixlen = prefixlen;
813 route->table = link_get_dhcp_route_table(link);
814 route->type = RTN_UNREACHABLE;
815 route->protocol = RTPROT_DHCP;
816
817 r = route_configure(route, link, dhcp6_route_handler, &ret);
818 if (r < 0)
819 return log_link_error_errno(link, r, "Failed to set unreachable route for DHCPv6 delegated subnet %s/%u: %m",
820 strna(buf), prefixlen);
821 if (r > 0)
822 link->dhcp6_route_configured = false;
823
824 link->dhcp6_route_messages++;
825
826 r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, ret);
827 if (r < 0)
828 return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet %s/%u: %m",
829 strna(buf), prefixlen);
830
831 (void) set_remove(link->dhcp6_routes_old, ret);
832
833 return 1;
834 }
835
836 static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
837 Link *link;
838 int r;
839
840 assert(dhcp6_link);
841 assert(dhcp6_link->dhcp6_lease);
842
843 HASHMAP_FOREACH(link, dhcp6_link->manager->links) {
844 if (link == dhcp6_link)
845 continue;
846
847 r = dhcp6_pd_prepare(link);
848 if (r < 0)
849 link_enter_failed(link);
850 }
851
852 for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) {
853 uint32_t lifetime_preferred, lifetime_valid;
854 union in_addr_union pd_prefix, prefix;
855 uint8_t pd_prefix_len;
856
857 r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len,
858 &lifetime_preferred, &lifetime_valid);
859 if (r < 0)
860 break;
861
862 r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len);
863 if (r < 0)
864 return r;
865 if (r == 0)
866 continue;
867
868 /* We are doing prefix allocation in two steps:
869 * 1. all those links that have a preferred subnet id will be assigned their subnet
870 * 2. all those links that remain will receive prefixes in sequential order. Prefixes
871 * that were previously already allocated to another link will be skipped.
872 * The assignment has to be split in two phases since subnet id
873 * preferences should be honored. Meaning that any subnet id should be
874 * handed out to the requesting link and not to some link that didn't
875 * specify any preference. */
876
877 assert(pd_prefix_len <= 64);
878
879 prefix = pd_prefix;
880 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
881 if (r < 0)
882 return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m");
883
884 if (DEBUG_LOGGING) {
885 uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
886 _cleanup_free_ char *buf = NULL;
887
888 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
889 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
890 n_prefixes, strna(buf), pd_prefix_len);
891 }
892
893 dhcp6_pd_prefix_distribute(dhcp6_link,
894 &prefix,
895 pd_prefix_len,
896 lifetime_preferred,
897 lifetime_valid,
898 true);
899
900 dhcp6_pd_prefix_distribute(dhcp6_link,
901 &prefix,
902 pd_prefix_len,
903 lifetime_preferred,
904 lifetime_valid,
905 false);
906 }
907
908 HASHMAP_FOREACH(link, dhcp6_link->manager->links) {
909 if (link == dhcp6_link)
910 continue;
911
912 r = dhcp6_pd_finalize(link);
913 if (r < 0)
914 link_enter_failed(link);
915 }
916
917 return 0;
918 }
919
920 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
921 int r;
922
923 assert(link);
924 assert(link->dhcp6_address_messages > 0);
925
926 link->dhcp6_address_messages--;
927
928 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
929 return 1;
930
931 r = sd_netlink_message_get_errno(m);
932 if (r < 0 && r != -EEXIST) {
933 log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 address");
934 link_enter_failed(link);
935 return 1;
936 } else if (r >= 0)
937 (void) manager_rtnl_process_address(rtnl, m, link->manager);
938
939 if (link->dhcp6_address_messages == 0) {
940 log_link_debug(link, "DHCPv6 addresses set");
941 link->dhcp6_address_configured = true;
942
943 r = dhcp6_remove_old(link, false);
944 if (r < 0) {
945 link_enter_failed(link);
946 return 1;
947 }
948 }
949
950 return 1;
951 }
952
953 static void log_dhcp6_address(Link *link, const Address *address, char **ret) {
954 char valid_buf[FORMAT_TIMESPAN_MAX], preferred_buf[FORMAT_TIMESPAN_MAX];
955 const char *valid_str = NULL, *preferred_str = NULL;
956 _cleanup_free_ char *buffer = NULL;
957 bool by_ndisc = false;
958 Address *existing;
959 NDiscAddress *na;
960 int log_level, r;
961
962 assert(link);
963 assert(address);
964
965 (void) in_addr_to_string(address->family, &address->in_addr, &buffer);
966 if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
967 valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
968 address->cinfo.ifa_valid * USEC_PER_SEC,
969 USEC_PER_SEC);
970 if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
971 preferred_str = format_timespan(preferred_buf, FORMAT_TIMESPAN_MAX,
972 address->cinfo.ifa_prefered * USEC_PER_SEC,
973 USEC_PER_SEC);
974
975 r = address_get(link, address, &existing);
976 if (r < 0) {
977 /* New address. */
978 log_level = LOG_INFO;
979 goto simple_log;
980 } else
981 log_level = LOG_DEBUG;
982
983 if (set_contains(link->dhcp6_addresses, address))
984 /* Already warned. */
985 goto simple_log;
986
987 if (address->prefixlen == existing->prefixlen)
988 /* Currently, only conflict in prefix length is reported. */
989 goto simple_log;
990
991 SET_FOREACH(na, link->ndisc_addresses)
992 if (address_compare_func(na->address, existing)) {
993 by_ndisc = true;
994 break;
995 }
996
997 log_link_warning(link, "DHCPv6 address %s/%u (valid %s%s, preferred %s%s) conflicts the existing address %s/%u%s.",
998 strnull(buffer), address->prefixlen,
999 valid_str ? "for " : "forever", strempty(valid_str),
1000 preferred_str ? "for " : "forever", strempty(preferred_str),
1001 strnull(buffer), existing->prefixlen,
1002 by_ndisc ? "assigned by NDISC. Please try to use or update IPv6Token= setting "
1003 "to change the address generated by NDISC, or disable UseAutonomousPrefix=" : "");
1004 goto finalize;
1005
1006 simple_log:
1007 log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s%s, preferred %s%s)",
1008 strnull(buffer), address->prefixlen,
1009 valid_str ? "for " : "forever", strempty(valid_str),
1010 preferred_str ? "for " : "forever", strempty(preferred_str));
1011
1012 finalize:
1013 if (ret)
1014 *ret = TAKE_PTR(buffer);
1015 }
1016
1017 static int dhcp6_update_address(
1018 Link *link,
1019 const struct in6_addr *ip6_addr,
1020 uint32_t lifetime_preferred,
1021 uint32_t lifetime_valid) {
1022
1023 _cleanup_(address_freep) Address *addr = NULL;
1024 _cleanup_free_ char *buffer = NULL;
1025 Address *ret;
1026 int r;
1027
1028 r = address_new(&addr);
1029 if (r < 0)
1030 return log_oom();
1031
1032 addr->family = AF_INET6;
1033 addr->in_addr.in6 = *ip6_addr;
1034 addr->flags = IFA_F_NOPREFIXROUTE;
1035 addr->prefixlen = 128;
1036 addr->cinfo.ifa_prefered = lifetime_preferred;
1037 addr->cinfo.ifa_valid = lifetime_valid;
1038
1039 log_dhcp6_address(link, addr, &buffer);
1040
1041 r = address_configure(addr, link, dhcp6_address_handler, &ret);
1042 if (r < 0)
1043 return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s/%u: %m",
1044 strnull(buffer), addr->prefixlen);
1045 if (r > 0)
1046 link->dhcp6_address_configured = false;
1047
1048 link->dhcp6_address_messages++;
1049
1050 r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret);
1051 if (r < 0)
1052 return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s/%u: %m",
1053 strnull(buffer), addr->prefixlen);
1054
1055 (void) set_remove(link->dhcp6_addresses_old, ret);
1056
1057 return 0;
1058 }
1059
1060 static int dhcp6_address_acquired(Link *link) {
1061 int r;
1062
1063 assert(link);
1064 assert(link->network);
1065 assert(link->dhcp6_lease);
1066
1067 if (!link->network->dhcp6_use_address)
1068 return 0;
1069
1070 for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) {
1071 uint32_t lifetime_preferred, lifetime_valid;
1072 struct in6_addr ip6_addr;
1073
1074 r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred, &lifetime_valid);
1075 if (r < 0)
1076 break;
1077
1078 r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid);
1079 if (r < 0)
1080 return r;
1081 }
1082
1083 if (link->network->dhcp6_use_fqdn) {
1084 const char *dhcpname = NULL;
1085 _cleanup_free_ char *hostname = NULL;
1086 (void) sd_dhcp6_lease_get_fqdn(link->dhcp6_lease, &dhcpname);
1087
1088 if (dhcpname) {
1089 r = shorten_overlong(dhcpname, &hostname);
1090 if (r < 0)
1091 log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname);
1092 if (r == 1)
1093 log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
1094 }
1095 if (hostname) {
1096 r = manager_set_hostname(link->manager, hostname);
1097 if (r < 0)
1098 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
1099 }
1100 }
1101
1102 return 0;
1103 }
1104
1105 static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
1106 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL;
1107 sd_dhcp6_lease *lease;
1108 Address *a;
1109 Route *rt;
1110 int r;
1111
1112 while ((a = set_steal_first(link->dhcp6_addresses))) {
1113 r = set_ensure_put(&link->dhcp6_addresses_old, &address_hash_ops, a);
1114 if (r < 0)
1115 return log_link_error_errno(link, r, "Failed to store old DHCPv6 address: %m");
1116 }
1117
1118 while ((rt = set_steal_first(link->dhcp6_routes))) {
1119 r = set_ensure_put(&link->dhcp6_routes_old, &route_hash_ops, rt);
1120 if (r < 0)
1121 return log_link_error_errno(link, r, "Failed to store old DHCPv6 route: %m");
1122 }
1123
1124 r = sd_dhcp6_client_get_lease(client, &lease);
1125 if (r < 0)
1126 return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m");
1127
1128 lease_old = TAKE_PTR(link->dhcp6_lease);
1129 link->dhcp6_lease = sd_dhcp6_lease_ref(lease);
1130
1131 r = dhcp6_address_acquired(link);
1132 if (r < 0)
1133 return r;
1134
1135 if (dhcp6_lease_has_pd_prefix(lease)) {
1136 r = dhcp6_pd_prefix_acquired(link);
1137 if (r < 0)
1138 return r;
1139 } else if (dhcp6_lease_has_pd_prefix(lease_old))
1140 /* When we had PD prefixes but not now, we need to remove them. */
1141 dhcp6_pd_prefix_lost(link);
1142
1143 if (link->dhcp6_address_messages == 0)
1144 link->dhcp6_address_configured = true;
1145 else
1146 log_link_debug(link, "Setting DHCPv6 addresses");
1147
1148 if (link->dhcp6_route_messages == 0)
1149 link->dhcp6_route_configured = true;
1150 else
1151 log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets");
1152
1153 r = dhcp6_remove_old(link, false);
1154 if (r < 0)
1155 return r;
1156
1157 if (link->dhcp6_address_configured && link->dhcp6_route_configured)
1158 link_check_ready(link);
1159 else
1160 link_set_state(link, LINK_STATE_CONFIGURING);
1161
1162 return 0;
1163 }
1164
1165 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) {
1166 return 0;
1167 }
1168
1169 static int dhcp6_lease_lost(Link *link) {
1170 int r;
1171
1172 assert(link);
1173 assert(link->manager);
1174
1175 log_link_info(link, "DHCPv6 lease lost");
1176
1177 if (dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
1178 dhcp6_pd_prefix_lost(link);
1179
1180 link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
1181
1182 r = dhcp6_remove(link);
1183 if (r < 0)
1184 return r;
1185
1186 return 0;
1187 }
1188
1189 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
1190 Link *link = userdata;
1191 int r;
1192
1193 assert(link);
1194 assert(link->network);
1195
1196 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1197 return;
1198
1199 switch (event) {
1200 case SD_DHCP6_CLIENT_EVENT_STOP:
1201 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
1202 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
1203 r = dhcp6_lease_lost(link);
1204 if (r < 0)
1205 link_enter_failed(link);
1206 break;
1207
1208 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
1209 r = dhcp6_lease_ip_acquired(client, link);
1210 if (r < 0) {
1211 link_enter_failed(link);
1212 return;
1213 }
1214
1215 _fallthrough_;
1216 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
1217 r = dhcp6_lease_information_acquired(client, link);
1218 if (r < 0)
1219 link_enter_failed(link);
1220 break;
1221
1222 default:
1223 if (event < 0)
1224 log_link_warning_errno(link, event, "DHCPv6 error: %m");
1225 else
1226 log_link_warning(link, "DHCPv6 unknown event: %d", event);
1227 return;
1228 }
1229 }
1230
1231 int dhcp6_request_address(Link *link, int ir) {
1232 int r, inf_req, pd;
1233 bool running;
1234
1235 assert(link);
1236 assert(link->dhcp6_client);
1237 assert(link->network);
1238 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0);
1239
1240 r = sd_dhcp6_client_is_running(link->dhcp6_client);
1241 if (r < 0)
1242 return r;
1243 running = r;
1244
1245 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
1246 if (r < 0)
1247 return r;
1248
1249 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
1250 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
1251
1252 r = sd_dhcp6_client_set_address_request(link->dhcp6_client, false);
1253 if (r < 0)
1254 return r;
1255
1256 ir = false;
1257 }
1258
1259 if (running) {
1260 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
1261 if (r < 0)
1262 return r;
1263
1264 if (inf_req == ir)
1265 return 0;
1266
1267 r = sd_dhcp6_client_stop(link->dhcp6_client);
1268 if (r < 0)
1269 return r;
1270 } else {
1271 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
1272 if (r < 0)
1273 return r;
1274 }
1275
1276 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
1277 if (r < 0)
1278 return r;
1279
1280 r = sd_dhcp6_client_start(link->dhcp6_client);
1281 if (r < 0)
1282 return r;
1283
1284 return 0;
1285 }
1286
1287 int dhcp6_request_prefix_delegation(Link *link) {
1288 Link *l;
1289
1290 assert(link);
1291 assert(link->manager);
1292
1293 if (!link_dhcp6_pd_is_enabled(link))
1294 return 0;
1295
1296 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
1297
1298 HASHMAP_FOREACH(l, link->manager->links) {
1299 int r, enabled;
1300
1301 if (l == link)
1302 continue;
1303
1304 if (!l->dhcp6_client)
1305 continue;
1306
1307 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
1308 if (r < 0) {
1309 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link: %m");
1310 link_enter_failed(l);
1311 continue;
1312 }
1313
1314 if (enabled == 0) {
1315 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
1316 if (r < 0) {
1317 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link: %m");
1318 link_enter_failed(l);
1319 continue;
1320 }
1321 }
1322
1323 r = sd_dhcp6_client_is_running(l->dhcp6_client);
1324 if (r <= 0)
1325 continue;
1326
1327 if (enabled != 0) {
1328 if (dhcp6_lease_has_pd_prefix(l->dhcp6_lease)) {
1329 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
1330 r = dhcp6_pd_prefix_acquired(l);
1331 if (r < 0)
1332 link_enter_failed(l);
1333 }
1334 continue;
1335 }
1336
1337 r = sd_dhcp6_client_stop(l->dhcp6_client);
1338 if (r < 0) {
1339 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link: %m");
1340 link_enter_failed(l);
1341 continue;
1342 }
1343
1344 r = sd_dhcp6_client_start(l->dhcp6_client);
1345 if (r < 0) {
1346 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link: %m");
1347 link_enter_failed(l);
1348 continue;
1349 }
1350
1351 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
1352 }
1353
1354 /* dhcp6_pd_prefix_acquired() may make the link in failed state. */
1355 if (link->state == LINK_STATE_FAILED)
1356 return -ENOANO;
1357
1358 return 0;
1359 }
1360
1361 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
1362 _cleanup_free_ char *hostname = NULL;
1363 const char *hn;
1364 int r;
1365
1366 assert(link);
1367
1368 if (!link->network->dhcp_send_hostname)
1369 hn = NULL;
1370 else if (link->network->dhcp_hostname)
1371 hn = link->network->dhcp_hostname;
1372 else {
1373 r = gethostname_strict(&hostname);
1374 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
1375 return r;
1376
1377 hn = hostname;
1378 }
1379
1380 r = sd_dhcp6_client_set_fqdn(client, hn);
1381 if (r == -EINVAL && hostname)
1382 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
1383 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
1384 else if (r < 0)
1385 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
1386
1387 return 0;
1388 }
1389
1390 static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
1391 Link *link;
1392
1393 assert(dhcp6_link);
1394 assert(dhcp6_link->manager);
1395
1396 HASHMAP_FOREACH(link, dhcp6_link->manager->links) {
1397 if (link == dhcp6_link)
1398 continue;
1399
1400 if (!link_dhcp6_pd_is_enabled(link))
1401 continue;
1402
1403 return true;
1404 }
1405
1406 return false;
1407 }
1408
1409 static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
1410 const DUID *duid;
1411 int r;
1412
1413 assert(link);
1414 assert(link->network);
1415 assert(client);
1416
1417 r = sd_dhcp6_client_set_mac(client, link->hw_addr.addr.bytes, link->hw_addr.length, link->iftype);
1418 if (r < 0)
1419 return r;
1420
1421 if (link->network->iaid_set) {
1422 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
1423 if (r < 0)
1424 return r;
1425 }
1426
1427 duid = link_get_duid(link);
1428 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
1429 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
1430 else
1431 r = sd_dhcp6_client_set_duid(client,
1432 duid->type,
1433 duid->raw_data_len > 0 ? duid->raw_data : NULL,
1434 duid->raw_data_len);
1435 if (r < 0)
1436 return r;
1437
1438 return 0;
1439 }
1440
1441 int dhcp6_configure(Link *link) {
1442 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1443 sd_dhcp6_option *vendor_option;
1444 sd_dhcp6_option *send_option;
1445 void *request_options;
1446 int r;
1447
1448 assert(link);
1449 assert(link->network);
1450
1451 if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
1452 return 0;
1453
1454 if (link->dhcp6_client)
1455 return 0;
1456
1457 r = sd_dhcp6_client_new(&client);
1458 if (r == -ENOMEM)
1459 return log_oom();
1460 if (r < 0)
1461 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
1462
1463 r = sd_dhcp6_client_attach_event(client, link->manager->event, 0);
1464 if (r < 0)
1465 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
1466
1467 r = dhcp6_set_identifier(link, client);
1468 if (r < 0)
1469 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set identifier: %m");
1470
1471 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) {
1472 r = sd_dhcp6_client_add_option(client, send_option);
1473 if (r == -EEXIST)
1474 continue;
1475 if (r < 0)
1476 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m");
1477 }
1478
1479 r = dhcp6_set_hostname(client, link);
1480 if (r < 0)
1481 return r;
1482
1483 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
1484 if (r < 0)
1485 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
1486
1487 if (link->network->dhcp6_rapid_commit) {
1488 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
1489 if (r < 0)
1490 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
1491 }
1492
1493 if (link->network->dhcp6_mudurl) {
1494 r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
1495 if (r < 0)
1496 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m");
1497 }
1498
1499 SET_FOREACH(request_options, link->network->dhcp6_request_options) {
1500 uint32_t option = PTR_TO_UINT32(request_options);
1501
1502 r = sd_dhcp6_client_set_request_option(client, option);
1503 if (r == -EEXIST) {
1504 log_link_debug(link, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
1505 continue;
1506 }
1507 if (r < 0)
1508 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option);
1509 }
1510
1511 if (link->network->dhcp6_user_class) {
1512 r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
1513 if (r < 0)
1514 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m");
1515 }
1516
1517 if (link->network->dhcp6_vendor_class) {
1518 r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
1519 if (r < 0)
1520 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m");
1521 }
1522
1523 ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options) {
1524 r = sd_dhcp6_client_add_vendor_option(client, vendor_option);
1525 if (r == -EEXIST)
1526 continue;
1527 if (r < 0)
1528 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m");
1529 }
1530
1531 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
1532 if (r < 0)
1533 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
1534
1535 if (dhcp6_enable_prefix_delegation(link)) {
1536 r = sd_dhcp6_client_set_prefix_delegation(client, true);
1537 if (r < 0)
1538 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
1539 }
1540
1541 if (link->network->dhcp6_pd_length > 0) {
1542 r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
1543 if (r < 0)
1544 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix hint: %m");
1545 }
1546
1547 link->dhcp6_client = TAKE_PTR(client);
1548
1549 return 0;
1550 }
1551
1552 int dhcp6_update_mac(Link *link) {
1553 bool restart;
1554 int r;
1555
1556 assert(link);
1557
1558 if (!link->dhcp6_client)
1559 return 0;
1560
1561 restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
1562
1563 if (restart) {
1564 r = sd_dhcp6_client_stop(link->dhcp6_client);
1565 if (r < 0)
1566 return r;
1567 }
1568
1569 r = dhcp6_set_identifier(link, link->dhcp6_client);
1570 if (r < 0)
1571 return r;
1572
1573 if (restart) {
1574 r = sd_dhcp6_client_start(link->dhcp6_client);
1575 if (r < 0)
1576 return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
1577 }
1578
1579 return 0;
1580 }
1581
1582 int link_serialize_dhcp6_client(Link *link, FILE *f) {
1583 _cleanup_free_ char *duid = NULL;
1584 uint32_t iaid;
1585 int r;
1586
1587 assert(link);
1588
1589 if (!link->dhcp6_client)
1590 return 0;
1591
1592 r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid);
1593 if (r >= 0)
1594 fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
1595
1596 r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
1597 if (r >= 0)
1598 fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
1599
1600 return 0;
1601 }
1602
1603 int config_parse_dhcp6_pd_hint(
1604 const char* unit,
1605 const char *filename,
1606 unsigned line,
1607 const char *section,
1608 unsigned section_line,
1609 const char *lvalue,
1610 int ltype,
1611 const char *rvalue,
1612 void *data,
1613 void *userdata) {
1614
1615 Network *network = data;
1616 int r;
1617
1618 assert(filename);
1619 assert(lvalue);
1620 assert(rvalue);
1621 assert(data);
1622
1623 r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length);
1624 if (r < 0) {
1625 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue);
1626 return 0;
1627 }
1628
1629 if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) {
1630 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length);
1631 network->dhcp6_pd_length = 0;
1632 return 0;
1633 }
1634
1635 return 0;
1636 }
1637
1638 int config_parse_dhcp6_mud_url(
1639 const char *unit,
1640 const char *filename,
1641 unsigned line,
1642 const char *section,
1643 unsigned section_line,
1644 const char *lvalue,
1645 int ltype,
1646 const char *rvalue,
1647 void *data,
1648 void *userdata) {
1649
1650 _cleanup_free_ char *unescaped = NULL;
1651 Network *network = data;
1652 int r;
1653
1654 assert(filename);
1655 assert(lvalue);
1656 assert(rvalue);
1657
1658 if (isempty(rvalue)) {
1659 network->dhcp6_mudurl = mfree(network->dhcp6_mudurl);
1660 return 0;
1661 }
1662
1663 r = cunescape(rvalue, 0, &unescaped);
1664 if (r < 0) {
1665 log_syntax(unit, LOG_WARNING, filename, line, r,
1666 "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue);
1667 return 0;
1668 }
1669
1670 if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) {
1671 log_syntax(unit, LOG_WARNING, filename, line, 0,
1672 "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
1673 return 0;
1674 }
1675
1676 return free_and_replace(network->dhcp6_mudurl, unescaped);
1677 }
1678
1679 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode,
1680 "Failed to parse WithoutRA= setting");
1681
1682 static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = {
1683 [DHCP6_CLIENT_START_MODE_NO] = "no",
1684 [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request",
1685 [DHCP6_CLIENT_START_MODE_SOLICIT] = "solicit",
1686 };
1687
1688 DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode);
1689
1690 int config_parse_dhcp6_pd_subnet_id(
1691 const char *unit,
1692 const char *filename,
1693 unsigned line,
1694 const char *section,
1695 unsigned section_line,
1696 const char *lvalue,
1697 int ltype,
1698 const char *rvalue,
1699 void *data,
1700 void *userdata) {
1701
1702 int64_t *p = data;
1703 uint64_t t;
1704 int r;
1705
1706 assert(filename);
1707 assert(lvalue);
1708 assert(rvalue);
1709 assert(data);
1710
1711 if (isempty(rvalue) || streq(rvalue, "auto")) {
1712 *p = -1;
1713 return 0;
1714 }
1715
1716 r = safe_atoux64(rvalue, &t);
1717 if (r < 0) {
1718 log_syntax(unit, LOG_WARNING, filename, line, r,
1719 "Failed to parse %s=, ignoring assignment: %s",
1720 lvalue, rvalue);
1721 return 0;
1722 }
1723 if (t > INT64_MAX) {
1724 log_syntax(unit, LOG_WARNING, filename, line, 0,
1725 "Invalid subnet id '%s', ignoring assignment.",
1726 rvalue);
1727 return 0;
1728 }
1729
1730 *p = (int64_t) t;
1731
1732 return 0;
1733 }
1734
1735 int config_parse_dhcp6_pd_token(
1736 const char *unit,
1737 const char *filename,
1738 unsigned line,
1739 const char *section,
1740 unsigned section_line,
1741 const char *lvalue,
1742 int ltype,
1743 const char *rvalue,
1744 void *data,
1745 void *userdata) {
1746
1747 union in_addr_union *addr = data, tmp;
1748 int r;
1749
1750 assert(filename);
1751 assert(lvalue);
1752 assert(rvalue);
1753 assert(data);
1754
1755 if (isempty(rvalue)) {
1756 *addr = IN_ADDR_NULL;
1757 return 0;
1758 }
1759
1760 r = in_addr_from_string(AF_INET6, rvalue, &tmp);
1761 if (r < 0) {
1762 log_syntax(unit, LOG_WARNING, filename, line, r,
1763 "Failed to parse DHCPv6 Prefix Delegation token, ignoring: %s", rvalue);
1764 return 0;
1765 }
1766
1767 if (in_addr_is_null(AF_INET6, &tmp)) {
1768 log_syntax(unit, LOG_WARNING, filename, line, 0,
1769 "DHCPv6 Prefix Delegation token cannot be the ANY address, ignoring: %s", rvalue);
1770 return 0;
1771 }
1772
1773 *addr = tmp;
1774
1775 return 0;
1776 }