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