]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
networkd-dhcp6: Fix logging of DHCPv6 prefix
[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 "network-internal.h"
15 #include "networkd-link.h"
16 #include "networkd-manager.h"
17 #include "siphash24.h"
18 #include "string-util.h"
19 #include "radv-internal.h"
20
21 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
22
23 static bool dhcp6_verify_link(Link *link) {
24 if (!link->network) {
25 log_link_info(link, "Link is not managed by us");
26 return false;
27 }
28
29 if (!IN_SET(link->network->router_prefix_delegation,
30 RADV_PREFIX_DELEGATION_DHCP6,
31 RADV_PREFIX_DELEGATION_BOTH)) {
32 log_link_debug(link, "Link does not request DHCPv6 prefix delegation");
33 return false;
34 }
35
36 return true;
37 }
38
39 static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
40 Manager *manager;
41 Link *l;
42 Iterator i;
43
44 assert(dhcp6_link);
45
46 manager = dhcp6_link->manager;
47 assert(manager);
48
49 HASHMAP_FOREACH(l, manager->links, i) {
50 if (l == dhcp6_link)
51 continue;
52
53 if (!dhcp6_verify_link(l))
54 continue;
55
56 return true;
57 }
58
59 return false;
60 }
61
62 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
63 Link *link) {
64 return 0;
65 }
66
67 static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
68 uint8_t prefix_len,
69 uint32_t lifetime_preferred,
70 uint32_t lifetime_valid) {
71 sd_radv *radv = link->radv;
72 int r;
73 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
74
75 r = sd_radv_prefix_new(&p);
76 if (r < 0)
77 return r;
78
79 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
80 if (r < 0)
81 return r;
82
83 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
84 if (r < 0)
85 return r;
86
87 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
88 if (r < 0)
89 return r;
90
91 r = sd_radv_stop(radv);
92 if (r < 0)
93 return r;
94
95 r = sd_radv_add_prefix(radv, p, true);
96 if (r < 0 && r != -EEXIST)
97 return r;
98
99 r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
100 if (r < 0)
101 return r;
102
103 return sd_radv_start(radv);
104 }
105
106 static Network *dhcp6_reset_pd_prefix_network(Link *link) {
107 assert(link);
108 assert(link->manager);
109 assert(link->manager->networks);
110
111 return link->manager->networks;
112 }
113
114 static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
115 struct in6_addr *pd_prefix,
116 uint8_t pd_prefix_len,
117 uint32_t lifetime_preferred,
118 uint32_t lifetime_valid) {
119 Link *link;
120 Manager *manager = dhcp6_link->manager;
121 union in_addr_union prefix;
122 uint64_t n_prefixes, n_used = 0;
123 _cleanup_free_ char *buf = NULL;
124 int r;
125
126 assert(manager);
127 assert(pd_prefix_len <= 64);
128
129 prefix.in6 = *pd_prefix;
130
131 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
132 if (r < 0)
133 return r;
134
135 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
136
137 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
138 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
139 n_prefixes, strnull(buf), pd_prefix_len);
140
141 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
142 Link *assigned_link;
143
144 if (n_used == n_prefixes) {
145 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
146 n_used, n_prefixes, strnull(buf), pd_prefix_len);
147
148 return -EAGAIN;
149 }
150
151 if (link == dhcp6_link)
152 continue;
153
154 if (!dhcp6_verify_link(link))
155 continue;
156
157 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
158 if (assigned_link != NULL && assigned_link != link)
159 continue;
160
161 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
162 lifetime_preferred, lifetime_valid);
163 if (r < 0) {
164 log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m",
165 assigned_link ? "update": "assign",
166 strnull(buf), pd_prefix_len);
167
168 if (assigned_link == NULL)
169 continue;
170
171 } else
172 log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 to link",
173 n_used + 1, n_prefixes, strnull(buf));
174
175 n_used++;
176
177 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
178 if (r < 0 && n_used < n_prefixes)
179 return r;
180 }
181
182 if (n_used < n_prefixes) {
183 Route *route;
184 uint64_t n = n_used;
185
186 r = route_new(&route);
187 if (r < 0)
188 return r;
189
190 route->family = AF_INET6;
191
192 while (n < n_prefixes) {
193 route_update(route, &prefix, pd_prefix_len, NULL, NULL,
194 0, 0, RTN_UNREACHABLE);
195
196 r = route_configure(route, dhcp6_link, NULL);
197 if (r < 0) {
198 route_free(route);
199 return r;
200 }
201
202 r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
203 if (r < 0)
204 return r;
205 }
206 }
207
208 return n_used;
209 }
210
211 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
212 int r;
213 sd_dhcp6_lease *lease;
214 struct in6_addr pd_prefix;
215 uint8_t pd_prefix_len;
216 uint32_t lifetime_preferred, lifetime_valid;
217 _cleanup_free_ char *buf = NULL;
218 Iterator i = ITERATOR_FIRST;
219
220 r = sd_dhcp6_client_get_lease(client, &lease);
221 if (r < 0)
222 return r;
223
224 dhcp6_reset_pd_prefix_network(link);
225 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
226
227 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len,
228 &lifetime_preferred,
229 &lifetime_valid) >= 0) {
230
231 if (pd_prefix_len > 64) {
232 (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
233 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
234 strnull(buf), pd_prefix_len);
235 continue;
236 }
237
238 if (pd_prefix_len < 48) {
239 (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
240 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
241 strnull(buf), pd_prefix_len);
242 }
243
244 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
245 pd_prefix_len,
246 lifetime_preferred,
247 lifetime_valid);
248 if (r < 0 && r != -EAGAIN)
249 return r;
250
251 if (r >= 0)
252 i = ITERATOR_FIRST;
253 }
254
255 return 0;
256 }
257
258 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
259 void *userdata) {
260 _cleanup_(link_unrefp) Link *link = userdata;
261 int r;
262
263 assert(link);
264
265 r = sd_netlink_message_get_errno(m);
266 if (r < 0 && r != -EEXIST) {
267 if (link->rtnl_extended_attrs) {
268 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
269
270 link->rtnl_extended_attrs = false;
271 dhcp6_lease_address_acquired(link->dhcp6_client, link);
272
273 return 1;
274 }
275
276 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
277
278 link_enter_failed(link);
279
280 } else if (r >= 0)
281 manager_rtnl_process_address(rtnl, m, link->manager);
282
283 return 1;
284 }
285
286 static int dhcp6_address_change(
287 Link *link,
288 struct in6_addr *ip6_addr,
289 uint32_t lifetime_preferred,
290 uint32_t lifetime_valid) {
291
292 _cleanup_(address_freep) Address *addr = NULL;
293 char buffer[INET6_ADDRSTRLEN];
294 int r;
295
296 r = address_new(&addr);
297 if (r < 0)
298 return r;
299
300 addr->family = AF_INET6;
301 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
302
303 addr->flags = IFA_F_NOPREFIXROUTE;
304 addr->prefixlen = 128;
305
306 addr->cinfo.ifa_prefered = lifetime_preferred;
307 addr->cinfo.ifa_valid = lifetime_valid;
308
309 log_link_info(link,
310 "DHCPv6 address %s/%d timeout preferred %d valid %d",
311 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
312 addr->prefixlen, lifetime_preferred, lifetime_valid);
313
314 r = address_configure(addr, link, dhcp6_address_handler, true);
315 if (r < 0)
316 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
317
318 return r;
319 }
320
321 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
322 int r;
323 sd_dhcp6_lease *lease;
324 struct in6_addr ip6_addr;
325 uint32_t lifetime_preferred, lifetime_valid;
326
327 r = sd_dhcp6_client_get_lease(client, &lease);
328 if (r < 0)
329 return r;
330
331 sd_dhcp6_lease_reset_address_iter(lease);
332
333 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
334 &lifetime_preferred,
335 &lifetime_valid) >= 0) {
336
337 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
338 if (r < 0)
339 return r;
340 }
341
342 return 0;
343 }
344
345 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
346 int r;
347 Link *link = userdata;
348
349 assert(link);
350 assert(link->network);
351
352 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
353 return;
354
355 switch(event) {
356 case SD_DHCP6_CLIENT_EVENT_STOP:
357 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
358 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
359 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
360 log_link_warning(link, "DHCPv6 lease lost");
361
362 (void) manager_dhcp6_prefix_remove_all(link->manager, link);
363
364 link->dhcp6_configured = false;
365 break;
366
367 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
368 r = dhcp6_lease_address_acquired(client, link);
369 if (r < 0) {
370 link_enter_failed(link);
371 return;
372 }
373
374 r = dhcp6_lease_pd_prefix_acquired(client, link);
375 if (r < 0)
376 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
377
378 _fallthrough_;
379 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
380 r = dhcp6_lease_information_acquired(client, link);
381 if (r < 0) {
382 link_enter_failed(link);
383 return;
384 }
385
386 link->dhcp6_configured = true;
387 break;
388
389 default:
390 if (event < 0)
391 log_link_warning_errno(link, event, "DHCPv6 error: %m");
392 else
393 log_link_warning(link, "DHCPv6 unknown event: %d", event);
394 return;
395 }
396
397 link_check_ready(link);
398 }
399
400 int dhcp6_request_address(Link *link, int ir) {
401 int r, inf_req;
402 bool running;
403
404 assert(link);
405 assert(link->dhcp6_client);
406 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
407
408 r = sd_dhcp6_client_is_running(link->dhcp6_client);
409 if (r < 0)
410 return r;
411 else
412 running = r;
413
414 if (running) {
415 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
416 if (r < 0)
417 return r;
418
419 if (inf_req == ir)
420 return 0;
421
422 r = sd_dhcp6_client_stop(link->dhcp6_client);
423 if (r < 0)
424 return r;
425 } else {
426 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
427 if (r < 0)
428 return r;
429 }
430
431 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
432 if (r < 0)
433 return r;
434
435 r = sd_dhcp6_client_start(link->dhcp6_client);
436 if (r < 0)
437 return r;
438
439 return 0;
440 }
441
442 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
443 _cleanup_free_ char *hostname = NULL;
444 const char *hn;
445 int r;
446
447 assert(link);
448
449 if (!link->network->dhcp_send_hostname)
450 hn = NULL;
451 else if (link->network->dhcp_hostname)
452 hn = link->network->dhcp_hostname;
453 else {
454 r = gethostname_strict(&hostname);
455 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
456 return r;
457
458 hn = hostname;
459 }
460
461 r = sd_dhcp6_client_set_fqdn(client, hn);
462 if (r == -EINVAL && hostname)
463 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
464 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
465 else if (r < 0)
466 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
467
468 return 0;
469 }
470
471 int dhcp6_configure(Link *link) {
472 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
473 const DUID *duid;
474 int r;
475
476 assert(link);
477 assert(link->network);
478
479 if (link->dhcp6_client)
480 return 0;
481
482 r = sd_dhcp6_client_new(&client);
483 if (r == -ENOMEM)
484 return log_oom();
485 if (r < 0)
486 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
487
488 r = sd_dhcp6_client_attach_event(client, NULL, 0);
489 if (r < 0)
490 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
491
492 r = sd_dhcp6_client_set_mac(client,
493 (const uint8_t *) &link->mac,
494 sizeof (link->mac), ARPHRD_ETHER);
495 if (r < 0)
496 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
497
498 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
499 if (r < 0)
500 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
501
502 duid = link_get_duid(link);
503 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
504 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
505 else
506 r = sd_dhcp6_client_set_duid(client,
507 duid->type,
508 duid->raw_data_len > 0 ? duid->raw_data : NULL,
509 duid->raw_data_len);
510 if (r < 0)
511 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
512
513 r = dhcp6_set_hostname(client, link);
514 if (r < 0)
515 return r;
516
517 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
518 if (r < 0)
519 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
520
521 if (link->network->rapid_commit) {
522 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
523 if (r < 0)
524 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
525 }
526
527 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
528 if (r < 0)
529 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
530
531 if (dhcp6_enable_prefix_delegation(link)) {
532 r = sd_dhcp6_client_set_prefix_delegation(client, true);
533 if (r < 0)
534 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
535 }
536
537 link->dhcp6_client = TAKE_PTR(client);
538
539 return 0;
540 }