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