]>
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; | |
c555a358 | 446 | usec_t lifetime_usec; |
fd3ef936 | 447 | size_t n_dns; |
c555a358 PF |
448 | int r; |
449 | ||
450 | if (!link->network->router_emit_dns) | |
451 | return 0; | |
452 | ||
453 | if (link->network->router_dns) { | |
fd3ef936 YW |
454 | struct in6_addr *p; |
455 | ||
456 | dns = new(struct in6_addr, link->network->n_router_dns); | |
4e361acc | 457 | if (!dns) |
c555a358 PF |
458 | return -ENOMEM; |
459 | ||
fd3ef936 YW |
460 | p = dns; |
461 | for (size_t i = 0; i < link->network->n_router_dns; i++) | |
462 | if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) { | |
463 | if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address)) | |
464 | *(p++) = link->ipv6ll_address; | |
465 | } else | |
466 | *(p++) = link->network->router_dns[i]; | |
467 | ||
468 | n_dns = p - dns; | |
c555a358 PF |
469 | lifetime_usec = link->network->router_dns_lifetime_usec; |
470 | ||
471 | goto set_dns; | |
472 | } | |
473 | ||
474 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
475 | ||
476 | r = radv_get_ip6dns(link->network, &dns, &n_dns); | |
477 | if (r > 0) | |
478 | goto set_dns; | |
479 | ||
480 | if (uplink) { | |
4e361acc | 481 | if (!uplink->network) { |
349a981d PF |
482 | log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us"); |
483 | return 0; | |
484 | } | |
485 | ||
c555a358 PF |
486 | r = radv_get_ip6dns(uplink->network, &dns, &n_dns); |
487 | if (r > 0) | |
488 | goto set_dns; | |
489 | } | |
490 | ||
491 | return 0; | |
492 | ||
493 | set_dns: | |
494 | return sd_radv_set_rdnss(link->radv, | |
495 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
496 | dns, n_dns); | |
497 | } | |
498 | ||
499 | static int radv_set_domains(Link *link, Link *uplink) { | |
5e2a51d5 | 500 | OrderedSet *search_domains; |
c555a358 | 501 | usec_t lifetime_usec; |
5e2a51d5 | 502 | _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */ |
c555a358 PF |
503 | |
504 | if (!link->network->router_emit_domains) | |
505 | return 0; | |
506 | ||
507 | search_domains = link->network->router_search_domains; | |
508 | lifetime_usec = link->network->router_dns_lifetime_usec; | |
509 | ||
510 | if (search_domains) | |
511 | goto set_domains; | |
512 | ||
513 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
514 | ||
515 | search_domains = link->network->search_domains; | |
516 | if (search_domains) | |
517 | goto set_domains; | |
518 | ||
519 | if (uplink) { | |
4e361acc | 520 | if (!uplink->network) { |
349a981d PF |
521 | log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us"); |
522 | return 0; | |
523 | } | |
524 | ||
c555a358 PF |
525 | search_domains = uplink->network->search_domains; |
526 | if (search_domains) | |
527 | goto set_domains; | |
528 | } | |
529 | ||
530 | return 0; | |
531 | ||
532 | set_domains: | |
5e2a51d5 ZJS |
533 | s = ordered_set_get_strv(search_domains); |
534 | if (!s) | |
535 | return log_oom(); | |
536 | ||
c555a358 PF |
537 | return sd_radv_set_dnssl(link->radv, |
538 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
5e2a51d5 | 539 | s); |
c555a358 PF |
540 | |
541 | } | |
542 | ||
543 | int radv_emit_dns(Link *link) { | |
544 | Link *uplink; | |
545 | int r; | |
546 | ||
547 | uplink = manager_find_uplink(link->manager, link); | |
548 | ||
549 | r = radv_set_dns(link, uplink); | |
550 | if (r < 0) | |
551 | log_link_warning_errno(link, r, "Could not set RA DNS: %m"); | |
552 | ||
553 | r = radv_set_domains(link, uplink); | |
554 | if (r < 0) | |
555 | log_link_warning_errno(link, r, "Could not set RA Domains: %m"); | |
556 | ||
557 | return 0; | |
558 | } | |
559 | ||
091214b6 | 560 | int radv_configure(Link *link) { |
95081e08 | 561 | RoutePrefix *q; |
091214b6 | 562 | Prefix *p; |
95081e08 | 563 | int r; |
091214b6 PF |
564 | |
565 | assert(link); | |
566 | assert(link->network); | |
567 | ||
568 | r = sd_radv_new(&link->radv); | |
569 | if (r < 0) | |
570 | return r; | |
571 | ||
572 | r = sd_radv_attach_event(link->radv, NULL, 0); | |
573 | if (r < 0) | |
574 | return r; | |
575 | ||
576 | r = sd_radv_set_mac(link->radv, &link->mac); | |
577 | if (r < 0) | |
578 | return r; | |
579 | ||
580 | r = sd_radv_set_ifindex(link->radv, link->ifindex); | |
581 | if (r < 0) | |
582 | return r; | |
583 | ||
584 | r = sd_radv_set_managed_information(link->radv, link->network->router_managed); | |
585 | if (r < 0) | |
586 | return r; | |
587 | ||
588 | r = sd_radv_set_other_information(link->radv, link->network->router_other_information); | |
589 | if (r < 0) | |
590 | return r; | |
591 | ||
301a2fb9 PF |
592 | /* a value of 0xffffffff represents infinity, 0x0 means this host is |
593 | not a router */ | |
091214b6 | 594 | r = sd_radv_set_router_lifetime(link->radv, |
945e3225 | 595 | DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC)); |
091214b6 PF |
596 | if (r < 0) |
597 | return r; | |
598 | ||
599 | if (link->network->router_lifetime_usec > 0) { | |
600 | r = sd_radv_set_preference(link->radv, | |
601 | link->network->router_preference); | |
602 | if (r < 0) | |
603 | return r; | |
604 | } | |
605 | ||
56a23cb4 PF |
606 | if (IN_SET(link->network->router_prefix_delegation, |
607 | RADV_PREFIX_DELEGATION_STATIC, | |
608 | RADV_PREFIX_DELEGATION_BOTH)) { | |
59ea6e57 | 609 | |
56a23cb4 | 610 | LIST_FOREACH(prefixes, p, link->network->static_prefixes) { |
d601b566 | 611 | r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); |
59ea6e57 LP |
612 | if (r == -EEXIST) |
613 | continue; | |
614 | if (r == -ENOEXEC) { | |
615 | log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section."); | |
616 | continue; | |
617 | } | |
618 | if (r < 0) | |
56a23cb4 PF |
619 | return r; |
620 | } | |
203d4df5 | 621 | |
95081e08 YW |
622 | LIST_FOREACH(route_prefixes, q, link->network->static_route_prefixes) { |
623 | r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false); | |
203d4df5 SS |
624 | if (r == -EEXIST) |
625 | continue; | |
626 | if (r < 0) | |
627 | return r; | |
628 | } | |
629 | ||
091214b6 PF |
630 | } |
631 | ||
fd3ef936 | 632 | return 0; |
091214b6 | 633 | } |
ca5ad760 YW |
634 | |
635 | int config_parse_radv_dns( | |
636 | const char *unit, | |
637 | const char *filename, | |
638 | unsigned line, | |
639 | const char *section, | |
640 | unsigned section_line, | |
641 | const char *lvalue, | |
642 | int ltype, | |
643 | const char *rvalue, | |
644 | void *data, | |
645 | void *userdata) { | |
646 | ||
647 | Network *n = data; | |
648 | const char *p = rvalue; | |
649 | int r; | |
650 | ||
651 | assert(filename); | |
652 | assert(lvalue); | |
653 | assert(rvalue); | |
654 | ||
655 | for (;;) { | |
656 | _cleanup_free_ char *w = NULL; | |
657 | union in_addr_union a; | |
658 | ||
659 | r = extract_first_word(&p, &w, NULL, 0); | |
660 | if (r == -ENOMEM) | |
661 | return log_oom(); | |
662 | if (r < 0) { | |
663 | log_syntax(unit, LOG_ERR, filename, line, r, | |
664 | "Failed to extract word, ignoring: %s", rvalue); | |
665 | return 0; | |
666 | } | |
667 | if (r == 0) | |
668 | break; | |
669 | ||
fd3ef936 YW |
670 | if (streq(w, "_link_local")) |
671 | a = IN_ADDR_NULL; | |
672 | else { | |
673 | r = in_addr_from_string(AF_INET6, w, &a); | |
674 | if (r < 0) { | |
675 | log_syntax(unit, LOG_ERR, filename, line, r, | |
676 | "Failed to parse DNS server address, ignoring: %s", w); | |
677 | continue; | |
678 | } | |
ca5ad760 | 679 | |
fd3ef936 YW |
680 | if (in_addr_is_null(AF_INET6, &a)) { |
681 | log_syntax(unit, LOG_ERR, filename, line, 0, | |
682 | "DNS server address is null, ignoring: %s", w); | |
683 | continue; | |
684 | } | |
685 | } | |
ca5ad760 | 686 | |
fd3ef936 YW |
687 | struct in6_addr *m; |
688 | m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr)); | |
689 | if (!m) | |
690 | return log_oom(); | |
ca5ad760 | 691 | |
fd3ef936 YW |
692 | m[n->n_router_dns++] = a.in6; |
693 | n->router_dns = m; | |
ca5ad760 YW |
694 | } |
695 | ||
696 | return 0; | |
697 | } | |
698 | ||
699 | int config_parse_radv_search_domains( | |
700 | const char *unit, | |
701 | const char *filename, | |
702 | unsigned line, | |
703 | const char *section, | |
704 | unsigned section_line, | |
705 | const char *lvalue, | |
706 | int ltype, | |
707 | const char *rvalue, | |
708 | void *data, | |
709 | void *userdata) { | |
710 | ||
711 | Network *n = data; | |
712 | const char *p = rvalue; | |
713 | int r; | |
714 | ||
715 | assert(filename); | |
716 | assert(lvalue); | |
717 | assert(rvalue); | |
718 | ||
719 | for (;;) { | |
720 | _cleanup_free_ char *w = NULL, *idna = NULL; | |
721 | ||
722 | r = extract_first_word(&p, &w, NULL, 0); | |
723 | if (r == -ENOMEM) | |
724 | return log_oom(); | |
725 | if (r < 0) { | |
726 | log_syntax(unit, LOG_ERR, filename, line, r, | |
727 | "Failed to extract word, ignoring: %s", rvalue); | |
728 | return 0; | |
729 | } | |
730 | if (r == 0) | |
731 | break; | |
732 | ||
733 | r = dns_name_apply_idna(w, &idna); | |
734 | if (r < 0) { | |
735 | log_syntax(unit, LOG_ERR, filename, line, r, | |
736 | "Failed to apply IDNA to domain name '%s', ignoring: %m", w); | |
737 | continue; | |
738 | } else if (r == 0) | |
739 | /* transfer ownership to simplify subsequent operations */ | |
740 | idna = TAKE_PTR(w); | |
741 | ||
742 | r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); | |
743 | if (r < 0) | |
744 | return r; | |
745 | ||
746 | r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); | |
747 | if (r < 0) | |
748 | return r; | |
749 | } | |
750 | ||
751 | return 0; | |
752 | } | |
753 | ||
754 | static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = { | |
755 | [RADV_PREFIX_DELEGATION_NONE] = "no", | |
756 | [RADV_PREFIX_DELEGATION_STATIC] = "static", | |
757 | [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6", | |
758 | [RADV_PREFIX_DELEGATION_BOTH] = "yes", | |
759 | }; | |
760 | ||
761 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( | |
762 | radv_prefix_delegation, | |
763 | RADVPrefixDelegation, | |
764 | RADV_PREFIX_DELEGATION_BOTH); | |
765 | ||
766 | int config_parse_router_prefix_delegation( | |
767 | const char *unit, | |
768 | const char *filename, | |
769 | unsigned line, | |
770 | const char *section, | |
771 | unsigned section_line, | |
772 | const char *lvalue, | |
773 | int ltype, | |
774 | const char *rvalue, | |
775 | void *data, | |
776 | void *userdata) { | |
777 | ||
778 | Network *network = userdata; | |
779 | RADVPrefixDelegation d; | |
780 | ||
781 | assert(filename); | |
782 | assert(section); | |
783 | assert(lvalue); | |
784 | assert(rvalue); | |
785 | assert(data); | |
786 | ||
787 | d = radv_prefix_delegation_from_string(rvalue); | |
788 | if (d < 0) { | |
789 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue); | |
790 | return 0; | |
791 | } | |
792 | ||
793 | network->router_prefix_delegation = d; | |
794 | ||
795 | return 0; | |
796 | } | |
797 | ||
798 | int config_parse_router_preference(const char *unit, | |
799 | const char *filename, | |
800 | unsigned line, | |
801 | const char *section, | |
802 | unsigned section_line, | |
803 | const char *lvalue, | |
804 | int ltype, | |
805 | const char *rvalue, | |
806 | void *data, | |
807 | void *userdata) { | |
808 | Network *network = userdata; | |
809 | ||
810 | assert(filename); | |
811 | assert(section); | |
812 | assert(lvalue); | |
813 | assert(rvalue); | |
814 | assert(data); | |
815 | ||
816 | if (streq(rvalue, "high")) | |
817 | network->router_preference = SD_NDISC_PREFERENCE_HIGH; | |
818 | else if (STR_IN_SET(rvalue, "medium", "normal", "default")) | |
819 | network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; | |
820 | else if (streq(rvalue, "low")) | |
821 | network->router_preference = SD_NDISC_PREFERENCE_LOW; | |
822 | else | |
823 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); | |
824 | ||
825 | return 0; | |
826 | } |