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