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