]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
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" |
091214b6 | 10 | #include "networkd-address.h" |
c555a358 | 11 | #include "networkd-manager.h" |
091214b6 | 12 | #include "networkd-radv.h" |
6e849e95 | 13 | #include "parse-util.h" |
091214b6 | 14 | #include "sd-radv.h" |
6e849e95 | 15 | #include "string-util.h" |
6b1dec66 | 16 | #include "string-table.h" |
51517f9e | 17 | #include "strv.h" |
6e849e95 | 18 | |
6e849e95 PF |
19 | void prefix_free(Prefix *prefix) { |
20 | if (!prefix) | |
21 | return; | |
22 | ||
23 | if (prefix->network) { | |
24 | LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix); | |
25 | assert(prefix->network->n_static_prefixes > 0); | |
26 | prefix->network->n_static_prefixes--; | |
27 | ||
ecee0abe | 28 | if (prefix->section) |
6e849e95 PF |
29 | hashmap_remove(prefix->network->prefixes_by_section, |
30 | prefix->section); | |
31 | } | |
32 | ||
ecee0abe | 33 | network_config_section_free(prefix->section); |
471e126b | 34 | sd_radv_prefix_unref(prefix->radv_prefix); |
6e849e95 PF |
35 | |
36 | free(prefix); | |
37 | } | |
38 | ||
95081e08 | 39 | static int prefix_new(Prefix **ret) { |
15b8332e | 40 | _cleanup_(prefix_freep) Prefix *prefix = NULL; |
6e849e95 PF |
41 | |
42 | prefix = new0(Prefix, 1); | |
43 | if (!prefix) | |
44 | return -ENOMEM; | |
45 | ||
46 | if (sd_radv_prefix_new(&prefix->radv_prefix) < 0) | |
47 | return -ENOMEM; | |
48 | ||
ae2a15bc | 49 | *ret = TAKE_PTR(prefix); |
6e849e95 PF |
50 | |
51 | return 0; | |
52 | } | |
53 | ||
9560e5b3 YW |
54 | static int prefix_new_static(Network *network, const char *filename, |
55 | unsigned section_line, Prefix **ret) { | |
8e766630 LP |
56 | _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; |
57 | _cleanup_(prefix_freep) Prefix *prefix = NULL; | |
6e849e95 PF |
58 | int r; |
59 | ||
60 | assert(network); | |
61 | assert(ret); | |
62 | assert(!!filename == (section_line > 0)); | |
63 | ||
64 | if (filename) { | |
65 | r = network_config_section_new(filename, section_line, &n); | |
66 | if (r < 0) | |
67 | return r; | |
68 | ||
69 | if (section_line) { | |
70 | prefix = hashmap_get(network->prefixes_by_section, n); | |
71 | if (prefix) { | |
1cc6c93a | 72 | *ret = TAKE_PTR(prefix); |
6e849e95 PF |
73 | |
74 | return 0; | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | r = prefix_new(&prefix); | |
80 | if (r < 0) | |
81 | return r; | |
82 | ||
0f7f2769 YW |
83 | prefix->network = network; |
84 | LIST_APPEND(prefixes, network->static_prefixes, prefix); | |
85 | network->n_static_prefixes++; | |
86 | ||
6e849e95 | 87 | if (filename) { |
1cc6c93a | 88 | prefix->section = TAKE_PTR(n); |
6e849e95 | 89 | |
3e570042 YW |
90 | r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops); |
91 | if (r < 0) | |
92 | return r; | |
93 | ||
94 | r = hashmap_put(network->prefixes_by_section, prefix->section, prefix); | |
6e849e95 PF |
95 | if (r < 0) |
96 | return r; | |
97 | } | |
98 | ||
1cc6c93a | 99 | *ret = TAKE_PTR(prefix); |
6e849e95 PF |
100 | |
101 | return 0; | |
102 | } | |
103 | ||
95081e08 YW |
104 | static int route_prefix_new(RoutePrefix **ret) { |
105 | _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL; | |
203d4df5 | 106 | |
95081e08 | 107 | prefix = new0(RoutePrefix, 1); |
203d4df5 SS |
108 | if (!prefix) |
109 | return -ENOMEM; | |
110 | ||
111 | if (sd_radv_route_prefix_new(&prefix->radv_route_prefix) < 0) | |
112 | return -ENOMEM; | |
113 | ||
114 | *ret = TAKE_PTR(prefix); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
95081e08 | 119 | void route_prefix_free(RoutePrefix *prefix) { |
203d4df5 SS |
120 | if (!prefix) |
121 | return; | |
122 | ||
123 | if (prefix->network) { | |
95081e08 | 124 | LIST_REMOVE(route_prefixes, prefix->network->static_route_prefixes, prefix); |
203d4df5 SS |
125 | assert(prefix->network->n_static_route_prefixes > 0); |
126 | prefix->network->n_static_route_prefixes--; | |
127 | ||
128 | if (prefix->section) | |
129 | hashmap_remove(prefix->network->route_prefixes_by_section, | |
130 | prefix->section); | |
131 | } | |
132 | ||
133 | network_config_section_free(prefix->section); | |
471e126b | 134 | sd_radv_route_prefix_unref(prefix->radv_route_prefix); |
203d4df5 SS |
135 | |
136 | free(prefix); | |
137 | } | |
138 | ||
139 | static int route_prefix_new_static(Network *network, const char *filename, | |
95081e08 | 140 | unsigned section_line, RoutePrefix **ret) { |
203d4df5 | 141 | _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; |
95081e08 | 142 | _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL; |
203d4df5 SS |
143 | int r; |
144 | ||
145 | assert(network); | |
146 | assert(ret); | |
147 | assert(!!filename == (section_line > 0)); | |
148 | ||
149 | if (filename) { | |
150 | r = network_config_section_new(filename, section_line, &n); | |
151 | if (r < 0) | |
152 | return r; | |
153 | ||
154 | if (section_line) { | |
155 | prefix = hashmap_get(network->route_prefixes_by_section, n); | |
156 | if (prefix) { | |
157 | *ret = TAKE_PTR(prefix); | |
158 | ||
159 | return 0; | |
160 | } | |
161 | } | |
162 | } | |
163 | ||
164 | r = route_prefix_new(&prefix); | |
165 | if (r < 0) | |
166 | return r; | |
167 | ||
168 | prefix->network = network; | |
95081e08 | 169 | LIST_APPEND(route_prefixes, network->static_route_prefixes, prefix); |
203d4df5 SS |
170 | network->n_static_route_prefixes++; |
171 | ||
172 | if (filename) { | |
173 | prefix->section = TAKE_PTR(n); | |
174 | ||
175 | r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops); | |
176 | if (r < 0) | |
177 | return r; | |
178 | ||
179 | r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix); | |
180 | if (r < 0) | |
181 | return r; | |
182 | } | |
183 | ||
184 | *ret = TAKE_PTR(prefix); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
6e849e95 | 189 | int config_parse_prefix(const char *unit, |
203d4df5 SS |
190 | const char *filename, |
191 | unsigned line, | |
192 | const char *section, | |
193 | unsigned section_line, | |
194 | const char *lvalue, | |
195 | int ltype, | |
196 | const char *rvalue, | |
197 | void *data, | |
198 | void *userdata) { | |
6e849e95 PF |
199 | |
200 | Network *network = userdata; | |
fcbf4cb7 | 201 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; |
6e849e95 PF |
202 | uint8_t prefixlen = 64; |
203 | union in_addr_union in6addr; | |
204 | int r; | |
205 | ||
206 | assert(filename); | |
207 | assert(section); | |
208 | assert(lvalue); | |
209 | assert(rvalue); | |
210 | assert(data); | |
211 | ||
212 | r = prefix_new_static(network, filename, section_line, &p); | |
213 | if (r < 0) | |
214 | return r; | |
215 | ||
216 | r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); | |
217 | if (r < 0) { | |
218 | log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); | |
219 | return 0; | |
220 | } | |
221 | ||
222 | if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0) | |
223 | return -EADDRNOTAVAIL; | |
224 | ||
225 | log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue); | |
226 | ||
227 | p = NULL; | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | int config_parse_prefix_flags(const char *unit, | |
233 | const char *filename, | |
234 | unsigned line, | |
235 | const char *section, | |
236 | unsigned section_line, | |
237 | const char *lvalue, | |
238 | int ltype, | |
239 | const char *rvalue, | |
240 | void *data, | |
241 | void *userdata) { | |
242 | Network *network = userdata; | |
fcbf4cb7 | 243 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; |
6e849e95 PF |
244 | int r, val; |
245 | ||
246 | assert(filename); | |
247 | assert(section); | |
248 | assert(lvalue); | |
249 | assert(rvalue); | |
250 | assert(data); | |
251 | ||
252 | r = prefix_new_static(network, filename, section_line, &p); | |
253 | if (r < 0) | |
254 | return r; | |
255 | ||
256 | r = parse_boolean(rvalue); | |
257 | if (r < 0) { | |
258 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); | |
259 | return 0; | |
260 | } | |
261 | ||
262 | val = r; | |
263 | ||
264 | if (streq(lvalue, "OnLink")) | |
265 | r = sd_radv_prefix_set_onlink(p->radv_prefix, val); | |
266 | else if (streq(lvalue, "AddressAutoconfiguration")) | |
267 | r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val); | |
268 | if (r < 0) | |
269 | return r; | |
270 | ||
271 | p = NULL; | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | int config_parse_prefix_lifetime(const char *unit, | |
277 | const char *filename, | |
278 | unsigned line, | |
279 | const char *section, | |
280 | unsigned section_line, | |
281 | const char *lvalue, | |
282 | int ltype, | |
283 | const char *rvalue, | |
284 | void *data, | |
285 | void *userdata) { | |
286 | Network *network = userdata; | |
fcbf4cb7 | 287 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; |
6e849e95 PF |
288 | usec_t usec; |
289 | int r; | |
290 | ||
291 | assert(filename); | |
292 | assert(section); | |
293 | assert(lvalue); | |
294 | assert(rvalue); | |
295 | assert(data); | |
296 | ||
297 | r = prefix_new_static(network, filename, section_line, &p); | |
298 | if (r < 0) | |
299 | return r; | |
300 | ||
301 | r = parse_sec(rvalue, &usec); | |
302 | if (r < 0) { | |
303 | log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); | |
304 | return 0; | |
305 | } | |
306 | ||
307 | /* a value of 0xffffffff represents infinity */ | |
308 | if (streq(lvalue, "PreferredLifetimeSec")) | |
309 | r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix, | |
310 | DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
311 | else if (streq(lvalue, "ValidLifetimeSec")) | |
312 | r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix, | |
313 | DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
314 | if (r < 0) | |
315 | return r; | |
316 | ||
317 | p = NULL; | |
318 | ||
319 | return 0; | |
320 | } | |
091214b6 | 321 | |
203d4df5 SS |
322 | int config_parse_route_prefix(const char *unit, |
323 | const char *filename, | |
324 | unsigned line, | |
325 | const char *section, | |
326 | unsigned section_line, | |
327 | const char *lvalue, | |
328 | int ltype, | |
329 | const char *rvalue, | |
330 | void *data, | |
331 | void *userdata) { | |
332 | ||
333 | Network *network = userdata; | |
95081e08 | 334 | _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; |
203d4df5 SS |
335 | uint8_t prefixlen = 64; |
336 | union in_addr_union in6addr; | |
337 | int r; | |
338 | ||
339 | assert(filename); | |
340 | assert(section); | |
341 | assert(lvalue); | |
342 | assert(rvalue); | |
343 | assert(data); | |
344 | ||
345 | r = route_prefix_new_static(network, filename, section_line, &p); | |
346 | if (r < 0) | |
347 | return r; | |
348 | ||
349 | r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); | |
350 | if (r < 0) { | |
351 | log_syntax(unit, LOG_ERR, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue); | |
352 | return 0; | |
353 | } | |
354 | ||
355 | if (sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen) < 0) | |
356 | return -EADDRNOTAVAIL; | |
357 | ||
358 | log_syntax(unit, LOG_INFO, filename, line, r, "Found route prefix %s", rvalue); | |
359 | ||
360 | p = NULL; | |
361 | ||
362 | return 0; | |
363 | } | |
364 | ||
365 | int config_parse_route_prefix_lifetime(const char *unit, | |
366 | const char *filename, | |
367 | unsigned line, | |
368 | const char *section, | |
369 | unsigned section_line, | |
370 | const char *lvalue, | |
371 | int ltype, | |
372 | const char *rvalue, | |
373 | void *data, | |
374 | void *userdata) { | |
375 | Network *network = userdata; | |
95081e08 | 376 | _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; |
203d4df5 SS |
377 | usec_t usec; |
378 | int r; | |
379 | ||
380 | assert(filename); | |
381 | assert(section); | |
382 | assert(lvalue); | |
383 | assert(rvalue); | |
384 | assert(data); | |
385 | ||
386 | r = route_prefix_new_static(network, filename, section_line, &p); | |
387 | if (r < 0) | |
388 | return r; | |
389 | ||
390 | r = parse_sec(rvalue, &usec); | |
391 | if (r < 0) { | |
095b3a7d YW |
392 | log_syntax(unit, LOG_ERR, filename, line, r, |
393 | "Route lifetime is invalid, ignoring assignment: %s", rvalue); | |
203d4df5 SS |
394 | return 0; |
395 | } | |
396 | ||
397 | /* a value of 0xffffffff represents infinity */ | |
398 | r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
399 | if (r < 0) | |
400 | return r; | |
401 | ||
402 | p = NULL; | |
403 | ||
404 | return 0; | |
405 | } | |
406 | ||
c555a358 PF |
407 | static int radv_get_ip6dns(Network *network, struct in6_addr **dns, |
408 | size_t *n_dns) { | |
409 | _cleanup_free_ struct in6_addr *addresses = NULL; | |
410 | size_t i, n_addresses = 0, n_allocated = 0; | |
411 | ||
412 | assert(network); | |
413 | assert(dns); | |
414 | assert(n_dns); | |
415 | ||
416 | for (i = 0; i < network->n_dns; i++) { | |
417 | union in_addr_union *addr; | |
418 | ||
419 | if (network->dns[i].family != AF_INET6) | |
420 | continue; | |
421 | ||
422 | addr = &network->dns[i].address; | |
423 | ||
424 | if (in_addr_is_null(AF_INET6, addr) || | |
425 | in_addr_is_link_local(AF_INET6, addr) || | |
426 | in_addr_is_localhost(AF_INET6, addr)) | |
427 | continue; | |
428 | ||
429 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) | |
430 | return -ENOMEM; | |
431 | ||
432 | addresses[n_addresses++] = addr->in6; | |
433 | } | |
434 | ||
435 | if (addresses) { | |
ae2a15bc | 436 | *dns = TAKE_PTR(addresses); |
c555a358 PF |
437 | |
438 | *n_dns = n_addresses; | |
439 | } | |
440 | ||
441 | return n_addresses; | |
442 | } | |
443 | ||
444 | static int radv_set_dns(Link *link, Link *uplink) { | |
445 | _cleanup_free_ struct in6_addr *dns = NULL; | |
446 | size_t n_dns; | |
447 | usec_t lifetime_usec; | |
448 | int r; | |
449 | ||
450 | if (!link->network->router_emit_dns) | |
451 | return 0; | |
452 | ||
453 | if (link->network->router_dns) { | |
454 | dns = newdup(struct in6_addr, link->network->router_dns, | |
455 | link->network->n_router_dns); | |
4e361acc | 456 | if (!dns) |
c555a358 PF |
457 | return -ENOMEM; |
458 | ||
459 | n_dns = link->network->n_router_dns; | |
460 | lifetime_usec = link->network->router_dns_lifetime_usec; | |
461 | ||
462 | goto set_dns; | |
463 | } | |
464 | ||
465 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
466 | ||
467 | r = radv_get_ip6dns(link->network, &dns, &n_dns); | |
468 | if (r > 0) | |
469 | goto set_dns; | |
470 | ||
471 | if (uplink) { | |
4e361acc | 472 | if (!uplink->network) { |
349a981d PF |
473 | log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us"); |
474 | return 0; | |
475 | } | |
476 | ||
c555a358 PF |
477 | r = radv_get_ip6dns(uplink->network, &dns, &n_dns); |
478 | if (r > 0) | |
479 | goto set_dns; | |
480 | } | |
481 | ||
482 | return 0; | |
483 | ||
484 | set_dns: | |
485 | return sd_radv_set_rdnss(link->radv, | |
486 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
487 | dns, n_dns); | |
488 | } | |
489 | ||
490 | static int radv_set_domains(Link *link, Link *uplink) { | |
5e2a51d5 | 491 | OrderedSet *search_domains; |
c555a358 | 492 | usec_t lifetime_usec; |
5e2a51d5 | 493 | _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */ |
c555a358 PF |
494 | |
495 | if (!link->network->router_emit_domains) | |
496 | return 0; | |
497 | ||
498 | search_domains = link->network->router_search_domains; | |
499 | lifetime_usec = link->network->router_dns_lifetime_usec; | |
500 | ||
501 | if (search_domains) | |
502 | goto set_domains; | |
503 | ||
504 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
505 | ||
506 | search_domains = link->network->search_domains; | |
507 | if (search_domains) | |
508 | goto set_domains; | |
509 | ||
510 | if (uplink) { | |
4e361acc | 511 | if (!uplink->network) { |
349a981d PF |
512 | log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us"); |
513 | return 0; | |
514 | } | |
515 | ||
c555a358 PF |
516 | search_domains = uplink->network->search_domains; |
517 | if (search_domains) | |
518 | goto set_domains; | |
519 | } | |
520 | ||
521 | return 0; | |
522 | ||
523 | set_domains: | |
5e2a51d5 ZJS |
524 | s = ordered_set_get_strv(search_domains); |
525 | if (!s) | |
526 | return log_oom(); | |
527 | ||
c555a358 PF |
528 | return sd_radv_set_dnssl(link->radv, |
529 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
5e2a51d5 | 530 | s); |
c555a358 PF |
531 | |
532 | } | |
533 | ||
534 | int radv_emit_dns(Link *link) { | |
535 | Link *uplink; | |
536 | int r; | |
537 | ||
538 | uplink = manager_find_uplink(link->manager, link); | |
539 | ||
540 | r = radv_set_dns(link, uplink); | |
541 | if (r < 0) | |
542 | log_link_warning_errno(link, r, "Could not set RA DNS: %m"); | |
543 | ||
544 | r = radv_set_domains(link, uplink); | |
545 | if (r < 0) | |
546 | log_link_warning_errno(link, r, "Could not set RA Domains: %m"); | |
547 | ||
548 | return 0; | |
549 | } | |
550 | ||
091214b6 | 551 | int radv_configure(Link *link) { |
95081e08 | 552 | RoutePrefix *q; |
091214b6 | 553 | Prefix *p; |
95081e08 | 554 | int r; |
091214b6 PF |
555 | |
556 | assert(link); | |
557 | assert(link->network); | |
558 | ||
559 | r = sd_radv_new(&link->radv); | |
560 | if (r < 0) | |
561 | return r; | |
562 | ||
563 | r = sd_radv_attach_event(link->radv, NULL, 0); | |
564 | if (r < 0) | |
565 | return r; | |
566 | ||
567 | r = sd_radv_set_mac(link->radv, &link->mac); | |
568 | if (r < 0) | |
569 | return r; | |
570 | ||
571 | r = sd_radv_set_ifindex(link->radv, link->ifindex); | |
572 | if (r < 0) | |
573 | return r; | |
574 | ||
575 | r = sd_radv_set_managed_information(link->radv, link->network->router_managed); | |
576 | if (r < 0) | |
577 | return r; | |
578 | ||
579 | r = sd_radv_set_other_information(link->radv, link->network->router_other_information); | |
580 | if (r < 0) | |
581 | return r; | |
582 | ||
301a2fb9 PF |
583 | /* a value of 0xffffffff represents infinity, 0x0 means this host is |
584 | not a router */ | |
091214b6 | 585 | r = sd_radv_set_router_lifetime(link->radv, |
945e3225 | 586 | DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC)); |
091214b6 PF |
587 | if (r < 0) |
588 | return r; | |
589 | ||
590 | if (link->network->router_lifetime_usec > 0) { | |
591 | r = sd_radv_set_preference(link->radv, | |
592 | link->network->router_preference); | |
593 | if (r < 0) | |
594 | return r; | |
595 | } | |
596 | ||
56a23cb4 PF |
597 | if (IN_SET(link->network->router_prefix_delegation, |
598 | RADV_PREFIX_DELEGATION_STATIC, | |
599 | RADV_PREFIX_DELEGATION_BOTH)) { | |
59ea6e57 | 600 | |
56a23cb4 | 601 | LIST_FOREACH(prefixes, p, link->network->static_prefixes) { |
d601b566 | 602 | r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); |
59ea6e57 LP |
603 | if (r == -EEXIST) |
604 | continue; | |
605 | if (r == -ENOEXEC) { | |
606 | log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section."); | |
607 | continue; | |
608 | } | |
609 | if (r < 0) | |
56a23cb4 PF |
610 | return r; |
611 | } | |
203d4df5 | 612 | |
95081e08 YW |
613 | LIST_FOREACH(route_prefixes, q, link->network->static_route_prefixes) { |
614 | r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false); | |
203d4df5 SS |
615 | if (r == -EEXIST) |
616 | continue; | |
617 | if (r < 0) | |
618 | return r; | |
619 | } | |
620 | ||
091214b6 PF |
621 | } |
622 | ||
c555a358 | 623 | return radv_emit_dns(link); |
091214b6 | 624 | } |
ca5ad760 YW |
625 | |
626 | int config_parse_radv_dns( | |
627 | const char *unit, | |
628 | const char *filename, | |
629 | unsigned line, | |
630 | const char *section, | |
631 | unsigned section_line, | |
632 | const char *lvalue, | |
633 | int ltype, | |
634 | const char *rvalue, | |
635 | void *data, | |
636 | void *userdata) { | |
637 | ||
638 | Network *n = data; | |
639 | const char *p = rvalue; | |
640 | int r; | |
641 | ||
642 | assert(filename); | |
643 | assert(lvalue); | |
644 | assert(rvalue); | |
645 | ||
646 | for (;;) { | |
647 | _cleanup_free_ char *w = NULL; | |
648 | union in_addr_union a; | |
649 | ||
650 | r = extract_first_word(&p, &w, NULL, 0); | |
651 | if (r == -ENOMEM) | |
652 | return log_oom(); | |
653 | if (r < 0) { | |
654 | log_syntax(unit, LOG_ERR, filename, line, r, | |
655 | "Failed to extract word, ignoring: %s", rvalue); | |
656 | return 0; | |
657 | } | |
658 | if (r == 0) | |
659 | break; | |
660 | ||
661 | if (in_addr_from_string(AF_INET6, w, &a) >= 0) { | |
662 | struct in6_addr *m; | |
663 | ||
664 | m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr)); | |
665 | if (!m) | |
666 | return log_oom(); | |
667 | ||
668 | m[n->n_router_dns++] = a.in6; | |
669 | n->router_dns = m; | |
670 | ||
671 | } else | |
672 | log_syntax(unit, LOG_ERR, filename, line, 0, | |
673 | "Failed to parse DNS server address, ignoring: %s", w); | |
674 | } | |
675 | ||
676 | return 0; | |
677 | } | |
678 | ||
679 | int config_parse_radv_search_domains( | |
680 | const char *unit, | |
681 | const char *filename, | |
682 | unsigned line, | |
683 | const char *section, | |
684 | unsigned section_line, | |
685 | const char *lvalue, | |
686 | int ltype, | |
687 | const char *rvalue, | |
688 | void *data, | |
689 | void *userdata) { | |
690 | ||
691 | Network *n = data; | |
692 | const char *p = rvalue; | |
693 | int r; | |
694 | ||
695 | assert(filename); | |
696 | assert(lvalue); | |
697 | assert(rvalue); | |
698 | ||
699 | for (;;) { | |
700 | _cleanup_free_ char *w = NULL, *idna = NULL; | |
701 | ||
702 | r = extract_first_word(&p, &w, NULL, 0); | |
703 | if (r == -ENOMEM) | |
704 | return log_oom(); | |
705 | if (r < 0) { | |
706 | log_syntax(unit, LOG_ERR, filename, line, r, | |
707 | "Failed to extract word, ignoring: %s", rvalue); | |
708 | return 0; | |
709 | } | |
710 | if (r == 0) | |
711 | break; | |
712 | ||
713 | r = dns_name_apply_idna(w, &idna); | |
714 | if (r < 0) { | |
715 | log_syntax(unit, LOG_ERR, filename, line, r, | |
716 | "Failed to apply IDNA to domain name '%s', ignoring: %m", w); | |
717 | continue; | |
718 | } else if (r == 0) | |
719 | /* transfer ownership to simplify subsequent operations */ | |
720 | idna = TAKE_PTR(w); | |
721 | ||
722 | r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); | |
723 | if (r < 0) | |
724 | return r; | |
725 | ||
726 | r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); | |
727 | if (r < 0) | |
728 | return r; | |
729 | } | |
730 | ||
731 | return 0; | |
732 | } | |
733 | ||
734 | static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = { | |
735 | [RADV_PREFIX_DELEGATION_NONE] = "no", | |
736 | [RADV_PREFIX_DELEGATION_STATIC] = "static", | |
737 | [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6", | |
738 | [RADV_PREFIX_DELEGATION_BOTH] = "yes", | |
739 | }; | |
740 | ||
741 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( | |
742 | radv_prefix_delegation, | |
743 | RADVPrefixDelegation, | |
744 | RADV_PREFIX_DELEGATION_BOTH); | |
745 | ||
746 | int config_parse_router_prefix_delegation( | |
747 | const char *unit, | |
748 | const char *filename, | |
749 | unsigned line, | |
750 | const char *section, | |
751 | unsigned section_line, | |
752 | const char *lvalue, | |
753 | int ltype, | |
754 | const char *rvalue, | |
755 | void *data, | |
756 | void *userdata) { | |
757 | ||
758 | Network *network = userdata; | |
759 | RADVPrefixDelegation d; | |
760 | ||
761 | assert(filename); | |
762 | assert(section); | |
763 | assert(lvalue); | |
764 | assert(rvalue); | |
765 | assert(data); | |
766 | ||
767 | d = radv_prefix_delegation_from_string(rvalue); | |
768 | if (d < 0) { | |
769 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue); | |
770 | return 0; | |
771 | } | |
772 | ||
773 | network->router_prefix_delegation = d; | |
774 | ||
775 | return 0; | |
776 | } | |
777 | ||
778 | int config_parse_router_preference(const char *unit, | |
779 | const char *filename, | |
780 | unsigned line, | |
781 | const char *section, | |
782 | unsigned section_line, | |
783 | const char *lvalue, | |
784 | int ltype, | |
785 | const char *rvalue, | |
786 | void *data, | |
787 | void *userdata) { | |
788 | Network *network = userdata; | |
789 | ||
790 | assert(filename); | |
791 | assert(section); | |
792 | assert(lvalue); | |
793 | assert(rvalue); | |
794 | assert(data); | |
795 | ||
796 | if (streq(rvalue, "high")) | |
797 | network->router_preference = SD_NDISC_PREFERENCE_HIGH; | |
798 | else if (STR_IN_SET(rvalue, "medium", "normal", "default")) | |
799 | network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; | |
800 | else if (streq(rvalue, "low")) | |
801 | network->router_preference = SD_NDISC_PREFERENCE_LOW; | |
802 | else | |
803 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); | |
804 | ||
805 | return 0; | |
806 | } |