]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
network: fix memleak and double free
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <netinet/ether.h>
7 #include <linux/if.h>
8 #include "sd-radv.h"
9
10 #include "sd-dhcp6-client.h"
11
12 #include "hashmap.h"
13 #include "hostname-util.h"
14 #include "missing_network.h"
15 #include "network-internal.h"
16 #include "networkd-link.h"
17 #include "networkd-manager.h"
18 #include "siphash24.h"
19 #include "string-util.h"
20 #include "radv-internal.h"
21
22 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
23
24 static bool dhcp6_get_prefix_delegation(Link *link) {
25 if (!link->network)
26 return false;
27
28 return IN_SET(link->network->router_prefix_delegation,
29 RADV_PREFIX_DELEGATION_DHCP6,
30 RADV_PREFIX_DELEGATION_BOTH);
31 }
32
33 static 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
47 if (!dhcp6_get_prefix_delegation(l))
48 continue;
49
50 return true;
51 }
52
53 return false;
54 }
55
56 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
57 Link *link) {
58 return 0;
59 }
60
61 static 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
93 r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
94 if (r < 0)
95 return r;
96
97 return sd_radv_start(radv);
98 }
99
100 static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
101 int r;
102
103 assert(link);
104
105 r = sd_netlink_message_get_errno(m);
106 if (r < 0)
107 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
108
109 return 1;
110 }
111
112 int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
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
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;
129 Route *route;
130
131 if (pd_prefix_len >= 64)
132 continue;
133
134 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
135
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 }
143
144 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
145
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;
152 }
153
154 log_link_debug(link, "Removing unreachable route %s/%u",
155 strnull(buf), pd_prefix_len);
156 }
157
158 return 0;
159 }
160
161 static 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;
169 uint64_t n_prefixes, n_used = 0;
170 _cleanup_free_ char *buf = NULL;
171 _cleanup_free_ char *assigned_buf = NULL;
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
183 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
184
185 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
186 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
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) {
193 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
194 n_used, n_prefixes, strnull(buf), pd_prefix_len);
195
196 return -EAGAIN;
197 }
198
199 if (link == dhcp6_link)
200 continue;
201
202 if (!dhcp6_get_prefix_delegation(link))
203 continue;
204
205 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
206 if (assigned_link != NULL && assigned_link != link)
207 continue;
208
209 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
210 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
211 lifetime_preferred, lifetime_valid);
212 if (r < 0) {
213 log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
214 assigned_link ? "update": "assign",
215 strnull(assigned_buf),
216 strnull(buf), pd_prefix_len);
217
218 if (!assigned_link)
219 continue;
220
221 } else
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);
226
227 n_used++;
228
229 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
230 if (r < 0 && n_used < n_prefixes)
231 return r;
232 }
233
234 return 0;
235 }
236
237 static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
238 int r;
239
240 assert(link);
241
242 r = sd_netlink_message_get_errno(m);
243 if (r < 0 && r != -EEXIST)
244 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
245
246 return 1;
247 }
248
249 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
250 int r;
251 sd_dhcp6_lease *lease;
252 union in_addr_union pd_prefix;
253 uint8_t pd_prefix_len;
254 uint32_t lifetime_preferred, lifetime_valid;
255 Iterator i = ITERATOR_FIRST;
256
257 r = sd_dhcp6_client_get_lease(client, &lease);
258 if (r < 0)
259 return r;
260
261 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
262
263 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
264 &lifetime_preferred,
265 &lifetime_valid) >= 0) {
266
267 _cleanup_free_ char *buf = NULL;
268
269 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
270
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
277 if (pd_prefix_len < 48)
278 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
279 strnull(buf), pd_prefix_len);
280
281 if (pd_prefix_len < 64) {
282 uint32_t table;
283 Route *route;
284
285 table = link_get_dhcp_route_table(link);
286
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);
296
297 r = route_configure(route, link, dhcp6_route_handler);
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);
302 continue;
303 }
304
305 log_link_debug(link, "Configuring unreachable route for %s/%u",
306 strnull(buf), pd_prefix_len);
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,
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
324 int 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
388 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
389 int r;
390
391 assert(link);
392
393 r = sd_netlink_message_get_errno(m);
394 if (r < 0 && r != -EEXIST) {
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
404 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
405
406 link_enter_failed(link);
407
408 } else if (r >= 0)
409 manager_rtnl_process_address(rtnl, m, link->manager);
410
411 return 1;
412 }
413
414 static int dhcp6_address_change(
415 Link *link,
416 struct in6_addr *ip6_addr,
417 uint32_t lifetime_preferred,
418 uint32_t lifetime_valid) {
419
420 _cleanup_(address_freep) Address *addr = NULL;
421 char buffer[INET6_ADDRSTRLEN];
422 int r;
423
424 r = address_new(&addr);
425 if (r < 0)
426 return r;
427
428 addr->family = AF_INET6;
429 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
430
431 addr->flags = IFA_F_NOPREFIXROUTE;
432 addr->prefixlen = 128;
433
434 addr->cinfo.ifa_prefered = lifetime_preferred;
435 addr->cinfo.ifa_valid = lifetime_valid;
436
437 log_link_info(link,
438 "DHCPv6 address %s/%d timeout preferred %d valid %d",
439 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
440 addr->prefixlen, lifetime_preferred, lifetime_valid);
441
442 r = address_configure(addr, link, dhcp6_address_handler, true);
443 if (r < 0)
444 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
445
446 return r;
447 }
448
449 static 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;
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,
462 &lifetime_preferred,
463 &lifetime_valid) >= 0) {
464
465 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
466 if (r < 0)
467 return r;
468 }
469
470 return 0;
471 }
472
473 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
474 int r;
475 Link *link = userdata;
476
477 assert(link);
478 assert(link->network);
479
480 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
481 return;
482
483 switch(event) {
484 case SD_DHCP6_CLIENT_EVENT_STOP:
485 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
486 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
487 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
488 log_link_warning(link, "DHCPv6 lease lost");
489
490 (void) dhcp6_lease_pd_prefix_lost(client, link);
491 (void) manager_dhcp6_prefix_remove_all(link->manager, link);
492
493 link->dhcp6_configured = false;
494 break;
495
496 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
497 r = dhcp6_lease_address_acquired(client, link);
498 if (r < 0) {
499 link_enter_failed(link);
500 return;
501 }
502
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
507 _fallthrough_;
508 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
509 r = dhcp6_lease_information_acquired(client, link);
510 if (r < 0) {
511 link_enter_failed(link);
512 return;
513 }
514
515 link->dhcp6_configured = true;
516 break;
517
518 default:
519 if (event < 0)
520 log_link_warning_errno(link, event, "DHCPv6 error: %m");
521 else
522 log_link_warning(link, "DHCPv6 unknown event: %d", event);
523 return;
524 }
525
526 link_check_ready(link);
527 }
528
529 int dhcp6_request_address(Link *link, int ir) {
530 int r, inf_req, pd;
531 bool running;
532
533 assert(link);
534 assert(link->dhcp6_client);
535 assert(link->network);
536 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
537
538 r = sd_dhcp6_client_is_running(link->dhcp6_client);
539 if (r < 0)
540 return r;
541 else
542 running = r;
543
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
559 if (running) {
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
567 r = sd_dhcp6_client_stop(link->dhcp6_client);
568 if (r < 0)
569 return r;
570 } else {
571 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
572 if (r < 0)
573 return r;
574 }
575
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);
581 if (r < 0)
582 return r;
583
584 return 0;
585 }
586
587 static 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
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;
614 }
615
616 int dhcp6_configure(Link *link) {
617 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
618 const DUID *duid;
619 int r;
620
621 assert(link);
622 assert(link->network);
623
624 if (link->dhcp6_client)
625 return 0;
626
627 r = sd_dhcp6_client_new(&client);
628 if (r == -ENOMEM)
629 return log_oom();
630 if (r < 0)
631 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
632
633 r = sd_dhcp6_client_attach_event(client, NULL, 0);
634 if (r < 0)
635 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
636
637 r = sd_dhcp6_client_set_mac(client,
638 (const uint8_t *) &link->mac,
639 sizeof (link->mac), ARPHRD_ETHER);
640 if (r < 0)
641 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
642
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 }
648
649 duid = link_get_duid(link);
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);
657 if (r < 0)
658 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
659
660 r = dhcp6_set_hostname(client, link);
661 if (r < 0)
662 return r;
663
664 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
665 if (r < 0)
666 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
667
668 if (link->network->rapid_commit) {
669 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
670 if (r < 0)
671 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
672 }
673
674 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
675 if (r < 0)
676 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
677
678 if (dhcp6_enable_prefix_delegation(link)) {
679 r = sd_dhcp6_client_set_prefix_delegation(client, true);
680 if (r < 0)
681 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
682 }
683
684 link->dhcp6_client = TAKE_PTR(client);
685
686 return 0;
687 }