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