]>
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 | |
bd6379ec SS |
322 | int config_parse_prefix_assign( |
323 | const char *unit, | |
324 | const char *filename, | |
325 | unsigned line, | |
326 | const char *section, | |
327 | unsigned section_line, | |
328 | const char *lvalue, | |
329 | int ltype, | |
330 | const char *rvalue, | |
331 | void *data, | |
332 | void *userdata) { | |
333 | ||
334 | Network *network = userdata; | |
335 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; | |
336 | int r; | |
337 | ||
338 | assert(filename); | |
339 | assert(section); | |
340 | assert(lvalue); | |
341 | assert(rvalue); | |
342 | assert(data); | |
343 | ||
344 | r = prefix_new_static(network, filename, section_line, &p); | |
345 | if (r < 0) | |
346 | return r; | |
347 | ||
348 | r = parse_boolean(rvalue); | |
349 | if (r < 0) { | |
350 | log_syntax(unit, LOG_ERR, filename, line, r, | |
351 | "Failed to parse %s=, ignoring assignment: %s", | |
352 | lvalue, rvalue); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | p->assign = r; | |
357 | p = NULL; | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
203d4df5 SS |
362 | int config_parse_route_prefix(const char *unit, |
363 | const char *filename, | |
364 | unsigned line, | |
365 | const char *section, | |
366 | unsigned section_line, | |
367 | const char *lvalue, | |
368 | int ltype, | |
369 | const char *rvalue, | |
370 | void *data, | |
371 | void *userdata) { | |
372 | ||
373 | Network *network = userdata; | |
95081e08 | 374 | _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; |
203d4df5 SS |
375 | uint8_t prefixlen = 64; |
376 | union in_addr_union in6addr; | |
377 | int r; | |
378 | ||
379 | assert(filename); | |
380 | assert(section); | |
381 | assert(lvalue); | |
382 | assert(rvalue); | |
383 | assert(data); | |
384 | ||
385 | r = route_prefix_new_static(network, filename, section_line, &p); | |
386 | if (r < 0) | |
387 | return r; | |
388 | ||
389 | r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); | |
390 | if (r < 0) { | |
391 | log_syntax(unit, LOG_ERR, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue); | |
392 | return 0; | |
393 | } | |
394 | ||
395 | if (sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen) < 0) | |
396 | return -EADDRNOTAVAIL; | |
397 | ||
398 | log_syntax(unit, LOG_INFO, filename, line, r, "Found route prefix %s", rvalue); | |
399 | ||
400 | p = NULL; | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | int config_parse_route_prefix_lifetime(const char *unit, | |
406 | const char *filename, | |
407 | unsigned line, | |
408 | const char *section, | |
409 | unsigned section_line, | |
410 | const char *lvalue, | |
411 | int ltype, | |
412 | const char *rvalue, | |
413 | void *data, | |
414 | void *userdata) { | |
415 | Network *network = userdata; | |
95081e08 | 416 | _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; |
203d4df5 SS |
417 | usec_t usec; |
418 | int r; | |
419 | ||
420 | assert(filename); | |
421 | assert(section); | |
422 | assert(lvalue); | |
423 | assert(rvalue); | |
424 | assert(data); | |
425 | ||
426 | r = route_prefix_new_static(network, filename, section_line, &p); | |
427 | if (r < 0) | |
428 | return r; | |
429 | ||
430 | r = parse_sec(rvalue, &usec); | |
431 | if (r < 0) { | |
095b3a7d YW |
432 | log_syntax(unit, LOG_ERR, filename, line, r, |
433 | "Route lifetime is invalid, ignoring assignment: %s", rvalue); | |
203d4df5 SS |
434 | return 0; |
435 | } | |
436 | ||
437 | /* a value of 0xffffffff represents infinity */ | |
438 | r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
439 | if (r < 0) | |
440 | return r; | |
441 | ||
442 | p = NULL; | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
c555a358 PF |
447 | static int radv_get_ip6dns(Network *network, struct in6_addr **dns, |
448 | size_t *n_dns) { | |
449 | _cleanup_free_ struct in6_addr *addresses = NULL; | |
450 | size_t i, n_addresses = 0, n_allocated = 0; | |
451 | ||
452 | assert(network); | |
453 | assert(dns); | |
454 | assert(n_dns); | |
455 | ||
456 | for (i = 0; i < network->n_dns; i++) { | |
457 | union in_addr_union *addr; | |
458 | ||
459 | if (network->dns[i].family != AF_INET6) | |
460 | continue; | |
461 | ||
462 | addr = &network->dns[i].address; | |
463 | ||
464 | if (in_addr_is_null(AF_INET6, addr) || | |
465 | in_addr_is_link_local(AF_INET6, addr) || | |
466 | in_addr_is_localhost(AF_INET6, addr)) | |
467 | continue; | |
468 | ||
469 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) | |
470 | return -ENOMEM; | |
471 | ||
472 | addresses[n_addresses++] = addr->in6; | |
473 | } | |
474 | ||
475 | if (addresses) { | |
ae2a15bc | 476 | *dns = TAKE_PTR(addresses); |
c555a358 PF |
477 | |
478 | *n_dns = n_addresses; | |
479 | } | |
480 | ||
481 | return n_addresses; | |
482 | } | |
483 | ||
484 | static int radv_set_dns(Link *link, Link *uplink) { | |
485 | _cleanup_free_ struct in6_addr *dns = NULL; | |
c555a358 | 486 | usec_t lifetime_usec; |
fd3ef936 | 487 | size_t n_dns; |
c555a358 PF |
488 | int r; |
489 | ||
490 | if (!link->network->router_emit_dns) | |
491 | return 0; | |
492 | ||
493 | if (link->network->router_dns) { | |
fd3ef936 YW |
494 | struct in6_addr *p; |
495 | ||
496 | dns = new(struct in6_addr, link->network->n_router_dns); | |
4e361acc | 497 | if (!dns) |
c555a358 PF |
498 | return -ENOMEM; |
499 | ||
fd3ef936 YW |
500 | p = dns; |
501 | for (size_t i = 0; i < link->network->n_router_dns; i++) | |
502 | if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) { | |
503 | if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address)) | |
504 | *(p++) = link->ipv6ll_address; | |
505 | } else | |
506 | *(p++) = link->network->router_dns[i]; | |
507 | ||
508 | n_dns = p - dns; | |
c555a358 PF |
509 | lifetime_usec = link->network->router_dns_lifetime_usec; |
510 | ||
511 | goto set_dns; | |
512 | } | |
513 | ||
514 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
515 | ||
516 | r = radv_get_ip6dns(link->network, &dns, &n_dns); | |
517 | if (r > 0) | |
518 | goto set_dns; | |
519 | ||
520 | if (uplink) { | |
4e361acc | 521 | if (!uplink->network) { |
349a981d PF |
522 | log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us"); |
523 | return 0; | |
524 | } | |
525 | ||
c555a358 PF |
526 | r = radv_get_ip6dns(uplink->network, &dns, &n_dns); |
527 | if (r > 0) | |
528 | goto set_dns; | |
529 | } | |
530 | ||
531 | return 0; | |
532 | ||
533 | set_dns: | |
534 | return sd_radv_set_rdnss(link->radv, | |
535 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
536 | dns, n_dns); | |
537 | } | |
538 | ||
539 | static int radv_set_domains(Link *link, Link *uplink) { | |
5e2a51d5 | 540 | OrderedSet *search_domains; |
c555a358 | 541 | usec_t lifetime_usec; |
5e2a51d5 | 542 | _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */ |
c555a358 PF |
543 | |
544 | if (!link->network->router_emit_domains) | |
545 | return 0; | |
546 | ||
547 | search_domains = link->network->router_search_domains; | |
548 | lifetime_usec = link->network->router_dns_lifetime_usec; | |
549 | ||
550 | if (search_domains) | |
551 | goto set_domains; | |
552 | ||
553 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
554 | ||
555 | search_domains = link->network->search_domains; | |
556 | if (search_domains) | |
557 | goto set_domains; | |
558 | ||
559 | if (uplink) { | |
4e361acc | 560 | if (!uplink->network) { |
349a981d PF |
561 | log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us"); |
562 | return 0; | |
563 | } | |
564 | ||
c555a358 PF |
565 | search_domains = uplink->network->search_domains; |
566 | if (search_domains) | |
567 | goto set_domains; | |
568 | } | |
569 | ||
570 | return 0; | |
571 | ||
572 | set_domains: | |
5e2a51d5 ZJS |
573 | s = ordered_set_get_strv(search_domains); |
574 | if (!s) | |
575 | return log_oom(); | |
576 | ||
c555a358 PF |
577 | return sd_radv_set_dnssl(link->radv, |
578 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
5e2a51d5 | 579 | s); |
c555a358 PF |
580 | |
581 | } | |
582 | ||
583 | int radv_emit_dns(Link *link) { | |
584 | Link *uplink; | |
585 | int r; | |
586 | ||
587 | uplink = manager_find_uplink(link->manager, link); | |
588 | ||
589 | r = radv_set_dns(link, uplink); | |
590 | if (r < 0) | |
591 | log_link_warning_errno(link, r, "Could not set RA DNS: %m"); | |
592 | ||
593 | r = radv_set_domains(link, uplink); | |
594 | if (r < 0) | |
595 | log_link_warning_errno(link, r, "Could not set RA Domains: %m"); | |
596 | ||
597 | return 0; | |
598 | } | |
599 | ||
091214b6 | 600 | int radv_configure(Link *link) { |
95081e08 | 601 | RoutePrefix *q; |
091214b6 | 602 | Prefix *p; |
95081e08 | 603 | int r; |
091214b6 PF |
604 | |
605 | assert(link); | |
606 | assert(link->network); | |
607 | ||
608 | r = sd_radv_new(&link->radv); | |
609 | if (r < 0) | |
610 | return r; | |
611 | ||
612 | r = sd_radv_attach_event(link->radv, NULL, 0); | |
613 | if (r < 0) | |
614 | return r; | |
615 | ||
616 | r = sd_radv_set_mac(link->radv, &link->mac); | |
617 | if (r < 0) | |
618 | return r; | |
619 | ||
620 | r = sd_radv_set_ifindex(link->radv, link->ifindex); | |
621 | if (r < 0) | |
622 | return r; | |
623 | ||
624 | r = sd_radv_set_managed_information(link->radv, link->network->router_managed); | |
625 | if (r < 0) | |
626 | return r; | |
627 | ||
628 | r = sd_radv_set_other_information(link->radv, link->network->router_other_information); | |
629 | if (r < 0) | |
630 | return r; | |
631 | ||
301a2fb9 PF |
632 | /* a value of 0xffffffff represents infinity, 0x0 means this host is |
633 | not a router */ | |
091214b6 | 634 | r = sd_radv_set_router_lifetime(link->radv, |
945e3225 | 635 | DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC)); |
091214b6 PF |
636 | if (r < 0) |
637 | return r; | |
638 | ||
639 | if (link->network->router_lifetime_usec > 0) { | |
640 | r = sd_radv_set_preference(link->radv, | |
641 | link->network->router_preference); | |
642 | if (r < 0) | |
643 | return r; | |
644 | } | |
645 | ||
56a23cb4 PF |
646 | if (IN_SET(link->network->router_prefix_delegation, |
647 | RADV_PREFIX_DELEGATION_STATIC, | |
648 | RADV_PREFIX_DELEGATION_BOTH)) { | |
59ea6e57 | 649 | |
56a23cb4 | 650 | LIST_FOREACH(prefixes, p, link->network->static_prefixes) { |
d601b566 | 651 | r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); |
59ea6e57 LP |
652 | if (r == -EEXIST) |
653 | continue; | |
654 | if (r == -ENOEXEC) { | |
655 | log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section."); | |
656 | continue; | |
657 | } | |
658 | if (r < 0) | |
56a23cb4 PF |
659 | return r; |
660 | } | |
203d4df5 | 661 | |
95081e08 YW |
662 | LIST_FOREACH(route_prefixes, q, link->network->static_route_prefixes) { |
663 | r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false); | |
203d4df5 SS |
664 | if (r == -EEXIST) |
665 | continue; | |
666 | if (r < 0) | |
667 | return r; | |
668 | } | |
669 | ||
091214b6 PF |
670 | } |
671 | ||
fd3ef936 | 672 | return 0; |
091214b6 | 673 | } |
ca5ad760 YW |
674 | |
675 | int config_parse_radv_dns( | |
676 | const char *unit, | |
677 | const char *filename, | |
678 | unsigned line, | |
679 | const char *section, | |
680 | unsigned section_line, | |
681 | const char *lvalue, | |
682 | int ltype, | |
683 | const char *rvalue, | |
684 | void *data, | |
685 | void *userdata) { | |
686 | ||
687 | Network *n = data; | |
688 | const char *p = rvalue; | |
689 | int r; | |
690 | ||
691 | assert(filename); | |
692 | assert(lvalue); | |
693 | assert(rvalue); | |
694 | ||
695 | for (;;) { | |
696 | _cleanup_free_ char *w = NULL; | |
697 | union in_addr_union a; | |
698 | ||
699 | r = extract_first_word(&p, &w, NULL, 0); | |
700 | if (r == -ENOMEM) | |
701 | return log_oom(); | |
702 | if (r < 0) { | |
703 | log_syntax(unit, LOG_ERR, filename, line, r, | |
704 | "Failed to extract word, ignoring: %s", rvalue); | |
705 | return 0; | |
706 | } | |
707 | if (r == 0) | |
708 | break; | |
709 | ||
fd3ef936 YW |
710 | if (streq(w, "_link_local")) |
711 | a = IN_ADDR_NULL; | |
712 | else { | |
713 | r = in_addr_from_string(AF_INET6, w, &a); | |
714 | if (r < 0) { | |
715 | log_syntax(unit, LOG_ERR, filename, line, r, | |
716 | "Failed to parse DNS server address, ignoring: %s", w); | |
717 | continue; | |
718 | } | |
ca5ad760 | 719 | |
fd3ef936 YW |
720 | if (in_addr_is_null(AF_INET6, &a)) { |
721 | log_syntax(unit, LOG_ERR, filename, line, 0, | |
722 | "DNS server address is null, ignoring: %s", w); | |
723 | continue; | |
724 | } | |
725 | } | |
ca5ad760 | 726 | |
fd3ef936 YW |
727 | struct in6_addr *m; |
728 | m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr)); | |
729 | if (!m) | |
730 | return log_oom(); | |
ca5ad760 | 731 | |
fd3ef936 YW |
732 | m[n->n_router_dns++] = a.in6; |
733 | n->router_dns = m; | |
ca5ad760 YW |
734 | } |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
739 | int config_parse_radv_search_domains( | |
740 | const char *unit, | |
741 | const char *filename, | |
742 | unsigned line, | |
743 | const char *section, | |
744 | unsigned section_line, | |
745 | const char *lvalue, | |
746 | int ltype, | |
747 | const char *rvalue, | |
748 | void *data, | |
749 | void *userdata) { | |
750 | ||
751 | Network *n = data; | |
752 | const char *p = rvalue; | |
753 | int r; | |
754 | ||
755 | assert(filename); | |
756 | assert(lvalue); | |
757 | assert(rvalue); | |
758 | ||
759 | for (;;) { | |
760 | _cleanup_free_ char *w = NULL, *idna = NULL; | |
761 | ||
762 | r = extract_first_word(&p, &w, NULL, 0); | |
763 | if (r == -ENOMEM) | |
764 | return log_oom(); | |
765 | if (r < 0) { | |
766 | log_syntax(unit, LOG_ERR, filename, line, r, | |
767 | "Failed to extract word, ignoring: %s", rvalue); | |
768 | return 0; | |
769 | } | |
770 | if (r == 0) | |
771 | break; | |
772 | ||
773 | r = dns_name_apply_idna(w, &idna); | |
774 | if (r < 0) { | |
775 | log_syntax(unit, LOG_ERR, filename, line, r, | |
776 | "Failed to apply IDNA to domain name '%s', ignoring: %m", w); | |
777 | continue; | |
778 | } else if (r == 0) | |
779 | /* transfer ownership to simplify subsequent operations */ | |
780 | idna = TAKE_PTR(w); | |
781 | ||
782 | r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); | |
783 | if (r < 0) | |
784 | return r; | |
785 | ||
786 | r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); | |
787 | if (r < 0) | |
788 | return r; | |
789 | } | |
790 | ||
791 | return 0; | |
792 | } | |
793 | ||
794 | static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = { | |
795 | [RADV_PREFIX_DELEGATION_NONE] = "no", | |
796 | [RADV_PREFIX_DELEGATION_STATIC] = "static", | |
797 | [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6", | |
798 | [RADV_PREFIX_DELEGATION_BOTH] = "yes", | |
799 | }; | |
800 | ||
801 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( | |
802 | radv_prefix_delegation, | |
803 | RADVPrefixDelegation, | |
804 | RADV_PREFIX_DELEGATION_BOTH); | |
805 | ||
806 | int config_parse_router_prefix_delegation( | |
807 | const char *unit, | |
808 | const char *filename, | |
809 | unsigned line, | |
810 | const char *section, | |
811 | unsigned section_line, | |
812 | const char *lvalue, | |
813 | int ltype, | |
814 | const char *rvalue, | |
815 | void *data, | |
816 | void *userdata) { | |
817 | ||
818 | Network *network = userdata; | |
819 | RADVPrefixDelegation d; | |
820 | ||
821 | assert(filename); | |
822 | assert(section); | |
823 | assert(lvalue); | |
824 | assert(rvalue); | |
825 | assert(data); | |
826 | ||
827 | d = radv_prefix_delegation_from_string(rvalue); | |
828 | if (d < 0) { | |
829 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue); | |
830 | return 0; | |
831 | } | |
832 | ||
833 | network->router_prefix_delegation = d; | |
834 | ||
835 | return 0; | |
836 | } | |
837 | ||
838 | int config_parse_router_preference(const char *unit, | |
839 | const char *filename, | |
840 | unsigned line, | |
841 | const char *section, | |
842 | unsigned section_line, | |
843 | const char *lvalue, | |
844 | int ltype, | |
845 | const char *rvalue, | |
846 | void *data, | |
847 | void *userdata) { | |
848 | Network *network = userdata; | |
849 | ||
850 | assert(filename); | |
851 | assert(section); | |
852 | assert(lvalue); | |
853 | assert(rvalue); | |
854 | assert(data); | |
855 | ||
856 | if (streq(rvalue, "high")) | |
857 | network->router_preference = SD_NDISC_PREFERENCE_HIGH; | |
858 | else if (STR_IN_SET(rvalue, "medium", "normal", "default")) | |
859 | network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; | |
860 | else if (streq(rvalue, "low")) | |
861 | network->router_preference = SD_NDISC_PREFERENCE_LOW; | |
862 | else | |
863 | log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); | |
864 | ||
865 | return 0; | |
866 | } | |
02e9e34b AR |
867 | |
868 | int config_parse_router_prefix_subnet_id(const char *unit, | |
869 | const char *filename, | |
870 | unsigned line, | |
871 | const char *section, | |
872 | unsigned section_line, | |
873 | const char *lvalue, | |
874 | int ltype, | |
875 | const char *rvalue, | |
876 | void *data, | |
877 | void *userdata) { | |
878 | Network *network = userdata; | |
879 | uint64_t t; | |
880 | int r; | |
881 | ||
882 | assert(filename); | |
883 | assert(lvalue); | |
884 | assert(rvalue); | |
885 | assert(data); | |
886 | ||
887 | if (isempty(rvalue) || streq(rvalue, "auto")) { | |
888 | network->router_prefix_subnet_id = -1; | |
889 | return 0; | |
890 | } | |
891 | ||
892 | r = safe_atoux64(rvalue, &t); | |
893 | if (r < 0 || t > INT64_MAX) { | |
894 | log_syntax(unit, LOG_ERR, filename, line, r, | |
895 | "Subnet id '%s' is invalid, ignoring assignment.", | |
896 | rvalue); | |
897 | return 0; | |
898 | } | |
899 | ||
900 | network->router_prefix_subnet_id = (int64_t)t; | |
901 | ||
902 | return 0; | |
903 | } |