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