]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
a13c50e7 | 2 | /*** |
810adae9 | 3 | Copyright © 2014 Intel Corporation. All rights reserved. |
a13c50e7 TG |
4 | ***/ |
5 | ||
9d96e6c3 | 6 | #include <netinet/icmp6.h> |
23f53b99 | 7 | #include <arpa/inet.h> |
a13c50e7 | 8 | |
a13c50e7 TG |
9 | #include "sd-ndisc.h" |
10 | ||
d909e4af | 11 | #include "missing_network.h" |
ca5ad760 | 12 | #include "networkd-dhcp6.h" |
73854ba1 | 13 | #include "networkd-manager.h" |
1e7a0e21 | 14 | #include "networkd-ndisc.h" |
23f53b99 | 15 | #include "networkd-route.h" |
ac24e418 | 16 | #include "string-table.h" |
5f506a55 | 17 | #include "string-util.h" |
51517f9e | 18 | #include "strv.h" |
1e7a0e21 LP |
19 | |
20 | #define NDISC_DNSSL_MAX 64U | |
21 | #define NDISC_RDNSS_MAX 64U | |
6554550f | 22 | #define NDISC_PREFIX_LFT_MIN 7200U |
fe307276 | 23 | |
5f506a55 SS |
24 | #define DAD_CONFLICTS_IDGEN_RETRIES_RFC7217 3 |
25 | ||
26 | /* https://tools.ietf.org/html/rfc5453 */ | |
27 | /* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */ | |
28 | ||
29 | #define SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }) | |
30 | #define SUBNET_ROUTER_ANYCAST_PREFIXLEN 8 | |
31 | #define RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x02, 0x00, 0x5E, 0xFF, 0xFE } }) | |
32 | #define RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN 5 | |
33 | #define RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291 ((struct in6_addr) { .s6_addr = { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }) | |
34 | #define RESERVED_SUBNET_ANYCAST_PREFIXLEN 7 | |
35 | ||
36 | #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e) | |
37 | ||
69203fba YW |
38 | static int ndisc_remove_old(Link *link, bool force); |
39 | ||
40 | static int ndisc_address_callback(Address *address) { | |
41 | Address *a; | |
42 | Iterator i; | |
43 | ||
44 | assert(address); | |
45 | assert(address->link); | |
46 | ||
47 | /* Make this called only once */ | |
48 | SET_FOREACH(a, address->link->ndisc_addresses, i) | |
49 | a->callback = NULL; | |
50 | ||
51 | return ndisc_remove_old(address->link, true); | |
52 | } | |
53 | ||
54 | static int ndisc_remove_old(Link *link, bool force) { | |
55 | Address *address; | |
56 | Route *route; | |
b0b97766 YW |
57 | NDiscDNSSL *dnssl; |
58 | NDiscRDNSS *rdnss; | |
69203fba YW |
59 | Iterator i; |
60 | int k, r = 0; | |
61 | ||
62 | assert(link); | |
63 | ||
64 | if (!force) { | |
65 | bool set_callback = !set_isempty(link->ndisc_addresses); | |
66 | ||
67 | if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) | |
68 | return 0; | |
69 | ||
70 | SET_FOREACH(address, link->ndisc_addresses, i) | |
71 | if (address_is_ready(address)) { | |
72 | set_callback = false; | |
73 | break; | |
74 | } | |
75 | ||
76 | if (set_callback) { | |
77 | SET_FOREACH(address, link->ndisc_addresses, i) | |
78 | address->callback = ndisc_address_callback; | |
79 | return 0; | |
80 | } | |
81 | } | |
82 | ||
83 | if (!set_isempty(link->ndisc_addresses_old) || !set_isempty(link->ndisc_routes_old)) | |
84 | log_link_debug(link, "Removing old NDisc addresses and routes."); | |
85 | ||
86 | link_dirty(link); | |
87 | ||
88 | SET_FOREACH(address, link->ndisc_addresses_old, i) { | |
89 | k = address_remove(address, link, NULL); | |
90 | if (k < 0) | |
91 | r = k; | |
92 | } | |
93 | ||
94 | SET_FOREACH(route, link->ndisc_routes_old, i) { | |
95 | k = route_remove(route, link, NULL); | |
96 | if (k < 0) | |
97 | r = k; | |
98 | } | |
99 | ||
b0b97766 YW |
100 | SET_FOREACH(rdnss, link->ndisc_rdnss, i) |
101 | if (rdnss->marked) | |
102 | free(set_remove(link->ndisc_rdnss, rdnss)); | |
103 | ||
104 | SET_FOREACH(dnssl, link->ndisc_dnssl, i) | |
105 | if (dnssl->marked) | |
106 | free(set_remove(link->ndisc_dnssl, dnssl)); | |
107 | ||
69203fba YW |
108 | return r; |
109 | } | |
110 | ||
d98c546d | 111 | static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
3b015d40 TG |
112 | int r; |
113 | ||
114 | assert(link); | |
d98c546d | 115 | assert(link->ndisc_routes_messages > 0); |
3b015d40 | 116 | |
d98c546d | 117 | link->ndisc_routes_messages--; |
3b015d40 | 118 | |
4ff296b0 YW |
119 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) |
120 | return 1; | |
121 | ||
3b015d40 | 122 | r = sd_netlink_message_get_errno(m); |
4ff296b0 | 123 | if (r < 0 && r != -EEXIST) { |
d98c546d | 124 | log_link_message_error_errno(link, m, r, "Could not set NDisc route"); |
4ff296b0 YW |
125 | link_enter_failed(link); |
126 | return 1; | |
127 | } | |
3b015d40 | 128 | |
d98c546d YW |
129 | if (link->ndisc_routes_messages == 0) { |
130 | log_link_debug(link, "NDisc routes set."); | |
131 | link->ndisc_routes_configured = true; | |
69203fba YW |
132 | |
133 | r = ndisc_remove_old(link, false); | |
134 | if (r < 0) { | |
135 | link_enter_failed(link); | |
136 | return 1; | |
137 | } | |
138 | ||
3b015d40 TG |
139 | link_check_ready(link); |
140 | } | |
141 | ||
142 | return 1; | |
143 | } | |
144 | ||
d98c546d | 145 | static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
73854ba1 YW |
146 | int r; |
147 | ||
148 | assert(link); | |
d98c546d | 149 | assert(link->ndisc_addresses_messages > 0); |
73854ba1 | 150 | |
d98c546d | 151 | link->ndisc_addresses_messages--; |
73854ba1 | 152 | |
4ff296b0 YW |
153 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) |
154 | return 1; | |
155 | ||
73854ba1 | 156 | r = sd_netlink_message_get_errno(m); |
4ff296b0 | 157 | if (r < 0 && r != -EEXIST) { |
d98c546d | 158 | log_link_message_error_errno(link, m, r, "Could not set NDisc address"); |
4ff296b0 YW |
159 | link_enter_failed(link); |
160 | return 1; | |
161 | } else if (r >= 0) | |
162 | (void) manager_rtnl_process_address(rtnl, m, link->manager); | |
73854ba1 | 163 | |
d98c546d YW |
164 | if (link->ndisc_addresses_messages == 0) { |
165 | log_link_debug(link, "NDisc SLAAC addresses set."); | |
166 | link->ndisc_addresses_configured = true; | |
69203fba YW |
167 | |
168 | r = ndisc_remove_old(link, false); | |
169 | if (r < 0) { | |
170 | link_enter_failed(link); | |
171 | return 1; | |
172 | } | |
173 | ||
4ff296b0 YW |
174 | r = link_request_set_routes(link); |
175 | if (r < 0) { | |
176 | link_enter_failed(link); | |
177 | return 1; | |
178 | } | |
73854ba1 YW |
179 | } |
180 | ||
181 | return 1; | |
182 | } | |
183 | ||
69203fba YW |
184 | static int ndisc_route_configure(Route *route, Link *link) { |
185 | Route *ret; | |
186 | int r; | |
187 | ||
188 | assert(route); | |
189 | assert(link); | |
190 | ||
191 | r = route_configure(route, link, ndisc_route_handler, &ret); | |
192 | if (r < 0) | |
193 | return log_link_error_errno(link, r, "Failed to set NDisc route: %m"); | |
194 | ||
195 | link->ndisc_routes_messages++; | |
196 | ||
197 | r = set_ensure_put(&link->ndisc_routes, &route_hash_ops, ret); | |
198 | if (r < 0) | |
199 | return log_link_error_errno(link, r, "Failed to store NDisc route: %m"); | |
200 | ||
201 | (void) set_remove(link->ndisc_routes_old, ret); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static int ndisc_address_configure(Address *address, Link *link) { | |
207 | Address *ret; | |
208 | int r; | |
209 | ||
210 | assert(address); | |
211 | assert(link); | |
212 | ||
213 | r = address_configure(address, link, ndisc_address_handler, true, &ret); | |
214 | if (r < 0) | |
215 | return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m"); | |
216 | ||
217 | link->ndisc_addresses_messages++; | |
218 | ||
219 | r = set_ensure_put(&link->ndisc_addresses, &address_hash_ops, ret); | |
220 | if (r < 0) | |
221 | return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m"); | |
222 | ||
223 | (void) set_remove(link->ndisc_addresses_old, ret); | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
d5017c84 | 228 | static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { |
8e766630 | 229 | _cleanup_(route_freep) Route *route = NULL; |
ce2ea782 | 230 | union in_addr_union gateway; |
1e7a0e21 LP |
231 | uint16_t lifetime; |
232 | unsigned preference; | |
d6fceaf1 | 233 | uint32_t mtu; |
3b015d40 | 234 | usec_t time_now; |
13e8a49a | 235 | int r; |
3b015d40 | 236 | |
3b015d40 | 237 | assert(link); |
1e7a0e21 | 238 | assert(rt); |
3b015d40 | 239 | |
1e7a0e21 | 240 | r = sd_ndisc_router_get_lifetime(rt, &lifetime); |
d5017c84 | 241 | if (r < 0) |
13e8a49a | 242 | return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); |
d5017c84 | 243 | |
1e7a0e21 | 244 | if (lifetime == 0) /* not a default router */ |
d5017c84 | 245 | return 0; |
1e7a0e21 | 246 | |
ce2ea782 | 247 | r = sd_ndisc_router_get_address(rt, &gateway.in6); |
d5017c84 | 248 | if (r < 0) |
13e8a49a | 249 | return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); |
1e7a0e21 | 250 | |
5eec0a08 YW |
251 | if (address_exists(link, AF_INET6, &gateway)) { |
252 | if (DEBUG_LOGGING) { | |
253 | _cleanup_free_ char *buffer = NULL; | |
6d7c7615 | 254 | |
5eec0a08 YW |
255 | (void) in_addr_to_string(AF_INET6, &gateway, &buffer); |
256 | log_link_debug(link, "No NDisc route added, gateway %s matches local address", | |
257 | strnull(buffer)); | |
6d7c7615 | 258 | } |
5eec0a08 | 259 | return 0; |
ce2ea782 | 260 | } |
6d7c7615 | 261 | |
1e7a0e21 | 262 | r = sd_ndisc_router_get_preference(rt, &preference); |
d5017c84 | 263 | if (r < 0) |
13e8a49a | 264 | return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); |
1e7a0e21 LP |
265 | |
266 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
d5017c84 | 267 | if (r < 0) |
13e8a49a | 268 | return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); |
1e7a0e21 | 269 | |
d6fceaf1 | 270 | r = sd_ndisc_router_get_mtu(rt, &mtu); |
29b5ad08 JT |
271 | if (r == -ENODATA) |
272 | mtu = 0; | |
d5017c84 | 273 | else if (r < 0) |
13e8a49a | 274 | return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m"); |
d6fceaf1 | 275 | |
1e7a0e21 | 276 | r = route_new(&route); |
d5017c84 | 277 | if (r < 0) |
13e8a49a | 278 | return log_oom(); |
1e7a0e21 LP |
279 | |
280 | route->family = AF_INET6; | |
bdb9f580 | 281 | route->table = link_get_ipv6_accept_ra_route_table(link); |
1bf1bfd9 | 282 | route->priority = link->network->dhcp6_route_metric; |
1e7a0e21 LP |
283 | route->protocol = RTPROT_RA; |
284 | route->pref = preference; | |
ce2ea782 | 285 | route->gw = gateway; |
1e7a0e21 | 286 | route->lifetime = time_now + lifetime * USEC_PER_SEC; |
d6fceaf1 | 287 | route->mtu = mtu; |
1e7a0e21 | 288 | |
69203fba | 289 | r = ndisc_route_configure(route, link); |
13e8a49a YW |
290 | if (r < 0) |
291 | return log_link_error_errno(link, r, "Could not set default route: %m"); | |
d5017c84 | 292 | |
1985c54f YW |
293 | Route *route_gw; |
294 | LIST_FOREACH(routes, route_gw, link->network->static_routes) { | |
295 | if (!route_gw->gateway_from_dhcp) | |
296 | continue; | |
297 | ||
298 | if (route_gw->family != AF_INET6) | |
299 | continue; | |
300 | ||
301 | route_gw->gw = gateway; | |
302 | ||
69203fba | 303 | r = ndisc_route_configure(route_gw, link); |
13e8a49a YW |
304 | if (r < 0) |
305 | return log_link_error_errno(link, r, "Could not set gateway: %m"); | |
1985c54f YW |
306 | } |
307 | ||
d5017c84 | 308 | return 0; |
1e7a0e21 LP |
309 | } |
310 | ||
92ee90af YW |
311 | static bool stableprivate_address_is_valid(const struct in6_addr *addr) { |
312 | assert(addr); | |
313 | ||
314 | /* According to rfc4291, generated address should not be in the following ranges. */ | |
315 | ||
316 | if (memcmp(addr, &SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291, SUBNET_ROUTER_ANYCAST_PREFIXLEN) == 0) | |
317 | return false; | |
318 | ||
319 | if (memcmp(addr, &RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291, RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN) == 0) | |
320 | return false; | |
321 | ||
322 | if (memcmp(addr, &RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291, RESERVED_SUBNET_ANYCAST_PREFIXLEN) == 0) | |
323 | return false; | |
324 | ||
325 | return true; | |
326 | } | |
327 | ||
328 | static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) { | |
329 | _cleanup_free_ struct in6_addr *addr = NULL; | |
330 | sd_id128_t secret_key; | |
331 | struct siphash state; | |
332 | uint64_t rid; | |
333 | size_t l; | |
334 | int r; | |
335 | ||
336 | /* According to rfc7217 section 5.1 | |
337 | * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) */ | |
338 | ||
339 | r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key); | |
340 | if (r < 0) | |
341 | return log_error_errno(r, "Failed to generate key: %m"); | |
342 | ||
343 | siphash24_init(&state, secret_key.bytes); | |
344 | ||
345 | l = MAX(DIV_ROUND_UP(prefix_len, 8), 8); | |
346 | siphash24_compress(prefix, l, &state); | |
347 | siphash24_compress_string(link->ifname, &state); | |
348 | siphash24_compress(&link->mac, sizeof(struct ether_addr), &state); | |
349 | siphash24_compress(&dad_counter, sizeof(uint8_t), &state); | |
350 | ||
351 | rid = htole64(siphash24_finalize(&state)); | |
352 | ||
353 | addr = new(struct in6_addr, 1); | |
354 | if (!addr) | |
355 | return log_oom(); | |
356 | ||
357 | memcpy(addr->s6_addr, prefix->s6_addr, l); | |
358 | memcpy(addr->s6_addr + l, &rid, 16 - l); | |
359 | ||
360 | if (!stableprivate_address_is_valid(addr)) { | |
361 | *ret = NULL; | |
362 | return 0; | |
363 | } | |
364 | ||
365 | *ret = TAKE_PTR(addr); | |
366 | return 1; | |
367 | } | |
368 | ||
369 | static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address, uint8_t prefixlen, Set **ret) { | |
c24c83dc | 370 | _cleanup_set_free_free_ Set *addresses = NULL; |
5f506a55 SS |
371 | IPv6Token *j; |
372 | Iterator i; | |
373 | int r; | |
374 | ||
5f506a55 | 375 | assert(link); |
c24c83dc KF |
376 | assert(address); |
377 | assert(ret); | |
378 | ||
92ee90af | 379 | addresses = set_new(&in6_addr_hash_ops); |
c24c83dc KF |
380 | if (!addresses) |
381 | return log_oom(); | |
5f506a55 | 382 | |
2c621495 | 383 | ORDERED_SET_FOREACH(j, link->network->ipv6_tokens, i) { |
92ee90af | 384 | _cleanup_free_ struct in6_addr *new_address = NULL; |
c24c83dc | 385 | |
5f506a55 | 386 | if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE |
92ee90af | 387 | && IN6_ARE_ADDR_EQUAL(&j->prefix, address)) { |
0ddad04e KF |
388 | /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop |
389 | does not actually attempt Duplicate Address Detection; the counter will be incremented | |
390 | only when the address generation algorithm produces an invalid address, and the loop | |
391 | may exit with an address which ends up being unusable due to duplication on the link. | |
392 | */ | |
5f506a55 | 393 | for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) { |
92ee90af | 394 | r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address); |
5f506a55 | 395 | if (r < 0) |
92ee90af YW |
396 | return r; |
397 | if (r > 0) | |
c24c83dc | 398 | break; |
5f506a55 | 399 | } |
e2c4070e | 400 | } else if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC) { |
92ee90af YW |
401 | new_address = new(struct in6_addr, 1); |
402 | if (!new_address) | |
403 | return log_oom(); | |
5f506a55 | 404 | |
92ee90af YW |
405 | memcpy(new_address->s6_addr, address->s6_addr, 8); |
406 | memcpy(new_address->s6_addr + 8, j->prefix.s6_addr + 8, 8); | |
407 | } | |
c24c83dc | 408 | |
92ee90af | 409 | if (new_address) { |
c24c83dc KF |
410 | r = set_put(addresses, new_address); |
411 | if (r < 0) | |
13e8a49a | 412 | return log_link_error_errno(link, r, "Failed to store SLAAC address: %m"); |
92ee90af YW |
413 | else if (r == 0) |
414 | log_link_debug_errno(link, r, "Generated SLAAC address is duplicated, ignoring."); | |
415 | else | |
416 | TAKE_PTR(new_address); | |
c24c83dc KF |
417 | } |
418 | } | |
419 | ||
420 | /* fall back to EUI-64 if no tokens provided addresses */ | |
421 | if (set_isempty(addresses)) { | |
92ee90af | 422 | _cleanup_free_ struct in6_addr *new_address = NULL; |
c24c83dc | 423 | |
92ee90af YW |
424 | new_address = newdup(struct in6_addr, address, 1); |
425 | if (!new_address) | |
c24c83dc KF |
426 | return log_oom(); |
427 | ||
92ee90af | 428 | r = generate_ipv6_eui_64_address(link, new_address); |
a781ddef SS |
429 | if (r < 0) |
430 | return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m"); | |
431 | ||
c24c83dc KF |
432 | r = set_put(addresses, new_address); |
433 | if (r < 0) | |
13e8a49a | 434 | return log_link_error_errno(link, r, "Failed to store SLAAC address: %m"); |
92ee90af | 435 | |
c24c83dc | 436 | TAKE_PTR(new_address); |
5f506a55 SS |
437 | } |
438 | ||
c24c83dc | 439 | *ret = TAKE_PTR(addresses); |
5f506a55 SS |
440 | |
441 | return 0; | |
442 | } | |
c24c83dc | 443 | |
d5017c84 | 444 | static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { |
5f506a55 | 445 | uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining; |
c24c83dc | 446 | _cleanup_set_free_free_ Set *addresses = NULL; |
8e766630 | 447 | _cleanup_(address_freep) Address *address = NULL; |
92ee90af | 448 | struct in6_addr addr, *a; |
1e7a0e21 | 449 | unsigned prefixlen; |
5f506a55 | 450 | usec_t time_now; |
c24c83dc | 451 | Iterator i; |
1e7a0e21 LP |
452 | int r; |
453 | ||
454 | assert(link); | |
455 | assert(rt); | |
456 | ||
6554550f | 457 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); |
d5017c84 | 458 | if (r < 0) |
13e8a49a | 459 | return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); |
6554550f | 460 | |
1e7a0e21 | 461 | r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); |
d5017c84 YW |
462 | if (r < 0) |
463 | return log_link_error_errno(link, r, "Failed to get prefix length: %m"); | |
1e7a0e21 LP |
464 | |
465 | r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid); | |
d5017c84 YW |
466 | if (r < 0) |
467 | return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); | |
1e7a0e21 LP |
468 | |
469 | r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred); | |
d5017c84 YW |
470 | if (r < 0) |
471 | return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); | |
3b015d40 | 472 | |
92bdc3ff SS |
473 | /* The preferred lifetime is never greater than the valid lifetime */ |
474 | if (lifetime_preferred > lifetime_valid) | |
d5017c84 | 475 | return 0; |
92bdc3ff | 476 | |
92ee90af | 477 | r = sd_ndisc_router_prefix_get_address(rt, &addr); |
d5017c84 YW |
478 | if (r < 0) |
479 | return log_link_error_errno(link, r, "Failed to get prefix address: %m"); | |
1e7a0e21 | 480 | |
92ee90af | 481 | r = ndisc_router_generate_addresses(link, &addr, prefixlen, &addresses); |
5f506a55 | 482 | if (r < 0) |
13e8a49a | 483 | return r; |
c24c83dc | 484 | |
92ee90af YW |
485 | r = address_new(&address); |
486 | if (r < 0) | |
487 | return log_oom(); | |
488 | ||
489 | address->family = AF_INET6; | |
490 | address->prefixlen = prefixlen; | |
491 | address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; | |
492 | address->cinfo.ifa_prefered = lifetime_preferred; | |
493 | ||
c24c83dc | 494 | SET_FOREACH(a, addresses, i) { |
13e8a49a YW |
495 | Address *existing_address; |
496 | ||
c24c83dc | 497 | /* see RFC4862 section 5.5.3.e */ |
92ee90af | 498 | r = address_get(link, AF_INET6, (union in_addr_union *) a, prefixlen, &existing_address); |
c24c83dc KF |
499 | if (r > 0) { |
500 | lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC; | |
501 | if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining) | |
92ee90af | 502 | address->cinfo.ifa_valid = lifetime_valid; |
c24c83dc | 503 | else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN) |
92ee90af | 504 | address->cinfo.ifa_valid = lifetime_remaining; |
c24c83dc | 505 | else |
92ee90af | 506 | address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN; |
c24c83dc | 507 | } else if (lifetime_valid > 0) |
92ee90af | 508 | address->cinfo.ifa_valid = lifetime_valid; |
6554550f | 509 | else |
01c344bd | 510 | continue; /* see RFC4862 section 5.5.3.d */ |
6554550f | 511 | |
92ee90af | 512 | if (address->cinfo.ifa_valid == 0) |
c24c83dc | 513 | continue; |
3b015d40 | 514 | |
92ee90af YW |
515 | address->in_addr.in6 = *a; |
516 | ||
69203fba | 517 | r = ndisc_address_configure(address, link); |
13e8a49a YW |
518 | if (r < 0) |
519 | return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); | |
3b015d40 | 520 | } |
d5017c84 YW |
521 | |
522 | return 0; | |
3b015d40 TG |
523 | } |
524 | ||
d5017c84 | 525 | static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { |
8e766630 | 526 | _cleanup_(route_freep) Route *route = NULL; |
3b015d40 | 527 | usec_t time_now; |
1e7a0e21 LP |
528 | uint32_t lifetime; |
529 | unsigned prefixlen; | |
3b015d40 TG |
530 | int r; |
531 | ||
3b015d40 | 532 | assert(link); |
1e7a0e21 | 533 | assert(rt); |
3b015d40 | 534 | |
1e7a0e21 | 535 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); |
d5017c84 | 536 | if (r < 0) |
13e8a49a | 537 | return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); |
1e7a0e21 LP |
538 | |
539 | r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); | |
d5017c84 YW |
540 | if (r < 0) |
541 | return log_link_error_errno(link, r, "Failed to get prefix length: %m"); | |
1e7a0e21 LP |
542 | |
543 | r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime); | |
d5017c84 YW |
544 | if (r < 0) |
545 | return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); | |
3b015d40 TG |
546 | |
547 | r = route_new(&route); | |
d5017c84 | 548 | if (r < 0) |
13e8a49a | 549 | return log_oom(); |
3b015d40 | 550 | |
3b015d40 | 551 | route->family = AF_INET6; |
bdb9f580 | 552 | route->table = link_get_ipv6_accept_ra_route_table(link); |
1bf1bfd9 | 553 | route->priority = link->network->dhcp6_route_metric; |
3b015d40 TG |
554 | route->protocol = RTPROT_RA; |
555 | route->flags = RTM_F_PREFIX; | |
3b015d40 TG |
556 | route->dst_prefixlen = prefixlen; |
557 | route->lifetime = time_now + lifetime * USEC_PER_SEC; | |
558 | ||
1e7a0e21 | 559 | r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6); |
d5017c84 YW |
560 | if (r < 0) |
561 | return log_link_error_errno(link, r, "Failed to get prefix address: %m"); | |
1e7a0e21 | 562 | |
69203fba | 563 | r = ndisc_route_configure(route, link); |
13e8a49a YW |
564 | if (r < 0) |
565 | return log_link_error_errno(link, r, "Could not set prefix route: %m");; | |
d5017c84 YW |
566 | |
567 | return 0; | |
3b015d40 TG |
568 | } |
569 | ||
d5017c84 | 570 | static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { |
8e766630 | 571 | _cleanup_(route_freep) Route *route = NULL; |
1e7a0e21 LP |
572 | struct in6_addr gateway; |
573 | uint32_t lifetime; | |
574 | unsigned preference, prefixlen; | |
fe307276 | 575 | usec_t time_now; |
7a695d8e | 576 | int r; |
a13c50e7 TG |
577 | |
578 | assert(link); | |
a13c50e7 | 579 | |
1e7a0e21 | 580 | r = sd_ndisc_router_route_get_lifetime(rt, &lifetime); |
d5017c84 | 581 | if (r < 0) |
13e8a49a | 582 | return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); |
d5017c84 | 583 | |
1e7a0e21 | 584 | if (lifetime == 0) |
d5017c84 | 585 | return 0; |
a13c50e7 | 586 | |
1e7a0e21 | 587 | r = sd_ndisc_router_get_address(rt, &gateway); |
d5017c84 | 588 | if (r < 0) |
13e8a49a | 589 | return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); |
3b015d40 | 590 | |
1e7a0e21 | 591 | r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen); |
d5017c84 | 592 | if (r < 0) |
13e8a49a | 593 | return log_link_error_errno(link, r, "Failed to get route prefix length: %m"); |
1e7a0e21 LP |
594 | |
595 | r = sd_ndisc_router_route_get_preference(rt, &preference); | |
d5017c84 | 596 | if (r < 0) |
13e8a49a | 597 | return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); |
1e7a0e21 LP |
598 | |
599 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
d5017c84 | 600 | if (r < 0) |
13e8a49a | 601 | return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); |
3b015d40 TG |
602 | |
603 | r = route_new(&route); | |
d5017c84 | 604 | if (r < 0) |
13e8a49a | 605 | return log_oom(); |
3b015d40 | 606 | |
3b015d40 | 607 | route->family = AF_INET6; |
bdb9f580 | 608 | route->table = link_get_ipv6_accept_ra_route_table(link); |
1bf1bfd9 | 609 | route->priority = link->network->dhcp6_route_metric; |
3b015d40 | 610 | route->protocol = RTPROT_RA; |
1e7a0e21 LP |
611 | route->pref = preference; |
612 | route->gw.in6 = gateway; | |
613 | route->dst_prefixlen = prefixlen; | |
3b015d40 TG |
614 | route->lifetime = time_now + lifetime * USEC_PER_SEC; |
615 | ||
1e7a0e21 | 616 | r = sd_ndisc_router_route_get_address(rt, &route->dst.in6); |
d5017c84 YW |
617 | if (r < 0) |
618 | return log_link_error_errno(link, r, "Failed to get route address: %m"); | |
1e7a0e21 | 619 | |
69203fba | 620 | r = ndisc_route_configure(route, link); |
13e8a49a YW |
621 | if (r < 0) |
622 | return log_link_error_errno(link, r, "Could not set additional route: %m"); | |
d5017c84 YW |
623 | |
624 | return 0; | |
9d96e6c3 | 625 | } |
a13c50e7 | 626 | |
7a08d314 | 627 | static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) { |
1e7a0e21 LP |
628 | siphash24_compress(&x->address, sizeof(x->address), state); |
629 | } | |
630 | ||
7a08d314 | 631 | static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) { |
1e7a0e21 LP |
632 | return memcmp(&a->address, &b->address, sizeof(a->address)); |
633 | } | |
634 | ||
b0b97766 YW |
635 | DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( |
636 | ndisc_rdnss_hash_ops, | |
637 | NDiscRDNSS, | |
638 | ndisc_rdnss_hash_func, | |
639 | ndisc_rdnss_compare_func, | |
640 | free); | |
1e7a0e21 | 641 | |
d5017c84 | 642 | static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { |
1e7a0e21 LP |
643 | uint32_t lifetime; |
644 | const struct in6_addr *a; | |
b0b97766 | 645 | NDiscRDNSS *rdnss; |
1e7a0e21 | 646 | usec_t time_now; |
b0b97766 | 647 | Iterator i; |
13e8a49a | 648 | int n, r; |
1e7a0e21 LP |
649 | |
650 | assert(link); | |
651 | assert(rt); | |
652 | ||
653 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
d5017c84 | 654 | if (r < 0) |
13e8a49a | 655 | return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); |
1e7a0e21 LP |
656 | |
657 | r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime); | |
d5017c84 | 658 | if (r < 0) |
13e8a49a | 659 | return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m"); |
1e7a0e21 LP |
660 | |
661 | n = sd_ndisc_router_rdnss_get_addresses(rt, &a); | |
d5017c84 | 662 | if (n < 0) |
13e8a49a | 663 | return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); |
1e7a0e21 | 664 | |
b0b97766 YW |
665 | SET_FOREACH(rdnss, link->ndisc_rdnss, i) |
666 | rdnss->marked = true; | |
1e7a0e21 | 667 | |
b0b97766 YW |
668 | if (lifetime == 0) |
669 | return 0; | |
1e7a0e21 | 670 | |
b0b97766 YW |
671 | if (n >= (int) NDISC_RDNSS_MAX) { |
672 | log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX); | |
673 | n = NDISC_RDNSS_MAX; | |
674 | } | |
1e7a0e21 | 675 | |
b0b97766 YW |
676 | for (int j = 0; j < n; j++) { |
677 | _cleanup_free_ NDiscRDNSS *x = NULL; | |
678 | NDiscRDNSS d = { | |
679 | .address = a[j], | |
680 | }; | |
1e7a0e21 | 681 | |
b0b97766 YW |
682 | rdnss = set_get(link->ndisc_rdnss, &d); |
683 | if (rdnss) { | |
684 | rdnss->marked = false; | |
685 | rdnss->valid_until = time_now + lifetime * USEC_PER_SEC; | |
1e7a0e21 LP |
686 | continue; |
687 | } | |
688 | ||
d5017c84 YW |
689 | x = new(NDiscRDNSS, 1); |
690 | if (!x) | |
691 | return log_oom(); | |
1e7a0e21 | 692 | |
d5017c84 | 693 | *x = (NDiscRDNSS) { |
b0b97766 | 694 | .address = a[j], |
d5017c84 YW |
695 | .valid_until = time_now + lifetime * USEC_PER_SEC, |
696 | }; | |
1e7a0e21 | 697 | |
35e601d4 | 698 | r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x)); |
d5017c84 YW |
699 | if (r < 0) |
700 | return log_oom(); | |
1e7a0e21 | 701 | assert(r > 0); |
1e7a0e21 | 702 | } |
d5017c84 YW |
703 | |
704 | return 0; | |
1e7a0e21 LP |
705 | } |
706 | ||
7a08d314 | 707 | static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) { |
f281fc1e | 708 | siphash24_compress_string(NDISC_DNSSL_DOMAIN(x), state); |
1e7a0e21 LP |
709 | } |
710 | ||
7a08d314 | 711 | static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) { |
1e7a0e21 LP |
712 | return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b)); |
713 | } | |
714 | ||
b0b97766 YW |
715 | DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( |
716 | ndisc_dnssl_hash_ops, | |
717 | NDiscDNSSL, | |
718 | ndisc_dnssl_hash_func, | |
719 | ndisc_dnssl_compare_func, | |
720 | free); | |
1e7a0e21 | 721 | |
13e8a49a | 722 | static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { |
1e7a0e21 LP |
723 | _cleanup_strv_free_ char **l = NULL; |
724 | uint32_t lifetime; | |
725 | usec_t time_now; | |
b0b97766 YW |
726 | NDiscDNSSL *dnssl; |
727 | Iterator i; | |
728 | char **j; | |
1e7a0e21 LP |
729 | int r; |
730 | ||
731 | assert(link); | |
732 | assert(rt); | |
733 | ||
734 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
13e8a49a YW |
735 | if (r < 0) |
736 | return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); | |
1e7a0e21 LP |
737 | |
738 | r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime); | |
13e8a49a YW |
739 | if (r < 0) |
740 | return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m"); | |
1e7a0e21 LP |
741 | |
742 | r = sd_ndisc_router_dnssl_get_domains(rt, &l); | |
13e8a49a YW |
743 | if (r < 0) |
744 | return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); | |
1e7a0e21 | 745 | |
b0b97766 YW |
746 | SET_FOREACH(dnssl, link->ndisc_dnssl, i) |
747 | dnssl->marked = true; | |
1e7a0e21 | 748 | |
b0b97766 YW |
749 | if (lifetime == 0) |
750 | return 0; | |
a34349e7 | 751 | |
b0b97766 YW |
752 | if (strv_length(l) >= NDISC_DNSSL_MAX) { |
753 | log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX); | |
754 | STRV_FOREACH(j, l + NDISC_DNSSL_MAX) | |
755 | *j = mfree(*j); | |
756 | } | |
1e7a0e21 | 757 | |
b0b97766 YW |
758 | STRV_FOREACH(j, l) { |
759 | _cleanup_free_ NDiscDNSSL *s = NULL; | |
1e7a0e21 | 760 | |
b0b97766 YW |
761 | s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1); |
762 | if (!s) | |
763 | return log_oom(); | |
1e7a0e21 | 764 | |
b0b97766 | 765 | strcpy(NDISC_DNSSL_DOMAIN(s), *j); |
1e7a0e21 | 766 | |
b0b97766 YW |
767 | dnssl = set_get(link->ndisc_dnssl, s); |
768 | if (dnssl) { | |
769 | dnssl->marked = false; | |
770 | dnssl->valid_until = time_now + lifetime * USEC_PER_SEC; | |
1e7a0e21 LP |
771 | continue; |
772 | } | |
773 | ||
a34349e7 | 774 | s->valid_until = time_now + lifetime * USEC_PER_SEC; |
1e7a0e21 | 775 | |
35e601d4 | 776 | r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s)); |
13e8a49a YW |
777 | if (r < 0) |
778 | return log_oom(); | |
1e7a0e21 | 779 | assert(r > 0); |
1e7a0e21 | 780 | } |
13e8a49a YW |
781 | |
782 | return 0; | |
1e7a0e21 LP |
783 | } |
784 | ||
e8c9b5b0 | 785 | static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { |
1e7a0e21 | 786 | assert(link); |
55d3fdcf | 787 | assert(link->network); |
1e7a0e21 LP |
788 | assert(rt); |
789 | ||
13e8a49a | 790 | for (int r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) { |
1e7a0e21 LP |
791 | uint8_t type; |
792 | ||
e8c9b5b0 | 793 | if (r < 0) |
13e8a49a | 794 | return log_link_error_errno(link, r, "Failed to iterate through options: %m"); |
1e7a0e21 | 795 | if (r == 0) /* EOF */ |
13e8a49a | 796 | return 0; |
1e7a0e21 LP |
797 | |
798 | r = sd_ndisc_router_option_get_type(rt, &type); | |
e8c9b5b0 | 799 | if (r < 0) |
13e8a49a | 800 | return log_link_error_errno(link, r, "Failed to get RA option type: %m"); |
1e7a0e21 LP |
801 | |
802 | switch (type) { | |
803 | ||
804 | case SD_NDISC_OPTION_PREFIX_INFORMATION: { | |
55d3fdcf | 805 | union in_addr_union a; |
1e7a0e21 LP |
806 | uint8_t flags; |
807 | ||
55d3fdcf YW |
808 | r = sd_ndisc_router_prefix_get_address(rt, &a.in6); |
809 | if (r < 0) | |
810 | return log_link_error_errno(link, r, "Failed to get prefix address: %m"); | |
811 | ||
6b000af4 | 812 | if (set_contains(link->network->ndisc_deny_listed_prefix, &a.in6)) { |
55d3fdcf YW |
813 | if (DEBUG_LOGGING) { |
814 | _cleanup_free_ char *b = NULL; | |
815 | ||
816 | (void) in_addr_to_string(AF_INET6, &a, &b); | |
6b000af4 | 817 | log_link_debug(link, "Prefix '%s' is deny-listed, ignoring", strna(b)); |
55d3fdcf | 818 | } |
55d3fdcf YW |
819 | break; |
820 | } | |
821 | ||
1e7a0e21 | 822 | r = sd_ndisc_router_prefix_get_flags(rt, &flags); |
e8c9b5b0 | 823 | if (r < 0) |
13e8a49a | 824 | return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m"); |
1e7a0e21 | 825 | |
87d8a4de | 826 | if (link->network->ipv6_accept_ra_use_onlink_prefix && |
13e8a49a YW |
827 | FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) { |
828 | r = ndisc_router_process_onlink_prefix(link, rt); | |
829 | if (r < 0) | |
830 | return r; | |
831 | } | |
062c2eea | 832 | |
87d8a4de | 833 | if (link->network->ipv6_accept_ra_use_autonomous_prefix && |
13e8a49a YW |
834 | FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) { |
835 | r = ndisc_router_process_autonomous_prefix(link, rt); | |
836 | if (r < 0) | |
837 | return r; | |
838 | } | |
1e7a0e21 LP |
839 | break; |
840 | } | |
841 | ||
842 | case SD_NDISC_OPTION_ROUTE_INFORMATION: | |
13e8a49a YW |
843 | r = ndisc_router_process_route(link, rt); |
844 | if (r < 0) | |
845 | return r; | |
1e7a0e21 LP |
846 | break; |
847 | ||
848 | case SD_NDISC_OPTION_RDNSS: | |
13e8a49a YW |
849 | if (link->network->ipv6_accept_ra_use_dns) { |
850 | r = ndisc_router_process_rdnss(link, rt); | |
851 | if (r < 0) | |
852 | return r; | |
853 | } | |
1e7a0e21 LP |
854 | break; |
855 | ||
856 | case SD_NDISC_OPTION_DNSSL: | |
13e8a49a YW |
857 | if (link->network->ipv6_accept_ra_use_dns) { |
858 | r = ndisc_router_process_dnssl(link, rt); | |
859 | if (r < 0) | |
860 | return r; | |
861 | } | |
1e7a0e21 LP |
862 | break; |
863 | } | |
1e7a0e21 LP |
864 | } |
865 | } | |
866 | ||
d5017c84 | 867 | static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { |
69203fba YW |
868 | Address *address; |
869 | Route *route; | |
1e7a0e21 | 870 | uint64_t flags; |
86e2be7b | 871 | int r; |
1e7a0e21 LP |
872 | |
873 | assert(link); | |
874 | assert(link->network); | |
875 | assert(link->manager); | |
876 | assert(rt); | |
877 | ||
69203fba YW |
878 | link->ndisc_addresses_configured = false; |
879 | link->ndisc_routes_configured = false; | |
880 | ||
881 | link_dirty(link); | |
882 | ||
883 | while ((address = set_steal_first(link->ndisc_addresses))) { | |
884 | r = set_ensure_put(&link->ndisc_addresses_old, &address_hash_ops, address); | |
885 | if (r < 0) | |
886 | return log_link_error_errno(link, r, "Failed to store old NDisc SLAAC address: %m"); | |
887 | } | |
888 | ||
889 | while ((route = set_steal_first(link->ndisc_routes))) { | |
890 | r = set_ensure_put(&link->ndisc_routes_old, &route_hash_ops, route); | |
891 | if (r < 0) | |
892 | return log_link_error_errno(link, r, "Failed to store old NDisc route: %m"); | |
893 | } | |
894 | ||
1e7a0e21 | 895 | r = sd_ndisc_router_get_flags(rt, &flags); |
d5017c84 | 896 | if (r < 0) |
13e8a49a | 897 | return log_link_error_errno(link, r, "Failed to get RA flags: %m"); |
1e7a0e21 | 898 | |
ac24e418 SS |
899 | if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) && link->network->ipv6_accept_ra_start_dhcp6_client)) { |
900 | ||
901 | if (link->network->ipv6_accept_ra_start_dhcp6_client == IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS) | |
902 | r = dhcp6_request_address(link, false); | |
903 | else | |
904 | /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ | |
905 | r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); | |
1e7a0e21 | 906 | if (r < 0 && r != -EBUSY) |
13e8a49a | 907 | return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); |
69203fba | 908 | else |
1e7a0e21 LP |
909 | log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); |
910 | } | |
911 | ||
13e8a49a YW |
912 | r = ndisc_router_process_default(link, rt); |
913 | if (r < 0) | |
914 | return r; | |
915 | r = ndisc_router_process_options(link, rt); | |
916 | if (r < 0) | |
917 | return r; | |
d5017c84 | 918 | |
69203fba YW |
919 | if (link->ndisc_addresses_messages == 0) |
920 | link->ndisc_addresses_configured = true; | |
921 | else { | |
922 | log_link_debug(link, "Setting SLAAC addresses."); | |
923 | ||
924 | /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). | |
925 | * Before they are called, the related flags must be cleared. Otherwise, the link | |
926 | * becomes configured state before routes are configured. */ | |
927 | link->static_routes_configured = false; | |
928 | link->static_nexthops_configured = false; | |
929 | } | |
930 | ||
931 | if (link->ndisc_routes_messages == 0) | |
932 | link->ndisc_routes_configured = true; | |
933 | else | |
934 | log_link_debug(link, "Setting NDisc routes."); | |
935 | ||
936 | r = ndisc_remove_old(link, false); | |
937 | if (r < 0) | |
938 | return r; | |
939 | ||
940 | if (link->ndisc_addresses_configured && link->ndisc_routes_configured) | |
941 | link_check_ready(link); | |
942 | else | |
943 | link_set_state(link, LINK_STATE_CONFIGURING); | |
944 | ||
945 | return 0; | |
1e7a0e21 LP |
946 | } |
947 | ||
948 | static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { | |
9d96e6c3 | 949 | Link *link = userdata; |
13e8a49a | 950 | int r; |
a13c50e7 | 951 | |
9d96e6c3 | 952 | assert(link); |
a13c50e7 | 953 | |
9d96e6c3 TG |
954 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) |
955 | return; | |
a13c50e7 | 956 | |
9d96e6c3 | 957 | switch (event) { |
1e7a0e21 LP |
958 | |
959 | case SD_NDISC_EVENT_ROUTER: | |
13e8a49a YW |
960 | r = ndisc_router_handler(link, rt); |
961 | if (r < 0) { | |
962 | link_enter_failed(link); | |
963 | return; | |
964 | } | |
1e7a0e21 LP |
965 | break; |
966 | ||
9d96e6c3 | 967 | case SD_NDISC_EVENT_TIMEOUT: |
a8c10331 | 968 | log_link_debug(link, "NDisc handler get timeout event"); |
3336e946 YW |
969 | if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) { |
970 | link->ndisc_addresses_configured = true; | |
971 | link->ndisc_routes_configured = true; | |
972 | link_check_ready(link); | |
973 | } | |
9d96e6c3 TG |
974 | break; |
975 | default: | |
a8c10331 | 976 | assert_not_reached("Unknown NDisc event"); |
a13c50e7 TG |
977 | } |
978 | } | |
979 | ||
980 | int ndisc_configure(Link *link) { | |
981 | int r; | |
982 | ||
1e7a0e21 LP |
983 | assert(link); |
984 | ||
985 | r = sd_ndisc_new(&link->ndisc); | |
986 | if (r < 0) | |
987 | return r; | |
a13c50e7 | 988 | |
1e7a0e21 | 989 | r = sd_ndisc_attach_event(link->ndisc, NULL, 0); |
a13c50e7 TG |
990 | if (r < 0) |
991 | return r; | |
992 | ||
1e7a0e21 | 993 | r = sd_ndisc_set_mac(link->ndisc, &link->mac); |
a13c50e7 TG |
994 | if (r < 0) |
995 | return r; | |
996 | ||
1e7a0e21 | 997 | r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex); |
a13c50e7 TG |
998 | if (r < 0) |
999 | return r; | |
1000 | ||
1e7a0e21 | 1001 | r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link); |
a13c50e7 TG |
1002 | if (r < 0) |
1003 | return r; | |
1004 | ||
1e7a0e21 LP |
1005 | return 0; |
1006 | } | |
1007 | ||
1008 | void ndisc_vacuum(Link *link) { | |
1009 | NDiscRDNSS *r; | |
1010 | NDiscDNSSL *d; | |
1011 | Iterator i; | |
1012 | usec_t time_now; | |
b0b97766 | 1013 | bool updated = false; |
1e7a0e21 LP |
1014 | |
1015 | assert(link); | |
1016 | ||
1017 | /* Removes all RDNSS and DNSSL entries whose validity time has passed */ | |
1018 | ||
1019 | time_now = now(clock_boottime_or_monotonic()); | |
1020 | ||
1021 | SET_FOREACH(r, link->ndisc_rdnss, i) | |
1022 | if (r->valid_until < time_now) { | |
02affb4e | 1023 | free(set_remove(link->ndisc_rdnss, r)); |
b0b97766 | 1024 | updated = true; |
1e7a0e21 | 1025 | } |
a13c50e7 | 1026 | |
1e7a0e21 LP |
1027 | SET_FOREACH(d, link->ndisc_dnssl, i) |
1028 | if (d->valid_until < time_now) { | |
02affb4e | 1029 | free(set_remove(link->ndisc_dnssl, d)); |
b0b97766 | 1030 | updated = true; |
1e7a0e21 | 1031 | } |
b0b97766 YW |
1032 | |
1033 | if (updated) | |
1034 | link_dirty(link); | |
a13c50e7 | 1035 | } |
c69305ff LP |
1036 | |
1037 | void ndisc_flush(Link *link) { | |
1038 | assert(link); | |
1039 | ||
1040 | /* Removes all RDNSS and DNSSL entries, without exception */ | |
1041 | ||
b0b97766 YW |
1042 | link->ndisc_rdnss = set_free(link->ndisc_rdnss); |
1043 | link->ndisc_dnssl = set_free(link->ndisc_dnssl); | |
c69305ff | 1044 | } |
e520ce64 | 1045 | |
5f506a55 SS |
1046 | int ipv6token_new(IPv6Token **ret) { |
1047 | IPv6Token *p; | |
1048 | ||
1049 | p = new(IPv6Token, 1); | |
1050 | if (!p) | |
1051 | return -ENOMEM; | |
1052 | ||
1053 | *p = (IPv6Token) { | |
1054 | .address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_NONE, | |
1055 | }; | |
1056 | ||
1057 | *ret = TAKE_PTR(p); | |
1058 | ||
1059 | return 0; | |
1060 | } | |
1061 | ||
2c621495 YW |
1062 | static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) { |
1063 | siphash24_compress(&p->address_generation_type, sizeof(p->address_generation_type), state); | |
1064 | siphash24_compress(&p->prefix, sizeof(p->prefix), state); | |
1065 | } | |
1066 | ||
1067 | static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) { | |
1068 | int r; | |
1069 | ||
1070 | r = CMP(a->address_generation_type, b->address_generation_type); | |
1071 | if (r != 0) | |
1072 | return r; | |
1073 | ||
1074 | return memcmp(&a->prefix, &b->prefix, sizeof(struct in6_addr)); | |
1075 | } | |
1076 | ||
1077 | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( | |
5f506a55 | 1078 | ipv6_token_hash_ops, |
5f506a55 | 1079 | IPv6Token, |
2c621495 YW |
1080 | ipv6_token_hash_func, |
1081 | ipv6_token_compare_func, | |
5f506a55 SS |
1082 | free); |
1083 | ||
6b000af4 | 1084 | int config_parse_ndisc_deny_listed_prefix( |
e520ce64 SS |
1085 | const char *unit, |
1086 | const char *filename, | |
1087 | unsigned line, | |
1088 | const char *section, | |
1089 | unsigned section_line, | |
1090 | const char *lvalue, | |
1091 | int ltype, | |
1092 | const char *rvalue, | |
1093 | void *data, | |
1094 | void *userdata) { | |
1095 | ||
1096 | Network *network = data; | |
1097 | const char *p; | |
1098 | int r; | |
1099 | ||
1100 | assert(filename); | |
1101 | assert(lvalue); | |
1102 | assert(rvalue); | |
1103 | assert(data); | |
1104 | ||
1105 | if (isempty(rvalue)) { | |
6b000af4 | 1106 | network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix); |
e520ce64 SS |
1107 | return 0; |
1108 | } | |
1109 | ||
1110 | for (p = rvalue;;) { | |
1111 | _cleanup_free_ char *n = NULL; | |
1112 | _cleanup_free_ struct in6_addr *a = NULL; | |
1113 | union in_addr_union ip; | |
1114 | ||
1115 | r = extract_first_word(&p, &n, NULL, 0); | |
d96edb2c YW |
1116 | if (r == -ENOMEM) |
1117 | return log_oom(); | |
e520ce64 | 1118 | if (r < 0) { |
d96edb2c | 1119 | log_syntax(unit, LOG_WARNING, filename, line, r, |
a8c10331 | 1120 | "Failed to parse NDisc deny-listed prefix, ignoring assignment: %s", |
e520ce64 SS |
1121 | rvalue); |
1122 | return 0; | |
1123 | } | |
1124 | if (r == 0) | |
1125 | return 0; | |
1126 | ||
1127 | r = in_addr_from_string(AF_INET6, n, &ip); | |
1128 | if (r < 0) { | |
d96edb2c | 1129 | log_syntax(unit, LOG_WARNING, filename, line, r, |
a8c10331 | 1130 | "NDisc deny-listed prefix is invalid, ignoring assignment: %s", n); |
e520ce64 SS |
1131 | continue; |
1132 | } | |
1133 | ||
6b000af4 | 1134 | if (set_contains(network->ndisc_deny_listed_prefix, &ip.in6)) |
e4443f9b YW |
1135 | continue; |
1136 | ||
e520ce64 SS |
1137 | a = newdup(struct in6_addr, &ip.in6, 1); |
1138 | if (!a) | |
1139 | return log_oom(); | |
1140 | ||
6b000af4 | 1141 | r = set_ensure_consume(&network->ndisc_deny_listed_prefix, &in6_addr_hash_ops, TAKE_PTR(a)); |
35e601d4 ZJS |
1142 | if (r < 0) |
1143 | return log_oom(); | |
e520ce64 | 1144 | } |
e520ce64 | 1145 | } |
5f506a55 SS |
1146 | |
1147 | int config_parse_address_generation_type( | |
1148 | const char *unit, | |
1149 | const char *filename, | |
1150 | unsigned line, | |
1151 | const char *section, | |
1152 | unsigned section_line, | |
1153 | const char *lvalue, | |
1154 | int ltype, | |
1155 | const char *rvalue, | |
1156 | void *data, | |
1157 | void *userdata) { | |
1158 | ||
1159 | _cleanup_free_ IPv6Token *token = NULL; | |
5f506a55 SS |
1160 | union in_addr_union buffer; |
1161 | Network *network = data; | |
1162 | const char *p; | |
1163 | int r; | |
1164 | ||
1165 | assert(filename); | |
1166 | assert(lvalue); | |
1167 | assert(rvalue); | |
1168 | assert(data); | |
1169 | ||
1170 | if (isempty(rvalue)) { | |
2c621495 | 1171 | network->ipv6_tokens = ordered_set_free(network->ipv6_tokens); |
5f506a55 SS |
1172 | return 0; |
1173 | } | |
1174 | ||
5f506a55 SS |
1175 | r = ipv6token_new(&token); |
1176 | if (r < 0) | |
1177 | return log_oom(); | |
1178 | ||
b751c3e7 | 1179 | if ((p = startswith(rvalue, "static:"))) |
e2c4070e | 1180 | token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC; |
b751c3e7 | 1181 | else if ((p = startswith(rvalue, "prefixstable:"))) |
5f506a55 SS |
1182 | token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE; |
1183 | else { | |
e2c4070e | 1184 | token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC; |
5f506a55 | 1185 | p = rvalue; |
f0c1ad30 YW |
1186 | } |
1187 | ||
5f506a55 SS |
1188 | r = in_addr_from_string(AF_INET6, p, &buffer); |
1189 | if (r < 0) { | |
d96edb2c | 1190 | log_syntax(unit, LOG_WARNING, filename, line, r, |
5f506a55 SS |
1191 | "Failed to parse IPv6 %s, ignoring: %s", lvalue, rvalue); |
1192 | return 0; | |
1193 | } | |
1194 | ||
1195 | if (in_addr_is_null(AF_INET6, &buffer)) { | |
d96edb2c | 1196 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
5f506a55 SS |
1197 | "IPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue); |
1198 | return 0; | |
1199 | } | |
1200 | ||
1201 | token->prefix = buffer.in6; | |
1202 | ||
2c621495 | 1203 | r = ordered_set_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops); |
5f506a55 SS |
1204 | if (r < 0) |
1205 | return log_oom(); | |
1206 | ||
2c621495 YW |
1207 | r = ordered_set_put(network->ipv6_tokens, token); |
1208 | if (r == -EEXIST) | |
1209 | log_syntax(unit, LOG_DEBUG, filename, line, r, | |
1210 | "IPv6 token '%s' is duplicated, ignoring: %m", rvalue); | |
1211 | else if (r < 0) | |
d96edb2c | 1212 | log_syntax(unit, LOG_WARNING, filename, line, r, |
2c621495 YW |
1213 | "Failed to store IPv6 token '%s', ignoring: %m", rvalue); |
1214 | else | |
1215 | TAKE_PTR(token); | |
5f506a55 SS |
1216 | |
1217 | return 0; | |
1218 | } | |
ac24e418 SS |
1219 | |
1220 | DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, | |
1221 | "Failed to parse DHCPv6Client= setting") | |
1222 | static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = { | |
1223 | [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no", | |
1224 | [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always", | |
1225 | [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes", | |
1226 | }; | |
1227 | ||
1228 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES); |