]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
a13c50e7 TG |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright (C) 2014 Intel Corporation. All rights reserved. | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
9d96e6c3 | 21 | #include <netinet/icmp6.h> |
23f53b99 | 22 | #include <arpa/inet.h> |
a13c50e7 | 23 | |
a13c50e7 TG |
24 | #include "sd-ndisc.h" |
25 | ||
1e7a0e21 | 26 | #include "networkd-ndisc.h" |
23f53b99 | 27 | #include "networkd-route.h" |
1e7a0e21 LP |
28 | |
29 | #define NDISC_DNSSL_MAX 64U | |
30 | #define NDISC_RDNSS_MAX 64U | |
6554550f | 31 | #define NDISC_PREFIX_LFT_MIN 7200U |
fe307276 | 32 | |
3b015d40 TG |
33 | static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { |
34 | _cleanup_link_unref_ Link *link = userdata; | |
35 | int r; | |
36 | ||
37 | assert(link); | |
38 | assert(link->ndisc_messages > 0); | |
39 | ||
313cefa1 | 40 | link->ndisc_messages--; |
3b015d40 TG |
41 | |
42 | r = sd_netlink_message_get_errno(m); | |
43 | if (r < 0 && r != -EEXIST) { | |
44 | log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); | |
45 | link_enter_failed(link); | |
46 | } | |
47 | ||
48 | if (link->ndisc_messages == 0) { | |
49 | link->ndisc_configured = true; | |
50 | link_check_ready(link); | |
51 | } | |
52 | ||
53 | return 1; | |
54 | } | |
55 | ||
1e7a0e21 LP |
56 | static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { |
57 | _cleanup_route_free_ Route *route = NULL; | |
58 | struct in6_addr gateway; | |
59 | uint16_t lifetime; | |
60 | unsigned preference; | |
d6fceaf1 | 61 | uint32_t mtu; |
3b015d40 TG |
62 | usec_t time_now; |
63 | int r; | |
6d7c7615 PF |
64 | Address *address; |
65 | Iterator i; | |
3b015d40 | 66 | |
3b015d40 | 67 | assert(link); |
1e7a0e21 | 68 | assert(rt); |
3b015d40 | 69 | |
1e7a0e21 LP |
70 | r = sd_ndisc_router_get_lifetime(rt, &lifetime); |
71 | if (r < 0) { | |
72 | log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); | |
73 | return; | |
74 | } | |
75 | if (lifetime == 0) /* not a default router */ | |
76 | return; | |
77 | ||
78 | r = sd_ndisc_router_get_address(rt, &gateway); | |
79 | if (r < 0) { | |
80 | log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); | |
81 | return; | |
82 | } | |
83 | ||
6d7c7615 PF |
84 | SET_FOREACH(address, link->addresses, i) { |
85 | if (!memcmp(&gateway, &address->in_addr.in6, | |
86 | sizeof(address->in_addr.in6))) { | |
87 | char buffer[INET6_ADDRSTRLEN]; | |
88 | ||
89 | log_link_debug(link, "No NDisc route added, gateway %s matches local address", | |
90 | inet_ntop(AF_INET6, | |
91 | &address->in_addr.in6, | |
92 | buffer, sizeof(buffer))); | |
93 | return; | |
94 | } | |
95 | } | |
96 | ||
97 | SET_FOREACH(address, link->addresses_foreign, i) { | |
98 | if (!memcmp(&gateway, &address->in_addr.in6, | |
99 | sizeof(address->in_addr.in6))) { | |
100 | char buffer[INET6_ADDRSTRLEN]; | |
101 | ||
102 | log_link_debug(link, "No NDisc route added, gateway %s matches local address", | |
103 | inet_ntop(AF_INET6, | |
104 | &address->in_addr.in6, | |
105 | buffer, sizeof(buffer))); | |
106 | return; | |
107 | } | |
108 | } | |
109 | ||
1e7a0e21 LP |
110 | r = sd_ndisc_router_get_preference(rt, &preference); |
111 | if (r < 0) { | |
112 | log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); | |
113 | return; | |
114 | } | |
115 | ||
116 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
117 | if (r < 0) { | |
118 | log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); | |
119 | return; | |
120 | } | |
121 | ||
d6fceaf1 | 122 | r = sd_ndisc_router_get_mtu(rt, &mtu); |
29b5ad08 JT |
123 | if (r == -ENODATA) |
124 | mtu = 0; | |
125 | else if (r < 0) { | |
d6fceaf1 SS |
126 | log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m"); |
127 | return; | |
128 | } | |
129 | ||
1e7a0e21 LP |
130 | r = route_new(&route); |
131 | if (r < 0) { | |
132 | log_link_error_errno(link, r, "Could not allocate route: %m"); | |
133 | return; | |
134 | } | |
135 | ||
136 | route->family = AF_INET6; | |
2ba31d29 | 137 | route->table = link->network->ipv6_accept_ra_route_table; |
91b8fd3c | 138 | route->priority = link->network->dhcp_route_metric; |
1e7a0e21 LP |
139 | route->protocol = RTPROT_RA; |
140 | route->pref = preference; | |
141 | route->gw.in6 = gateway; | |
142 | route->lifetime = time_now + lifetime * USEC_PER_SEC; | |
d6fceaf1 | 143 | route->mtu = mtu; |
1e7a0e21 LP |
144 | |
145 | r = route_configure(route, link, ndisc_netlink_handler); | |
146 | if (r < 0) { | |
147 | log_link_warning_errno(link, r, "Could not set default route: %m"); | |
148 | link_enter_failed(link); | |
149 | return; | |
150 | } | |
151 | ||
152 | link->ndisc_messages++; | |
153 | } | |
154 | ||
155 | static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { | |
156 | _cleanup_address_free_ Address *address = NULL; | |
6554550f HW |
157 | Address *existing_address; |
158 | uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining; | |
159 | usec_t time_now; | |
1e7a0e21 LP |
160 | unsigned prefixlen; |
161 | int r; | |
162 | ||
163 | assert(link); | |
164 | assert(rt); | |
165 | ||
6554550f HW |
166 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); |
167 | if (r < 0) { | |
168 | log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); | |
169 | return; | |
170 | } | |
171 | ||
1e7a0e21 LP |
172 | r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); |
173 | if (r < 0) { | |
174 | log_link_error_errno(link, r, "Failed to get prefix length: %m"); | |
175 | return; | |
176 | } | |
177 | ||
178 | r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid); | |
179 | if (r < 0) { | |
180 | log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); | |
3b015d40 | 181 | return; |
1e7a0e21 LP |
182 | } |
183 | ||
184 | r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred); | |
185 | if (r < 0) { | |
186 | log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); | |
187 | return; | |
188 | } | |
3b015d40 TG |
189 | |
190 | r = address_new(&address); | |
191 | if (r < 0) { | |
192 | log_link_error_errno(link, r, "Could not allocate address: %m"); | |
193 | return; | |
194 | } | |
195 | ||
3b015d40 | 196 | address->family = AF_INET6; |
1e7a0e21 LP |
197 | r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6); |
198 | if (r < 0) { | |
199 | log_link_error_errno(link, r, "Failed to get prefix address: %m"); | |
200 | return; | |
201 | } | |
202 | ||
3b015d40 | 203 | if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) |
fb84d896 | 204 | memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8); |
3b015d40 | 205 | else { |
fe307276 | 206 | /* see RFC4291 section 2.5.1 */ |
3a437557 NM |
207 | address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0]; |
208 | address->in_addr.in6.s6_addr[8] ^= 1 << 1; | |
209 | address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1]; | |
210 | address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2]; | |
211 | address->in_addr.in6.s6_addr[11] = 0xff; | |
212 | address->in_addr.in6.s6_addr[12] = 0xfe; | |
213 | address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3]; | |
214 | address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4]; | |
215 | address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5]; | |
3b015d40 TG |
216 | } |
217 | address->prefixlen = prefixlen; | |
f217be19 | 218 | address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; |
3b015d40 | 219 | address->cinfo.ifa_prefered = lifetime_preferred; |
6554550f HW |
220 | |
221 | /* see RFC4862 section 5.5.3.e */ | |
222 | r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address); | |
223 | if (r > 0) { | |
224 | lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC; | |
225 | if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining) | |
226 | address->cinfo.ifa_valid = lifetime_valid; | |
227 | else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN) | |
228 | address->cinfo.ifa_valid = lifetime_remaining; | |
229 | else | |
230 | address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN; | |
231 | } else if (lifetime_valid > 0) | |
232 | address->cinfo.ifa_valid = lifetime_valid; | |
233 | else | |
234 | return; /* see RFC4862 section 5.5.3.d */ | |
235 | ||
236 | if (address->cinfo.ifa_valid == 0) | |
237 | return; | |
3b015d40 TG |
238 | |
239 | r = address_configure(address, link, ndisc_netlink_handler, true); | |
240 | if (r < 0) { | |
241 | log_link_warning_errno(link, r, "Could not set SLAAC address: %m"); | |
242 | link_enter_failed(link); | |
243 | return; | |
244 | } | |
245 | ||
313cefa1 | 246 | link->ndisc_messages++; |
3b015d40 TG |
247 | } |
248 | ||
1e7a0e21 | 249 | static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { |
3b015d40 | 250 | _cleanup_route_free_ Route *route = NULL; |
3b015d40 | 251 | usec_t time_now; |
1e7a0e21 LP |
252 | uint32_t lifetime; |
253 | unsigned prefixlen; | |
3b015d40 TG |
254 | int r; |
255 | ||
3b015d40 | 256 | assert(link); |
1e7a0e21 | 257 | assert(rt); |
3b015d40 | 258 | |
1e7a0e21 LP |
259 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); |
260 | if (r < 0) { | |
261 | log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); | |
3b015d40 | 262 | return; |
1e7a0e21 LP |
263 | } |
264 | ||
265 | r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); | |
266 | if (r < 0) { | |
267 | log_link_error_errno(link, r, "Failed to get prefix length: %m"); | |
268 | return; | |
269 | } | |
270 | ||
271 | r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime); | |
272 | if (r < 0) { | |
273 | log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); | |
274 | return; | |
275 | } | |
3b015d40 TG |
276 | |
277 | r = route_new(&route); | |
278 | if (r < 0) { | |
279 | log_link_error_errno(link, r, "Could not allocate route: %m"); | |
280 | return; | |
281 | } | |
282 | ||
3b015d40 | 283 | route->family = AF_INET6; |
2ba31d29 | 284 | route->table = link->network->ipv6_accept_ra_route_table; |
91b8fd3c | 285 | route->priority = link->network->dhcp_route_metric; |
3b015d40 TG |
286 | route->protocol = RTPROT_RA; |
287 | route->flags = RTM_F_PREFIX; | |
3b015d40 TG |
288 | route->dst_prefixlen = prefixlen; |
289 | route->lifetime = time_now + lifetime * USEC_PER_SEC; | |
290 | ||
1e7a0e21 LP |
291 | r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6); |
292 | if (r < 0) { | |
293 | log_link_error_errno(link, r, "Failed to get prefix address: %m"); | |
294 | return; | |
295 | } | |
296 | ||
3b015d40 TG |
297 | r = route_configure(route, link, ndisc_netlink_handler); |
298 | if (r < 0) { | |
299 | log_link_warning_errno(link, r, "Could not set prefix route: %m"); | |
300 | link_enter_failed(link); | |
301 | return; | |
302 | } | |
303 | ||
313cefa1 | 304 | link->ndisc_messages++; |
3b015d40 TG |
305 | } |
306 | ||
1e7a0e21 | 307 | static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { |
3b015d40 | 308 | _cleanup_route_free_ Route *route = NULL; |
1e7a0e21 LP |
309 | struct in6_addr gateway; |
310 | uint32_t lifetime; | |
311 | unsigned preference, prefixlen; | |
fe307276 | 312 | usec_t time_now; |
7a695d8e | 313 | int r; |
a13c50e7 TG |
314 | |
315 | assert(link); | |
a13c50e7 | 316 | |
1e7a0e21 LP |
317 | r = sd_ndisc_router_route_get_lifetime(rt, &lifetime); |
318 | if (r < 0) { | |
319 | log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); | |
320 | return; | |
321 | } | |
322 | if (lifetime == 0) | |
a13c50e7 TG |
323 | return; |
324 | ||
1e7a0e21 LP |
325 | r = sd_ndisc_router_get_address(rt, &gateway); |
326 | if (r < 0) { | |
327 | log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); | |
328 | return; | |
7a695d8e | 329 | } |
3b015d40 | 330 | |
1e7a0e21 LP |
331 | r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen); |
332 | if (r < 0) { | |
333 | log_link_warning_errno(link, r, "Failed to get route prefix length: %m"); | |
3b015d40 | 334 | return; |
1e7a0e21 LP |
335 | } |
336 | ||
337 | r = sd_ndisc_router_route_get_preference(rt, &preference); | |
338 | if (r < 0) { | |
339 | log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); | |
340 | return; | |
341 | } | |
342 | ||
343 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
344 | if (r < 0) { | |
345 | log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); | |
346 | return; | |
347 | } | |
3b015d40 TG |
348 | |
349 | r = route_new(&route); | |
350 | if (r < 0) { | |
351 | log_link_error_errno(link, r, "Could not allocate route: %m"); | |
352 | return; | |
353 | } | |
354 | ||
3b015d40 | 355 | route->family = AF_INET6; |
2ba31d29 | 356 | route->table = link->network->ipv6_accept_ra_route_table; |
3b015d40 | 357 | route->protocol = RTPROT_RA; |
1e7a0e21 LP |
358 | route->pref = preference; |
359 | route->gw.in6 = gateway; | |
360 | route->dst_prefixlen = prefixlen; | |
3b015d40 TG |
361 | route->lifetime = time_now + lifetime * USEC_PER_SEC; |
362 | ||
1e7a0e21 LP |
363 | r = sd_ndisc_router_route_get_address(rt, &route->dst.in6); |
364 | if (r < 0) { | |
365 | log_link_error_errno(link, r, "Failed to get route address: %m"); | |
366 | return; | |
367 | } | |
368 | ||
3b015d40 TG |
369 | r = route_configure(route, link, ndisc_netlink_handler); |
370 | if (r < 0) { | |
1e7a0e21 | 371 | log_link_warning_errno(link, r, "Could not set additional route: %m"); |
3b015d40 TG |
372 | link_enter_failed(link); |
373 | return; | |
374 | } | |
375 | ||
313cefa1 | 376 | link->ndisc_messages++; |
9d96e6c3 | 377 | } |
a13c50e7 | 378 | |
1e7a0e21 LP |
379 | static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) { |
380 | const NDiscRDNSS *x = p; | |
381 | ||
382 | siphash24_compress(&x->address, sizeof(x->address), state); | |
383 | } | |
384 | ||
385 | static int ndisc_rdnss_compare_func(const void *_a, const void *_b) { | |
386 | const NDiscRDNSS *a = _a, *b = _b; | |
387 | ||
388 | return memcmp(&a->address, &b->address, sizeof(a->address)); | |
389 | } | |
390 | ||
391 | static const struct hash_ops ndisc_rdnss_hash_ops = { | |
392 | .hash = ndisc_rdnss_hash_func, | |
393 | .compare = ndisc_rdnss_compare_func | |
394 | }; | |
395 | ||
396 | static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { | |
397 | uint32_t lifetime; | |
398 | const struct in6_addr *a; | |
399 | usec_t time_now; | |
400 | int i, n, r; | |
401 | ||
402 | assert(link); | |
403 | assert(rt); | |
404 | ||
405 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
406 | if (r < 0) { | |
407 | log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); | |
408 | return; | |
409 | } | |
410 | ||
411 | r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime); | |
412 | if (r < 0) { | |
413 | log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); | |
414 | return; | |
415 | } | |
416 | ||
417 | n = sd_ndisc_router_rdnss_get_addresses(rt, &a); | |
418 | if (n < 0) { | |
419 | log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m"); | |
420 | return; | |
421 | } | |
422 | ||
423 | for (i = 0; i < n; i++) { | |
424 | NDiscRDNSS d = { | |
425 | .address = a[i] | |
426 | }, *x; | |
427 | ||
428 | if (lifetime == 0) { | |
429 | (void) set_remove(link->ndisc_rdnss, &d); | |
430 | link_dirty(link); | |
431 | continue; | |
432 | } | |
433 | ||
434 | x = set_get(link->ndisc_rdnss, &d); | |
435 | if (x) { | |
436 | x->valid_until = time_now + lifetime * USEC_PER_SEC; | |
437 | continue; | |
438 | } | |
439 | ||
440 | ndisc_vacuum(link); | |
441 | ||
442 | if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) { | |
443 | log_link_warning(link, "Too many RDNSS records per link, ignoring."); | |
444 | continue; | |
445 | } | |
446 | ||
447 | r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops); | |
448 | if (r < 0) { | |
449 | log_oom(); | |
450 | return; | |
451 | } | |
452 | ||
453 | x = new0(NDiscRDNSS, 1); | |
454 | if (!x) { | |
455 | log_oom(); | |
456 | return; | |
457 | } | |
458 | ||
459 | x->address = a[i]; | |
460 | x->valid_until = time_now + lifetime * USEC_PER_SEC; | |
461 | ||
462 | r = set_put(link->ndisc_rdnss, x); | |
463 | if (r < 0) { | |
464 | free(x); | |
465 | log_oom(); | |
466 | return; | |
467 | } | |
468 | ||
469 | assert(r > 0); | |
470 | link_dirty(link); | |
471 | } | |
472 | } | |
473 | ||
474 | static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) { | |
475 | const NDiscDNSSL *x = p; | |
476 | ||
477 | siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state); | |
478 | } | |
479 | ||
480 | static int ndisc_dnssl_compare_func(const void *_a, const void *_b) { | |
481 | const NDiscDNSSL *a = _a, *b = _b; | |
482 | ||
483 | return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b)); | |
484 | } | |
485 | ||
486 | static const struct hash_ops ndisc_dnssl_hash_ops = { | |
487 | .hash = ndisc_dnssl_hash_func, | |
488 | .compare = ndisc_dnssl_compare_func | |
489 | }; | |
490 | ||
491 | static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { | |
492 | _cleanup_strv_free_ char **l = NULL; | |
493 | uint32_t lifetime; | |
494 | usec_t time_now; | |
495 | char **i; | |
496 | int r; | |
497 | ||
498 | assert(link); | |
499 | assert(rt); | |
500 | ||
501 | r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); | |
502 | if (r < 0) { | |
503 | log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); | |
504 | return; | |
505 | } | |
506 | ||
507 | r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime); | |
508 | if (r < 0) { | |
509 | log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); | |
510 | return; | |
511 | } | |
512 | ||
513 | r = sd_ndisc_router_dnssl_get_domains(rt, &l); | |
514 | if (r < 0) { | |
515 | log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m"); | |
516 | return; | |
517 | } | |
518 | ||
519 | STRV_FOREACH(i, l) { | |
a34349e7 | 520 | _cleanup_free_ NDiscDNSSL *s; |
1e7a0e21 LP |
521 | NDiscDNSSL *x; |
522 | ||
a34349e7 DM |
523 | s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1); |
524 | if (!s) { | |
525 | log_oom(); | |
526 | return; | |
527 | } | |
528 | ||
529 | strcpy(NDISC_DNSSL_DOMAIN(s), *i); | |
1e7a0e21 LP |
530 | |
531 | if (lifetime == 0) { | |
a34349e7 | 532 | (void) set_remove(link->ndisc_dnssl, s); |
1e7a0e21 LP |
533 | link_dirty(link); |
534 | continue; | |
535 | } | |
536 | ||
a34349e7 | 537 | x = set_get(link->ndisc_dnssl, s); |
1e7a0e21 LP |
538 | if (x) { |
539 | x->valid_until = time_now + lifetime * USEC_PER_SEC; | |
540 | continue; | |
541 | } | |
542 | ||
543 | ndisc_vacuum(link); | |
544 | ||
545 | if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) { | |
546 | log_link_warning(link, "Too many DNSSL records per link, ignoring."); | |
547 | continue; | |
548 | } | |
549 | ||
550 | r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops); | |
551 | if (r < 0) { | |
552 | log_oom(); | |
553 | return; | |
554 | } | |
555 | ||
a34349e7 | 556 | s->valid_until = time_now + lifetime * USEC_PER_SEC; |
1e7a0e21 | 557 | |
a34349e7 | 558 | r = set_put(link->ndisc_dnssl, s); |
1e7a0e21 | 559 | if (r < 0) { |
1e7a0e21 LP |
560 | log_oom(); |
561 | return; | |
562 | } | |
563 | ||
a34349e7 | 564 | s = NULL; |
1e7a0e21 LP |
565 | assert(r > 0); |
566 | link_dirty(link); | |
567 | } | |
568 | } | |
569 | ||
570 | static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { | |
571 | int r; | |
572 | ||
573 | assert(link); | |
574 | assert(rt); | |
575 | ||
576 | r = sd_ndisc_router_option_rewind(rt); | |
577 | for (;;) { | |
578 | uint8_t type; | |
579 | ||
580 | if (r < 0) { | |
581 | log_link_warning_errno(link, r, "Failed to iterate through options: %m"); | |
582 | return; | |
583 | } | |
584 | if (r == 0) /* EOF */ | |
585 | break; | |
586 | ||
587 | r = sd_ndisc_router_option_get_type(rt, &type); | |
588 | if (r < 0) { | |
589 | log_link_warning_errno(link, r, "Failed to get RA option type: %m"); | |
590 | return; | |
591 | } | |
592 | ||
593 | switch (type) { | |
594 | ||
595 | case SD_NDISC_OPTION_PREFIX_INFORMATION: { | |
596 | uint8_t flags; | |
597 | ||
598 | r = sd_ndisc_router_prefix_get_flags(rt, &flags); | |
599 | if (r < 0) { | |
600 | log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); | |
601 | return; | |
602 | } | |
603 | ||
604 | if (flags & ND_OPT_PI_FLAG_ONLINK) | |
605 | ndisc_router_process_onlink_prefix(link, rt); | |
606 | if (flags & ND_OPT_PI_FLAG_AUTO) | |
607 | ndisc_router_process_autonomous_prefix(link, rt); | |
608 | ||
609 | break; | |
610 | } | |
611 | ||
612 | case SD_NDISC_OPTION_ROUTE_INFORMATION: | |
613 | ndisc_router_process_route(link, rt); | |
614 | break; | |
615 | ||
616 | case SD_NDISC_OPTION_RDNSS: | |
fe0252e5 CS |
617 | if (link->network->ipv6_accept_ra_use_dns) |
618 | ndisc_router_process_rdnss(link, rt); | |
1e7a0e21 LP |
619 | break; |
620 | ||
621 | case SD_NDISC_OPTION_DNSSL: | |
fe0252e5 CS |
622 | if (link->network->ipv6_accept_ra_use_dns) |
623 | ndisc_router_process_dnssl(link, rt); | |
1e7a0e21 LP |
624 | break; |
625 | } | |
626 | ||
627 | r = sd_ndisc_router_option_next(rt); | |
628 | } | |
629 | } | |
630 | ||
631 | static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) { | |
632 | uint64_t flags; | |
633 | int r; | |
634 | ||
635 | assert(link); | |
636 | assert(link->network); | |
637 | assert(link->manager); | |
638 | assert(rt); | |
639 | ||
640 | r = sd_ndisc_router_get_flags(rt, &flags); | |
641 | if (r < 0) { | |
642 | log_link_warning_errno(link, r, "Failed to get RA flags: %m"); | |
643 | return; | |
644 | } | |
645 | ||
646 | if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { | |
647 | /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ | |
648 | r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); | |
649 | if (r < 0 && r != -EBUSY) | |
650 | log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); | |
651 | else | |
652 | log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); | |
653 | } | |
654 | ||
655 | ndisc_router_process_default(link, rt); | |
656 | ndisc_router_process_options(link, rt); | |
657 | } | |
658 | ||
659 | static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { | |
9d96e6c3 | 660 | Link *link = userdata; |
a13c50e7 | 661 | |
9d96e6c3 | 662 | assert(link); |
a13c50e7 | 663 | |
9d96e6c3 TG |
664 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) |
665 | return; | |
a13c50e7 | 666 | |
9d96e6c3 | 667 | switch (event) { |
1e7a0e21 LP |
668 | |
669 | case SD_NDISC_EVENT_ROUTER: | |
670 | ndisc_router_handler(link, rt); | |
671 | break; | |
672 | ||
9d96e6c3 | 673 | case SD_NDISC_EVENT_TIMEOUT: |
962b0647 TG |
674 | link->ndisc_configured = true; |
675 | link_check_ready(link); | |
676 | ||
9d96e6c3 TG |
677 | break; |
678 | default: | |
679 | log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); | |
a13c50e7 TG |
680 | } |
681 | } | |
682 | ||
683 | int ndisc_configure(Link *link) { | |
684 | int r; | |
685 | ||
1e7a0e21 LP |
686 | assert(link); |
687 | ||
688 | r = sd_ndisc_new(&link->ndisc); | |
689 | if (r < 0) | |
690 | return r; | |
a13c50e7 | 691 | |
1e7a0e21 | 692 | r = sd_ndisc_attach_event(link->ndisc, NULL, 0); |
a13c50e7 TG |
693 | if (r < 0) |
694 | return r; | |
695 | ||
1e7a0e21 | 696 | r = sd_ndisc_set_mac(link->ndisc, &link->mac); |
a13c50e7 TG |
697 | if (r < 0) |
698 | return r; | |
699 | ||
1e7a0e21 | 700 | r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex); |
a13c50e7 TG |
701 | if (r < 0) |
702 | return r; | |
703 | ||
1e7a0e21 | 704 | r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link); |
a13c50e7 TG |
705 | if (r < 0) |
706 | return r; | |
707 | ||
1e7a0e21 LP |
708 | return 0; |
709 | } | |
710 | ||
711 | void ndisc_vacuum(Link *link) { | |
712 | NDiscRDNSS *r; | |
713 | NDiscDNSSL *d; | |
714 | Iterator i; | |
715 | usec_t time_now; | |
716 | ||
717 | assert(link); | |
718 | ||
719 | /* Removes all RDNSS and DNSSL entries whose validity time has passed */ | |
720 | ||
721 | time_now = now(clock_boottime_or_monotonic()); | |
722 | ||
723 | SET_FOREACH(r, link->ndisc_rdnss, i) | |
724 | if (r->valid_until < time_now) { | |
02affb4e | 725 | free(set_remove(link->ndisc_rdnss, r)); |
1e7a0e21 LP |
726 | link_dirty(link); |
727 | } | |
a13c50e7 | 728 | |
1e7a0e21 LP |
729 | SET_FOREACH(d, link->ndisc_dnssl, i) |
730 | if (d->valid_until < time_now) { | |
02affb4e | 731 | free(set_remove(link->ndisc_dnssl, d)); |
1e7a0e21 LP |
732 | link_dirty(link); |
733 | } | |
a13c50e7 | 734 | } |
c69305ff LP |
735 | |
736 | void ndisc_flush(Link *link) { | |
737 | assert(link); | |
738 | ||
739 | /* Removes all RDNSS and DNSSL entries, without exception */ | |
740 | ||
741 | link->ndisc_rdnss = set_free_free(link->ndisc_rdnss); | |
742 | link->ndisc_dnssl = set_free_free(link->ndisc_dnssl); | |
743 | } |