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