]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-radv.c
network: fix possible NULL-pointer dereference
[thirdparty/systemd.git] / src / network / networkd-radv.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
091214b6 2/***
810adae9 3 Copyright © 2017 Intel Corporation. All rights reserved.
091214b6
PF
4***/
5
6#include <netinet/icmp6.h>
7#include <arpa/inet.h>
8
ca5ad760 9#include "dns-domain.h"
f09a4747 10#include "networkd-address-generation.h"
3b6a3bde 11#include "networkd-address.h"
d5ebcf65 12#include "networkd-dhcp-prefix-delegation.h"
b5ce4047 13#include "networkd-link.h"
c555a358 14#include "networkd-manager.h"
b5ce4047 15#include "networkd-network.h"
a254fab2 16#include "networkd-queue.h"
091214b6 17#include "networkd-radv.h"
344b3cff 18#include "networkd-route-util.h"
6e849e95 19#include "parse-util.h"
4f1ac4a3 20#include "radv-internal.h"
6e849e95 21#include "string-util.h"
6b1dec66 22#include "string-table.h"
51517f9e 23#include "strv.h"
6e849e95 24
0943b3b7
YW
25void network_adjust_radv(Network *network) {
26 assert(network);
27
28 /* After this function is called, network->router_prefix_delegation can be treated as a boolean. */
29
a27588d4 30 if (network->dhcp_pd < 0)
0943b3b7 31 /* For backward compatibility. */
a27588d4 32 network->dhcp_pd = FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_DHCP6);
0943b3b7
YW
33
34 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
35 if (network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE)
f81ac115 36 log_warning("%s: IPv6PrefixDelegation= is enabled but IPv6 link-local addressing is disabled. "
0943b3b7
YW
37 "Disabling IPv6PrefixDelegation=.", network->filename);
38
39 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
40 }
41
42 if (network->router_prefix_delegation == RADV_PREFIX_DELEGATION_NONE) {
43 network->n_router_dns = 0;
44 network->router_dns = mfree(network->router_dns);
45 network->router_search_domains = ordered_set_free(network->router_search_domains);
46 }
47
48 if (!FLAGS_SET(network->router_prefix_delegation, RADV_PREFIX_DELEGATION_STATIC)) {
49 network->prefixes_by_section = hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
50 network->route_prefixes_by_section = hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
51 }
52}
53
0f96a823 54bool link_radv_enabled(Link *link) {
0943b3b7
YW
55 assert(link);
56
e096cab2 57 if (!link_may_have_ipv6ll(link))
0943b3b7
YW
58 return false;
59
218a8502
YW
60 if (link->hw_addr.length != ETH_ALEN)
61 return false;
62
0943b3b7
YW
63 return link->network->router_prefix_delegation;
64}
65
064dfb05 66Prefix *prefix_free(Prefix *prefix) {
6e849e95 67 if (!prefix)
064dfb05 68 return NULL;
6e849e95
PF
69
70 if (prefix->network) {
ecb0e85e
YW
71 assert(prefix->section);
72 hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
6e849e95
PF
73 }
74
307fe3cd 75 config_section_free(prefix->section);
e609cd06 76 set_free(prefix->tokens);
6e849e95 77
064dfb05 78 return mfree(prefix);
6e849e95
PF
79}
80
307fe3cd 81DEFINE_SECTION_CLEANUP_FUNCTIONS(Prefix, prefix_free);
064dfb05 82
a8d4a210 83static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
307fe3cd 84 _cleanup_(config_section_freep) ConfigSection *n = NULL;
8e766630 85 _cleanup_(prefix_freep) Prefix *prefix = NULL;
6e849e95
PF
86 int r;
87
88 assert(network);
89 assert(ret);
ecb0e85e
YW
90 assert(filename);
91 assert(section_line > 0);
6e849e95 92
307fe3cd 93 r = config_section_new(filename, section_line, &n);
ecb0e85e
YW
94 if (r < 0)
95 return r;
6e849e95 96
ecb0e85e
YW
97 prefix = hashmap_get(network->prefixes_by_section, n);
98 if (prefix) {
99 *ret = TAKE_PTR(prefix);
100 return 0;
6e849e95
PF
101 }
102
2ac41679
YW
103 prefix = new(Prefix, 1);
104 if (!prefix)
105 return -ENOMEM;
106
107 *prefix = (Prefix) {
108 .network = network,
109 .section = TAKE_PTR(n),
6e849e95 110
c9e2c2da
YW
111 .preferred_lifetime = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
112 .valid_lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
2ac41679
YW
113 .onlink = true,
114 .address_auto_configuration = true,
115 };
6e849e95 116
307fe3cd 117 r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
ecb0e85e
YW
118 if (r < 0)
119 return r;
6e849e95 120
1cc6c93a 121 *ret = TAKE_PTR(prefix);
6e849e95
PF
122 return 0;
123}
124
064dfb05 125RoutePrefix *route_prefix_free(RoutePrefix *prefix) {
203d4df5 126 if (!prefix)
064dfb05 127 return NULL;
203d4df5
SS
128
129 if (prefix->network) {
ecb0e85e
YW
130 assert(prefix->section);
131 hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
203d4df5
SS
132 }
133
307fe3cd 134 config_section_free(prefix->section);
203d4df5 135
064dfb05
YW
136 return mfree(prefix);
137}
138
307fe3cd 139DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutePrefix, route_prefix_free);
064dfb05 140
a8d4a210 141static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
307fe3cd 142 _cleanup_(config_section_freep) ConfigSection *n = NULL;
95081e08 143 _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
203d4df5
SS
144 int r;
145
146 assert(network);
147 assert(ret);
ecb0e85e
YW
148 assert(filename);
149 assert(section_line > 0);
203d4df5 150
307fe3cd 151 r = config_section_new(filename, section_line, &n);
ecb0e85e
YW
152 if (r < 0)
153 return r;
203d4df5 154
ecb0e85e
YW
155 prefix = hashmap_get(network->route_prefixes_by_section, n);
156 if (prefix) {
157 *ret = TAKE_PTR(prefix);
158 return 0;
203d4df5
SS
159 }
160
2ac41679
YW
161 prefix = new(RoutePrefix, 1);
162 if (!prefix)
163 return -ENOMEM;
203d4df5 164
2ac41679
YW
165 *prefix = (RoutePrefix) {
166 .network = network,
167 .section = TAKE_PTR(n),
168
c9e2c2da 169 .lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
2ac41679 170 };
203d4df5 171
307fe3cd 172 r = hashmap_ensure_put(&network->route_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
ecb0e85e
YW
173 if (r < 0)
174 return r;
203d4df5
SS
175
176 *ret = TAKE_PTR(prefix);
203d4df5
SS
177 return 0;
178}
179
3b6a3bde
YW
180int link_request_radv_addresses(Link *link) {
181 Prefix *p;
182 int r;
183
184 assert(link);
185
186 if (!link_radv_enabled(link))
187 return 0;
188
189 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
e609cd06 190 _cleanup_set_free_ Set *addresses = NULL;
2ac41679 191 struct in6_addr *a;
3b6a3bde
YW
192
193 if (!p->assign)
194 continue;
195
e609cd06 196 /* radv_generate_addresses() below requires the prefix length <= 64. */
2110040b 197 if (p->prefixlen > 64)
fcd7ad52 198 continue;
fcd7ad52 199
2ac41679 200 r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
3b6a3bde 201 if (r < 0)
e609cd06 202 return r;
00f1261d 203
e609cd06
YW
204 SET_FOREACH(a, addresses) {
205 _cleanup_(address_freep) Address *address = NULL;
3b6a3bde 206
e609cd06
YW
207 r = address_new(&address);
208 if (r < 0)
209 return -ENOMEM;
3b6a3bde 210
e609cd06
YW
211 address->source = NETWORK_CONFIG_SOURCE_STATIC;
212 address->family = AF_INET6;
213 address->in_addr.in6 = *a;
2ac41679 214 address->prefixlen = p->prefixlen;
e609cd06
YW
215 address->route_metric = p->route_metric;
216
217 r = link_request_static_address(link, TAKE_PTR(address), true);
218 if (r < 0)
219 return r;
220 }
3b6a3bde
YW
221 }
222
223 return 0;
224}
225
2ac41679
YW
226static uint32_t usec_to_lifetime(usec_t usec) {
227 uint64_t t;
228
229 if (usec == USEC_INFINITY)
230 return UINT32_MAX;
231
232 t = DIV_ROUND_UP(usec, USEC_PER_SEC);
233 if (t >= UINT32_MAX)
234 return UINT32_MAX;
235
236 return (uint32_t) t;
237}
238
239static int radv_set_prefix(Link *link, Prefix *prefix) {
240 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
241 int r;
242
243 assert(link);
244 assert(link->radv);
245 assert(prefix);
246
247 r = sd_radv_prefix_new(&p);
248 if (r < 0)
249 return r;
250
251 r = sd_radv_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
252 if (r < 0)
253 return r;
254
95e104e0 255 r = sd_radv_prefix_set_preferred_lifetime(p, prefix->preferred_lifetime, USEC_INFINITY);
2ac41679
YW
256 if (r < 0)
257 return r;
258
95e104e0 259 r = sd_radv_prefix_set_valid_lifetime(p, prefix->valid_lifetime, USEC_INFINITY);
2ac41679
YW
260 if (r < 0)
261 return r;
262
263 r = sd_radv_prefix_set_onlink(p, prefix->onlink);
264 if (r < 0)
265 return r;
266
267 r = sd_radv_prefix_set_address_autoconfiguration(p, prefix->address_auto_configuration);
268 if (r < 0)
269 return r;
270
95e104e0 271 return sd_radv_add_prefix(link->radv, p);
2ac41679
YW
272}
273
274static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
275 _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
276 int r;
277
278 assert(link);
279 assert(link->radv);
280 assert(prefix);
281
282 r = sd_radv_route_prefix_new(&p);
283 if (r < 0)
284 return r;
285
286 r = sd_radv_route_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
287 if (r < 0)
288 return r;
289
95e104e0 290 r = sd_radv_route_prefix_set_lifetime(p, prefix->lifetime, USEC_INFINITY);
2ac41679
YW
291 if (r < 0)
292 return r;
293
95e104e0 294 return sd_radv_add_route_prefix(link->radv, p);
2ac41679
YW
295}
296
0943b3b7
YW
297static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
298 _cleanup_free_ struct in6_addr *addresses = NULL;
299 size_t n_addresses = 0;
6e849e95 300
0943b3b7
YW
301 assert(network);
302 assert(ret_addresses);
303 assert(ret_size);
6e849e95 304
0943b3b7
YW
305 for (size_t i = 0; i < network->n_dns; i++) {
306 union in_addr_union *addr;
6e849e95 307
0943b3b7
YW
308 if (network->dns[i]->family != AF_INET6)
309 continue;
6e849e95 310
0943b3b7 311 addr = &network->dns[i]->address;
6e849e95 312
0943b3b7
YW
313 if (in_addr_is_null(AF_INET6, addr) ||
314 in_addr_is_link_local(AF_INET6, addr) ||
315 in_addr_is_localhost(AF_INET6, addr))
316 continue;
317
318 if (!GREEDY_REALLOC(addresses, n_addresses + 1))
319 return -ENOMEM;
320
321 addresses[n_addresses++] = addr->in6;
c9778516 322 }
6e849e95 323
0943b3b7
YW
324 *ret_addresses = TAKE_PTR(addresses);
325 *ret_size = n_addresses;
6e849e95 326
0943b3b7 327 return n_addresses;
6e849e95
PF
328}
329
0943b3b7
YW
330static int radv_set_dns(Link *link, Link *uplink) {
331 _cleanup_free_ struct in6_addr *dns = NULL;
0943b3b7 332 size_t n_dns;
c9778516 333 int r;
6e849e95 334
0943b3b7 335 if (!link->network->router_emit_dns)
6e849e95 336 return 0;
6e849e95 337
0943b3b7
YW
338 if (link->network->router_dns) {
339 struct in6_addr *p;
6e849e95 340
0943b3b7
YW
341 dns = new(struct in6_addr, link->network->n_router_dns);
342 if (!dns)
343 return -ENOMEM;
6e849e95 344
0943b3b7
YW
345 p = dns;
346 for (size_t i = 0; i < link->network->n_router_dns; i++)
347 if (in6_addr_is_null(&link->network->router_dns[i])) {
348 if (in6_addr_is_set(&link->ipv6ll_address))
349 *(p++) = link->ipv6ll_address;
350 } else
351 *(p++) = link->network->router_dns[i];
6e849e95 352
0943b3b7 353 n_dns = p - dns;
a8d4a210 354
0943b3b7
YW
355 goto set_dns;
356 }
6e849e95 357
0943b3b7
YW
358 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
359 if (r > 0)
360 goto set_dns;
6e849e95 361
0943b3b7
YW
362 if (uplink) {
363 assert(uplink->network);
364
365 r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
366 if (r > 0)
367 goto set_dns;
6e849e95
PF
368 }
369
6e849e95 370 return 0;
bd6379ec 371
0943b3b7
YW
372set_dns:
373 return sd_radv_set_rdnss(link->radv,
165a654e 374 usec_to_lifetime(link->network->router_dns_lifetime_usec),
0943b3b7
YW
375 dns, n_dns);
376}
bd6379ec 377
0943b3b7 378static int radv_set_domains(Link *link, Link *uplink) {
0943b3b7 379 _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
165a654e 380 OrderedSet *search_domains;
bd6379ec 381
0943b3b7 382 if (!link->network->router_emit_domains)
bd6379ec 383 return 0;
bd6379ec 384
0943b3b7 385 search_domains = link->network->router_search_domains;
bd6379ec 386
0943b3b7
YW
387 if (search_domains)
388 goto set_domains;
0e1fb1d0 389
0943b3b7
YW
390 search_domains = link->network->search_domains;
391 if (search_domains)
392 goto set_domains;
0e1fb1d0 393
0943b3b7
YW
394 if (uplink) {
395 assert(uplink->network);
0e1fb1d0 396
0943b3b7
YW
397 search_domains = uplink->network->search_domains;
398 if (search_domains)
399 goto set_domains;
0e1fb1d0
YW
400 }
401
0e1fb1d0 402 return 0;
0e1fb1d0 403
0943b3b7
YW
404set_domains:
405 s = ordered_set_get_strv(search_domains);
406 if (!s)
407 return log_oom();
203d4df5 408
0943b3b7 409 return sd_radv_set_dnssl(link->radv,
165a654e 410 usec_to_lifetime(link->network->router_dns_lifetime_usec),
0943b3b7 411 s);
203d4df5 412
0943b3b7 413}
203d4df5 414
0943b3b7 415static int radv_find_uplink(Link *link, Link **ret) {
f6032ff3
YW
416 int r;
417
0943b3b7 418 assert(link);
203d4df5 419
0943b3b7
YW
420 if (link->network->router_uplink_name)
421 return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
203d4df5 422
0943b3b7
YW
423 if (link->network->router_uplink_index > 0)
424 return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
425
426 if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
a27588d4
YW
427 if (link_dhcp_pd_is_enabled(link))
428 r = dhcp_pd_find_uplink(link, ret); /* When DHCP-PD is enabled, use its uplink. */
f6032ff3
YW
429 else
430 r = manager_find_uplink(link->manager, AF_INET6, link, ret);
431 if (r < 0)
432 /* It is not necessary to propagate error in automatic selection. */
0943b3b7 433 *ret = NULL;
c9778516
YW
434 return 0;
435 }
203d4df5 436
0943b3b7 437 *ret = NULL;
203d4df5
SS
438 return 0;
439}
440
0943b3b7 441static int radv_configure(Link *link) {
0943b3b7
YW
442 Link *uplink = NULL;
443 RoutePrefix *q;
444 Prefix *p;
203d4df5
SS
445 int r;
446
0943b3b7
YW
447 assert(link);
448 assert(link->network);
203d4df5 449
0943b3b7
YW
450 if (link->radv)
451 return -EBUSY;
452
453 r = sd_radv_new(&link->radv);
203d4df5 454 if (r < 0)
0943b3b7 455 return r;
203d4df5 456
0943b3b7
YW
457 r = sd_radv_attach_event(link->radv, link->manager->event, 0);
458 if (r < 0)
459 return r;
203d4df5 460
3be64aa4
YW
461 if (link->hw_addr.length == ETH_ALEN) {
462 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
463 if (r < 0)
464 return r;
465 }
203d4df5 466
0943b3b7
YW
467 r = sd_radv_set_ifindex(link->radv, link->ifindex);
468 if (r < 0)
469 return r;
203d4df5 470
0943b3b7
YW
471 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
472 if (r < 0)
473 return r;
203d4df5 474
0943b3b7
YW
475 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
476 if (r < 0)
477 return r;
c555a358 478
7003b114 479 r = sd_radv_set_router_lifetime(link->radv, link->network->router_lifetime_usec);
0943b3b7
YW
480 if (r < 0)
481 return r;
c555a358 482
7003b114 483 if (link->network->router_lifetime_usec > 0) {
0943b3b7
YW
484 r = sd_radv_set_preference(link->radv, link->network->router_preference);
485 if (r < 0)
486 return r;
487 }
c555a358 488
0943b3b7 489 HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
2ac41679
YW
490 r = radv_set_prefix(link, p);
491 if (r < 0 && r != -EEXIST)
0943b3b7
YW
492 return r;
493 }
c555a358 494
0943b3b7 495 HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
2ac41679
YW
496 r = radv_set_route_prefix(link, q);
497 if (r < 0 && r != -EEXIST)
0943b3b7
YW
498 return r;
499 }
c555a358 500
0943b3b7 501 (void) radv_find_uplink(link, &uplink);
c555a358 502
0943b3b7
YW
503 r = radv_set_dns(link, uplink);
504 if (r < 0)
505 return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
c555a358 506
0943b3b7
YW
507 r = radv_set_domains(link, uplink);
508 if (r < 0)
509 return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
c555a358 510
0943b3b7 511 return 0;
c555a358
PF
512}
513
0943b3b7
YW
514int radv_update_mac(Link *link) {
515 bool restart;
c555a358
PF
516 int r;
517
0943b3b7 518 assert(link);
c555a358 519
0943b3b7
YW
520 if (!link->radv)
521 return 0;
fd3ef936 522
3be64aa4
YW
523 if (link->hw_addr.length != ETH_ALEN)
524 return 0;
525
0943b3b7 526 restart = sd_radv_is_running(link->radv);
c555a358 527
0943b3b7
YW
528 r = sd_radv_stop(link->radv);
529 if (r < 0)
530 return r;
fd3ef936 531
0943b3b7
YW
532 r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
533 if (r < 0)
534 return r;
c555a358 535
0943b3b7
YW
536 if (restart) {
537 r = sd_radv_start(link->radv);
538 if (r < 0)
539 return r;
c555a358
PF
540 }
541
0943b3b7
YW
542 return 0;
543}
c555a358 544
0943b3b7
YW
545static int radv_is_ready_to_configure(Link *link) {
546 bool needs_uplink = false;
547 int r;
c555a358 548
0943b3b7
YW
549 assert(link);
550 assert(link->network);
349a981d 551
0943b3b7
YW
552 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
553 return false;
c555a358 554
0943b3b7
YW
555 if (in6_addr_is_null(&link->ipv6ll_address))
556 return false;
c555a358 557
218a8502
YW
558 if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
559 return false;
560
0943b3b7
YW
561 if (link->network->router_emit_dns && !link->network->router_dns) {
562 _cleanup_free_ struct in6_addr *dns = NULL;
563 size_t n_dns;
c555a358 564
0943b3b7
YW
565 r = network_get_ipv6_dns(link->network, &dns, &n_dns);
566 if (r < 0)
567 return r;
c555a358 568
0943b3b7
YW
569 needs_uplink = r == 0;
570 }
c555a358 571
0943b3b7
YW
572 if (link->network->router_emit_domains &&
573 !link->network->router_search_domains &&
574 !link->network->search_domains)
575 needs_uplink = true;
576
577 if (needs_uplink) {
578 Link *uplink = NULL;
c555a358 579
0943b3b7
YW
580 if (radv_find_uplink(link, &uplink) < 0)
581 return false;
c555a358 582
0943b3b7
YW
583 if (uplink && !uplink->network)
584 return false;
585 }
c555a358 586
0943b3b7
YW
587 return true;
588}
c555a358 589
09d09207 590static int radv_process_request(Request *req, Link *link, void *userdata) {
0943b3b7 591 int r;
349a981d 592
ff51134c 593 assert(link);
c555a358 594
0943b3b7
YW
595 r = radv_is_ready_to_configure(link);
596 if (r <= 0)
597 return r;
5e2a51d5 598
0943b3b7
YW
599 r = radv_configure(link);
600 if (r < 0)
601 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
c555a358 602
0943b3b7 603 if (link_has_carrier(link)) {
0f96a823 604 r = radv_start(link);
0943b3b7
YW
605 if (r < 0)
606 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
607 }
608
609 log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
610 link_has_carrier(link) ? " and started." : "");
611 return 1;
c555a358
PF
612}
613
0943b3b7
YW
614int link_request_radv(Link *link) {
615 int r;
63295b42 616
0943b3b7 617 assert(link);
63295b42 618
0943b3b7
YW
619 if (!link_radv_enabled(link))
620 return 0;
63295b42 621
0943b3b7 622 if (link->radv)
63295b42 623 return 0;
63295b42 624
09d09207 625 r = link_queue_request(link, REQUEST_TYPE_RADV, radv_process_request, NULL);
0943b3b7
YW
626 if (r < 0)
627 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
628
629 log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
63295b42
YW
630 return 0;
631}
632
0f96a823
YW
633int radv_start(Link *link) {
634 int r;
635
636 assert(link);
637 assert(link->network);
638
639 if (!link->radv)
640 return 0;
641
642 if (!link_has_carrier(link))
643 return 0;
644
645 if (in6_addr_is_null(&link->ipv6ll_address))
646 return 0;
647
648 if (sd_radv_is_running(link->radv))
649 return 0;
650
a27588d4
YW
651 if (link->network->dhcp_pd_announce) {
652 r = dhcp_request_prefix_delegation(link);
0f96a823 653 if (r < 0)
a27588d4 654 return log_link_debug_errno(link, r, "Failed to request DHCP delegated subnet prefix: %m");
0f96a823
YW
655 }
656
657 log_link_debug(link, "Starting IPv6 Router Advertisements");
658 return sd_radv_start(link->radv);
659}
660
0943b3b7
YW
661int radv_add_prefix(
662 Link *link,
663 const struct in6_addr *prefix,
664 uint8_t prefix_len,
16bc8635
YW
665 usec_t lifetime_preferred_usec,
666 usec_t lifetime_valid_usec) {
0943b3b7
YW
667
668 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
95081e08 669 int r;
091214b6
PF
670
671 assert(link);
091214b6 672
0943b3b7
YW
673 if (!link->radv)
674 return 0;
bc9e40c9 675
0943b3b7 676 r = sd_radv_prefix_new(&p);
091214b6
PF
677 if (r < 0)
678 return r;
679
0943b3b7 680 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
091214b6
PF
681 if (r < 0)
682 return r;
683
c9e2c2da 684 r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
091214b6
PF
685 if (r < 0)
686 return r;
687
c9e2c2da 688 r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
091214b6
PF
689 if (r < 0)
690 return r;
691
95e104e0 692 r = sd_radv_add_prefix(link->radv, p);
0943b3b7 693 if (r < 0 && r != -EEXIST)
091214b6
PF
694 return r;
695
0943b3b7
YW
696 return 0;
697}
091214b6 698
2110040b
YW
699static int prefix_section_verify(Prefix *p) {
700 assert(p);
701
702 if (section_is_invalid(p->section))
703 return -EINVAL;
704
705 if (in6_addr_is_null(&p->prefix))
706 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
707 "%s: [IPv6Prefix] section without Prefix= field configured, "
708 "or specified prefix is the null address. "
709 "Ignoring [IPv6Prefix] section from line %u.",
710 p->section->filename, p->section->line);
711
712 if (p->prefixlen < 3 || p->prefixlen > 128)
713 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
714 "%s: Invalid prefix length %u is specified in [IPv6Prefix] section. "
715 "Valid range is 3…128. Ignoring [IPv6Prefix] section from line %u.",
716 p->section->filename, p->prefixlen, p->section->line);
717
718 if (p->prefixlen > 64) {
c71384a9
ZJS
719 log_info("%s:%u: Unusual prefix length %u (> 64) is specified in [IPv6Prefix] section from line %s%s.",
720 p->section->filename, p->section->line,
721 p->prefixlen,
2110040b 722 p->assign ? ", refusing to assign an address in " : "",
c71384a9 723 p->assign ? IN6_ADDR_PREFIX_TO_STRING(&p->prefix, p->prefixlen) : "");
2110040b
YW
724
725 p->assign = false;
726 }
727
728 if (p->valid_lifetime == 0)
729 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
730 "%s: The valid lifetime of prefix cannot be zero. "
731 "Ignoring [IPv6Prefix] section from line %u.",
732 p->section->filename, p->section->line);
733
734 if (p->preferred_lifetime > p->valid_lifetime)
735 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
736 "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
737 "Ignoring [IPv6Prefix] section from line %u.",
738 p->section->filename,
739 FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
740 FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
741 p->section->line);
742
743 return 0;
744}
745
0943b3b7 746void network_drop_invalid_prefixes(Network *network) {
2110040b 747 Prefix *p;
2075e596 748
0943b3b7
YW
749 assert(network);
750
2110040b
YW
751 HASHMAP_FOREACH(p, network->prefixes_by_section)
752 if (prefix_section_verify(p) < 0)
753 prefix_free(p);
754}
755
756static int route_prefix_section_verify(RoutePrefix *p) {
757 if (section_is_invalid(p->section))
758 return -EINVAL;
759
760 if (p->prefixlen > 128)
761 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
762 "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
763 "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
764 p->section->filename, p->prefixlen, p->section->line);
765
766 if (p->lifetime == 0)
767 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
768 "%s: The lifetime of route cannot be zero. "
769 "Ignoring [IPv6RoutePrefix] section from line %u.",
770 p->section->filename, p->section->line);
771
772 return 0;
0943b3b7
YW
773}
774
775void network_drop_invalid_route_prefixes(Network *network) {
2110040b 776 RoutePrefix *p;
0943b3b7
YW
777
778 assert(network);
779
2110040b
YW
780 HASHMAP_FOREACH(p, network->route_prefixes_by_section)
781 if (route_prefix_section_verify(p) < 0)
782 route_prefix_free(p);
0943b3b7
YW
783}
784
785int config_parse_prefix(
786 const char *unit,
787 const char *filename,
788 unsigned line,
789 const char *section,
790 unsigned section_line,
791 const char *lvalue,
792 int ltype,
793 const char *rvalue,
794 void *data,
795 void *userdata) {
796
0943b3b7 797 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679
YW
798 Network *network = userdata;
799 union in_addr_union a;
0943b3b7
YW
800 int r;
801
802 assert(filename);
803 assert(section);
804 assert(lvalue);
805 assert(rvalue);
2ac41679 806 assert(userdata);
0943b3b7
YW
807
808 r = prefix_new_static(network, filename, section_line, &p);
091214b6 809 if (r < 0)
0943b3b7 810 return log_oom();
091214b6 811
2ac41679 812 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 813 if (r < 0) {
2ac41679
YW
814 log_syntax(unit, LOG_WARNING, filename, line, r,
815 "Prefix is invalid, ignoring assignment: %s", rvalue);
0943b3b7 816 return 0;
8a08bbfc 817 }
cf72568a
YW
818
819 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 820 p->prefix = a.in6;
203d4df5 821
2ac41679 822 TAKE_PTR(p);
0943b3b7
YW
823 return 0;
824}
a254fab2 825
2ac41679 826int config_parse_prefix_boolean(
0943b3b7
YW
827 const char *unit,
828 const char *filename,
829 unsigned line,
830 const char *section,
831 unsigned section_line,
832 const char *lvalue,
833 int ltype,
834 const char *rvalue,
835 void *data,
836 void *userdata) {
a254fab2 837
0943b3b7 838 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 839 Network *network = userdata;
0943b3b7
YW
840 int r;
841
842 assert(filename);
843 assert(section);
844 assert(lvalue);
845 assert(rvalue);
2ac41679 846 assert(userdata);
0943b3b7
YW
847
848 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 849 if (r < 0)
0943b3b7
YW
850 return log_oom();
851
852 r = parse_boolean(rvalue);
853 if (r < 0) {
2ac41679
YW
854 log_syntax(unit, LOG_WARNING, filename, line, r,
855 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
0943b3b7
YW
856 return 0;
857 }
858
859 if (streq(lvalue, "OnLink"))
2ac41679 860 p->onlink = r;
0943b3b7 861 else if (streq(lvalue, "AddressAutoconfiguration"))
2ac41679
YW
862 p->address_auto_configuration = r;
863 else if (streq(lvalue, "Assign"))
864 p->assign = r;
865 else
866 assert_not_reached();
a254fab2 867
2ac41679 868 TAKE_PTR(p);
fd3ef936 869 return 0;
091214b6 870}
ca5ad760 871
0943b3b7
YW
872int config_parse_prefix_lifetime(
873 const char *unit,
874 const char *filename,
875 unsigned line,
876 const char *section,
877 unsigned section_line,
878 const char *lvalue,
879 int ltype,
880 const char *rvalue,
881 void *data,
882 void *userdata) {
883
0943b3b7 884 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 885 Network *network = userdata;
0943b3b7 886 usec_t usec;
be9363cc
YW
887 int r;
888
0943b3b7
YW
889 assert(filename);
890 assert(section);
891 assert(lvalue);
892 assert(rvalue);
2ac41679 893 assert(userdata);
be9363cc 894
0943b3b7 895 r = prefix_new_static(network, filename, section_line, &p);
a391901e 896 if (r < 0)
0943b3b7 897 return log_oom();
be9363cc 898
0943b3b7
YW
899 r = parse_sec(rvalue, &usec);
900 if (r < 0) {
2ac41679
YW
901 log_syntax(unit, LOG_WARNING, filename, line, r,
902 "Lifetime is invalid, ignoring assignment: %s", rvalue);
0943b3b7
YW
903 return 0;
904 }
be9363cc 905
2ac41679
YW
906 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
907 log_syntax(unit, LOG_WARNING, filename, line, 0,
908 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7 909 return 0;
be9363cc
YW
910 }
911
2ac41679
YW
912 if (streq(lvalue, "PreferredLifetimeSec"))
913 p->preferred_lifetime = usec;
914 else if (streq(lvalue, "ValidLifetimeSec"))
915 p->valid_lifetime = usec;
916 else
917 assert_not_reached();
63295b42 918
2ac41679 919 TAKE_PTR(p);
0943b3b7 920 return 0;
a254fab2
YW
921}
922
0943b3b7
YW
923int config_parse_prefix_metric(
924 const char *unit,
925 const char *filename,
926 unsigned line,
927 const char *section,
928 unsigned section_line,
929 const char *lvalue,
930 int ltype,
931 const char *rvalue,
932 void *data,
933 void *userdata) {
a254fab2 934
0943b3b7 935 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
2ac41679 936 Network *network = userdata;
0943b3b7 937 int r;
a254fab2 938
0943b3b7
YW
939 assert(filename);
940 assert(section);
941 assert(lvalue);
942 assert(rvalue);
2ac41679 943 assert(userdata);
a254fab2 944
0943b3b7 945 r = prefix_new_static(network, filename, section_line, &p);
a254fab2 946 if (r < 0)
0943b3b7 947 return log_oom();
a254fab2 948
0943b3b7
YW
949 r = safe_atou32(rvalue, &p->route_metric);
950 if (r < 0) {
951 log_syntax(unit, LOG_WARNING, filename, line, r,
952 "Failed to parse %s=, ignoring assignment: %s",
953 lvalue, rvalue);
954 return 0;
a254fab2
YW
955 }
956
0943b3b7 957 TAKE_PTR(p);
0943b3b7 958 return 0;
a254fab2
YW
959}
960
e609cd06
YW
961int config_parse_prefix_token(
962 const char *unit,
963 const char *filename,
964 unsigned line,
965 const char *section,
966 unsigned section_line,
967 const char *lvalue,
968 int ltype,
969 const char *rvalue,
970 void *data,
971 void *userdata) {
972
973 _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
974 Network *network = userdata;
975 int r;
976
977 assert(filename);
978 assert(section);
979 assert(lvalue);
980 assert(rvalue);
981 assert(userdata);
982
983 r = prefix_new_static(network, filename, section_line, &p);
984 if (r < 0)
985 return log_oom();
986
987 r = config_parse_address_generation_type(unit, filename, line, section, section_line,
988 lvalue, ltype, rvalue, &p->tokens, userdata);
989 if (r < 0)
990 return r;
991
992 TAKE_PTR(p);
993 return 0;
994}
995
0943b3b7
YW
996int config_parse_route_prefix(
997 const char *unit,
998 const char *filename,
999 unsigned line,
1000 const char *section,
1001 unsigned section_line,
1002 const char *lvalue,
1003 int ltype,
1004 const char *rvalue,
1005 void *data,
1006 void *userdata) {
1007
0943b3b7 1008 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
2ac41679
YW
1009 Network *network = userdata;
1010 union in_addr_union a;
a254fab2
YW
1011 int r;
1012
0943b3b7
YW
1013 assert(filename);
1014 assert(section);
1015 assert(lvalue);
1016 assert(rvalue);
2ac41679 1017 assert(userdata);
a254fab2 1018
0943b3b7
YW
1019 r = route_prefix_new_static(network, filename, section_line, &p);
1020 if (r < 0)
1021 return log_oom();
1022
2ac41679 1023 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
0943b3b7 1024 if (r < 0) {
2ac41679
YW
1025 log_syntax(unit, LOG_WARNING, filename, line, r,
1026 "Route prefix is invalid, ignoring assignment: %s", rvalue);
a254fab2 1027 return 0;
0943b3b7 1028 }
cf72568a
YW
1029
1030 (void) in6_addr_mask(&a.in6, p->prefixlen);
2ac41679 1031 p->prefix = a.in6;
a254fab2 1032
2ac41679 1033 TAKE_PTR(p);
a254fab2
YW
1034 return 0;
1035}
1036
0943b3b7
YW
1037int config_parse_route_prefix_lifetime(
1038 const char *unit,
1039 const char *filename,
1040 unsigned line,
1041 const char *section,
1042 unsigned section_line,
1043 const char *lvalue,
1044 int ltype,
1045 const char *rvalue,
1046 void *data,
1047 void *userdata) {
a8d4a210 1048
0943b3b7 1049 _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
2ac41679 1050 Network *network = userdata;
0943b3b7 1051 usec_t usec;
1d596fde
YW
1052 int r;
1053
0943b3b7
YW
1054 assert(filename);
1055 assert(section);
1056 assert(lvalue);
1057 assert(rvalue);
2ac41679 1058 assert(userdata);
1d596fde 1059
0943b3b7 1060 r = route_prefix_new_static(network, filename, section_line, &p);
1d596fde 1061 if (r < 0)
0943b3b7 1062 return log_oom();
1d596fde 1063
0943b3b7
YW
1064 r = parse_sec(rvalue, &usec);
1065 if (r < 0) {
1066 log_syntax(unit, LOG_WARNING, filename, line, r,
1067 "Route lifetime is invalid, ignoring assignment: %s", rvalue);
1068 return 0;
1069 }
1d596fde 1070
2ac41679
YW
1071 if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
1072 log_syntax(unit, LOG_WARNING, filename, line, 0,
1073 "Lifetime is too long, ignoring assignment: %s", rvalue);
0943b3b7
YW
1074 return 0;
1075 }
1d596fde 1076
2ac41679 1077 p->lifetime = usec;
1d596fde 1078
2ac41679 1079 TAKE_PTR(p);
1d596fde
YW
1080 return 0;
1081}
1082
ca5ad760
YW
1083int config_parse_radv_dns(
1084 const char *unit,
1085 const char *filename,
1086 unsigned line,
1087 const char *section,
1088 unsigned section_line,
1089 const char *lvalue,
1090 int ltype,
1091 const char *rvalue,
1092 void *data,
1093 void *userdata) {
1094
1095 Network *n = data;
ca5ad760
YW
1096 int r;
1097
1098 assert(filename);
1099 assert(lvalue);
1100 assert(rvalue);
1101
a3c1a949
YW
1102 if (isempty(rvalue)) {
1103 n->n_router_dns = 0;
1104 n->router_dns = mfree(n->router_dns);
1105 return 0;
1106 }
1107
d96edb2c 1108 for (const char *p = rvalue;;) {
ca5ad760
YW
1109 _cleanup_free_ char *w = NULL;
1110 union in_addr_union a;
1111
1112 r = extract_first_word(&p, &w, NULL, 0);
1113 if (r == -ENOMEM)
1114 return log_oom();
1115 if (r < 0) {
d96edb2c 1116 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1117 "Failed to extract word, ignoring: %s", rvalue);
1118 return 0;
1119 }
1120 if (r == 0)
d96edb2c 1121 return 0;
ca5ad760 1122
fd3ef936
YW
1123 if (streq(w, "_link_local"))
1124 a = IN_ADDR_NULL;
1125 else {
1126 r = in_addr_from_string(AF_INET6, w, &a);
1127 if (r < 0) {
d96edb2c 1128 log_syntax(unit, LOG_WARNING, filename, line, r,
fd3ef936
YW
1129 "Failed to parse DNS server address, ignoring: %s", w);
1130 continue;
1131 }
ca5ad760 1132
fd3ef936 1133 if (in_addr_is_null(AF_INET6, &a)) {
d96edb2c 1134 log_syntax(unit, LOG_WARNING, filename, line, 0,
fd3ef936
YW
1135 "DNS server address is null, ignoring: %s", w);
1136 continue;
1137 }
1138 }
ca5ad760 1139
fd3ef936
YW
1140 struct in6_addr *m;
1141 m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
1142 if (!m)
1143 return log_oom();
ca5ad760 1144
fd3ef936
YW
1145 m[n->n_router_dns++] = a.in6;
1146 n->router_dns = m;
ca5ad760 1147 }
ca5ad760
YW
1148}
1149
1150int config_parse_radv_search_domains(
1151 const char *unit,
1152 const char *filename,
1153 unsigned line,
1154 const char *section,
1155 unsigned section_line,
1156 const char *lvalue,
1157 int ltype,
1158 const char *rvalue,
1159 void *data,
1160 void *userdata) {
1161
1162 Network *n = data;
ca5ad760
YW
1163 int r;
1164
1165 assert(filename);
1166 assert(lvalue);
1167 assert(rvalue);
1168
a3c1a949
YW
1169 if (isempty(rvalue)) {
1170 n->router_search_domains = ordered_set_free(n->router_search_domains);
1171 return 0;
1172 }
1173
d96edb2c 1174 for (const char *p = rvalue;;) {
ca5ad760
YW
1175 _cleanup_free_ char *w = NULL, *idna = NULL;
1176
1177 r = extract_first_word(&p, &w, NULL, 0);
1178 if (r == -ENOMEM)
1179 return log_oom();
1180 if (r < 0) {
d96edb2c 1181 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1182 "Failed to extract word, ignoring: %s", rvalue);
1183 return 0;
1184 }
1185 if (r == 0)
d96edb2c 1186 return 0;
ca5ad760
YW
1187
1188 r = dns_name_apply_idna(w, &idna);
1189 if (r < 0) {
d96edb2c 1190 log_syntax(unit, LOG_WARNING, filename, line, r,
ca5ad760
YW
1191 "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
1192 continue;
1193 } else if (r == 0)
1194 /* transfer ownership to simplify subsequent operations */
1195 idna = TAKE_PTR(w);
1196
5e276772 1197 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops_free);
ca5ad760 1198 if (r < 0)
d96edb2c 1199 return log_oom();
ca5ad760
YW
1200
1201 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
1202 if (r < 0)
d96edb2c 1203 return log_oom();
ca5ad760 1204 }
ca5ad760
YW
1205}
1206
1207static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
a8d4a210 1208 [RADV_PREFIX_DELEGATION_NONE] = "no",
ca5ad760 1209 [RADV_PREFIX_DELEGATION_STATIC] = "static",
a8d4a210
YW
1210 [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
1211 [RADV_PREFIX_DELEGATION_BOTH] = "yes",
ca5ad760
YW
1212};
1213
1214DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
1215 radv_prefix_delegation,
1216 RADVPrefixDelegation,
1217 RADV_PREFIX_DELEGATION_BOTH);
1218
27ff0490
YW
1219int config_parse_router_prefix_delegation(
1220 const char *unit,
1221 const char *filename,
1222 unsigned line,
1223 const char *section,
1224 unsigned section_line,
1225 const char *lvalue,
1226 int ltype,
1227 const char *rvalue,
1228 void *data,
1229 void *userdata) {
1230
1231 RADVPrefixDelegation val, *ra = data;
1232 int r;
1233
1234 assert(filename);
1235 assert(lvalue);
1236 assert(rvalue);
1237 assert(data);
1238
1239 if (streq(lvalue, "IPv6SendRA")) {
1240 r = parse_boolean(rvalue);
1241 if (r < 0) {
1242 log_syntax(unit, LOG_WARNING, filename, line, r,
1243 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1244 return 0;
1245 }
1246
1247 /* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
1248 * need to explicitly enable DHCPv6PrefixDelegation=. */
1249 *ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
1250 return 0;
1251 }
1252
1253 /* For backward compatibility */
1254 val = radv_prefix_delegation_from_string(rvalue);
1255 if (val < 0) {
b98680b2 1256 log_syntax(unit, LOG_WARNING, filename, line, val,
27ff0490
YW
1257 "Invalid %s= setting, ignoring assignment: %s", lvalue, rvalue);
1258 return 0;
1259 }
1260
1261 *ra = val;
1262 return 0;
1263}
a8d4a210 1264
4f1ac4a3
YW
1265int config_parse_router_lifetime(
1266 const char *unit,
1267 const char *filename,
1268 unsigned line,
1269 const char *section,
1270 unsigned section_line,
1271 const char *lvalue,
1272 int ltype,
1273 const char *rvalue,
1274 void *data,
1275 void *userdata) {
1276
1277 usec_t usec, *lifetime = data;
1278 int r;
1279
1280 assert(filename);
1281 assert(section);
1282 assert(lvalue);
1283 assert(rvalue);
1284 assert(data);
1285
1286 if (isempty(rvalue)) {
1287 *lifetime = RADV_DEFAULT_ROUTER_LIFETIME_USEC;
1288 return 0;
1289 }
1290
1291 r = parse_sec(rvalue, &usec);
1292 if (r < 0) {
1293 log_syntax(unit, LOG_WARNING, filename, line, r,
1294 "Failed to parse router lifetime, ignoring assignment: %s", rvalue);
1295 return 0;
1296 }
1297 if (usec > 0) {
1298 if (usec < RADV_MIN_ROUTER_LIFETIME_USEC) {
1299 log_syntax(unit, LOG_WARNING, filename, line, 0,
1300 "Router lifetime %s is too short, using %s.",
1301 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1302 FORMAT_TIMESPAN(RADV_MIN_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1303 usec = RADV_MIN_ROUTER_LIFETIME_USEC;
1304 } else if (usec > RADV_MAX_ROUTER_LIFETIME_USEC) {
1305 log_syntax(unit, LOG_WARNING, filename, line, 0,
1306 "Router lifetime %s is too large, using %s.",
1307 FORMAT_TIMESPAN(usec, USEC_PER_SEC),
1308 FORMAT_TIMESPAN(RADV_MAX_ROUTER_LIFETIME_USEC, USEC_PER_SEC));
1309 usec = RADV_MAX_ROUTER_LIFETIME_USEC;
1310 }
1311 }
1312
1313 *lifetime = usec;
1314 return 0;
1315}
1316
a8d4a210
YW
1317int config_parse_router_preference(
1318 const char *unit,
1319 const char *filename,
1320 unsigned line,
1321 const char *section,
1322 unsigned section_line,
1323 const char *lvalue,
1324 int ltype,
1325 const char *rvalue,
1326 void *data,
1327 void *userdata) {
1328
ca5ad760
YW
1329 Network *network = userdata;
1330
1331 assert(filename);
1332 assert(section);
1333 assert(lvalue);
1334 assert(rvalue);
1335 assert(data);
1336
1337 if (streq(rvalue, "high"))
1338 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
1339 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
1340 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
1341 else if (streq(rvalue, "low"))
1342 network->router_preference = SD_NDISC_PREFERENCE_LOW;
1343 else
d96edb2c
YW
1344 log_syntax(unit, LOG_WARNING, filename, line, 0,
1345 "Invalid router preference, ignoring assignment: %s", rvalue);
ca5ad760
YW
1346
1347 return 0;
1348}