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