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