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