]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp6.c
mount: disable mount-storm protection while mount unit is starting.
[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
6#include <netinet/ether.h>
7#include <linux/if.h>
76c3246d 8#include "sd-radv.h"
5c79bd79 9
5c79bd79
PF
10#include "sd-dhcp6-client.h"
11
76c3246d 12#include "hashmap.h"
8006aa32 13#include "hostname-util.h"
07630cea 14#include "network-internal.h"
23f53b99
TG
15#include "networkd-link.h"
16#include "networkd-manager.h"
76c3246d
PF
17#include "siphash24.h"
18#include "string-util.h"
19#include "radv-internal.h"
07630cea 20
be3a09b7
PF
21static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
22
03d4fc2e
PF
23static bool dhcp6_get_prefix_delegation(Link *link) {
24 if (!link->network)
103b81ee 25 return false;
103b81ee 26
a9eea606
YW
27 return IN_SET(link->network->router_prefix_delegation,
28 RADV_PREFIX_DELEGATION_DHCP6,
29 RADV_PREFIX_DELEGATION_BOTH);
103b81ee
PF
30}
31
32static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
33 Manager *manager;
34 Link *l;
35 Iterator i;
36
37 assert(dhcp6_link);
38
39 manager = dhcp6_link->manager;
40 assert(manager);
41
42 HASHMAP_FOREACH(l, manager->links, i) {
43 if (l == dhcp6_link)
44 continue;
45
03d4fc2e 46 if (!dhcp6_get_prefix_delegation(l))
103b81ee
PF
47 continue;
48
49 return true;
50 }
51
52 return false;
53}
54
c62c4628
PF
55static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
56 Link *link) {
57 return 0;
58}
59
76c3246d
PF
60static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
61 uint8_t prefix_len,
62 uint32_t lifetime_preferred,
63 uint32_t lifetime_valid) {
64 sd_radv *radv = link->radv;
65 int r;
66 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
67
68 r = sd_radv_prefix_new(&p);
69 if (r < 0)
70 return r;
71
72 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
73 if (r < 0)
74 return r;
75
76 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
77 if (r < 0)
78 return r;
79
80 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
81 if (r < 0)
82 return r;
83
84 r = sd_radv_stop(radv);
85 if (r < 0)
86 return r;
87
88 r = sd_radv_add_prefix(radv, p, true);
89 if (r < 0 && r != -EEXIST)
90 return r;
91
92 r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
93 if (r < 0)
94 return r;
95
96 return sd_radv_start(radv);
97}
98
302a796f 99static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
494c868d
PF
100 int r;
101
1046bf9b
YW
102 assert(link);
103
494c868d
PF
104 r = sd_netlink_message_get_errno(m);
105 if (r < 0)
1046bf9b 106 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
494c868d 107
0ae286e6 108 return 1;
494c868d
PF
109}
110
65dd5e31 111int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
494c868d
PF
112 int r;
113 sd_dhcp6_lease *lease;
114 union in_addr_union pd_prefix;
115 uint8_t pd_prefix_len;
116 uint32_t lifetime_preferred, lifetime_valid;
117
118 r = sd_dhcp6_client_get_lease(client, &lease);
119 if (r < 0)
120 return r;
121
494c868d
PF
122 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
123
124 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
125 &lifetime_preferred,
126 &lifetime_valid) >= 0) {
127 _cleanup_free_ char *buf = NULL;
dd5ab7d9 128 _cleanup_free_ Route *route = NULL;
494c868d
PF
129
130 if (pd_prefix_len > 64)
131 continue;
132
133 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
134
135 if (pd_prefix_len < 64) {
136 r = route_new(&route);
137 if (r < 0) {
138 log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
139 strnull(buf),
140 pd_prefix_len);
141 continue;
142 }
143
144 route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
145 0, 0, 0, &route);
146 route_update(route, NULL, 0, NULL, NULL, 0, 0,
147 RTN_UNREACHABLE);
148
c8ee637e 149 r = route_remove(route, link, dhcp6_route_remove_handler);
494c868d
PF
150 if (r < 0) {
151 (void) in_addr_to_string(AF_INET6,
152 &pd_prefix, &buf);
153
154 log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
155 strnull(buf),
156 pd_prefix_len);
dd5ab7d9 157
494c868d
PF
158 continue;
159 }
494c868d
PF
160
161 log_link_debug(link, "Removing unreachable route %s/%u",
162 strnull(buf), pd_prefix_len);
163 }
164 }
165
166 return 0;
167}
168
76c3246d
PF
169static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
170 struct in6_addr *pd_prefix,
171 uint8_t pd_prefix_len,
172 uint32_t lifetime_preferred,
173 uint32_t lifetime_valid) {
174 Link *link;
175 Manager *manager = dhcp6_link->manager;
176 union in_addr_union prefix;
9fb96abd 177 uint64_t n_prefixes, n_used = 0;
76c3246d 178 _cleanup_free_ char *buf = NULL;
eb75b919 179 _cleanup_free_ char *assigned_buf = NULL;
76c3246d
PF
180 int r;
181
182 assert(manager);
183 assert(pd_prefix_len <= 64);
184
185 prefix.in6 = *pd_prefix;
186
187 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
188 if (r < 0)
189 return r;
190
9fb96abd 191 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
76c3246d
PF
192
193 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
9fb96abd 194 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
76c3246d
PF
195 n_prefixes, strnull(buf), pd_prefix_len);
196
197 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
198 Link *assigned_link;
199
200 if (n_used == n_prefixes) {
9fb96abd 201 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
76c3246d
PF
202 n_used, n_prefixes, strnull(buf), pd_prefix_len);
203
204 return -EAGAIN;
205 }
206
207 if (link == dhcp6_link)
208 continue;
209
03d4fc2e 210 if (!dhcp6_get_prefix_delegation(link))
76c3246d
PF
211 continue;
212
213 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
214 if (assigned_link != NULL && assigned_link != link)
215 continue;
216
eb75b919 217 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
76c3246d
PF
218 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
219 lifetime_preferred, lifetime_valid);
220 if (r < 0) {
eb75b919 221 log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
76c3246d 222 assigned_link ? "update": "assign",
eb75b919 223 strnull(assigned_buf),
76c3246d
PF
224 strnull(buf), pd_prefix_len);
225
226 if (assigned_link == NULL)
227 continue;
228
229 } else
eb75b919
PF
230 log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
231 n_used + 1, n_prefixes,
232 strnull(assigned_buf),
233 strnull(buf), pd_prefix_len);
76c3246d
PF
234
235 n_used++;
236
37f52406 237 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
76c3246d
PF
238 if (r < 0 && n_used < n_prefixes)
239 return r;
240 }
241
494c868d
PF
242 return 0;
243}
26db55f3 244
302a796f 245static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
494c868d 246 int r;
73922903 247
1046bf9b
YW
248 assert(link);
249
494c868d
PF
250 r = sd_netlink_message_get_errno(m);
251 if (r < 0 && r != -EEXIST)
1046bf9b 252 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
73922903 253
0ae286e6 254 return 1;
76c3246d
PF
255}
256
494c868d 257
76c3246d
PF
258static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
259 int r;
260 sd_dhcp6_lease *lease;
494c868d 261 union in_addr_union pd_prefix;
76c3246d
PF
262 uint8_t pd_prefix_len;
263 uint32_t lifetime_preferred, lifetime_valid;
264 _cleanup_free_ char *buf = NULL;
265 Iterator i = ITERATOR_FIRST;
266
267 r = sd_dhcp6_client_get_lease(client, &lease);
268 if (r < 0)
269 return r;
270
76c3246d
PF
271 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
272
494c868d 273 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
76c3246d
PF
274 &lifetime_preferred,
275 &lifetime_valid) >= 0) {
276
277 if (pd_prefix_len > 64) {
494c868d 278 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
76c3246d
PF
279 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
280 strnull(buf), pd_prefix_len);
281 continue;
282 }
283
3ec8303f 284 if (pd_prefix_len < 48) {
494c868d 285 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
3ec8303f
PF
286 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
287 strnull(buf), pd_prefix_len);
288 }
289
494c868d
PF
290 if (pd_prefix_len < 64) {
291 Route *route = NULL;
292
293 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
294
295 r = route_new(&route);
296 if (r < 0) {
297 log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m",
298 strnull(buf),
299 pd_prefix_len);
300 continue;
301 }
302
303 route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
304 0, 0, 0, &route);
305 route_update(route, NULL, 0, NULL, NULL, 0, 0,
306 RTN_UNREACHABLE);
307
c8ee637e 308 r = route_configure(route, link, dhcp6_route_handler);
494c868d
PF
309 if (r < 0) {
310 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
311 strnull(buf),
312 pd_prefix_len);
313 route_free(route);
314 continue;
315 }
494c868d
PF
316
317 route_free(route);
318
319 log_link_debug(link, "Configuring unreachable route for %s/%u",
320 strnull(buf), pd_prefix_len);
321
322 } else
323 log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
324
325 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
76c3246d
PF
326 pd_prefix_len,
327 lifetime_preferred,
328 lifetime_valid);
329 if (r < 0 && r != -EAGAIN)
330 return r;
331
332 if (r >= 0)
333 i = ITERATOR_FIRST;
334 }
335
336 return 0;
337}
338
10752343
PF
339int dhcp6_request_prefix_delegation(Link *link) {
340 Link *l;
341 Iterator i;
342
343 assert_return(link, -EINVAL);
344 assert_return(link->manager, -EOPNOTSUPP);
345
346 if (dhcp6_get_prefix_delegation(link) <= 0)
347 return 0;
348
349 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
350
351 HASHMAP_FOREACH(l, link->manager->links, i) {
352 int r, enabled;
353
354 if (l == link)
355 continue;
356
357 if (!l->dhcp6_client)
358 continue;
359
360 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
361 if (r < 0) {
362 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
363 continue;
364 }
365
366 if (enabled == 0) {
367 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
368 if (r < 0) {
369 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
370 continue;
371 }
372 }
373
374 r = sd_dhcp6_client_is_running(l->dhcp6_client);
375 if (r <= 0)
376 continue;
377
378 if (enabled != 0) {
379 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
380 (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
381
382 continue;
383 }
384
385 r = sd_dhcp6_client_stop(l->dhcp6_client);
386 if (r < 0) {
387 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
388 continue;
389 }
390
391 r = sd_dhcp6_client_start(l->dhcp6_client);
392 if (r < 0) {
393 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
394 continue;
395 }
396
397 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
398 }
399
400 return 0;
401}
402
302a796f 403static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
c62c4628
PF
404 int r;
405
406 assert(link);
407
1c4baffc 408 r = sd_netlink_message_get_errno(m);
c62c4628 409 if (r < 0 && r != -EEXIST) {
be3a09b7
PF
410 if (link->rtnl_extended_attrs) {
411 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
412
413 link->rtnl_extended_attrs = false;
414 dhcp6_lease_address_acquired(link->dhcp6_client, link);
415
416 return 1;
417 }
418
e53fc357 419 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
c62c4628
PF
420
421 link_enter_failed(link);
422
423 } else if (r >= 0)
200a0868 424 manager_rtnl_process_address(rtnl, m, link->manager);
c62c4628
PF
425
426 return 1;
427}
428
1e7a0e21
LP
429static int dhcp6_address_change(
430 Link *link,
431 struct in6_addr *ip6_addr,
432 uint32_t lifetime_preferred,
433 uint32_t lifetime_valid) {
434
8e766630 435 _cleanup_(address_freep) Address *addr = NULL;
1e7a0e21
LP
436 char buffer[INET6_ADDRSTRLEN];
437 int r;
c62c4628 438
f0213e37 439 r = address_new(&addr);
c62c4628
PF
440 if (r < 0)
441 return r;
442
443 addr->family = AF_INET6;
444 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
851c9f82
PF
445
446 addr->flags = IFA_F_NOPREFIXROUTE;
6d8f6b0b 447 addr->prefixlen = 128;
c62c4628
PF
448
449 addr->cinfo.ifa_prefered = lifetime_preferred;
450 addr->cinfo.ifa_valid = lifetime_valid;
451
f2341e0a 452 log_link_info(link,
1e7a0e21
LP
453 "DHCPv6 address %s/%d timeout preferred %d valid %d",
454 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
f2341e0a 455 addr->prefixlen, lifetime_preferred, lifetime_valid);
c62c4628 456
66669078 457 r = address_configure(addr, link, dhcp6_address_handler, true);
c62c4628 458 if (r < 0)
f2341e0a 459 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
c62c4628
PF
460
461 return r;
462}
463
c62c4628
PF
464static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
465 int r;
466 sd_dhcp6_lease *lease;
467 struct in6_addr ip6_addr;
468 uint32_t lifetime_preferred, lifetime_valid;
c62c4628
PF
469
470 r = sd_dhcp6_client_get_lease(client, &lease);
471 if (r < 0)
472 return r;
473
474 sd_dhcp6_lease_reset_address_iter(lease);
475
476 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
483d099e
ZJS
477 &lifetime_preferred,
478 &lifetime_valid) >= 0) {
c62c4628 479
6d8f6b0b 480 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
c62c4628
PF
481 if (r < 0)
482 return r;
483 }
484
485 return 0;
486}
487
5c79bd79 488static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
c62c4628 489 int r;
5c79bd79
PF
490 Link *link = userdata;
491
492 assert(link);
493 assert(link->network);
5c79bd79
PF
494
495 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
496 return;
497
498 switch(event) {
10c9ce61
DH
499 case SD_DHCP6_CLIENT_EVENT_STOP:
500 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
501 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
3098562c
TG
502 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
503 log_link_warning(link, "DHCPv6 lease lost");
18d29550 504
494c868d 505 (void) dhcp6_lease_pd_prefix_lost(client, link);
76c3246d
PF
506 (void) manager_dhcp6_prefix_remove_all(link->manager, link);
507
18d29550 508 link->dhcp6_configured = false;
c62c4628
PF
509 break;
510
10c9ce61 511 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
c62c4628
PF
512 r = dhcp6_lease_address_acquired(client, link);
513 if (r < 0) {
514 link_enter_failed(link);
515 return;
516 }
517
76c3246d
PF
518 r = dhcp6_lease_pd_prefix_acquired(client, link);
519 if (r < 0)
520 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
521
4831981d 522 _fallthrough_;
10c9ce61 523 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
c62c4628
PF
524 r = dhcp6_lease_information_acquired(client, link);
525 if (r < 0) {
526 link_enter_failed(link);
527 return;
528 }
5c79bd79 529
18d29550 530 link->dhcp6_configured = true;
5c79bd79
PF
531 break;
532
533 default:
534 if (event < 0)
e53fc357 535 log_link_warning_errno(link, event, "DHCPv6 error: %m");
5c79bd79 536 else
e53fc357 537 log_link_warning(link, "DHCPv6 unknown event: %d", event);
5c79bd79
PF
538 return;
539 }
18d29550 540
8012cd39 541 link_check_ready(link);
5c79bd79
PF
542}
543
720bec40 544int dhcp6_request_address(Link *link, int ir) {
125f20b4 545 int r, inf_req, pd;
7a695d8e 546 bool running;
5c79bd79 547
7a695d8e
TG
548 assert(link);
549 assert(link->dhcp6_client);
125f20b4 550 assert(link->network);
720bec40 551 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
85bd849f 552
7a695d8e
TG
553 r = sd_dhcp6_client_is_running(link->dhcp6_client);
554 if (r < 0)
555 return r;
556 else
5d904a6a 557 running = r;
e6604041 558
125f20b4
PF
559 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
560 if (r < 0)
561 return r;
562
563 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
564 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
565
566 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
567 false);
568 if (r < 0 )
569 return r;
570
571 ir = false;
572 }
573
7a695d8e 574 if (running) {
720bec40
TY
575 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
576 if (r < 0)
577 return r;
578
579 if (inf_req == ir)
580 return 0;
581
7a695d8e
TG
582 r = sd_dhcp6_client_stop(link->dhcp6_client);
583 if (r < 0)
584 return r;
720bec40
TY
585 } else {
586 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
587 if (r < 0)
588 return r;
7a695d8e 589 }
85bd849f 590
720bec40
TY
591 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
592 if (r < 0)
593 return r;
594
595 r = sd_dhcp6_client_start(link->dhcp6_client);
7a695d8e
TG
596 if (r < 0)
597 return r;
85bd849f 598
7a695d8e
TG
599 return 0;
600}
18d29550 601
8006aa32
SA
602static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
603 _cleanup_free_ char *hostname = NULL;
604 const char *hn;
605 int r;
606
607 assert(link);
608
609 if (!link->network->dhcp_send_hostname)
610 hn = NULL;
611 else if (link->network->dhcp_hostname)
612 hn = link->network->dhcp_hostname;
613 else {
614 r = gethostname_strict(&hostname);
615 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
616 return r;
617
618 hn = hostname;
619 }
620
a8494759
YW
621 r = sd_dhcp6_client_set_fqdn(client, hn);
622 if (r == -EINVAL && hostname)
623 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
624 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
625 else if (r < 0)
626 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
627
628 return 0;
8006aa32
SA
629}
630
7a695d8e 631int dhcp6_configure(Link *link) {
5bad7ebd 632 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8341a5c3 633 const DUID *duid;
fb5c8216 634 int r;
7a695d8e
TG
635
636 assert(link);
fb5c8216 637 assert(link->network);
7a695d8e 638
62379e88
TG
639 if (link->dhcp6_client)
640 return 0;
641
7a695d8e 642 r = sd_dhcp6_client_new(&client);
5bad7ebd
YW
643 if (r == -ENOMEM)
644 return log_oom();
7a695d8e 645 if (r < 0)
5bad7ebd 646 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
5c79bd79 647
7a695d8e 648 r = sd_dhcp6_client_attach_event(client, NULL, 0);
5c79bd79 649 if (r < 0)
5bad7ebd 650 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
5c79bd79 651
7a695d8e 652 r = sd_dhcp6_client_set_mac(client,
5c79bd79
PF
653 (const uint8_t *) &link->mac,
654 sizeof (link->mac), ARPHRD_ETHER);
e6604041 655 if (r < 0)
5bad7ebd 656 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
5c79bd79 657
8217ed5e
TH
658 if (link->network->iaid_set) {
659 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
660 if (r < 0)
661 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
662 }
413708d1 663
f24648a6 664 duid = link_get_duid(link);
0cf7c3fd
YW
665 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
666 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
667 else
668 r = sd_dhcp6_client_set_duid(client,
669 duid->type,
670 duid->raw_data_len > 0 ? duid->raw_data : NULL,
671 duid->raw_data_len);
413708d1 672 if (r < 0)
5bad7ebd 673 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
413708d1 674
8006aa32 675 r = dhcp6_set_hostname(client, link);
8006aa32 676 if (r < 0)
a8494759 677 return r;
8006aa32 678
2f8e7633 679 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
e6604041 680 if (r < 0)
5bad7ebd 681 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
5c79bd79 682
fb5c8216
SS
683 if (link->network->rapid_commit) {
684 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
685 if (r < 0)
5bad7ebd 686 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
fb5c8216
SS
687 }
688
7a695d8e 689 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
e6604041 690 if (r < 0)
5bad7ebd 691 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
5c79bd79 692
103b81ee
PF
693 if (dhcp6_enable_prefix_delegation(link)) {
694 r = sd_dhcp6_client_set_prefix_delegation(client, true);
695 if (r < 0)
5bad7ebd 696 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
103b81ee
PF
697 }
698
5bad7ebd 699 link->dhcp6_client = TAKE_PTR(client);
e6604041 700
7a695d8e 701 return 0;
5c79bd79 702}