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