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