Branch data 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 : : }
|