]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ndisc.c
network: update debug log when foreign routes are received with ManageForeignRoutes=no
[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"
ac24e418 16#include "string-table.h"
5f506a55 17#include "string-util.h"
51517f9e 18#include "strv.h"
1e7a0e21
LP
19
20#define NDISC_DNSSL_MAX 64U
21#define NDISC_RDNSS_MAX 64U
6554550f 22#define NDISC_PREFIX_LFT_MIN 7200U
fe307276 23
5f506a55
SS
24#define DAD_CONFLICTS_IDGEN_RETRIES_RFC7217 3
25
26/* https://tools.ietf.org/html/rfc5453 */
27/* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */
28
29#define SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } })
30#define SUBNET_ROUTER_ANYCAST_PREFIXLEN 8
31#define RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x02, 0x00, 0x5E, 0xFF, 0xFE } })
32#define RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN 5
33#define RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291 ((struct in6_addr) { .s6_addr = { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } })
34#define RESERVED_SUBNET_ANYCAST_PREFIXLEN 7
35
36#define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
37
38static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
39 assert(addr);
40
41 /* According to rfc4291, generated address should not be in the following ranges. */
42
43 if (memcmp(addr, &SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291, SUBNET_ROUTER_ANYCAST_PREFIXLEN) == 0)
44 return false;
45
46 if (memcmp(addr, &RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291, RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN) == 0)
47 return false;
48
49 if (memcmp(addr, &RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291, RESERVED_SUBNET_ANYCAST_PREFIXLEN) == 0)
50 return false;
51
52 return true;
53}
54
55static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr *addr) {
56 sd_id128_t secret_key;
57 struct siphash state;
58 uint64_t rid;
59 size_t l;
60 int r;
61
62 /* According to rfc7217 section 5.1
63 * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) */
64
65 r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key);
66 if (r < 0)
67 return log_error_errno(r, "Failed to generate key: %m");
68
69 siphash24_init(&state, secret_key.bytes);
70
71 l = MAX(DIV_ROUND_UP(prefix_len, 8), 8);
72 siphash24_compress(prefix, l, &state);
73 siphash24_compress(link->ifname, strlen(link->ifname), &state);
74 siphash24_compress(&link->mac, sizeof(struct ether_addr), &state);
75 siphash24_compress(&dad_counter, sizeof(uint8_t), &state);
76
77 rid = htole64(siphash24_finalize(&state));
78
79 memcpy(addr->s6_addr, prefix->s6_addr, l);
845d784e 80 memcpy(addr->s6_addr + l, &rid, 16 - l);
5f506a55
SS
81
82 return 0;
83}
84
d98c546d 85static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
3b015d40
TG
86 int r;
87
88 assert(link);
d98c546d 89 assert(link->ndisc_routes_messages > 0);
3b015d40 90
d98c546d 91 link->ndisc_routes_messages--;
3b015d40 92
4ff296b0
YW
93 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
94 return 1;
95
3b015d40 96 r = sd_netlink_message_get_errno(m);
4ff296b0 97 if (r < 0 && r != -EEXIST) {
d98c546d 98 log_link_message_error_errno(link, m, r, "Could not set NDisc route");
4ff296b0
YW
99 link_enter_failed(link);
100 return 1;
101 }
3b015d40 102
d98c546d
YW
103 if (link->ndisc_routes_messages == 0) {
104 log_link_debug(link, "NDisc routes set.");
105 link->ndisc_routes_configured = true;
3b015d40
TG
106 link_check_ready(link);
107 }
108
109 return 1;
110}
111
d98c546d 112static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
73854ba1
YW
113 int r;
114
115 assert(link);
d98c546d 116 assert(link->ndisc_addresses_messages > 0);
73854ba1 117
d98c546d 118 link->ndisc_addresses_messages--;
73854ba1 119
4ff296b0
YW
120 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
121 return 1;
122
73854ba1 123 r = sd_netlink_message_get_errno(m);
4ff296b0 124 if (r < 0 && r != -EEXIST) {
d98c546d 125 log_link_message_error_errno(link, m, r, "Could not set NDisc address");
4ff296b0
YW
126 link_enter_failed(link);
127 return 1;
128 } else if (r >= 0)
129 (void) manager_rtnl_process_address(rtnl, m, link->manager);
73854ba1 130
d98c546d
YW
131 if (link->ndisc_addresses_messages == 0) {
132 log_link_debug(link, "NDisc SLAAC addresses set.");
133 link->ndisc_addresses_configured = true;
4ff296b0
YW
134 r = link_request_set_routes(link);
135 if (r < 0) {
136 link_enter_failed(link);
137 return 1;
138 }
73854ba1
YW
139 }
140
141 return 1;
142}
143
d5017c84 144static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
8e766630 145 _cleanup_(route_freep) Route *route = NULL;
ce2ea782 146 union in_addr_union gateway;
1e7a0e21
LP
147 uint16_t lifetime;
148 unsigned preference;
d6fceaf1 149 uint32_t mtu;
3b015d40 150 usec_t time_now;
6d7c7615
PF
151 Address *address;
152 Iterator i;
13e8a49a 153 int r;
3b015d40 154
3b015d40 155 assert(link);
1e7a0e21 156 assert(rt);
3b015d40 157
1e7a0e21 158 r = sd_ndisc_router_get_lifetime(rt, &lifetime);
d5017c84 159 if (r < 0)
13e8a49a 160 return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
d5017c84 161
1e7a0e21 162 if (lifetime == 0) /* not a default router */
d5017c84 163 return 0;
1e7a0e21 164
ce2ea782 165 r = sd_ndisc_router_get_address(rt, &gateway.in6);
d5017c84 166 if (r < 0)
13e8a49a 167 return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
1e7a0e21 168
ce2ea782
YW
169 SET_FOREACH(address, link->addresses, i) {
170 if (address->family != AF_INET6)
171 continue;
172 if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
13e8a49a
YW
173 if (DEBUG_LOGGING) {
174 _cleanup_free_ char *buffer = NULL;
6d7c7615 175
13e8a49a
YW
176 (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer);
177 log_link_debug(link, "No NDisc route added, gateway %s matches local address",
178 strnull(buffer));
179 }
d5017c84 180 return 0;
6d7c7615 181 }
ce2ea782 182 }
6d7c7615 183
ce2ea782
YW
184 SET_FOREACH(address, link->addresses_foreign, i) {
185 if (address->family != AF_INET6)
186 continue;
187 if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
13e8a49a
YW
188 if (DEBUG_LOGGING) {
189 _cleanup_free_ char *buffer = NULL;
6d7c7615 190
13e8a49a
YW
191 (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer);
192 log_link_debug(link, "No NDisc route added, gateway %s matches local address",
193 strnull(buffer));
194 }
d5017c84 195 return 0;
6d7c7615 196 }
ce2ea782 197 }
6d7c7615 198
1e7a0e21 199 r = sd_ndisc_router_get_preference(rt, &preference);
d5017c84 200 if (r < 0)
13e8a49a 201 return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
1e7a0e21
LP
202
203 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
d5017c84 204 if (r < 0)
13e8a49a 205 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
1e7a0e21 206
d6fceaf1 207 r = sd_ndisc_router_get_mtu(rt, &mtu);
29b5ad08
JT
208 if (r == -ENODATA)
209 mtu = 0;
d5017c84 210 else if (r < 0)
13e8a49a 211 return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
d6fceaf1 212
1e7a0e21 213 r = route_new(&route);
d5017c84 214 if (r < 0)
13e8a49a 215 return log_oom();
1e7a0e21
LP
216
217 route->family = AF_INET6;
bdb9f580 218 route->table = link_get_ipv6_accept_ra_route_table(link);
1bf1bfd9 219 route->priority = link->network->dhcp6_route_metric;
1e7a0e21
LP
220 route->protocol = RTPROT_RA;
221 route->pref = preference;
ce2ea782 222 route->gw = gateway;
1e7a0e21 223 route->lifetime = time_now + lifetime * USEC_PER_SEC;
d6fceaf1 224 route->mtu = mtu;
1e7a0e21 225
d98c546d 226 r = route_configure(route, link, ndisc_route_handler);
13e8a49a
YW
227 if (r < 0)
228 return log_link_error_errno(link, r, "Could not set default route: %m");
c4423317 229 if (r > 0)
d98c546d 230 link->ndisc_routes_messages++;
d5017c84 231
1985c54f
YW
232 Route *route_gw;
233 LIST_FOREACH(routes, route_gw, link->network->static_routes) {
234 if (!route_gw->gateway_from_dhcp)
235 continue;
236
237 if (route_gw->family != AF_INET6)
238 continue;
239
240 route_gw->gw = gateway;
241
d98c546d 242 r = route_configure(route_gw, link, ndisc_route_handler);
13e8a49a
YW
243 if (r < 0)
244 return log_link_error_errno(link, r, "Could not set gateway: %m");
1985c54f 245 if (r > 0)
d98c546d 246 link->ndisc_routes_messages++;
1985c54f
YW
247 }
248
d5017c84 249 return 0;
1e7a0e21
LP
250}
251
c24c83dc
KF
252static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address, Set **ret) {
253 _cleanup_set_free_free_ Set *addresses = NULL;
5f506a55
SS
254 struct in6_addr addr;
255 IPv6Token *j;
256 Iterator i;
257 int r;
258
5f506a55 259 assert(link);
c24c83dc
KF
260 assert(address);
261 assert(ret);
262
263 addresses = set_new(&address_hash_ops);
264 if (!addresses)
265 return log_oom();
5f506a55
SS
266
267 addr = address->in_addr.in6;
c24c83dc
KF
268 ORDERED_HASHMAP_FOREACH(j, link->network->ipv6_tokens, i) {
269 bool have_address = false;
270 _cleanup_(address_freep) Address *new_address = NULL;
271
272 r = address_new(&new_address);
273 if (r < 0)
274 return log_oom();
275
276 *new_address = *address;
277
5f506a55
SS
278 if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE
279 && memcmp(&j->prefix, &addr, FAMILY_ADDRESS_SIZE(address->family)) == 0) {
0ddad04e
KF
280 /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop
281 does not actually attempt Duplicate Address Detection; the counter will be incremented
282 only when the address generation algorithm produces an invalid address, and the loop
283 may exit with an address which ends up being unusable due to duplication on the link.
284 */
5f506a55 285 for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) {
c24c83dc 286 r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address->in_addr.in6);
5f506a55 287 if (r < 0)
c24c83dc 288 break;
5f506a55 289
c24c83dc 290 if (stableprivate_address_is_valid(&new_address->in_addr.in6)) {
87f9d6ea 291 have_address = true;
5f506a55
SS
292 break;
293 }
294 }
e2c4070e 295 } else if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC) {
845d784e 296 memcpy(new_address->in_addr.in6.s6_addr + 8, j->prefix.s6_addr + 8, 8);
87f9d6ea 297 have_address = true;
5f506a55
SS
298 }
299
c24c83dc
KF
300 if (have_address) {
301 new_address->prefixlen = prefixlen;
302 new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
303 new_address->cinfo.ifa_prefered = lifetime_preferred;
304
305 r = set_put(addresses, new_address);
306 if (r < 0)
13e8a49a 307 return log_link_error_errno(link, r, "Failed to store SLAAC address: %m");
c24c83dc
KF
308 TAKE_PTR(new_address);
309 }
310 }
311
312 /* fall back to EUI-64 if no tokens provided addresses */
313 if (set_isempty(addresses)) {
314 _cleanup_(address_freep) Address *new_address = NULL;
315
316 r = address_new(&new_address);
317 if (r < 0)
318 return log_oom();
319
320 *new_address = *address;
321
a781ddef
SS
322 r = generate_ipv6_eui_64_address(link, &new_address->in_addr.in6);
323 if (r < 0)
324 return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m");
325
c24c83dc
KF
326 new_address->prefixlen = prefixlen;
327 new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
328 new_address->cinfo.ifa_prefered = lifetime_preferred;
329
330 r = set_put(addresses, new_address);
331 if (r < 0)
13e8a49a 332 return log_link_error_errno(link, r, "Failed to store SLAAC address: %m");
c24c83dc 333 TAKE_PTR(new_address);
5f506a55
SS
334 }
335
c24c83dc 336 *ret = TAKE_PTR(addresses);
5f506a55
SS
337
338 return 0;
339}
c24c83dc 340
d5017c84 341static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
5f506a55 342 uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
c24c83dc 343 _cleanup_set_free_free_ Set *addresses = NULL;
8e766630 344 _cleanup_(address_freep) Address *address = NULL;
1e7a0e21 345 unsigned prefixlen;
5f506a55 346 usec_t time_now;
13e8a49a 347 Address *a;
c24c83dc 348 Iterator i;
1e7a0e21
LP
349 int r;
350
351 assert(link);
352 assert(rt);
353
6554550f 354 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
d5017c84 355 if (r < 0)
13e8a49a 356 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
6554550f 357
1e7a0e21 358 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
d5017c84
YW
359 if (r < 0)
360 return log_link_error_errno(link, r, "Failed to get prefix length: %m");
1e7a0e21
LP
361
362 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
d5017c84
YW
363 if (r < 0)
364 return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
1e7a0e21
LP
365
366 r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
d5017c84
YW
367 if (r < 0)
368 return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
3b015d40 369
92bdc3ff
SS
370 /* The preferred lifetime is never greater than the valid lifetime */
371 if (lifetime_preferred > lifetime_valid)
d5017c84 372 return 0;
92bdc3ff 373
3b015d40 374 r = address_new(&address);
d5017c84 375 if (r < 0)
13e8a49a 376 return log_oom();
3b015d40 377
3b015d40 378 address->family = AF_INET6;
1e7a0e21 379 r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
d5017c84
YW
380 if (r < 0)
381 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
1e7a0e21 382
c24c83dc 383 r = ndisc_router_generate_addresses(link, prefixlen, lifetime_preferred, address, &addresses);
5f506a55 384 if (r < 0)
13e8a49a 385 return r;
c24c83dc
KF
386
387 SET_FOREACH(a, addresses, i) {
13e8a49a
YW
388 Address *existing_address;
389
c24c83dc
KF
390 /* see RFC4862 section 5.5.3.e */
391 r = address_get(link, a->family, &a->in_addr, a->prefixlen, &existing_address);
392 if (r > 0) {
393 lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
394 if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
395 a->cinfo.ifa_valid = lifetime_valid;
396 else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
397 a->cinfo.ifa_valid = lifetime_remaining;
398 else
399 a->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
400 } else if (lifetime_valid > 0)
401 a->cinfo.ifa_valid = lifetime_valid;
6554550f 402 else
01c344bd 403 continue; /* see RFC4862 section 5.5.3.d */
6554550f 404
c24c83dc
KF
405 if (a->cinfo.ifa_valid == 0)
406 continue;
3b015d40 407
d98c546d 408 r = address_configure(a, link, ndisc_address_handler, true);
13e8a49a
YW
409 if (r < 0)
410 return log_link_error_errno(link, r, "Could not set SLAAC address: %m");
c24c83dc 411 if (r > 0)
d98c546d 412 link->ndisc_addresses_messages++;
3b015d40 413 }
d5017c84
YW
414
415 return 0;
3b015d40
TG
416}
417
d5017c84 418static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
8e766630 419 _cleanup_(route_freep) Route *route = NULL;
3b015d40 420 usec_t time_now;
1e7a0e21
LP
421 uint32_t lifetime;
422 unsigned prefixlen;
3b015d40
TG
423 int r;
424
3b015d40 425 assert(link);
1e7a0e21 426 assert(rt);
3b015d40 427
1e7a0e21 428 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
d5017c84 429 if (r < 0)
13e8a49a 430 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
1e7a0e21
LP
431
432 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
d5017c84
YW
433 if (r < 0)
434 return log_link_error_errno(link, r, "Failed to get prefix length: %m");
1e7a0e21
LP
435
436 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
d5017c84
YW
437 if (r < 0)
438 return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
3b015d40
TG
439
440 r = route_new(&route);
d5017c84 441 if (r < 0)
13e8a49a 442 return log_oom();
3b015d40 443
3b015d40 444 route->family = AF_INET6;
bdb9f580 445 route->table = link_get_ipv6_accept_ra_route_table(link);
1bf1bfd9 446 route->priority = link->network->dhcp6_route_metric;
3b015d40
TG
447 route->protocol = RTPROT_RA;
448 route->flags = RTM_F_PREFIX;
3b015d40
TG
449 route->dst_prefixlen = prefixlen;
450 route->lifetime = time_now + lifetime * USEC_PER_SEC;
451
1e7a0e21 452 r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
d5017c84
YW
453 if (r < 0)
454 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
1e7a0e21 455
d98c546d 456 r = route_configure(route, link, ndisc_route_handler);
13e8a49a
YW
457 if (r < 0)
458 return log_link_error_errno(link, r, "Could not set prefix route: %m");;
c4423317 459 if (r > 0)
d98c546d 460 link->ndisc_routes_messages++;
d5017c84
YW
461
462 return 0;
3b015d40
TG
463}
464
d5017c84 465static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
8e766630 466 _cleanup_(route_freep) Route *route = NULL;
1e7a0e21
LP
467 struct in6_addr gateway;
468 uint32_t lifetime;
469 unsigned preference, prefixlen;
fe307276 470 usec_t time_now;
7a695d8e 471 int r;
a13c50e7
TG
472
473 assert(link);
a13c50e7 474
1e7a0e21 475 r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
d5017c84 476 if (r < 0)
13e8a49a 477 return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
d5017c84 478
1e7a0e21 479 if (lifetime == 0)
d5017c84 480 return 0;
a13c50e7 481
1e7a0e21 482 r = sd_ndisc_router_get_address(rt, &gateway);
d5017c84 483 if (r < 0)
13e8a49a 484 return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
3b015d40 485
1e7a0e21 486 r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
d5017c84 487 if (r < 0)
13e8a49a 488 return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
1e7a0e21
LP
489
490 r = sd_ndisc_router_route_get_preference(rt, &preference);
d5017c84 491 if (r < 0)
13e8a49a 492 return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
1e7a0e21
LP
493
494 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
d5017c84 495 if (r < 0)
13e8a49a 496 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
3b015d40
TG
497
498 r = route_new(&route);
d5017c84 499 if (r < 0)
13e8a49a 500 return log_oom();
3b015d40 501
3b015d40 502 route->family = AF_INET6;
bdb9f580 503 route->table = link_get_ipv6_accept_ra_route_table(link);
1bf1bfd9 504 route->priority = link->network->dhcp6_route_metric;
3b015d40 505 route->protocol = RTPROT_RA;
1e7a0e21
LP
506 route->pref = preference;
507 route->gw.in6 = gateway;
508 route->dst_prefixlen = prefixlen;
3b015d40
TG
509 route->lifetime = time_now + lifetime * USEC_PER_SEC;
510
1e7a0e21 511 r = sd_ndisc_router_route_get_address(rt, &route->dst.in6);
d5017c84
YW
512 if (r < 0)
513 return log_link_error_errno(link, r, "Failed to get route address: %m");
1e7a0e21 514
d98c546d 515 r = route_configure(route, link, ndisc_route_handler);
13e8a49a
YW
516 if (r < 0)
517 return log_link_error_errno(link, r, "Could not set additional route: %m");
c4423317 518 if (r > 0)
d98c546d 519 link->ndisc_routes_messages++;
d5017c84
YW
520
521 return 0;
9d96e6c3 522}
a13c50e7 523
7a08d314 524static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) {
1e7a0e21
LP
525 siphash24_compress(&x->address, sizeof(x->address), state);
526}
527
7a08d314 528static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
1e7a0e21
LP
529 return memcmp(&a->address, &b->address, sizeof(a->address));
530}
531
7a08d314 532DEFINE_PRIVATE_HASH_OPS(ndisc_rdnss_hash_ops, NDiscRDNSS, ndisc_rdnss_hash_func, ndisc_rdnss_compare_func);
1e7a0e21 533
d5017c84 534static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
1e7a0e21
LP
535 uint32_t lifetime;
536 const struct in6_addr *a;
537 usec_t time_now;
13e8a49a 538 int n, r;
1e7a0e21
LP
539
540 assert(link);
541 assert(rt);
542
543 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
d5017c84 544 if (r < 0)
13e8a49a 545 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
1e7a0e21
LP
546
547 r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
d5017c84 548 if (r < 0)
13e8a49a 549 return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m");
1e7a0e21
LP
550
551 n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
d5017c84 552 if (n < 0)
13e8a49a 553 return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
1e7a0e21 554
13e8a49a 555 for (int i = 0; i < n; i++) {
d5017c84 556 _cleanup_free_ NDiscRDNSS *x = NULL;
1e7a0e21 557 NDiscRDNSS d = {
d5017c84
YW
558 .address = a[i],
559 }, *y;
1e7a0e21
LP
560
561 if (lifetime == 0) {
562 (void) set_remove(link->ndisc_rdnss, &d);
563 link_dirty(link);
564 continue;
565 }
566
d5017c84
YW
567 y = set_get(link->ndisc_rdnss, &d);
568 if (y) {
569 y->valid_until = time_now + lifetime * USEC_PER_SEC;
1e7a0e21
LP
570 continue;
571 }
572
573 ndisc_vacuum(link);
574
575 if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
576 log_link_warning(link, "Too many RDNSS records per link, ignoring.");
577 continue;
578 }
579
d5017c84
YW
580 x = new(NDiscRDNSS, 1);
581 if (!x)
582 return log_oom();
1e7a0e21 583
d5017c84
YW
584 *x = (NDiscRDNSS) {
585 .address = a[i],
586 .valid_until = time_now + lifetime * USEC_PER_SEC,
587 };
1e7a0e21 588
35e601d4 589 r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
d5017c84
YW
590 if (r < 0)
591 return log_oom();
1e7a0e21 592 assert(r > 0);
35e601d4 593
1e7a0e21
LP
594 link_dirty(link);
595 }
d5017c84
YW
596
597 return 0;
1e7a0e21
LP
598}
599
7a08d314 600static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
1e7a0e21
LP
601 siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
602}
603
7a08d314 604static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
1e7a0e21
LP
605 return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
606}
607
7a08d314 608DEFINE_PRIVATE_HASH_OPS(ndisc_dnssl_hash_ops, NDiscDNSSL, ndisc_dnssl_hash_func, ndisc_dnssl_compare_func);
1e7a0e21 609
13e8a49a 610static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
1e7a0e21
LP
611 _cleanup_strv_free_ char **l = NULL;
612 uint32_t lifetime;
613 usec_t time_now;
614 char **i;
615 int r;
616
617 assert(link);
618 assert(rt);
619
620 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
13e8a49a
YW
621 if (r < 0)
622 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
1e7a0e21
LP
623
624 r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
13e8a49a
YW
625 if (r < 0)
626 return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m");
1e7a0e21
LP
627
628 r = sd_ndisc_router_dnssl_get_domains(rt, &l);
13e8a49a
YW
629 if (r < 0)
630 return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
1e7a0e21
LP
631
632 STRV_FOREACH(i, l) {
13e8a49a 633 _cleanup_free_ NDiscDNSSL *s = NULL;
1e7a0e21
LP
634 NDiscDNSSL *x;
635
a34349e7 636 s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
13e8a49a
YW
637 if (!s)
638 return log_oom();
a34349e7
DM
639
640 strcpy(NDISC_DNSSL_DOMAIN(s), *i);
1e7a0e21
LP
641
642 if (lifetime == 0) {
a34349e7 643 (void) set_remove(link->ndisc_dnssl, s);
1e7a0e21
LP
644 link_dirty(link);
645 continue;
646 }
647
a34349e7 648 x = set_get(link->ndisc_dnssl, s);
1e7a0e21
LP
649 if (x) {
650 x->valid_until = time_now + lifetime * USEC_PER_SEC;
651 continue;
652 }
653
654 ndisc_vacuum(link);
655
656 if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
657 log_link_warning(link, "Too many DNSSL records per link, ignoring.");
658 continue;
659 }
660
a34349e7 661 s->valid_until = time_now + lifetime * USEC_PER_SEC;
1e7a0e21 662
35e601d4 663 r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
13e8a49a
YW
664 if (r < 0)
665 return log_oom();
1e7a0e21 666 assert(r > 0);
de7fef4b 667
1e7a0e21
LP
668 link_dirty(link);
669 }
13e8a49a
YW
670
671 return 0;
1e7a0e21
LP
672}
673
e8c9b5b0 674static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
1e7a0e21 675 assert(link);
55d3fdcf 676 assert(link->network);
1e7a0e21
LP
677 assert(rt);
678
13e8a49a 679 for (int r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
1e7a0e21
LP
680 uint8_t type;
681
e8c9b5b0 682 if (r < 0)
13e8a49a 683 return log_link_error_errno(link, r, "Failed to iterate through options: %m");
1e7a0e21 684 if (r == 0) /* EOF */
13e8a49a 685 return 0;
1e7a0e21
LP
686
687 r = sd_ndisc_router_option_get_type(rt, &type);
e8c9b5b0 688 if (r < 0)
13e8a49a 689 return log_link_error_errno(link, r, "Failed to get RA option type: %m");
1e7a0e21
LP
690
691 switch (type) {
692
693 case SD_NDISC_OPTION_PREFIX_INFORMATION: {
55d3fdcf 694 union in_addr_union a;
1e7a0e21
LP
695 uint8_t flags;
696
55d3fdcf
YW
697 r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
698 if (r < 0)
699 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
700
6b000af4 701 if (set_contains(link->network->ndisc_deny_listed_prefix, &a.in6)) {
55d3fdcf
YW
702 if (DEBUG_LOGGING) {
703 _cleanup_free_ char *b = NULL;
704
705 (void) in_addr_to_string(AF_INET6, &a, &b);
6b000af4 706 log_link_debug(link, "Prefix '%s' is deny-listed, ignoring", strna(b));
55d3fdcf 707 }
55d3fdcf
YW
708 break;
709 }
710
1e7a0e21 711 r = sd_ndisc_router_prefix_get_flags(rt, &flags);
e8c9b5b0 712 if (r < 0)
13e8a49a 713 return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m");
1e7a0e21 714
87d8a4de 715 if (link->network->ipv6_accept_ra_use_onlink_prefix &&
13e8a49a
YW
716 FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
717 r = ndisc_router_process_onlink_prefix(link, rt);
718 if (r < 0)
719 return r;
720 }
062c2eea 721
87d8a4de 722 if (link->network->ipv6_accept_ra_use_autonomous_prefix &&
13e8a49a
YW
723 FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
724 r = ndisc_router_process_autonomous_prefix(link, rt);
725 if (r < 0)
726 return r;
727 }
1e7a0e21
LP
728 break;
729 }
730
731 case SD_NDISC_OPTION_ROUTE_INFORMATION:
13e8a49a
YW
732 r = ndisc_router_process_route(link, rt);
733 if (r < 0)
734 return r;
1e7a0e21
LP
735 break;
736
737 case SD_NDISC_OPTION_RDNSS:
13e8a49a
YW
738 if (link->network->ipv6_accept_ra_use_dns) {
739 r = ndisc_router_process_rdnss(link, rt);
740 if (r < 0)
741 return r;
742 }
1e7a0e21
LP
743 break;
744
745 case SD_NDISC_OPTION_DNSSL:
13e8a49a
YW
746 if (link->network->ipv6_accept_ra_use_dns) {
747 r = ndisc_router_process_dnssl(link, rt);
748 if (r < 0)
749 return r;
750 }
1e7a0e21
LP
751 break;
752 }
1e7a0e21
LP
753 }
754}
755
d5017c84 756static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
1e7a0e21 757 uint64_t flags;
86e2be7b 758 int r;
1e7a0e21
LP
759
760 assert(link);
761 assert(link->network);
762 assert(link->manager);
763 assert(rt);
764
765 r = sd_ndisc_router_get_flags(rt, &flags);
d5017c84 766 if (r < 0)
13e8a49a 767 return log_link_error_errno(link, r, "Failed to get RA flags: %m");
1e7a0e21 768
ac24e418
SS
769 if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) && link->network->ipv6_accept_ra_start_dhcp6_client)) {
770
771 if (link->network->ipv6_accept_ra_start_dhcp6_client == IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS)
772 r = dhcp6_request_address(link, false);
773 else
774 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
775 r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
1e7a0e21 776 if (r < 0 && r != -EBUSY)
13e8a49a 777 return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
d5017c84 778 else {
1e7a0e21 779 log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
d5017c84
YW
780 r = 0;
781 }
1e7a0e21
LP
782 }
783
13e8a49a
YW
784 r = ndisc_router_process_default(link, rt);
785 if (r < 0)
786 return r;
787 r = ndisc_router_process_options(link, rt);
788 if (r < 0)
789 return r;
d5017c84
YW
790
791 return r;
1e7a0e21
LP
792}
793
794static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
9d96e6c3 795 Link *link = userdata;
13e8a49a 796 int r;
a13c50e7 797
9d96e6c3 798 assert(link);
a13c50e7 799
9d96e6c3
TG
800 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
801 return;
a13c50e7 802
9d96e6c3 803 switch (event) {
1e7a0e21
LP
804
805 case SD_NDISC_EVENT_ROUTER:
d98c546d
YW
806 link->ndisc_addresses_configured = false;
807 link->ndisc_routes_configured = false;
808
13e8a49a
YW
809 r = ndisc_router_handler(link, rt);
810 if (r < 0) {
811 link_enter_failed(link);
812 return;
813 }
d98c546d
YW
814
815 if (link->ndisc_addresses_messages == 0)
816 link->ndisc_addresses_configured = true;
0c816fcc 817 else {
d98c546d
YW
818 log_link_debug(link, "Setting SLAAC addresses.");
819
0c816fcc
YW
820 /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
821 * Before they are called, the related flags must be cleared. Otherwise, the link
822 * becomes configured state before routes are configured. */
823 link->static_routes_configured = false;
824 link->static_nexthops_configured = false;
825 }
826
d98c546d
YW
827 if (link->ndisc_routes_messages == 0)
828 link->ndisc_routes_configured = true;
829 else
830 log_link_debug(link, "Setting NDisc routes.");
831
832 if (link->ndisc_addresses_configured && link->ndisc_routes_configured)
833 link_check_ready(link);
834 else
835 link_set_state(link, LINK_STATE_CONFIGURING);
1e7a0e21
LP
836 break;
837
9d96e6c3 838 case SD_NDISC_EVENT_TIMEOUT:
a4623f84 839 log_link_debug(link, "NDISC handler get timeout event");
d98c546d
YW
840 link->ndisc_addresses_configured = true;
841 link->ndisc_routes_configured = true;
962b0647
TG
842 link_check_ready(link);
843
9d96e6c3
TG
844 break;
845 default:
13e8a49a 846 assert_not_reached("IPv6 Neighbor Discovery unknown event");
a13c50e7
TG
847 }
848}
849
850int ndisc_configure(Link *link) {
851 int r;
852
1e7a0e21
LP
853 assert(link);
854
855 r = sd_ndisc_new(&link->ndisc);
856 if (r < 0)
857 return r;
a13c50e7 858
1e7a0e21 859 r = sd_ndisc_attach_event(link->ndisc, NULL, 0);
a13c50e7
TG
860 if (r < 0)
861 return r;
862
1e7a0e21 863 r = sd_ndisc_set_mac(link->ndisc, &link->mac);
a13c50e7
TG
864 if (r < 0)
865 return r;
866
1e7a0e21 867 r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
a13c50e7
TG
868 if (r < 0)
869 return r;
870
1e7a0e21 871 r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
a13c50e7
TG
872 if (r < 0)
873 return r;
874
1e7a0e21
LP
875 return 0;
876}
877
878void ndisc_vacuum(Link *link) {
879 NDiscRDNSS *r;
880 NDiscDNSSL *d;
881 Iterator i;
882 usec_t time_now;
883
884 assert(link);
885
886 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
887
888 time_now = now(clock_boottime_or_monotonic());
889
890 SET_FOREACH(r, link->ndisc_rdnss, i)
891 if (r->valid_until < time_now) {
02affb4e 892 free(set_remove(link->ndisc_rdnss, r));
1e7a0e21
LP
893 link_dirty(link);
894 }
a13c50e7 895
1e7a0e21
LP
896 SET_FOREACH(d, link->ndisc_dnssl, i)
897 if (d->valid_until < time_now) {
02affb4e 898 free(set_remove(link->ndisc_dnssl, d));
1e7a0e21
LP
899 link_dirty(link);
900 }
a13c50e7 901}
c69305ff
LP
902
903void ndisc_flush(Link *link) {
904 assert(link);
905
906 /* Removes all RDNSS and DNSSL entries, without exception */
907
908 link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
909 link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
910}
e520ce64 911
5f506a55
SS
912int ipv6token_new(IPv6Token **ret) {
913 IPv6Token *p;
914
915 p = new(IPv6Token, 1);
916 if (!p)
917 return -ENOMEM;
918
919 *p = (IPv6Token) {
920 .address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_NONE,
921 };
922
923 *ret = TAKE_PTR(p);
924
925 return 0;
926}
927
928DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
929 ipv6_token_hash_ops,
930 void,
931 trivial_hash_func,
932 trivial_compare_func,
933 IPv6Token,
934 free);
935
6b000af4 936int config_parse_ndisc_deny_listed_prefix(
e520ce64
SS
937 const char *unit,
938 const char *filename,
939 unsigned line,
940 const char *section,
941 unsigned section_line,
942 const char *lvalue,
943 int ltype,
944 const char *rvalue,
945 void *data,
946 void *userdata) {
947
948 Network *network = data;
949 const char *p;
950 int r;
951
952 assert(filename);
953 assert(lvalue);
954 assert(rvalue);
955 assert(data);
956
957 if (isempty(rvalue)) {
6b000af4 958 network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix);
e520ce64
SS
959 return 0;
960 }
961
962 for (p = rvalue;;) {
963 _cleanup_free_ char *n = NULL;
964 _cleanup_free_ struct in6_addr *a = NULL;
965 union in_addr_union ip;
966
967 r = extract_first_word(&p, &n, NULL, 0);
d96edb2c
YW
968 if (r == -ENOMEM)
969 return log_oom();
e520ce64 970 if (r < 0) {
d96edb2c 971 log_syntax(unit, LOG_WARNING, filename, line, r,
6b000af4 972 "Failed to parse NDISC deny-listed prefix, ignoring assignment: %s",
e520ce64
SS
973 rvalue);
974 return 0;
975 }
976 if (r == 0)
977 return 0;
978
979 r = in_addr_from_string(AF_INET6, n, &ip);
980 if (r < 0) {
d96edb2c 981 log_syntax(unit, LOG_WARNING, filename, line, r,
6b000af4 982 "NDISC deny-listed prefix is invalid, ignoring assignment: %s", n);
e520ce64
SS
983 continue;
984 }
985
6b000af4 986 if (set_contains(network->ndisc_deny_listed_prefix, &ip.in6))
e4443f9b
YW
987 continue;
988
e520ce64
SS
989 a = newdup(struct in6_addr, &ip.in6, 1);
990 if (!a)
991 return log_oom();
992
6b000af4 993 r = set_ensure_consume(&network->ndisc_deny_listed_prefix, &in6_addr_hash_ops, TAKE_PTR(a));
35e601d4
ZJS
994 if (r < 0)
995 return log_oom();
e520ce64 996 }
e520ce64 997}
5f506a55
SS
998
999int config_parse_address_generation_type(
1000 const char *unit,
1001 const char *filename,
1002 unsigned line,
1003 const char *section,
1004 unsigned section_line,
1005 const char *lvalue,
1006 int ltype,
1007 const char *rvalue,
1008 void *data,
1009 void *userdata) {
1010
1011 _cleanup_free_ IPv6Token *token = NULL;
5f506a55
SS
1012 union in_addr_union buffer;
1013 Network *network = data;
1014 const char *p;
1015 int r;
1016
1017 assert(filename);
1018 assert(lvalue);
1019 assert(rvalue);
1020 assert(data);
1021
1022 if (isempty(rvalue)) {
1023 network->ipv6_tokens = ordered_hashmap_free(network->ipv6_tokens);
1024 return 0;
1025 }
1026
5f506a55
SS
1027 r = ipv6token_new(&token);
1028 if (r < 0)
1029 return log_oom();
1030
b751c3e7 1031 if ((p = startswith(rvalue, "static:")))
e2c4070e 1032 token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC;
b751c3e7 1033 else if ((p = startswith(rvalue, "prefixstable:")))
5f506a55
SS
1034 token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE;
1035 else {
e2c4070e 1036 token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC;
5f506a55 1037 p = rvalue;
f0c1ad30
YW
1038 }
1039
5f506a55
SS
1040 r = in_addr_from_string(AF_INET6, p, &buffer);
1041 if (r < 0) {
d96edb2c 1042 log_syntax(unit, LOG_WARNING, filename, line, r,
5f506a55
SS
1043 "Failed to parse IPv6 %s, ignoring: %s", lvalue, rvalue);
1044 return 0;
1045 }
1046
1047 if (in_addr_is_null(AF_INET6, &buffer)) {
d96edb2c 1048 log_syntax(unit, LOG_WARNING, filename, line, 0,
5f506a55
SS
1049 "IPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue);
1050 return 0;
1051 }
1052
1053 token->prefix = buffer.in6;
1054
1055 r = ordered_hashmap_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops);
1056 if (r < 0)
1057 return log_oom();
1058
1059 r = ordered_hashmap_put(network->ipv6_tokens, &token->prefix, token);
1060 if (r < 0) {
d96edb2c 1061 log_syntax(unit, LOG_WARNING, filename, line, r,
5f506a55
SS
1062 "Failed to store IPv6 token '%s'", rvalue);
1063 return 0;
1064 }
1065
1066 TAKE_PTR(token);
1067
1068 return 0;
1069}
ac24e418
SS
1070
1071DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
1072 "Failed to parse DHCPv6Client= setting")
1073static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
1074 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no",
1075 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
1076 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes",
1077};
1078
1079DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);