]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-prefix-delegation.c
network/route-nexthop: introduce struct RouteNextHop and replace MultipathRoute with it
[thirdparty/systemd.git] / src / network / networkd-dhcp-prefix-delegation.c
CommitLineData
d5ebcf65
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
557e1b52
YW
3#include <linux/ipv6_route.h>
4
d8ec95c7 5#include "dhcp6-lease-internal.h"
d5ebcf65
YW
6#include "hashmap.h"
7#include "in-addr-prefix-util.h"
8#include "networkd-address-generation.h"
9#include "networkd-address.h"
10#include "networkd-dhcp-prefix-delegation.h"
11#include "networkd-dhcp6.h"
12#include "networkd-link.h"
13#include "networkd-manager.h"
14#include "networkd-queue.h"
15#include "networkd-radv.h"
16#include "networkd-route.h"
e49bad01 17#include "networkd-setlink.h"
d5ebcf65
YW
18#include "parse-util.h"
19#include "string-util.h"
20#include "strv.h"
e49bad01 21#include "tunnel.h"
d5ebcf65 22
a27588d4 23bool link_dhcp_pd_is_enabled(Link *link) {
d5ebcf65
YW
24 assert(link);
25
26 if (!link->network)
27 return false;
28
a27588d4 29 return link->network->dhcp_pd;
d5ebcf65
YW
30}
31
a27588d4 32bool dhcp_pd_is_uplink(Link *link, Link *target, bool accept_auto) {
d5ebcf65
YW
33 assert(link);
34 assert(target);
35
a27588d4 36 if (!link_dhcp_pd_is_enabled(link))
d5ebcf65
YW
37 return false;
38
a27588d4
YW
39 if (link->network->dhcp_pd_uplink_name)
40 return streq_ptr(target->ifname, link->network->dhcp_pd_uplink_name) ||
41 strv_contains(target->alternative_names, link->network->dhcp_pd_uplink_name);
d5ebcf65 42
a27588d4
YW
43 if (link->network->dhcp_pd_uplink_index > 0)
44 return target->ifindex == link->network->dhcp_pd_uplink_index;
d5ebcf65 45
a27588d4 46 if (link->network->dhcp_pd_uplink_index == UPLINK_INDEX_SELF)
d5ebcf65
YW
47 return link == target;
48
a27588d4 49 assert(link->network->dhcp_pd_uplink_index == UPLINK_INDEX_AUTO);
d5ebcf65
YW
50 return accept_auto;
51}
52
a27588d4 53static void link_remove_dhcp_pd_subnet_prefix(Link *link, const struct in6_addr *prefix) {
d5ebcf65
YW
54 void *key;
55
56 assert(link);
57 assert(link->manager);
58 assert(prefix);
59
a27588d4 60 if (hashmap_get(link->manager->links_by_dhcp_pd_subnet_prefix, prefix) != link)
d5ebcf65
YW
61 return;
62
a27588d4 63 hashmap_remove2(link->manager->links_by_dhcp_pd_subnet_prefix, prefix, &key);
d5ebcf65
YW
64 free(key);
65}
66
a27588d4 67static int link_add_dhcp_pd_subnet_prefix(Link *link, const struct in6_addr *prefix) {
d5ebcf65
YW
68 _cleanup_free_ struct in6_addr *copy = NULL;
69 int r;
70
71 assert(link);
72 assert(prefix);
73
74 copy = newdup(struct in6_addr, prefix, 1);
75 if (!copy)
76 return -ENOMEM;
77
a27588d4 78 r = hashmap_ensure_put(&link->manager->links_by_dhcp_pd_subnet_prefix, &in6_addr_hash_ops_free, copy, link);
d5ebcf65
YW
79 if (r < 0)
80 return r;
81 if (r > 0)
82 TAKE_PTR(copy);
83
84 return 0;
85}
86
a27588d4 87static int link_get_by_dhcp_pd_subnet_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) {
d5ebcf65
YW
88 Link *link;
89
90 assert(manager);
91 assert(prefix);
92
a27588d4 93 link = hashmap_get(manager->links_by_dhcp_pd_subnet_prefix, prefix);
d5ebcf65
YW
94 if (!link)
95 return -ENODEV;
96
97 if (ret)
98 *ret = link;
99 return 0;
100}
101
a27588d4 102static int dhcp_pd_get_assigned_subnet_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) {
d5ebcf65
YW
103 assert(link);
104 assert(pd_prefix);
105
a27588d4 106 if (!link_dhcp_pd_is_enabled(link))
d5ebcf65
YW
107 return -ENOENT;
108
a27588d4 109 if (link->network->dhcp_pd_assign) {
d5ebcf65
YW
110 Address *address;
111
112 SET_FOREACH(address, link->addresses) {
a27588d4 113 if (address->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
d5ebcf65
YW
114 continue;
115 assert(address->family == AF_INET6);
116
117 if (in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &address->in_addr.in6) <= 0)
118 continue;
119
120 if (ret) {
121 struct in6_addr prefix = address->in_addr.in6;
122
123 in6_addr_mask(&prefix, 64);
124 *ret = prefix;
125 }
126 return 0;
127 }
128 } else {
129 Route *route;
130
131 SET_FOREACH(route, link->routes) {
a27588d4 132 if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
d5ebcf65
YW
133 continue;
134 assert(route->family == AF_INET6);
135
136 if (in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &route->dst.in6) > 0) {
137 if (ret)
138 *ret = route->dst.in6;
139 return 0;
140 }
141 }
142 }
143
144 return -ENOENT;
145}
146
a27588d4 147int dhcp_pd_remove(Link *link, bool only_marked) {
372acaad 148 int ret = 0;
d5ebcf65
YW
149
150 assert(link);
151 assert(link->manager);
152
a27588d4 153 if (!link_dhcp_pd_is_enabled(link))
d5ebcf65
YW
154 return 0;
155
156 if (!only_marked)
a27588d4 157 link->dhcp_pd_configured = false;
d5ebcf65 158
a27588d4 159 if (!link->network->dhcp_pd_assign) {
d5ebcf65
YW
160 Route *route;
161
162 SET_FOREACH(route, link->routes) {
a27588d4 163 if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
d5ebcf65
YW
164 continue;
165 if (only_marked && !route_is_marked(route))
166 continue;
167
168 if (link->radv)
95931532 169 sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
d5ebcf65 170
a27588d4 171 link_remove_dhcp_pd_subnet_prefix(link, &route->dst.in6);
d5ebcf65 172
372acaad 173 RET_GATHER(ret, route_remove(route));
95eb38c8 174 route_cancel_request(route, link);
d5ebcf65
YW
175 }
176 } else {
177 Address *address;
178
179 SET_FOREACH(address, link->addresses) {
180 struct in6_addr prefix;
181
a27588d4 182 if (address->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
d5ebcf65
YW
183 continue;
184 if (only_marked && !address_is_marked(address))
185 continue;
186
187 prefix = address->in_addr.in6;
188 in6_addr_mask(&prefix, 64);
189
190 if (link->radv)
95931532 191 sd_radv_remove_prefix(link->radv, &prefix, 64);
d5ebcf65 192
a27588d4 193 link_remove_dhcp_pd_subnet_prefix(link, &prefix);
d5ebcf65 194
f22b586a 195 RET_GATHER(ret, address_remove_and_cancel(address, link));
d5ebcf65
YW
196 }
197 }
198
372acaad 199 return ret;
d5ebcf65
YW
200}
201
a27588d4 202static int dhcp_pd_check_ready(Link *link);
d5ebcf65 203
a27588d4 204static int dhcp_pd_address_ready_callback(Address *address) {
d5ebcf65
YW
205 Address *a;
206
207 assert(address);
208 assert(address->link);
209
210 SET_FOREACH(a, address->link->addresses)
a27588d4 211 if (a->source == NETWORK_CONFIG_SOURCE_DHCP_PD)
d5ebcf65
YW
212 a->callback = NULL;
213
a27588d4 214 return dhcp_pd_check_ready(address->link);
d5ebcf65
YW
215}
216
a27588d4 217static int dhcp_pd_check_ready(Link *link) {
d5ebcf65
YW
218 int r;
219
220 assert(link);
221 assert(link->network);
222
a27588d4
YW
223 if (link->dhcp_pd_messages > 0) {
224 log_link_debug(link, "%s(): DHCP-PD addresses and routes are not set.", __func__);
d5ebcf65
YW
225 return 0;
226 }
227
a27588d4 228 if (link->network->dhcp_pd_assign) {
d5ebcf65
YW
229 bool has_ready = false;
230 Address *address;
231
232 SET_FOREACH(address, link->addresses) {
a27588d4 233 if (address->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
d5ebcf65
YW
234 continue;
235 if (address_is_ready(address)) {
236 has_ready = true;
237 break;
238 }
239 }
240
241 if (!has_ready) {
242 SET_FOREACH(address, link->addresses)
a27588d4
YW
243 if (address->source == NETWORK_CONFIG_SOURCE_DHCP_PD)
244 address->callback = dhcp_pd_address_ready_callback;
d5ebcf65 245
a27588d4 246 log_link_debug(link, "%s(): no DHCP-PD address is ready.", __func__);
d5ebcf65
YW
247 return 0;
248 }
249 }
250
a27588d4 251 link->dhcp_pd_configured = true;
d5ebcf65 252
a27588d4 253 log_link_debug(link, "DHCP-PD addresses and routes set.");
d5ebcf65 254
a27588d4 255 r = dhcp_pd_remove(link, /* only_marked = */ true);
d5ebcf65
YW
256 if (r < 0)
257 return r;
258
259 link_check_ready(link);
260 return 1;
261}
262
80d62d4f 263static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
d5ebcf65
YW
264 int r;
265
266 assert(link);
d5ebcf65 267
2c0b49ba 268 r = route_configure_handler_internal(rtnl, m, link, route, "Failed to add prefix route for DHCP delegated subnet prefix");
d5ebcf65
YW
269 if (r <= 0)
270 return r;
271
a27588d4 272 r = dhcp_pd_check_ready(link);
d5ebcf65
YW
273 if (r < 0)
274 link_enter_failed(link);
275
276 return 1;
277}
278
38488bab 279static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, usec_t lifetime_usec) {
d5ebcf65
YW
280 _cleanup_(route_freep) Route *route = NULL;
281 Route *existing;
282 int r;
283
284 assert(link);
285 assert(link->network);
286 assert(prefix);
287
a27588d4 288 if (link->network->dhcp_pd_assign)
d5ebcf65
YW
289 return 0;
290
291 r = route_new(&route);
292 if (r < 0)
293 return r;
294
a27588d4 295 route->source = NETWORK_CONFIG_SOURCE_DHCP_PD;
d5ebcf65
YW
296 route->family = AF_INET6;
297 route->dst.in6 = *prefix;
38488bab 298 route->dst_prefixlen = 64;
d5ebcf65 299 route->protocol = RTPROT_DHCP;
a27588d4 300 route->priority = link->network->dhcp_pd_route_metric;
d5ebcf65
YW
301 route->lifetime_usec = lifetime_usec;
302
303 if (route_get(NULL, link, route, &existing) < 0)
a27588d4 304 link->dhcp_pd_configured = false;
d5ebcf65
YW
305 else
306 route_unmark(existing);
307
a27588d4
YW
308 r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
309 dhcp_pd_route_handler, NULL);
d5ebcf65 310 if (r < 0)
a27588d4 311 return log_link_error_errno(link, r, "Failed to request DHCP-PD prefix route: %m");
d5ebcf65
YW
312
313 return 0;
314}
315
80d62d4f 316static int dhcp_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
d5ebcf65
YW
317 int r;
318
319 assert(link);
d5ebcf65 320
a27588d4 321 r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCP-PD address");
d5ebcf65
YW
322 if (r <= 0)
323 return r;
324
a27588d4 325 r = dhcp_pd_check_ready(link);
d5ebcf65
YW
326 if (r < 0)
327 link_enter_failed(link);
328
329 return 1;
330}
331
a27588d4 332static void log_dhcp_pd_address(Link *link, const Address *address) {
d5ebcf65
YW
333 assert(address);
334 assert(address->family == AF_INET6);
335
b33dd04e 336 int log_level = address_get_harder(link, address, NULL) >= 0 ? LOG_DEBUG : LOG_INFO;
d5ebcf65
YW
337
338 if (log_level < log_get_max_level())
339 return;
340
a27588d4 341 log_link_full(link, log_level, "DHCP-PD address %s (valid %s, preferred %s)",
c71384a9 342 IN6_ADDR_PREFIX_TO_STRING(&address->in_addr.in6, address->prefixlen),
d5ebcf65
YW
343 FORMAT_LIFETIME(address->lifetime_valid_usec),
344 FORMAT_LIFETIME(address->lifetime_preferred_usec));
345}
346
a27588d4 347static int dhcp_pd_request_address(
d5ebcf65
YW
348 Link *link,
349 const struct in6_addr *prefix,
350 usec_t lifetime_preferred_usec,
351 usec_t lifetime_valid_usec) {
352
353 _cleanup_set_free_ Set *addresses = NULL;
354 struct in6_addr *a;
355 int r;
356
357 assert(link);
358 assert(link->network);
359 assert(prefix);
360
a27588d4 361 if (!link->network->dhcp_pd_assign)
d5ebcf65
YW
362 return 0;
363
38488bab 364 r = dhcp_pd_generate_addresses(link, prefix, &addresses);
d5ebcf65 365 if (r < 0)
a27588d4 366 return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCP delegated prefix: %m");
d5ebcf65
YW
367
368 SET_FOREACH(a, addresses) {
ebd96906 369 _cleanup_(address_unrefp) Address *address = NULL;
d5ebcf65
YW
370 Address *existing;
371
372 r = address_new(&address);
373 if (r < 0)
a27588d4 374 return log_link_error_errno(link, r, "Failed to allocate address for DHCP delegated prefix: %m");
d5ebcf65 375
a27588d4 376 address->source = NETWORK_CONFIG_SOURCE_DHCP_PD;
d5ebcf65
YW
377 address->family = AF_INET6;
378 address->in_addr.in6 = *a;
38488bab 379 address->prefixlen = 64;
d5ebcf65
YW
380 address->lifetime_preferred_usec = lifetime_preferred_usec;
381 address->lifetime_valid_usec = lifetime_valid_usec;
38488bab 382 SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp_pd_manage_temporary_address);
a27588d4 383 address->route_metric = link->network->dhcp_pd_route_metric;
d5ebcf65 384
a27588d4 385 log_dhcp_pd_address(link, address);
d5ebcf65 386
4b3590c3
TM
387 r = free_and_strdup_warn(&address->netlabel, link->network->dhcp_pd_netlabel);
388 if (r < 0)
389 return r;
390
d5ebcf65 391 if (address_get(link, address, &existing) < 0)
a27588d4 392 link->dhcp_pd_configured = false;
d5ebcf65
YW
393 else
394 address_unmark(existing);
395
f60e6558 396 r = link_request_address(link, address, &link->dhcp_pd_messages,
a27588d4 397 dhcp_pd_address_handler, NULL);
d5ebcf65 398 if (r < 0)
a27588d4 399 return log_link_error_errno(link, r, "Failed to request DHCP delegated prefix address: %m");
d5ebcf65
YW
400 }
401
402 return 0;
403}
404
a27588d4 405static int dhcp_pd_calculate_subnet_prefix(
d5ebcf65
YW
406 const struct in6_addr *pd_prefix,
407 uint8_t pd_prefix_len,
408 uint64_t subnet_id,
409 struct in6_addr *ret) {
410
411 struct in6_addr prefix;
412
413 assert(pd_prefix);
414 assert(pd_prefix_len <= 64);
415 assert(ret);
416
417 if (subnet_id >= UINT64_C(1) << (64 - pd_prefix_len))
418 return -ERANGE;
419
420 prefix = *pd_prefix;
421
422 if (pd_prefix_len < 32)
423 prefix.s6_addr32[0] |= htobe32(subnet_id >> 32);
424
425 prefix.s6_addr32[1] |= htobe32(subnet_id & 0xffffffff);
426
427 *ret = prefix;
428 return 0;
429}
430
a27588d4 431static int dhcp_pd_get_preferred_subnet_prefix(
d5ebcf65
YW
432 Link *link,
433 const struct in6_addr *pd_prefix,
434 uint8_t pd_prefix_len,
435 struct in6_addr *ret) {
436
437 struct in6_addr prefix;
438 Link *assigned_link;
439 int r;
440
441 assert(link);
442 assert(link->manager);
11d8a83f 443 assert(link->network);
d5ebcf65
YW
444 assert(pd_prefix);
445
a27588d4 446 if (link->network->dhcp_pd_subnet_id >= 0) {
d5ebcf65
YW
447 /* If the link has a preference for a particular subnet id try to allocate that */
448
a27588d4 449 r = dhcp_pd_calculate_subnet_prefix(pd_prefix, pd_prefix_len, link->network->dhcp_pd_subnet_id, &prefix);
d5ebcf65
YW
450 if (r < 0)
451 return log_link_warning_errno(link, r,
c0f86d66 452 "subnet id %" PRIi64 " is out of range. Only have %" PRIu64 " subnets.",
a27588d4 453 link->network->dhcp_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len));
d5ebcf65 454
d5ebcf65
YW
455 *ret = prefix;
456 return 0;
457 }
458
07b7337a
YW
459 if (dhcp_pd_get_assigned_subnet_prefix(link, pd_prefix, pd_prefix_len, ret) >= 0)
460 return 0;
461
d5ebcf65
YW
462 for (uint64_t n = 0; ; n++) {
463 /* If we do not have an allocation preference just iterate
464 * through the address space and return the first free prefix. */
465
a27588d4 466 r = dhcp_pd_calculate_subnet_prefix(pd_prefix, pd_prefix_len, n, &prefix);
d5ebcf65
YW
467 if (r < 0)
468 return log_link_warning_errno(link, r,
469 "Couldn't find a suitable prefix. Ran out of address space.");
470
471 /* Do not use explicitly requested subnet IDs. Note that the corresponding link may not
472 * appear yet. So, we need to check the ID is not used in any .network files. */
a27588d4 473 if (set_contains(link->manager->dhcp_pd_subnet_ids, &n))
d5ebcf65
YW
474 continue;
475
476 /* Check that the prefix is not assigned to another link. */
a27588d4 477 if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) < 0 ||
07b7337a
YW
478 assigned_link == link)
479 break;
d5ebcf65 480 }
07b7337a
YW
481
482 r = link_add_dhcp_pd_subnet_prefix(link, &prefix);
483 if (r < 0)
484 return log_link_warning_errno(link, r, "Failed to save acquired free subnet prefix: %m");
485
486 *ret = prefix;
487 return 0;
d5ebcf65
YW
488}
489
a27588d4 490static int dhcp_pd_assign_subnet_prefix(
d5ebcf65
YW
491 Link *link,
492 const struct in6_addr *pd_prefix,
493 uint8_t pd_prefix_len,
494 usec_t lifetime_preferred_usec,
38488bab
YW
495 usec_t lifetime_valid_usec,
496 bool is_uplink) {
d5ebcf65 497
d5ebcf65
YW
498 struct in6_addr prefix;
499 int r;
500
501 assert(link);
502 assert(link->network);
503 assert(pd_prefix);
504
07b7337a
YW
505 r = dhcp_pd_get_preferred_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix);
506 if (r < 0)
507 return r == -ERANGE ? 0 : r;
d5ebcf65 508
c71384a9 509 const char *pretty = IN6_ADDR_PREFIX_TO_STRING(&prefix, 64);
d5ebcf65 510
a27588d4 511 if (link_radv_enabled(link) && link->network->dhcp_pd_announce) {
38488bab
YW
512 if (is_uplink)
513 log_link_debug(link, "Ignoring Announce= setting on upstream interface.");
514 else {
515 r = radv_add_prefix(link, &prefix, 64, lifetime_preferred_usec, lifetime_valid_usec);
516 if (r < 0)
517 return log_link_warning_errno(link, r,
518 "Failed to assign/update prefix %s to IPv6 Router Advertisement: %m",
c71384a9 519 pretty);
38488bab 520 }
d5ebcf65
YW
521 }
522
38488bab 523 r = dhcp_pd_request_route(link, &prefix, lifetime_valid_usec);
d5ebcf65
YW
524 if (r < 0)
525 return log_link_warning_errno(link, r,
c71384a9 526 "Failed to assign/update route for prefix %s: %m", pretty);
d5ebcf65 527
38488bab 528 r = dhcp_pd_request_address(link, &prefix, lifetime_preferred_usec, lifetime_valid_usec);
d5ebcf65
YW
529 if (r < 0)
530 return log_link_warning_errno(link, r,
c71384a9 531 "Failed to assign/update address for prefix %s: %m", pretty);
d5ebcf65 532
c71384a9 533 log_link_debug(link, "Assigned prefix %s", pretty);
d5ebcf65
YW
534 return 1;
535}
536
a27588d4 537static int dhcp_pd_prepare(Link *link) {
d5ebcf65
YW
538 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
539 return 0;
540
a27588d4 541 if (!link_dhcp_pd_is_enabled(link))
d5ebcf65
YW
542 return 0;
543
a27588d4 544 if (link_radv_enabled(link) && link->network->dhcp_pd_announce && !link->radv)
d5ebcf65
YW
545 return 0;
546
2ccada8d
TH
547 link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP_PD);
548 link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP_PD);
d5ebcf65 549
c3cd5351 550 return 1;
d5ebcf65
YW
551}
552
a27588d4 553static int dhcp_pd_finalize(Link *link) {
d5ebcf65
YW
554 int r;
555
556 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
557 return 0;
558
a27588d4
YW
559 if (link->dhcp_pd_messages == 0) {
560 link->dhcp_pd_configured = false;
d5ebcf65 561
a27588d4 562 r = dhcp_pd_remove(link, /* only_marked = */ true);
d5ebcf65
YW
563 if (r < 0)
564 return r;
565 }
566
a27588d4 567 if (!link->dhcp_pd_configured)
d5ebcf65
YW
568 link_set_state(link, LINK_STATE_CONFIGURING);
569
570 link_check_ready(link);
571 return 0;
572}
573
a27588d4 574void dhcp_pd_prefix_lost(Link *uplink) {
da10d2d5 575 Route *route;
d5ebcf65
YW
576 Link *link;
577 int r;
578
a27588d4
YW
579 assert(uplink);
580 assert(uplink->manager);
d5ebcf65 581
a27588d4
YW
582 HASHMAP_FOREACH(link, uplink->manager->links_by_index) {
583 if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true))
d5ebcf65
YW
584 continue;
585
a27588d4 586 r = dhcp_pd_remove(link, /* only_marked = */ false);
d5ebcf65
YW
587 if (r < 0)
588 link_enter_failed(link);
589 }
590
a27588d4 591 SET_FOREACH(route, uplink->manager->routes) {
277521a1 592 if (!IN_SET(route->source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6))
da10d2d5
YW
593 continue;
594 if (route->family != AF_INET6)
595 continue;
596 if (route->type != RTN_UNREACHABLE)
597 continue;
a27588d4 598 if (!set_contains(uplink->dhcp_pd_prefixes,
da10d2d5
YW
599 &(struct in_addr_prefix) {
600 .family = AF_INET6,
601 .prefixlen = route->dst_prefixlen,
602 .address = route->dst }))
603 continue;
604
605 (void) route_remove(route);
606
a27588d4 607 route_cancel_request(route, uplink);
da10d2d5
YW
608 }
609
a27588d4 610 set_clear(uplink->dhcp_pd_prefixes);
d5ebcf65
YW
611}
612
e49bad01
YW
613void dhcp4_pd_prefix_lost(Link *uplink) {
614 Link *tunnel;
615
616 dhcp_pd_prefix_lost(uplink);
617
618 if (uplink->dhcp4_6rd_tunnel_name &&
619 link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, &tunnel) >= 0)
620 (void) link_remove(tunnel);
621}
622
80d62d4f 623static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
e49bad01
YW
624 int r;
625
626 assert(link);
e49bad01 627
2c0b49ba 628 r = route_configure_handler_internal(rtnl, m, link, route, "Failed to set unreachable route for DHCPv4 delegated prefix");
e49bad01
YW
629 if (r <= 0)
630 return r;
631
632 r = dhcp4_check_ready(link);
633 if (r < 0)
634 link_enter_failed(link);
635
636 return 1;
637}
638
80d62d4f 639static int dhcp6_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
d5ebcf65
YW
640 int r;
641
642 assert(link);
d5ebcf65 643
2c0b49ba 644 r = route_configure_handler_internal(rtnl, m, link, route, "Failed to set unreachable route for DHCPv6 delegated prefix");
d5ebcf65
YW
645 if (r <= 0)
646 return r;
647
648 r = dhcp6_check_ready(link);
649 if (r < 0)
650 link_enter_failed(link);
651
652 return 1;
653}
654
a27588d4 655static int dhcp_request_unreachable_route(
5ed10a19
YW
656 Link *link,
657 const struct in6_addr *addr,
658 uint8_t prefixlen,
659 usec_t lifetime_usec,
a27588d4
YW
660 NetworkConfigSource source,
661 const union in_addr_union *server_address,
662 unsigned *counter,
706cd859
YW
663 route_netlink_handler_t callback,
664 bool *configured) {
5ed10a19 665
d5ebcf65 666 _cleanup_(route_freep) Route *route = NULL;
d5ebcf65
YW
667 Route *existing;
668 int r;
669
670 assert(link);
671 assert(addr);
a27588d4 672 assert(IN_SET(source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6));
5ed10a19 673 assert(server_address);
a27588d4
YW
674 assert(counter);
675 assert(callback);
706cd859 676 assert(configured);
d5ebcf65 677
a536ec38 678 if (prefixlen >= 64) {
a27588d4 679 log_link_debug(link, "Not adding a blocking route for DHCP delegated prefix %s since the prefix has length >= 64.",
c71384a9 680 IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen));
d5ebcf65
YW
681 return 0;
682 }
683
684 r = route_new(&route);
685 if (r < 0)
686 return log_oom();
687
a27588d4 688 route->source = source;
5ed10a19 689 route->provider = *server_address;
d5ebcf65
YW
690 route->family = AF_INET6;
691 route->dst.in6 = *addr;
692 route->dst_prefixlen = prefixlen;
d5ebcf65
YW
693 route->type = RTN_UNREACHABLE;
694 route->protocol = RTPROT_DHCP;
557e1b52 695 route->priority = IP6_RT_PRIO_USER;
d5ebcf65
YW
696 route->lifetime_usec = lifetime_usec;
697
698 if (route_get(link->manager, NULL, route, &existing) < 0)
706cd859 699 *configured = false;
d5ebcf65
YW
700 else
701 route_unmark(existing);
702
a27588d4 703 r = link_request_route(link, TAKE_PTR(route), true, counter, callback, NULL);
c71384a9 704 if (r < 0)
a27588d4 705 return log_link_error_errno(link, r, "Failed to request unreachable route for DHCP delegated prefix %s: %m",
c71384a9 706 IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen));
d5ebcf65
YW
707
708 return 0;
709}
710
e49bad01
YW
711static int dhcp4_request_unreachable_route(
712 Link *link,
713 const struct in6_addr *addr,
714 uint8_t prefixlen,
715 usec_t lifetime_usec,
716 const union in_addr_union *server_address) {
717
718 return dhcp_request_unreachable_route(link, addr, prefixlen, lifetime_usec,
719 NETWORK_CONFIG_SOURCE_DHCP4, server_address,
706cd859
YW
720 &link->dhcp4_messages, dhcp4_unreachable_route_handler,
721 &link->dhcp4_configured);
e49bad01
YW
722}
723
a27588d4
YW
724static int dhcp6_request_unreachable_route(
725 Link *link,
726 const struct in6_addr *addr,
727 uint8_t prefixlen,
728 usec_t lifetime_usec,
729 const union in_addr_union *server_address) {
730
731 return dhcp_request_unreachable_route(link, addr, prefixlen, lifetime_usec,
732 NETWORK_CONFIG_SOURCE_DHCP6, server_address,
706cd859
YW
733 &link->dhcp6_messages, dhcp6_unreachable_route_handler,
734 &link->dhcp6_configured);
a27588d4
YW
735}
736
737static int dhcp_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t prefixlen) {
d5ebcf65
YW
738 struct in_addr_prefix *p;
739 int r;
740
741 assert(link);
742 assert(prefix);
743
744 p = new(struct in_addr_prefix, 1);
745 if (!p)
746 return log_oom();
747
748 *p = (struct in_addr_prefix) {
749 .family = AF_INET6,
750 .prefixlen = prefixlen,
751 .address.in6 = *prefix,
752 };
753
c71384a9
ZJS
754 int log_level = set_contains(link->dhcp_pd_prefixes, p) ? LOG_DEBUG :
755 prefixlen > 64 || prefixlen < 48 ? LOG_WARNING : LOG_INFO;
d5ebcf65 756 log_link_full(link,
c71384a9 757 log_level,
21feba0a 758 "DHCP: received delegated prefix %s%s",
c71384a9 759 IN6_ADDR_PREFIX_TO_STRING(prefix, prefixlen),
d5ebcf65
YW
760 prefixlen > 64 ? " with prefix length > 64, ignoring." :
761 prefixlen < 48 ? " with prefix length < 48, looks unusual.": "");
762
763 /* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */
a27588d4 764 r = set_ensure_consume(&link->dhcp_pd_prefixes, &in_addr_prefix_hash_ops_free, p);
d5ebcf65 765 if (r < 0)
c71384a9
ZJS
766 return log_link_error_errno(link, r, "Failed to store DHCP delegated prefix %s: %m",
767 IN6_ADDR_PREFIX_TO_STRING(prefix, prefixlen));
a536ec38 768 return 0;
d5ebcf65
YW
769}
770
e49bad01
YW
771static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const struct in_addr *br_address, usec_t lifetime_usec) {
772 _cleanup_(route_freep) Route *route = NULL;
773 Route *existing;
774 int r;
775
776 assert(link);
777 assert(br_address);
778
779 r = route_new(&route);
780 if (r < 0)
781 return log_link_debug_errno(link, r, "Failed to allocate default gateway for DHCP delegated prefix: %m");
782
783 route->source = NETWORK_CONFIG_SOURCE_DHCP_PD;
784 route->family = AF_INET6;
785 route->gw_family = AF_INET6;
786 route->gw.in6.s6_addr32[3] = br_address->s_addr;
787 route->scope = RT_SCOPE_UNIVERSE;
788 route->protocol = RTPROT_DHCP;
789 route->priority = IP6_RT_PRIO_USER;
790 route->lifetime_usec = lifetime_usec;
791
792 if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
793 link->dhcp_pd_configured = false;
794 else
795 route_unmark(existing);
796
797 r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
798 dhcp_pd_route_handler, NULL);
799 if (r < 0)
800 return log_link_debug_errno(link, r, "Failed to request default gateway for DHCP delegated prefix: %m");
801
802 return 0;
803}
804
805static void dhcp4_calculate_pd_prefix(
806 const struct in_addr *ipv4address,
807 uint8_t ipv4masklen,
808 const struct in6_addr *sixrd_prefix,
809 uint8_t sixrd_prefixlen,
810 struct in6_addr *ret_pd_prefix,
811 uint8_t *ret_pd_prefixlen) {
812
813 struct in6_addr pd_prefix;
814
815 assert(ipv4address);
816 assert(ipv4masklen <= 32);
817 assert(sixrd_prefix);
818 assert(32 - ipv4masklen + sixrd_prefixlen <= 128);
819 assert(ret_pd_prefix);
820
821 pd_prefix = *sixrd_prefix;
822 for (unsigned i = 0; i < (unsigned) (32 - ipv4masklen); i++)
823 if (ipv4address->s_addr & htobe32(UINT32_C(1) << (32 - ipv4masklen - i - 1)))
824 pd_prefix.s6_addr[(i + sixrd_prefixlen) / 8] |= 1 << (7 - (i + sixrd_prefixlen) % 8);
825
826 *ret_pd_prefix = pd_prefix;
827 if (ret_pd_prefixlen)
828 *ret_pd_prefixlen = 32 - ipv4masklen + sixrd_prefixlen;
829}
830
831static int dhcp4_pd_assign_subnet_prefix(Link *link, Link *uplink) {
832 uint8_t ipv4masklen, sixrd_prefixlen, pd_prefixlen;
833 struct in6_addr sixrd_prefix, pd_prefix;
834 const struct in_addr *br_addresses;
835 struct in_addr ipv4address;
a93538e8 836 usec_t lifetime_usec;
e49bad01
YW
837 int r;
838
839 assert(link);
840 assert(uplink);
def4741b 841 assert(uplink->manager);
e49bad01
YW
842 assert(uplink->dhcp_lease);
843
844 r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
845 if (r < 0)
846 return log_link_warning_errno(uplink, r, "Failed to get DHCPv4 address: %m");
847
a93538e8 848 r = sd_dhcp_lease_get_lifetime_timestamp(uplink->dhcp_lease, CLOCK_BOOTTIME, &lifetime_usec);
e49bad01
YW
849 if (r < 0)
850 return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
851
e49bad01
YW
852 r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, NULL);
853 if (r < 0)
21feba0a 854 return log_link_warning_errno(uplink, r, "Failed to get DHCPv4 6rd option: %m");
e49bad01
YW
855
856 dhcp4_calculate_pd_prefix(&ipv4address, ipv4masklen, &sixrd_prefix, sixrd_prefixlen, &pd_prefix, &pd_prefixlen);
857
858 if (pd_prefixlen > 64)
859 return 0;
860
861 r = dhcp_pd_prepare(link);
862 if (r <= 0)
863 return r;
864
865 if (streq_ptr(uplink->dhcp4_6rd_tunnel_name, link->ifname)) {
e49bad01
YW
866 r = dhcp4_pd_request_default_gateway_on_6rd_tunnel(link, &br_addresses[0], lifetime_usec);
867 if (r < 0)
868 return r;
e49bad01
YW
869 }
870
38488bab
YW
871 r = dhcp_pd_assign_subnet_prefix(link, &pd_prefix, pd_prefixlen, lifetime_usec, lifetime_usec, /* is_uplink = */ false);
872 if (r < 0)
873 return r;
874
e49bad01
YW
875 return dhcp_pd_finalize(link);
876}
877
878static int dhcp4_pd_6rd_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
879 int r;
880
881 assert(m);
882 assert(link);
883 assert(link->manager);
884 assert(link->dhcp4_6rd_tunnel_name);
885
886 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
887 return 0;
888
889 r = sd_netlink_message_get_errno(m);
890 if (r < 0) {
21feba0a 891 log_link_message_warning_errno(link, m, r, "Failed to create tunnel device for DHCPv4 6rd");
e49bad01
YW
892 link_enter_failed(link);
893 return 0;
894 }
895
896 return 0;
897}
898
899int dhcp4_pd_prefix_acquired(Link *uplink) {
900 _cleanup_free_ char *tunnel_name = NULL;
901 uint8_t ipv4masklen, sixrd_prefixlen, pd_prefixlen;
902 struct in6_addr sixrd_prefix, pd_prefix;
903 struct in_addr ipv4address;
904 union in_addr_union server_address;
21feba0a 905 const struct in_addr *br_addresses;
a93538e8 906 usec_t lifetime_usec;
e49bad01
YW
907 Link *link;
908 int r;
909
910 assert(uplink);
def4741b 911 assert(uplink->manager);
e49bad01
YW
912 assert(uplink->dhcp_lease);
913
914 r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
915 if (r < 0)
916 return log_link_warning_errno(uplink, r, "Failed to get DHCPv4 address: %m");
917
a93538e8 918 r = sd_dhcp_lease_get_lifetime_timestamp(uplink->dhcp_lease, CLOCK_BOOTTIME, &lifetime_usec);
e49bad01
YW
919 if (r < 0)
920 return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
921
e49bad01
YW
922 r = sd_dhcp_lease_get_server_identifier(uplink->dhcp_lease, &server_address.in);
923 if (r < 0)
924 return log_link_warning_errno(uplink, r, "Failed to get server address of DHCPv4 lease: %m");
925
21feba0a 926 r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, NULL);
e49bad01 927 if (r < 0)
21feba0a
YW
928 return log_link_warning_errno(uplink, r, "Failed to get DHCPv4 6rd option: %m");
929
c71384a9 930 if (DEBUG_LOGGING)
21feba0a 931 log_link_debug(uplink, "DHCPv4: 6rd option is acquired: IPv4_masklen=%u, 6rd_prefix=%s, br_address="IPV4_ADDRESS_FMT_STR,
c71384a9
ZJS
932 ipv4masklen,
933 IN6_ADDR_PREFIX_TO_STRING(&sixrd_prefix, sixrd_prefixlen),
934 IPV4_ADDRESS_FMT_VAL(*br_addresses));
e49bad01
YW
935
936 /* Calculate PD prefix */
937 dhcp4_calculate_pd_prefix(&ipv4address, ipv4masklen, &sixrd_prefix, sixrd_prefixlen, &pd_prefix, &pd_prefixlen);
938
939 /* Register and log PD prefix */
940 r = dhcp_pd_prefix_add(uplink, &pd_prefix, pd_prefixlen);
941 if (r < 0)
942 return r;
943
944 /* Request unreachable route */
945 r = dhcp4_request_unreachable_route(uplink, &pd_prefix, pd_prefixlen, lifetime_usec, &server_address);
946 if (r < 0)
947 return r;
948
949 /* Generate 6rd SIT tunnel device name. */
950 r = dhcp4_pd_create_6rd_tunnel_name(uplink, &tunnel_name);
951 if (r < 0)
952 return r;
953
954 /* Remove old tunnel device if exists. */
955 if (!streq_ptr(uplink->dhcp4_6rd_tunnel_name, tunnel_name)) {
956 Link *old_tunnel;
957
958 if (uplink->dhcp4_6rd_tunnel_name &&
959 link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, &old_tunnel) >= 0)
960 (void) link_remove(old_tunnel);
961
962 free_and_replace(uplink->dhcp4_6rd_tunnel_name, tunnel_name);
963 }
964
965 /* Create 6rd SIT tunnel device if it does not exist yet. */
966 if (link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, NULL) < 0) {
967 r = dhcp4_pd_create_6rd_tunnel(uplink, dhcp4_pd_6rd_tunnel_create_handler);
968 if (r < 0)
969 return r;
970 }
971
972 /* Then, assign subnet prefixes to downstream interfaces. */
973 HASHMAP_FOREACH(link, uplink->manager->links_by_index) {
974 if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true))
975 continue;
976
977 r = dhcp4_pd_assign_subnet_prefix(link, uplink);
978 if (r < 0) {
979 /* When failed on the upstream interface (i.e., the case link == uplink),
980 * immediately abort the assignment of the prefixes. As, the all assigned
981 * prefixes will be dropped soon in link_enter_failed(), and it is meaningless
982 * to continue the assignment. */
983 if (link == uplink)
984 return r;
985
986 link_enter_failed(link);
987 }
988 }
989
990 return 0;
991}
992
a27588d4 993static int dhcp6_pd_assign_subnet_prefixes(Link *link, Link *uplink) {
d5ebcf65
YW
994 int r;
995
5014e660
YW
996 assert(link);
997 assert(uplink);
998 assert(uplink->dhcp6_lease);
d5ebcf65 999
a27588d4 1000 r = dhcp_pd_prepare(link);
c3cd5351 1001 if (r <= 0)
5014e660 1002 return r;
d5ebcf65 1003
d8ec95c7
YW
1004 FOREACH_DHCP6_PD_PREFIX(uplink->dhcp6_lease) {
1005 usec_t lifetime_preferred_usec, lifetime_valid_usec;
d5ebcf65
YW
1006 struct in6_addr pd_prefix;
1007 uint8_t pd_prefix_len;
1008
d8ec95c7 1009 r = sd_dhcp6_lease_get_pd_prefix(uplink->dhcp6_lease, &pd_prefix, &pd_prefix_len);
d5ebcf65 1010 if (r < 0)
d8ec95c7 1011 return r;
d5ebcf65 1012
5014e660 1013 if (pd_prefix_len > 64)
d5ebcf65
YW
1014 continue;
1015
d5ebcf65
YW
1016 /* Mask prefix for safety. */
1017 r = in6_addr_mask(&pd_prefix, pd_prefix_len);
1018 if (r < 0)
5014e660 1019 return r;
d5ebcf65 1020
d8ec95c7
YW
1021 r = sd_dhcp6_lease_get_pd_lifetime_timestamp(uplink->dhcp6_lease, CLOCK_BOOTTIME,
1022 &lifetime_preferred_usec, &lifetime_valid_usec);
1023 if (r < 0)
1024 return r;
1025
38488bab 1026 r = dhcp_pd_assign_subnet_prefix(link, &pd_prefix, pd_prefix_len,
d8ec95c7 1027 lifetime_preferred_usec, lifetime_valid_usec,
38488bab 1028 /* is_uplink = */ link == uplink);
d5ebcf65
YW
1029 if (r < 0)
1030 return r;
1031 }
1032
a27588d4 1033 return dhcp_pd_finalize(link);
d5ebcf65
YW
1034}
1035
a27588d4 1036int dhcp6_pd_prefix_acquired(Link *uplink) {
5ed10a19 1037 union in_addr_union server_address;
d5ebcf65 1038 Link *link;
d5ebcf65
YW
1039 int r;
1040
a27588d4
YW
1041 assert(uplink);
1042 assert(uplink->dhcp6_lease);
d5ebcf65 1043
a27588d4 1044 r = sd_dhcp6_lease_get_server_address(uplink->dhcp6_lease, &server_address.in6);
5ed10a19 1045 if (r < 0)
a27588d4 1046 return log_link_warning_errno(uplink, r, "Failed to get server address of DHCPv6 lease: %m");
5ed10a19 1047
41664456 1048 /* First, logs acquired prefixes and request unreachable routes. */
d8ec95c7
YW
1049 FOREACH_DHCP6_PD_PREFIX(uplink->dhcp6_lease) {
1050 usec_t lifetime_valid_usec;
d5ebcf65
YW
1051 struct in6_addr pd_prefix;
1052 uint8_t pd_prefix_len;
1053
d8ec95c7 1054 r = sd_dhcp6_lease_get_pd_prefix(uplink->dhcp6_lease, &pd_prefix, &pd_prefix_len);
d5ebcf65 1055 if (r < 0)
d8ec95c7 1056 return r;
d5ebcf65 1057
d5ebcf65
YW
1058 /* Mask prefix for safety. */
1059 r = in6_addr_mask(&pd_prefix, pd_prefix_len);
41664456 1060 if (r < 0)
21feba0a 1061 return log_link_error_errno(uplink, r, "Failed to mask DHCPv6 delegated prefix: %m");
41664456 1062
a27588d4 1063 r = dhcp_pd_prefix_add(uplink, &pd_prefix, pd_prefix_len);
d5ebcf65
YW
1064 if (r < 0)
1065 return r;
1066
d8ec95c7
YW
1067 r = sd_dhcp6_lease_get_pd_lifetime_timestamp(uplink->dhcp6_lease, CLOCK_BOOTTIME,
1068 NULL, &lifetime_valid_usec);
1069 if (r < 0)
1070 return r;
1071
5235d739 1072 r = dhcp6_request_unreachable_route(uplink, &pd_prefix, pd_prefix_len,
d8ec95c7 1073 lifetime_valid_usec, &server_address);
d5ebcf65
YW
1074 if (r < 0)
1075 return r;
1076 }
1077
41664456 1078 /* Then, assign subnet prefixes. */
a27588d4
YW
1079 HASHMAP_FOREACH(link, uplink->manager->links_by_index) {
1080 if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true))
41664456
YW
1081 continue;
1082
a27588d4 1083 r = dhcp6_pd_assign_subnet_prefixes(link, uplink);
d5ebcf65 1084 if (r < 0) {
a27588d4 1085 /* When failed on the upstream interface (i.e., the case link == uplink),
41664456
YW
1086 * immediately abort the assignment of the prefixes. As, the all assigned
1087 * prefixes will be dropped soon in link_enter_failed(), and it is meaningless
1088 * to continue the assignment. */
a27588d4 1089 if (link == uplink)
d5ebcf65
YW
1090 return r;
1091
1092 link_enter_failed(link);
1093 }
1094 }
d5ebcf65
YW
1095
1096 return 0;
1097}
1098
e49bad01
YW
1099static bool dhcp4_pd_uplink_is_ready(Link *link) {
1100 assert(link);
1101
1102 if (!link->network)
1103 return false;
1104
1105 if (!link->network->dhcp_use_6rd)
1106 return false;
1107
1108 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
1109 return false;
1110
1111 if (!link->dhcp_client)
1112 return false;
1113
1114 if (sd_dhcp_client_is_running(link->dhcp_client) <= 0)
1115 return false;
1116
0ad8d953 1117 return sd_dhcp_lease_has_6rd(link->dhcp_lease);
e49bad01
YW
1118}
1119
d5ebcf65
YW
1120static bool dhcp6_pd_uplink_is_ready(Link *link) {
1121 assert(link);
1122
1123 if (!link->network)
1124 return false;
1125
1126 if (!link->network->dhcp6_use_pd_prefix)
1127 return false;
1128
1129 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
1130 return false;
1131
1132 if (!link->dhcp6_client)
1133 return false;
1134
1135 if (sd_dhcp6_client_is_running(link->dhcp6_client) <= 0)
1136 return false;
1137
fb70992d 1138 return sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease);
d5ebcf65
YW
1139}
1140
a27588d4 1141int dhcp_pd_find_uplink(Link *link, Link **ret) {
d5ebcf65
YW
1142 Link *uplink = NULL;
1143 int r = 0;
1144
1145 assert(link);
1146 assert(link->manager);
a27588d4 1147 assert(link_dhcp_pd_is_enabled(link));
d5ebcf65
YW
1148 assert(ret);
1149
a27588d4
YW
1150 if (link->network->dhcp_pd_uplink_name)
1151 r = link_get_by_name(link->manager, link->network->dhcp_pd_uplink_name, &uplink);
1152 else if (link->network->dhcp_pd_uplink_index > 0)
1153 r = link_get_by_index(link->manager, link->network->dhcp_pd_uplink_index, &uplink);
1154 else if (link->network->dhcp_pd_uplink_index == UPLINK_INDEX_SELF)
d5ebcf65
YW
1155 uplink = link;
1156 if (r < 0)
1157 return r;
1158
1159 if (uplink) {
e49bad01
YW
1160 if (dhcp4_pd_uplink_is_ready(uplink)) {
1161 *ret = uplink;
1162 return AF_INET;
1163 }
d5ebcf65 1164
e49bad01
YW
1165 if (dhcp6_pd_uplink_is_ready(uplink)) {
1166 *ret = uplink;
1167 return AF_INET6;
1168 }
1169
1170 return -EBUSY;
d5ebcf65
YW
1171 }
1172
1173 HASHMAP_FOREACH(uplink, link->manager->links_by_index) {
d5ebcf65 1174 /* Assume that there exists at most one link which acquired delegated prefixes. */
e49bad01
YW
1175 if (dhcp4_pd_uplink_is_ready(uplink)) {
1176 *ret = uplink;
1177 return AF_INET;
1178 }
1179
1180 if (dhcp6_pd_uplink_is_ready(uplink)) {
1181 *ret = uplink;
1182 return AF_INET6;
1183 }
d5ebcf65
YW
1184 }
1185
1186 return -ENODEV;
1187}
1188
a27588d4 1189int dhcp_request_prefix_delegation(Link *link) {
d5ebcf65 1190 Link *uplink;
e49bad01 1191 int r;
d5ebcf65
YW
1192
1193 assert(link);
1194
a27588d4 1195 if (!link_dhcp_pd_is_enabled(link))
d5ebcf65
YW
1196 return 0;
1197
e49bad01
YW
1198 r = dhcp_pd_find_uplink(link, &uplink);
1199 if (r < 0)
d5ebcf65
YW
1200 return 0;
1201
e49bad01
YW
1202 log_link_debug(link, "Requesting subnets of delegated prefixes acquired by DHCPv%c client on %s",
1203 r == AF_INET ? '4' : '6', uplink->ifname);
1204
1205 return r == AF_INET ?
1206 dhcp4_pd_assign_subnet_prefix(link, uplink) :
1207 dhcp6_pd_assign_subnet_prefixes(link, uplink);
d5ebcf65
YW
1208}
1209
a27588d4 1210int config_parse_dhcp_pd_subnet_id(
d5ebcf65
YW
1211 const char *unit,
1212 const char *filename,
1213 unsigned line,
1214 const char *section,
1215 unsigned section_line,
1216 const char *lvalue,
1217 int ltype,
1218 const char *rvalue,
1219 void *data,
1220 void *userdata) {
1221
99534007 1222 int64_t *p = ASSERT_PTR(data);
d5ebcf65
YW
1223 uint64_t t;
1224 int r;
1225
1226 assert(filename);
1227 assert(lvalue);
1228 assert(rvalue);
d5ebcf65
YW
1229
1230 if (isempty(rvalue) || streq(rvalue, "auto")) {
1231 *p = -1;
1232 return 0;
1233 }
1234
1235 r = safe_atoux64(rvalue, &t);
1236 if (r < 0) {
1237 log_syntax(unit, LOG_WARNING, filename, line, r,
1238 "Failed to parse %s=, ignoring assignment: %s",
1239 lvalue, rvalue);
1240 return 0;
1241 }
1242 if (t > INT64_MAX) {
1243 log_syntax(unit, LOG_WARNING, filename, line, 0,
1244 "Invalid subnet id '%s', ignoring assignment.",
1245 rvalue);
1246 return 0;
1247 }
1248
1249 *p = (int64_t) t;
1250
1251 return 0;
1252}