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