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