/src/systemd/src/network/networkd-dhcp6.c
Line | Count | Source (jump to first uncovered line) |
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 "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 | 0 | static bool dhcp6_get_prefix_delegation(Link *link) { |
25 | 0 | if (!link->network) |
26 | 0 | return false; |
27 | 0 | |
28 | 0 | return IN_SET(link->network->router_prefix_delegation, |
29 | 0 | RADV_PREFIX_DELEGATION_DHCP6, |
30 | 0 | RADV_PREFIX_DELEGATION_BOTH); |
31 | 0 | } |
32 | | |
33 | 0 | static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { |
34 | 0 | Manager *manager; |
35 | 0 | Link *l; |
36 | 0 | Iterator i; |
37 | 0 |
|
38 | 0 | assert(dhcp6_link); |
39 | 0 |
|
40 | 0 | manager = dhcp6_link->manager; |
41 | 0 | assert(manager); |
42 | 0 |
|
43 | 0 | HASHMAP_FOREACH(l, manager->links, i) { |
44 | 0 | if (l == dhcp6_link) |
45 | 0 | continue; |
46 | 0 | |
47 | 0 | if (!dhcp6_get_prefix_delegation(l)) |
48 | 0 | continue; |
49 | 0 | |
50 | 0 | return true; |
51 | 0 | } |
52 | 0 |
|
53 | 0 | return false; |
54 | 0 | } |
55 | | |
56 | | static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, |
57 | 0 | Link *link) { |
58 | 0 | return 0; |
59 | 0 | } |
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 | 0 | uint32_t lifetime_valid) { |
65 | 0 | sd_radv *radv = link->radv; |
66 | 0 | int r; |
67 | 0 | _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; |
68 | 0 |
|
69 | 0 | r = sd_radv_prefix_new(&p); |
70 | 0 | if (r < 0) |
71 | 0 | return r; |
72 | 0 | |
73 | 0 | r = sd_radv_prefix_set_prefix(p, prefix, prefix_len); |
74 | 0 | if (r < 0) |
75 | 0 | return r; |
76 | 0 | |
77 | 0 | r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred); |
78 | 0 | if (r < 0) |
79 | 0 | return r; |
80 | 0 | |
81 | 0 | r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid); |
82 | 0 | if (r < 0) |
83 | 0 | return r; |
84 | 0 | |
85 | 0 | r = sd_radv_stop(radv); |
86 | 0 | if (r < 0) |
87 | 0 | return r; |
88 | 0 | |
89 | 0 | r = sd_radv_add_prefix(radv, p, true); |
90 | 0 | if (r < 0 && r != -EEXIST) |
91 | 0 | return r; |
92 | 0 | |
93 | 0 | r = manager_dhcp6_prefix_add(link->manager, prefix, link); |
94 | 0 | if (r < 0) |
95 | 0 | return r; |
96 | 0 | |
97 | 0 | return sd_radv_start(radv); |
98 | 0 | } |
99 | | |
100 | 0 | static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { |
101 | 0 | int r; |
102 | 0 |
|
103 | 0 | assert(link); |
104 | 0 |
|
105 | 0 | r = sd_netlink_message_get_errno(m); |
106 | 0 | if (r < 0) |
107 | 0 | log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m"); |
108 | 0 |
|
109 | 0 | return 1; |
110 | 0 | } |
111 | | |
112 | 0 | int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { |
113 | 0 | int r; |
114 | 0 | sd_dhcp6_lease *lease; |
115 | 0 | union in_addr_union pd_prefix; |
116 | 0 | uint8_t pd_prefix_len; |
117 | 0 | uint32_t lifetime_preferred, lifetime_valid; |
118 | 0 |
|
119 | 0 | r = sd_dhcp6_client_get_lease(client, &lease); |
120 | 0 | if (r < 0) |
121 | 0 | return r; |
122 | 0 | |
123 | 0 | sd_dhcp6_lease_reset_pd_prefix_iter(lease); |
124 | 0 |
|
125 | 0 | while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, |
126 | 0 | &lifetime_preferred, |
127 | 0 | &lifetime_valid) >= 0) { |
128 | 0 | _cleanup_free_ char *buf = NULL; |
129 | 0 | Route *route; |
130 | 0 |
|
131 | 0 | if (pd_prefix_len >= 64) |
132 | 0 | continue; |
133 | 0 | |
134 | 0 | (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); |
135 | 0 |
|
136 | 0 | r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route); |
137 | 0 | if (r < 0) { |
138 | 0 | log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m", |
139 | 0 | strnull(buf), |
140 | 0 | pd_prefix_len); |
141 | 0 | continue; |
142 | 0 | } |
143 | 0 |
|
144 | 0 | route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE); |
145 | 0 |
|
146 | 0 | r = route_remove(route, link, dhcp6_route_remove_handler); |
147 | 0 | if (r < 0) { |
148 | 0 | log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m", |
149 | 0 | strnull(buf), |
150 | 0 | pd_prefix_len); |
151 | 0 | continue; |
152 | 0 | } |
153 | 0 |
|
154 | 0 | log_link_debug(link, "Removing unreachable route %s/%u", |
155 | 0 | strnull(buf), pd_prefix_len); |
156 | 0 | } |
157 | 0 |
|
158 | 0 | return 0; |
159 | 0 | } |
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 | 0 | uint32_t lifetime_valid) { |
166 | 0 | Link *link; |
167 | 0 | Manager *manager = dhcp6_link->manager; |
168 | 0 | union in_addr_union prefix; |
169 | 0 | uint64_t n_prefixes, n_used = 0; |
170 | 0 | _cleanup_free_ char *buf = NULL; |
171 | 0 | _cleanup_free_ char *assigned_buf = NULL; |
172 | 0 | int r; |
173 | 0 |
|
174 | 0 | assert(manager); |
175 | 0 | assert(pd_prefix_len <= 64); |
176 | 0 |
|
177 | 0 | prefix.in6 = *pd_prefix; |
178 | 0 |
|
179 | 0 | r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); |
180 | 0 | if (r < 0) |
181 | 0 | return r; |
182 | 0 | |
183 | 0 | n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); |
184 | 0 |
|
185 | 0 | (void) in_addr_to_string(AF_INET6, &prefix, &buf); |
186 | 0 | log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u", |
187 | 0 | n_prefixes, strnull(buf), pd_prefix_len); |
188 | 0 |
|
189 | 0 | while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) { |
190 | 0 | Link *assigned_link; |
191 | 0 |
|
192 | 0 | if (n_used == n_prefixes) { |
193 | 0 | log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u", |
194 | 0 | n_used, n_prefixes, strnull(buf), pd_prefix_len); |
195 | 0 |
|
196 | 0 | return -EAGAIN; |
197 | 0 | } |
198 | 0 |
|
199 | 0 | if (link == dhcp6_link) |
200 | 0 | continue; |
201 | 0 | |
202 | 0 | if (!dhcp6_get_prefix_delegation(link)) |
203 | 0 | continue; |
204 | 0 | |
205 | 0 | assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6); |
206 | 0 | if (assigned_link && assigned_link != link) |
207 | 0 | continue; |
208 | 0 | |
209 | 0 | (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); |
210 | 0 | r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64, |
211 | 0 | lifetime_preferred, lifetime_valid); |
212 | 0 | if (r < 0) { |
213 | 0 | log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m", |
214 | 0 | assigned_link ? "update": "assign", |
215 | 0 | strnull(assigned_buf), |
216 | 0 | strnull(buf), pd_prefix_len); |
217 | 0 |
|
218 | 0 | if (!assigned_link) |
219 | 0 | continue; |
220 | 0 | |
221 | 0 | } else |
222 | 0 | log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link", |
223 | 0 | n_used + 1, n_prefixes, |
224 | 0 | strnull(assigned_buf), |
225 | 0 | strnull(buf), pd_prefix_len); |
226 | 0 |
|
227 | 0 | n_used++; |
228 | 0 |
|
229 | 0 | r = in_addr_prefix_next(AF_INET6, &prefix, 64); |
230 | 0 | if (r < 0 && n_used < n_prefixes) |
231 | 0 | return r; |
232 | 0 | } |
233 | 0 |
|
234 | 0 | return 0; |
235 | 0 | } |
236 | | |
237 | 0 | static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { |
238 | 0 | int r; |
239 | 0 |
|
240 | 0 | assert(link); |
241 | 0 |
|
242 | 0 | r = sd_netlink_message_get_errno(m); |
243 | 0 | if (r < 0 && r != -EEXIST) |
244 | 0 | log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m"); |
245 | 0 |
|
246 | 0 | return 1; |
247 | 0 | } |
248 | | |
249 | 0 | static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { |
250 | 0 | int r; |
251 | 0 | sd_dhcp6_lease *lease; |
252 | 0 | union in_addr_union pd_prefix; |
253 | 0 | uint8_t pd_prefix_len; |
254 | 0 | uint32_t lifetime_preferred, lifetime_valid; |
255 | 0 | Iterator i = ITERATOR_FIRST; |
256 | 0 |
|
257 | 0 | r = sd_dhcp6_client_get_lease(client, &lease); |
258 | 0 | if (r < 0) |
259 | 0 | return r; |
260 | 0 | |
261 | 0 | sd_dhcp6_lease_reset_pd_prefix_iter(lease); |
262 | 0 |
|
263 | 0 | while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, |
264 | 0 | &lifetime_preferred, |
265 | 0 | &lifetime_valid) >= 0) { |
266 | 0 |
|
267 | 0 | _cleanup_free_ char *buf = NULL; |
268 | 0 |
|
269 | 0 | (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); |
270 | 0 |
|
271 | 0 | if (pd_prefix_len > 64) { |
272 | 0 | log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", |
273 | 0 | strnull(buf), pd_prefix_len); |
274 | 0 | continue; |
275 | 0 | } |
276 | 0 |
|
277 | 0 | if (pd_prefix_len < 48) |
278 | 0 | log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", |
279 | 0 | strnull(buf), pd_prefix_len); |
280 | 0 |
|
281 | 0 | if (pd_prefix_len < 64) { |
282 | 0 | uint32_t table; |
283 | 0 | Route *route; |
284 | 0 |
|
285 | 0 | table = link_get_dhcp_route_table(link); |
286 | 0 |
|
287 | 0 | r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, table, &route); |
288 | 0 | if (r < 0) { |
289 | 0 | log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m", |
290 | 0 | strnull(buf), |
291 | 0 | pd_prefix_len); |
292 | 0 | continue; |
293 | 0 | } |
294 | 0 |
|
295 | 0 | route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE); |
296 | 0 |
|
297 | 0 | r = route_configure(route, link, dhcp6_route_handler); |
298 | 0 | if (r < 0) { |
299 | 0 | log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m", |
300 | 0 | strnull(buf), |
301 | 0 | pd_prefix_len); |
302 | 0 | continue; |
303 | 0 | } |
304 | 0 |
|
305 | 0 | log_link_debug(link, "Configuring unreachable route for %s/%u", |
306 | 0 | strnull(buf), pd_prefix_len); |
307 | 0 | } else |
308 | 0 | log_link_debug(link, "Not adding a blocking route since distributed prefix is /64"); |
309 | 0 |
|
310 | 0 | r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6, |
311 | 0 | pd_prefix_len, |
312 | 0 | lifetime_preferred, |
313 | 0 | lifetime_valid); |
314 | 0 | if (r < 0 && r != -EAGAIN) |
315 | 0 | return r; |
316 | 0 | |
317 | 0 | if (r >= 0) |
318 | 0 | i = ITERATOR_FIRST; |
319 | 0 | } |
320 | 0 |
|
321 | 0 | return 0; |
322 | 0 | } |
323 | | |
324 | 0 | int dhcp6_request_prefix_delegation(Link *link) { |
325 | 0 | Link *l; |
326 | 0 | Iterator i; |
327 | 0 |
|
328 | 0 | assert_return(link, -EINVAL); |
329 | 0 | assert_return(link->manager, -EOPNOTSUPP); |
330 | 0 |
|
331 | 0 | if (dhcp6_get_prefix_delegation(link) <= 0) |
332 | 0 | return 0; |
333 | 0 | |
334 | 0 | log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); |
335 | 0 |
|
336 | 0 | HASHMAP_FOREACH(l, link->manager->links, i) { |
337 | 0 | int r, enabled; |
338 | 0 |
|
339 | 0 | if (l == link) |
340 | 0 | continue; |
341 | 0 | |
342 | 0 | if (!l->dhcp6_client) |
343 | 0 | continue; |
344 | 0 | |
345 | 0 | r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled); |
346 | 0 | if (r < 0) { |
347 | 0 | log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link"); |
348 | 0 | continue; |
349 | 0 | } |
350 | 0 |
|
351 | 0 | if (enabled == 0) { |
352 | 0 | r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1); |
353 | 0 | if (r < 0) { |
354 | 0 | log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link"); |
355 | 0 | continue; |
356 | 0 | } |
357 | 0 | } |
358 | 0 |
|
359 | 0 | r = sd_dhcp6_client_is_running(l->dhcp6_client); |
360 | 0 | if (r <= 0) |
361 | 0 | continue; |
362 | 0 | |
363 | 0 | if (enabled != 0) { |
364 | 0 | log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link"); |
365 | 0 | (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l); |
366 | 0 |
|
367 | 0 | continue; |
368 | 0 | } |
369 | 0 |
|
370 | 0 | r = sd_dhcp6_client_stop(l->dhcp6_client); |
371 | 0 | if (r < 0) { |
372 | 0 | log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link"); |
373 | 0 | continue; |
374 | 0 | } |
375 | 0 |
|
376 | 0 | r = sd_dhcp6_client_start(l->dhcp6_client); |
377 | 0 | if (r < 0) { |
378 | 0 | log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link"); |
379 | 0 | continue; |
380 | 0 | } |
381 | 0 |
|
382 | 0 | log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link"); |
383 | 0 | } |
384 | 0 |
|
385 | 0 | return 0; |
386 | 0 | } |
387 | | |
388 | 0 | static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
389 | 0 | int r; |
390 | 0 |
|
391 | 0 | assert(link); |
392 | 0 |
|
393 | 0 | r = sd_netlink_message_get_errno(m); |
394 | 0 | if (r < 0 && r != -EEXIST) { |
395 | 0 | if (link->rtnl_extended_attrs) { |
396 | 0 | log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism"); |
397 | 0 |
|
398 | 0 | link->rtnl_extended_attrs = false; |
399 | 0 | dhcp6_lease_address_acquired(link->dhcp6_client, link); |
400 | 0 |
|
401 | 0 | return 1; |
402 | 0 | } |
403 | 0 |
|
404 | 0 | log_link_error_errno(link, r, "Could not set DHCPv6 address: %m"); |
405 | 0 |
|
406 | 0 | link_enter_failed(link); |
407 | 0 |
|
408 | 0 | } else if (r >= 0) |
409 | 0 | manager_rtnl_process_address(rtnl, m, link->manager); |
410 | 0 |
|
411 | 0 | return 1; |
412 | 0 | } |
413 | | |
414 | | static int dhcp6_address_change( |
415 | | Link *link, |
416 | | struct in6_addr *ip6_addr, |
417 | | uint32_t lifetime_preferred, |
418 | 0 | uint32_t lifetime_valid) { |
419 | 0 |
|
420 | 0 | _cleanup_(address_freep) Address *addr = NULL; |
421 | 0 | _cleanup_free_ char *buffer = NULL; |
422 | 0 | int r; |
423 | 0 |
|
424 | 0 | r = address_new(&addr); |
425 | 0 | if (r < 0) |
426 | 0 | return r; |
427 | 0 | |
428 | 0 | addr->family = AF_INET6; |
429 | 0 | addr->in_addr.in6 = *ip6_addr; |
430 | 0 |
|
431 | 0 | addr->flags = IFA_F_NOPREFIXROUTE; |
432 | 0 | addr->prefixlen = 128; |
433 | 0 |
|
434 | 0 | addr->cinfo.ifa_prefered = lifetime_preferred; |
435 | 0 | addr->cinfo.ifa_valid = lifetime_valid; |
436 | 0 |
|
437 | 0 | (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer); |
438 | 0 | log_link_info(link, |
439 | 0 | "DHCPv6 address %s/%d timeout preferred %d valid %d", |
440 | 0 | strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); |
441 | 0 |
|
442 | 0 | r = address_configure(addr, link, dhcp6_address_handler, true); |
443 | 0 | if (r < 0) |
444 | 0 | log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); |
445 | 0 |
|
446 | 0 | return r; |
447 | 0 | } |
448 | | |
449 | 0 | static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { |
450 | 0 | int r; |
451 | 0 | sd_dhcp6_lease *lease; |
452 | 0 | struct in6_addr ip6_addr; |
453 | 0 | uint32_t lifetime_preferred, lifetime_valid; |
454 | 0 |
|
455 | 0 | r = sd_dhcp6_client_get_lease(client, &lease); |
456 | 0 | if (r < 0) |
457 | 0 | return r; |
458 | 0 | |
459 | 0 | sd_dhcp6_lease_reset_address_iter(lease); |
460 | 0 |
|
461 | 0 | while (sd_dhcp6_lease_get_address(lease, &ip6_addr, |
462 | 0 | &lifetime_preferred, |
463 | 0 | &lifetime_valid) >= 0) { |
464 | 0 |
|
465 | 0 | r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); |
466 | 0 | if (r < 0) |
467 | 0 | return r; |
468 | 0 | } |
469 | 0 |
|
470 | 0 | return 0; |
471 | 0 | } |
472 | | |
473 | 0 | static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { |
474 | 0 | int r; |
475 | 0 | Link *link = userdata; |
476 | 0 |
|
477 | 0 | assert(link); |
478 | 0 | assert(link->network); |
479 | 0 |
|
480 | 0 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) |
481 | 0 | return; |
482 | 0 | |
483 | 0 | switch(event) { |
484 | 0 | case SD_DHCP6_CLIENT_EVENT_STOP: |
485 | 0 | case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: |
486 | 0 | case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: |
487 | 0 | if (sd_dhcp6_client_get_lease(client, NULL) >= 0) |
488 | 0 | log_link_warning(link, "DHCPv6 lease lost"); |
489 | 0 |
|
490 | 0 | (void) dhcp6_lease_pd_prefix_lost(client, link); |
491 | 0 | (void) manager_dhcp6_prefix_remove_all(link->manager, link); |
492 | 0 |
|
493 | 0 | link->dhcp6_configured = false; |
494 | 0 | break; |
495 | 0 |
|
496 | 0 | case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: |
497 | 0 | r = dhcp6_lease_address_acquired(client, link); |
498 | 0 | if (r < 0) { |
499 | 0 | link_enter_failed(link); |
500 | 0 | return; |
501 | 0 | } |
502 | 0 | |
503 | 0 | r = dhcp6_lease_pd_prefix_acquired(client, link); |
504 | 0 | if (r < 0) |
505 | 0 | log_link_debug(link, "DHCPv6 did not receive prefixes to delegate"); |
506 | 0 |
|
507 | 0 | _fallthrough_; |
508 | 0 | case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: |
509 | 0 | r = dhcp6_lease_information_acquired(client, link); |
510 | 0 | if (r < 0) { |
511 | 0 | link_enter_failed(link); |
512 | 0 | return; |
513 | 0 | } |
514 | 0 | |
515 | 0 | link->dhcp6_configured = true; |
516 | 0 | break; |
517 | 0 |
|
518 | 0 | default: |
519 | 0 | if (event < 0) |
520 | 0 | log_link_warning_errno(link, event, "DHCPv6 error: %m"); |
521 | 0 | else |
522 | 0 | log_link_warning(link, "DHCPv6 unknown event: %d", event); |
523 | 0 | return; |
524 | 0 | } |
525 | 0 |
|
526 | 0 | link_check_ready(link); |
527 | 0 | } |
528 | | |
529 | 0 | int dhcp6_request_address(Link *link, int ir) { |
530 | 0 | int r, inf_req, pd; |
531 | 0 | bool running; |
532 | 0 |
|
533 | 0 | assert(link); |
534 | 0 | assert(link->dhcp6_client); |
535 | 0 | assert(link->network); |
536 | 0 | assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); |
537 | 0 |
|
538 | 0 | r = sd_dhcp6_client_is_running(link->dhcp6_client); |
539 | 0 | if (r < 0) |
540 | 0 | return r; |
541 | 0 | else |
542 | 0 | running = r; |
543 | 0 |
|
544 | 0 | r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd); |
545 | 0 | if (r < 0) |
546 | 0 | return r; |
547 | 0 | |
548 | 0 | if (pd && ir && link->network->dhcp6_force_pd_other_information) { |
549 | 0 | log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set"); |
550 | 0 |
|
551 | 0 | r = sd_dhcp6_client_set_address_request(link->dhcp6_client, |
552 | 0 | false); |
553 | 0 | if (r < 0 ) |
554 | 0 | return r; |
555 | 0 | |
556 | 0 | ir = false; |
557 | 0 | } |
558 | 0 |
|
559 | 0 | if (running) { |
560 | 0 | r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); |
561 | 0 | if (r < 0) |
562 | 0 | return r; |
563 | 0 | |
564 | 0 | if (inf_req == ir) |
565 | 0 | return 0; |
566 | 0 | |
567 | 0 | r = sd_dhcp6_client_stop(link->dhcp6_client); |
568 | 0 | if (r < 0) |
569 | 0 | return r; |
570 | 0 | } else { |
571 | 0 | r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); |
572 | 0 | if (r < 0) |
573 | 0 | return r; |
574 | 0 | } |
575 | 0 | |
576 | 0 | r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir); |
577 | 0 | if (r < 0) |
578 | 0 | return r; |
579 | 0 | |
580 | 0 | r = sd_dhcp6_client_start(link->dhcp6_client); |
581 | 0 | if (r < 0) |
582 | 0 | return r; |
583 | 0 | |
584 | 0 | return 0; |
585 | 0 | } |
586 | | |
587 | 0 | static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { |
588 | 0 | _cleanup_free_ char *hostname = NULL; |
589 | 0 | const char *hn; |
590 | 0 | int r; |
591 | 0 |
|
592 | 0 | assert(link); |
593 | 0 |
|
594 | 0 | if (!link->network->dhcp_send_hostname) |
595 | 0 | hn = NULL; |
596 | 0 | else if (link->network->dhcp_hostname) |
597 | 0 | hn = link->network->dhcp_hostname; |
598 | 0 | else { |
599 | 0 | r = gethostname_strict(&hostname); |
600 | 0 | if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */ |
601 | 0 | return r; |
602 | 0 | |
603 | 0 | hn = hostname; |
604 | 0 | } |
605 | 0 |
|
606 | 0 | r = sd_dhcp6_client_set_fqdn(client, hn); |
607 | 0 | if (r == -EINVAL && hostname) |
608 | 0 | /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */ |
609 | 0 | log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m"); |
610 | 0 | else if (r < 0) |
611 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m"); |
612 | 0 | |
613 | 0 | return 0; |
614 | 0 | } |
615 | | |
616 | 0 | int dhcp6_configure(Link *link) { |
617 | 0 | _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; |
618 | 0 | const DUID *duid; |
619 | 0 | int r; |
620 | 0 |
|
621 | 0 | assert(link); |
622 | 0 | assert(link->network); |
623 | 0 |
|
624 | 0 | if (link->dhcp6_client) |
625 | 0 | return 0; |
626 | 0 | |
627 | 0 | r = sd_dhcp6_client_new(&client); |
628 | 0 | if (r == -ENOMEM) |
629 | 0 | return log_oom(); |
630 | 0 | if (r < 0) |
631 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m"); |
632 | 0 | |
633 | 0 | r = sd_dhcp6_client_attach_event(client, NULL, 0); |
634 | 0 | if (r < 0) |
635 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m"); |
636 | 0 | |
637 | 0 | r = sd_dhcp6_client_set_mac(client, |
638 | 0 | (const uint8_t *) &link->mac, |
639 | 0 | sizeof (link->mac), ARPHRD_ETHER); |
640 | 0 | if (r < 0) |
641 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m"); |
642 | 0 | |
643 | 0 | if (link->network->iaid_set) { |
644 | 0 | r = sd_dhcp6_client_set_iaid(client, link->network->iaid); |
645 | 0 | if (r < 0) |
646 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m"); |
647 | 0 | } |
648 | 0 | |
649 | 0 | duid = link_get_duid(link); |
650 | 0 | if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) |
651 | 0 | r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time); |
652 | 0 | else |
653 | 0 | r = sd_dhcp6_client_set_duid(client, |
654 | 0 | duid->type, |
655 | 0 | duid->raw_data_len > 0 ? duid->raw_data : NULL, |
656 | 0 | duid->raw_data_len); |
657 | 0 | if (r < 0) |
658 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m"); |
659 | 0 | |
660 | 0 | r = dhcp6_set_hostname(client, link); |
661 | 0 | if (r < 0) |
662 | 0 | return r; |
663 | 0 | |
664 | 0 | r = sd_dhcp6_client_set_ifindex(client, link->ifindex); |
665 | 0 | if (r < 0) |
666 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m"); |
667 | 0 | |
668 | 0 | if (link->network->rapid_commit) { |
669 | 0 | r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT); |
670 | 0 | if (r < 0) |
671 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m"); |
672 | 0 | } |
673 | 0 | |
674 | 0 | r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); |
675 | 0 | if (r < 0) |
676 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m"); |
677 | 0 | |
678 | 0 | if (dhcp6_enable_prefix_delegation(link)) { |
679 | 0 | r = sd_dhcp6_client_set_prefix_delegation(client, true); |
680 | 0 | if (r < 0) |
681 | 0 | return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m"); |
682 | 0 | } |
683 | 0 | |
684 | 0 | link->dhcp6_client = TAKE_PTR(client); |
685 | 0 |
|
686 | 0 | return 0; |
687 | 0 | } |