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