]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp6.c
Merge pull request #32684 from YHNdnzj/pr-followups
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
5c79bd79 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
5c79bd79
PF
4***/
5
f8da534e 6#include "dhcp6-client-internal.h"
d8ec95c7 7#include "dhcp6-lease-internal.h"
d5ebcf65
YW
8#include "hashmap.h"
9#include "hostname-setup.h"
10#include "hostname-util.h"
11#include "networkd-address.h"
12#include "networkd-dhcp-prefix-delegation.h"
f8da534e 13#include "networkd-dhcp6-bus.h"
d5ebcf65
YW
14#include "networkd-dhcp6.h"
15#include "networkd-link.h"
16#include "networkd-manager.h"
d12fb2bc 17#include "networkd-ntp.h"
d5ebcf65
YW
18#include "networkd-queue.h"
19#include "networkd-route.h"
ef0a234a 20#include "networkd-state-file.h"
d5ebcf65
YW
21#include "string-table.h"
22#include "string-util.h"
9efa8a3c 23
d5ebcf65
YW
24bool link_dhcp6_with_address_enabled(Link *link) {
25 if (!link_dhcp6_enabled(link))
26 return false;
1633c457 27
d5ebcf65 28 return link->network->dhcp6_use_address;
76c3246d
PF
29}
30
d5ebcf65
YW
31static DHCP6ClientStartMode link_get_dhcp6_client_start_mode(Link *link) {
32 assert(link);
494c868d 33
d5ebcf65
YW
34 if (!link->network)
35 return DHCP6_CLIENT_START_MODE_NO;
1046bf9b 36
d5ebcf65
YW
37 /* When WithoutRA= is explicitly specified, then honor it. */
38 if (link->network->dhcp6_client_start_mode >= 0)
39 return link->network->dhcp6_client_start_mode;
4ff296b0 40
0bcc6557 41 /* When this interface itself is an uplink interface, then start dhcp6 client in solicit mode. */
a27588d4 42 if (dhcp_pd_is_uplink(link, link, /* accept_auto = */ false))
d5ebcf65 43 return DHCP6_CLIENT_START_MODE_SOLICIT;
4b409e85 44
d5ebcf65
YW
45 /* Otherwise, start dhcp6 client when RA is received. */
46 return DHCP6_CLIENT_START_MODE_NO;
494c868d
PF
47}
48
3b6a3bde 49static int dhcp6_remove(Link *link, bool only_marked) {
1633c457
YW
50 Address *address;
51 Route *route;
372acaad 52 int ret = 0;
494c868d 53
1633c457 54 assert(link);
8d01e44c 55 assert(link->manager);
494c868d 56
3b6a3bde
YW
57 if (!only_marked)
58 link->dhcp6_configured = false;
dd5ab7d9 59
8d01e44c 60 SET_FOREACH(route, link->manager->routes) {
3b6a3bde
YW
61 if (route->source != NETWORK_CONFIG_SOURCE_DHCP6)
62 continue;
63 if (only_marked && !route_is_marked(route))
64 continue;
494c868d 65
3caed9ea 66 RET_GATHER(ret, route_remove_and_cancel(route, link->manager));
494c868d
PF
67 }
68
3b6a3bde
YW
69 SET_FOREACH(address, link->addresses) {
70 if (address->source != NETWORK_CONFIG_SOURCE_DHCP6)
71 continue;
72 if (only_marked && !address_is_marked(address))
73 continue;
120b5c0b 74
f22b586a 75 RET_GATHER(ret, address_remove_and_cancel(address, link));
1633c457 76 }
76c3246d 77
372acaad 78 return ret;
1633c457 79}
76c3246d 80
3b6a3bde
YW
81static int dhcp6_address_ready_callback(Address *address) {
82 Address *a;
76c3246d 83
3b6a3bde
YW
84 assert(address);
85 assert(address->link);
76c3246d 86
3b6a3bde
YW
87 SET_FOREACH(a, address->link->addresses)
88 if (a->source == NETWORK_CONFIG_SOURCE_DHCP6)
89 a->callback = NULL;
76c3246d 90
3b6a3bde
YW
91 return dhcp6_check_ready(address->link);
92}
93
d5ebcf65 94int dhcp6_check_ready(Link *link) {
3b6a3bde 95 int r;
02e9e34b 96
3b6a3bde 97 assert(link);
fc4aa64c 98 assert(link->network);
02e9e34b 99
3b6a3bde
YW
100 if (link->dhcp6_messages > 0) {
101 log_link_debug(link, "%s(): DHCPv6 addresses and routes are not set.", __func__);
102 return 0;
76c3246d
PF
103 }
104
fc4aa64c 105 if (link->network->dhcp6_use_address &&
43a75266 106 sd_dhcp6_lease_has_address(link->dhcp6_lease) &&
fc4aa64c 107 !link_check_addresses_ready(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
f5e1781a 108 Address *address;
02e9e34b 109
3b6a3bde
YW
110 SET_FOREACH(address, link->addresses)
111 if (address->source == NETWORK_CONFIG_SOURCE_DHCP6)
112 address->callback = dhcp6_address_ready_callback;
113
114 log_link_debug(link, "%s(): no DHCPv6 address is ready.", __func__);
115 return 0;
116 }
117
118 link->dhcp6_configured = true;
119 log_link_debug(link, "DHCPv6 addresses and routes set.");
120
121 r = dhcp6_remove(link, /* only_marked = */ true);
122 if (r < 0)
123 return r;
124
125 link_check_ready(link);
126 return 0;
494c868d 127}
26db55f3 128
80d62d4f 129static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
c62c4628
PF
130 int r;
131
132 assert(link);
133
5a07fa9d
YW
134 r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 address");
135 if (r <= 0)
136 return r;
c62c4628 137
3b6a3bde
YW
138 r = dhcp6_check_ready(link);
139 if (r < 0)
140 link_enter_failed(link);
6545067a 141
c62c4628
PF
142 return 1;
143}
144
5d003031 145static int verify_dhcp6_address(Link *link, const Address *address) {
b432080d
YW
146 bool by_ndisc = false;
147 Address *existing;
3b6a3bde 148 int log_level;
b432080d
YW
149
150 assert(link);
151 assert(address);
b8ce3b44 152 assert(address->family == AF_INET6);
b432080d 153
84dbb3fd 154 const char *pretty = IN6_ADDR_TO_STRING(&address->in_addr.in6);
5291f26d 155
b33dd04e 156 if (address_get_harder(link, address, &existing) < 0) {
b432080d
YW
157 /* New address. */
158 log_level = LOG_INFO;
159 goto simple_log;
160 } else
161 log_level = LOG_DEBUG;
162
77de62f9 163 if (address_can_update(existing, address))
b432080d
YW
164 goto simple_log;
165
3b6a3bde
YW
166 if (existing->source == NETWORK_CONFIG_SOURCE_NDISC)
167 by_ndisc = true;
b432080d 168
5d003031 169 log_link_warning(link, "Ignoring DHCPv6 address %s/%u (valid %s, preferred %s) which conflicts with %s/%u%s.",
84dbb3fd 170 pretty, address->prefixlen,
16bc8635
YW
171 FORMAT_LIFETIME(address->lifetime_valid_usec),
172 FORMAT_LIFETIME(address->lifetime_preferred_usec),
84dbb3fd 173 pretty, existing->prefixlen,
5d003031
YW
174 by_ndisc ? " assigned by NDisc" : "");
175 if (by_ndisc)
176 log_link_warning(link, "Hint: use IPv6Token= setting to change the address generated by NDisc or set UseAutonomousPrefix=no.");
177
178 return -EEXIST;
b432080d
YW
179
180simple_log:
3b6a3bde 181 log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)",
84dbb3fd 182 pretty, address->prefixlen,
16bc8635
YW
183 FORMAT_LIFETIME(address->lifetime_valid_usec),
184 FORMAT_LIFETIME(address->lifetime_preferred_usec));
5d003031 185 return 0;
76c5a0f2
YW
186}
187
188static int dhcp6_request_address(
1e7a0e21 189 Link *link,
c30f9aaf 190 const struct in6_addr *server_address,
1633c457 191 const struct in6_addr *ip6_addr,
16bc8635
YW
192 usec_t lifetime_preferred_usec,
193 usec_t lifetime_valid_usec) {
1e7a0e21 194
ebd96906 195 _cleanup_(address_unrefp) Address *addr = NULL;
3b6a3bde 196 Address *existing;
1e7a0e21 197 int r;
c62c4628 198
f0213e37 199 r = address_new(&addr);
c62c4628 200 if (r < 0)
1633c457 201 return log_oom();
c62c4628 202
3b6a3bde 203 addr->source = NETWORK_CONFIG_SOURCE_DHCP6;
c30f9aaf 204 addr->provider.in6 = *server_address;
c62c4628 205 addr->family = AF_INET6;
57e44707 206 addr->in_addr.in6 = *ip6_addr;
851c9f82 207 addr->flags = IFA_F_NOPREFIXROUTE;
6d8f6b0b 208 addr->prefixlen = 128;
16bc8635
YW
209 addr->lifetime_preferred_usec = lifetime_preferred_usec;
210 addr->lifetime_valid_usec = lifetime_valid_usec;
c62c4628 211
5d003031
YW
212 if (verify_dhcp6_address(link, addr) < 0)
213 return 0;
1633c457 214
4b3590c3
TM
215 r = free_and_strdup_warn(&addr->netlabel, link->network->dhcp6_netlabel);
216 if (r < 0)
217 return r;
218
3b6a3bde
YW
219 if (address_get(link, addr, &existing) < 0)
220 link->dhcp6_configured = false;
221 else
222 address_unmark(existing);
1633c457 223
f60e6558 224 r = link_request_address(link, addr, &link->dhcp6_messages,
3b6a3bde 225 dhcp6_address_handler, NULL);
84dbb3fd
ZJS
226 if (r < 0)
227 return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s/128: %m",
228 IN6_ADDR_TO_STRING(ip6_addr));
4ff296b0 229 return 0;
c62c4628
PF
230}
231
1633c457 232static int dhcp6_address_acquired(Link *link) {
c30f9aaf 233 struct in6_addr server_address;
c62c4628 234 int r;
1633c457
YW
235
236 assert(link);
1536b7b2 237 assert(link->network);
1633c457
YW
238 assert(link->dhcp6_lease);
239
1536b7b2
YW
240 if (!link->network->dhcp6_use_address)
241 return 0;
242
c30f9aaf
YW
243 r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &server_address);
244 if (r < 0)
245 return log_link_warning_errno(link, r, "Failed to get server address of DHCPv6 lease: %m");
246
d8ec95c7
YW
247 FOREACH_DHCP6_ADDRESS(link->dhcp6_lease) {
248 usec_t lifetime_preferred_usec, lifetime_valid_usec;
1633c457
YW
249 struct in6_addr ip6_addr;
250
d8ec95c7 251 r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr);
1633c457 252 if (r < 0)
d8ec95c7
YW
253 return r;
254
255 r = sd_dhcp6_lease_get_address_lifetime_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME,
256 &lifetime_preferred_usec, &lifetime_valid_usec);
257 if (r < 0)
258 return r;
1633c457 259
c30f9aaf 260 r = dhcp6_request_address(link, &server_address, &ip6_addr,
d8ec95c7
YW
261 lifetime_preferred_usec,
262 lifetime_valid_usec);
1633c457
YW
263 if (r < 0)
264 return r;
265 }
266
38ba3da0 267 if (link->network->dhcp6_use_hostname) {
f963f895
VM
268 const char *dhcpname = NULL;
269 _cleanup_free_ char *hostname = NULL;
38ba3da0 270
f963f895
VM
271 (void) sd_dhcp6_lease_get_fqdn(link->dhcp6_lease, &dhcpname);
272
273 if (dhcpname) {
274 r = shorten_overlong(dhcpname, &hostname);
275 if (r < 0)
276 log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname);
277 if (r == 1)
278 log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
279 }
280 if (hostname) {
281 r = manager_set_hostname(link->manager, hostname);
282 if (r < 0)
283 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
284 }
285 }
286
1633c457
YW
287 return 0;
288}
289
290static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
291 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL;
c62c4628 292 sd_dhcp6_lease *lease;
1633c457 293 int r;
c62c4628 294
2ccada8d 295 link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6);
8d01e44c 296 manager_mark_routes(link->manager, NULL, NETWORK_CONFIG_SOURCE_DHCP6);
2a877f45 297
c62c4628
PF
298 r = sd_dhcp6_client_get_lease(client, &lease);
299 if (r < 0)
1633c457
YW
300 return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m");
301
302 lease_old = TAKE_PTR(link->dhcp6_lease);
303 link->dhcp6_lease = sd_dhcp6_lease_ref(lease);
c62c4628 304
1633c457
YW
305 r = dhcp6_address_acquired(link);
306 if (r < 0)
307 return r;
c62c4628 308
fb70992d 309 if (sd_dhcp6_lease_has_pd_prefix(lease)) {
1633c457 310 r = dhcp6_pd_prefix_acquired(link);
c62c4628
PF
311 if (r < 0)
312 return r;
fb70992d 313 } else if (sd_dhcp6_lease_has_pd_prefix(lease_old))
1633c457 314 /* When we had PD prefixes but not now, we need to remove them. */
a27588d4 315 dhcp_pd_prefix_lost(link);
c62c4628 316
3b6a3bde
YW
317 if (link->dhcp6_messages == 0) {
318 link->dhcp6_configured = true;
2a877f45 319
3b6a3bde
YW
320 r = dhcp6_remove(link, /* only_marked = */ true);
321 if (r < 0)
322 return r;
323 } else
324 log_link_debug(link, "Setting DHCPv6 addresses and routes");
1633c457 325
3b6a3bde 326 if (!link->dhcp6_configured)
1633c457
YW
327 link_set_state(link, LINK_STATE_CONFIGURING);
328
76c5a0f2 329 link_check_ready(link);
c62c4628
PF
330 return 0;
331}
332
1633c457 333static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) {
9709f9ed
YW
334 sd_dhcp6_lease *lease;
335 int r;
336
337 assert(client);
338 assert(link);
339
340 r = sd_dhcp6_client_get_lease(client, &lease);
341 if (r < 0)
342 return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m");
343
ef0a234a
YW
344 unref_and_replace_full(link->dhcp6_lease, lease, sd_dhcp6_lease_ref, sd_dhcp6_lease_unref);
345
346 link_dirty(link);
347 return 0;
1633c457
YW
348}
349
350static int dhcp6_lease_lost(Link *link) {
c62c4628 351 int r;
1633c457
YW
352
353 assert(link);
354 assert(link->manager);
355
d4dae4c2
YW
356 if (!link->dhcp6_lease)
357 return 0;
358
1633c457
YW
359 log_link_info(link, "DHCPv6 lease lost");
360
fb70992d 361 if (sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
a27588d4 362 dhcp_pd_prefix_lost(link);
1633c457
YW
363
364 link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
365
3b6a3bde 366 r = dhcp6_remove(link, /* only_marked = */ false);
1633c457
YW
367 if (r < 0)
368 return r;
369
370 return 0;
371}
372
373static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
99534007 374 Link *link = ASSERT_PTR(userdata);
814d8f96 375 int r = 0;
5c79bd79 376
5c79bd79 377 assert(link->network);
5c79bd79
PF
378
379 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
380 return;
381
1633c457 382 switch (event) {
10c9ce61
DH
383 case SD_DHCP6_CLIENT_EVENT_STOP:
384 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
385 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
1633c457 386 r = dhcp6_lease_lost(link);
c62c4628
PF
387 break;
388
10c9ce61 389 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
1633c457 390 r = dhcp6_lease_ip_acquired(client, link);
814d8f96 391 break;
c62c4628 392
10c9ce61 393 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
c62c4628 394 r = dhcp6_lease_information_acquired(client, link);
5c79bd79
PF
395 break;
396
397 default:
398 if (event < 0)
814d8f96 399 log_link_warning_errno(link, event, "DHCPv6 error, ignoring: %m");
5c79bd79 400 else
e53fc357 401 log_link_warning(link, "DHCPv6 unknown event: %d", event);
5c79bd79 402 }
814d8f96
YW
403 if (r < 0)
404 link_enter_failed(link);
5c79bd79
PF
405}
406
fac19a21
YW
407int dhcp6_start_on_ra(Link *link, bool information_request) {
408 int r;
5c79bd79 409
7a695d8e
TG
410 assert(link);
411 assert(link->dhcp6_client);
125f20b4 412 assert(link->network);
94876904 413 assert(in6_addr_is_link_local(&link->ipv6ll_address));
85bd849f 414
0e45721e 415 if (link_get_dhcp6_client_start_mode(link) != DHCP6_CLIENT_START_MODE_NO)
a6f44d61 416 /* When WithoutRA= is specified, then the DHCPv6 client should be already running in
fac19a21
YW
417 * the requested mode. Hence, ignore the requests by RA. */
418 return 0;
419
7a695d8e
TG
420 r = sd_dhcp6_client_is_running(link->dhcp6_client);
421 if (r < 0)
422 return r;
e6604041 423
fac19a21
YW
424 if (r > 0) {
425 int inf_req;
426
720bec40
TY
427 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
428 if (r < 0)
429 return r;
430
fac19a21 431 if (inf_req == information_request)
8b1cfab9 432 /* The client is already running in the requested mode. */
720bec40
TY
433 return 0;
434
8b1cfab9
YW
435 if (!inf_req) {
436 log_link_debug(link,
437 "The DHCPv6 client is already running in the managed mode, "
438 "refusing to start the client in the information requesting mode.");
439 return 0;
440 }
441
442 log_link_debug(link,
443 "The DHCPv6 client is running in the information requesting mode. "
444 "Restarting the client in the managed mode.");
445
7a695d8e
TG
446 r = sd_dhcp6_client_stop(link->dhcp6_client);
447 if (r < 0)
448 return r;
720bec40
TY
449 } else {
450 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
451 if (r < 0)
452 return r;
7a695d8e 453 }
85bd849f 454
fac19a21 455 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, information_request);
720bec40
TY
456 if (r < 0)
457 return r;
458
459 r = sd_dhcp6_client_start(link->dhcp6_client);
7a695d8e
TG
460 if (r < 0)
461 return r;
85bd849f 462
7a695d8e
TG
463 return 0;
464}
18d29550 465
294f129b 466int dhcp6_start(Link *link) {
0e45721e 467 DHCP6ClientStartMode start_mode;
ccffa166
YW
468 int r;
469
294f129b 470 assert(link);
fac19a21 471 assert(link->network);
294f129b
YW
472
473 if (!link->dhcp6_client)
474 return 0;
475
476 if (!link_dhcp6_enabled(link))
477 return 0;
478
ccffa166
YW
479 if (!link_has_carrier(link))
480 return 0;
481
fac19a21
YW
482 if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0)
483 return 0;
484
294f129b
YW
485 if (!in6_addr_is_link_local(&link->ipv6ll_address)) {
486 log_link_debug(link, "IPv6 link-local address is not set, delaying to start DHCPv6 client.");
487 return 0;
488 }
489
fac19a21
YW
490 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
491 if (r < 0)
492 return r;
294f129b 493
0e45721e
YW
494 start_mode = link_get_dhcp6_client_start_mode(link);
495 if (start_mode == DHCP6_CLIENT_START_MODE_NO)
496 return 0;
497
fac19a21 498 r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
0e45721e 499 start_mode == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
fac19a21
YW
500 if (r < 0)
501 return r;
502
503 r = sd_dhcp6_client_start(link->dhcp6_client);
ccffa166
YW
504 if (r < 0)
505 return r;
294f129b 506
ccffa166 507 return 1;
294f129b
YW
508}
509
8006aa32
SA
510static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
511 _cleanup_free_ char *hostname = NULL;
512 const char *hn;
513 int r;
514
515 assert(link);
516
b90480c8 517 if (!link->network->dhcp6_send_hostname)
8006aa32 518 hn = NULL;
b90480c8
RP
519 else if (link->network->dhcp6_hostname)
520 hn = link->network->dhcp6_hostname;
8006aa32
SA
521 else {
522 r = gethostname_strict(&hostname);
523 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
87160186 524 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to get hostname: %m");
8006aa32
SA
525
526 hn = hostname;
527 }
528
a8494759
YW
529 r = sd_dhcp6_client_set_fqdn(client, hn);
530 if (r == -EINVAL && hostname)
531 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
87160186 532 log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
a8494759 533 else if (r < 0)
87160186 534 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname: %m");
a8494759
YW
535
536 return 0;
8006aa32
SA
537}
538
eebba6dc
YW
539static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
540 const DUID *duid;
541 int r;
542
543 assert(link);
544 assert(link->network);
545 assert(client);
546
ca2b7cd8 547 r = sd_dhcp6_client_set_mac(client, link->hw_addr.bytes, link->hw_addr.length, link->iftype);
eebba6dc
YW
548 if (r < 0)
549 return r;
550
4e26a5ba
YW
551 if (link->network->dhcp6_iaid_set) {
552 r = sd_dhcp6_client_set_iaid(client, link->network->dhcp6_iaid);
eebba6dc
YW
553 if (r < 0)
554 return r;
555 }
556
4e26a5ba 557 duid = link_get_dhcp6_duid(link);
53488ea3
YW
558
559 if (duid->raw_data_len == 0)
560 switch (duid->type) {
561 case DUID_TYPE_LLT:
562 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
563 break;
564 case DUID_TYPE_LL:
565 r = sd_dhcp6_client_set_duid_ll(client);
566 break;
567 case DUID_TYPE_EN:
568 r = sd_dhcp6_client_set_duid_en(client);
569 break;
570 case DUID_TYPE_UUID:
571 r = sd_dhcp6_client_set_duid_uuid(client);
572 break;
573 default:
574 r = sd_dhcp6_client_set_duid_raw(client, duid->type, NULL, 0);
575 }
eebba6dc 576 else
53488ea3 577 r = sd_dhcp6_client_set_duid_raw(client, duid->type, duid->raw_data, duid->raw_data_len);
eebba6dc
YW
578 if (r < 0)
579 return r;
580
581 return 0;
582}
583
ccffa166 584static int dhcp6_configure(Link *link) {
5bad7ebd 585 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
b4ccc5de 586 sd_dhcp6_option *vendor_option;
e7d5fe17 587 sd_dhcp6_option *send_option;
35f6a5cb 588 void *request_options;
fb5c8216 589 int r;
7a695d8e
TG
590
591 assert(link);
fb5c8216 592 assert(link->network);
7a695d8e 593
62379e88 594 if (link->dhcp6_client)
87160186 595 return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "DHCPv6 client is already configured.");
62379e88 596
7a695d8e 597 r = sd_dhcp6_client_new(&client);
5bad7ebd 598 if (r == -ENOMEM)
bc8bd68d 599 return log_oom_debug();
7a695d8e 600 if (r < 0)
87160186 601 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to create DHCPv6 client: %m");
5c79bd79 602
4cf85000 603 r = sd_dhcp6_client_attach_event(client, link->manager->event, 0);
5c79bd79 604 if (r < 0)
87160186 605 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to attach event: %m");
5c79bd79 606
3b1dbdf0
YW
607 r = sd_dhcp6_client_attach_device(client, link->dev);
608 if (r < 0)
609 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to attach device: %m");
610
eebba6dc 611 r = dhcp6_set_identifier(link, client);
e6604041 612 if (r < 0)
87160186 613 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set identifier: %m");
413708d1 614
90e74a66 615 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) {
e7d5fe17
AD
616 r = sd_dhcp6_client_add_option(client, send_option);
617 if (r == -EEXIST)
618 continue;
619 if (r < 0)
87160186 620 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set option: %m");
e7d5fe17
AD
621 }
622
8006aa32 623 r = dhcp6_set_hostname(client, link);
8006aa32 624 if (r < 0)
a8494759 625 return r;
8006aa32 626
2f8e7633 627 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
e6604041 628 if (r < 0)
87160186 629 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set ifindex: %m");
5c79bd79 630
3175a8c2
SS
631 if (link->network->dhcp6_mudurl) {
632 r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
633 if (r < 0)
87160186 634 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set MUD URL: %m");
3175a8c2
SS
635 }
636
9646ffe2 637 if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
612caa26
YW
638 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVER);
639 if (r < 0)
640 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request DNS servers: %m");
641 }
642
7a169cb4 643 if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) > 0) {
612caa26
YW
644 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN);
645 if (r < 0)
646 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request domains: %m");
647 }
648
a75feb55
RP
649 if (link->network->dhcp6_use_captive_portal > 0) {
650 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CAPTIVE_PORTAL);
651 if (r < 0)
652 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request captive portal: %m");
653 }
654
d12fb2bc 655 if (link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
612caa26
YW
656 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER);
657 if (r < 0)
658 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request NTP servers: %m");
659
660 /* If the server does not provide NTP servers, then we fallback to use SNTP servers. */
661 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVER);
662 if (r < 0)
663 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request SNTP servers: %m");
664 }
665
90e74a66 666 SET_FOREACH(request_options, link->network->dhcp6_request_options) {
35f6a5cb
SS
667 uint32_t option = PTR_TO_UINT32(request_options);
668
669 r = sd_dhcp6_client_set_request_option(client, option);
670 if (r == -EEXIST) {
87160186 671 log_link_debug(link, "DHCPv6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
35f6a5cb
SS
672 continue;
673 }
35f6a5cb 674 if (r < 0)
87160186 675 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for '%u': %m", option);
35f6a5cb
SS
676 }
677
f37f2a6b
SS
678 if (link->network->dhcp6_user_class) {
679 r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
680 if (r < 0)
87160186 681 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set user class: %m");
f37f2a6b
SS
682 }
683
ed0d1b2e
SS
684 if (link->network->dhcp6_vendor_class) {
685 r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
686 if (r < 0)
87160186 687 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor class: %m");
ed0d1b2e
SS
688 }
689
90e74a66 690 ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options) {
b4ccc5de
SS
691 r = sd_dhcp6_client_add_vendor_option(client, vendor_option);
692 if (r == -EEXIST)
693 continue;
694 if (r < 0)
87160186 695 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor option: %m");
b4ccc5de
SS
696 }
697
7a695d8e 698 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
e6604041 699 if (r < 0)
87160186 700 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set callback: %m");
5c79bd79 701
f8da534e 702 r = dhcp6_client_set_state_callback(client, dhcp6_client_callback_bus, link);
703 if (r < 0)
704 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set state change callback: %m");
705
0f5ef9b6
YW
706 r = sd_dhcp6_client_set_prefix_delegation(client, link->network->dhcp6_use_pd_prefix);
707 if (r < 0)
d5f8fd5b 708 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to %s requesting prefixes to be delegated: %m",
0f5ef9b6 709 enable_disable(link->network->dhcp6_use_pd_prefix));
103b81ee 710
0bcc6557 711 /* Even if UseAddress=no, we need to request IA_NA, as the dhcp6 client may be started in solicit mode. */
d5f8fd5b
YW
712 r = sd_dhcp6_client_set_address_request(client, link->network->dhcp6_use_pd_prefix ? link->network->dhcp6_use_address : true);
713 if (r < 0)
714 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to %s requesting address: %m",
715 enable_disable(link->network->dhcp6_use_address));
716
df8bf726
YW
717 if (link->network->dhcp6_pd_prefix_length > 0) {
718 r = sd_dhcp6_client_set_prefix_delegation_hint(client,
719 link->network->dhcp6_pd_prefix_length,
720 &link->network->dhcp6_pd_prefix_hint);
2805536b 721 if (r < 0)
df8bf726 722 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation hint: %m");
2805536b
SS
723 }
724
50ee1fec
YW
725 r = sd_dhcp6_client_set_rapid_commit(client, link->network->dhcp6_use_rapid_commit);
726 if (r < 0)
727 return log_link_debug_errno(link, r,
728 "DHCPv6 CLIENT: Failed to %s rapid commit: %m",
729 enable_disable(link->network->dhcp6_use_rapid_commit));
730
b895aa5f 731 r = sd_dhcp6_client_set_send_release(client, link->network->dhcp6_send_release);
732 if (r < 0)
733 return log_link_debug_errno(link, r,
734 "DHCPv6 CLIENT: Failed to %s sending release message on stop: %m",
735 enable_disable(link->network->dhcp6_send_release));
736
5bad7ebd 737 link->dhcp6_client = TAKE_PTR(client);
e6604041 738
7a695d8e 739 return 0;
5c79bd79 740}
04ed9949 741
eebba6dc
YW
742int dhcp6_update_mac(Link *link) {
743 bool restart;
744 int r;
745
746 assert(link);
747
748 if (!link->dhcp6_client)
749 return 0;
750
751 restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
752
753 if (restart) {
754 r = sd_dhcp6_client_stop(link->dhcp6_client);
755 if (r < 0)
756 return r;
757 }
758
759 r = dhcp6_set_identifier(link, link->dhcp6_client);
760 if (r < 0)
761 return r;
762
763 if (restart) {
764 r = sd_dhcp6_client_start(link->dhcp6_client);
765 if (r < 0)
766 return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
767 }
768
769 return 0;
770}
771
09d09207 772static int dhcp6_process_request(Request *req, Link *link, void *userdata) {
ccffa166
YW
773 int r;
774
ff51134c 775 assert(link);
ccffa166 776
4b482e8b 777 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
ccffa166
YW
778 return 0;
779
780 r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
781 if (r <= 0)
782 return r;
783
784 r = dhcp6_configure(link);
785 if (r < 0)
786 return log_link_warning_errno(link, r, "Failed to configure DHCPv6 client: %m");
787
788 r = ndisc_start(link);
789 if (r < 0)
790 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
791
792 r = dhcp6_start(link);
793 if (r < 0)
794 return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
795
796 log_link_debug(link, "DHCPv6 client is configured%s.",
797 r > 0 ? ", acquiring DHCPv6 lease" : "");
ccffa166
YW
798 return 1;
799}
800
801int link_request_dhcp6_client(Link *link) {
802 int r;
803
804 assert(link);
805
52672db3 806 if (!link_dhcp6_enabled(link) && !link_ndisc_enabled(link))
ccffa166
YW
807 return 0;
808
809 if (link->dhcp6_client)
810 return 0;
811
09d09207 812 r = link_queue_request(link, REQUEST_TYPE_DHCP6_CLIENT, dhcp6_process_request, NULL);
ccffa166
YW
813 if (r < 0)
814 return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv6 client: %m");
815
816 log_link_debug(link, "Requested configuring of the DHCPv6 client.");
817 return 0;
818}
819
5460bde5
YW
820int link_serialize_dhcp6_client(Link *link, FILE *f) {
821 _cleanup_free_ char *duid = NULL;
822 uint32_t iaid;
823 int r;
824
825 assert(link);
826
827 if (!link->dhcp6_client)
828 return 0;
829
830 r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid);
831 if (r >= 0)
832 fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
833
3a6c2227 834 r = sd_dhcp6_client_get_duid_as_string(link->dhcp6_client, &duid);
5460bde5
YW
835 if (r >= 0)
836 fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
837
838 return 0;
839}
840
df8bf726 841int config_parse_dhcp6_pd_prefix_hint(
c24dd739
YW
842 const char* unit,
843 const char *filename,
844 unsigned line,
845 const char *section,
846 unsigned section_line,
847 const char *lvalue,
848 int ltype,
849 const char *rvalue,
850 void *data,
851 void *userdata) {
852
99534007 853 Network *network = ASSERT_PTR(userdata);
275468c0
YW
854 union in_addr_union u;
855 unsigned char prefixlen;
c24dd739
YW
856 int r;
857
858 assert(filename);
859 assert(lvalue);
860 assert(rvalue);
c24dd739 861
275468c0 862 r = in_addr_prefix_from_string(rvalue, AF_INET6, &u, &prefixlen);
c24dd739 863 if (r < 0) {
275468c0
YW
864 log_syntax(unit, LOG_WARNING, filename, line, r,
865 "Failed to parse %s=%s, ignoring assignment.", lvalue, rvalue);
c24dd739
YW
866 return 0;
867 }
868
275468c0
YW
869 if (prefixlen < 1 || prefixlen > 128) {
870 log_syntax(unit, LOG_WARNING, filename, line, 0,
871 "Invalid prefix length in %s=%s, ignoring assignment.", lvalue, rvalue);
c24dd739
YW
872 return 0;
873 }
874
df8bf726
YW
875 network->dhcp6_pd_prefix_hint = u.in6;
876 network->dhcp6_pd_prefix_length = prefixlen;
275468c0 877
c24dd739
YW
878 return 0;
879}
880
99e015e2
YW
881DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode,
882 "Failed to parse WithoutRA= setting");
883
884static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = {
885 [DHCP6_CLIENT_START_MODE_NO] = "no",
886 [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request",
887 [DHCP6_CLIENT_START_MODE_SOLICIT] = "solicit",
888};
889
890DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode);