]>
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) | |
d96edb2c | 214 | return log_oom(); |
6e849e95 PF |
215 | |
216 | r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); | |
217 | if (r < 0) { | |
d96edb2c | 218 | log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); |
6e849e95 PF |
219 | return 0; |
220 | } | |
221 | ||
c9778516 YW |
222 | r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen); |
223 | if (r < 0) { | |
224 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue); | |
225 | return 0; | |
226 | } | |
6e849e95 PF |
227 | |
228 | p = NULL; | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | int config_parse_prefix_flags(const char *unit, | |
234 | const char *filename, | |
235 | unsigned line, | |
236 | const char *section, | |
237 | unsigned section_line, | |
238 | const char *lvalue, | |
239 | int ltype, | |
240 | const char *rvalue, | |
241 | void *data, | |
242 | void *userdata) { | |
243 | Network *network = userdata; | |
fcbf4cb7 | 244 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; |
c9778516 | 245 | int r; |
6e849e95 PF |
246 | |
247 | assert(filename); | |
248 | assert(section); | |
249 | assert(lvalue); | |
250 | assert(rvalue); | |
251 | assert(data); | |
252 | ||
253 | r = prefix_new_static(network, filename, section_line, &p); | |
254 | if (r < 0) | |
d96edb2c | 255 | return log_oom(); |
6e849e95 PF |
256 | |
257 | r = parse_boolean(rvalue); | |
258 | if (r < 0) { | |
c9778516 | 259 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); |
6e849e95 PF |
260 | return 0; |
261 | } | |
262 | ||
6e849e95 | 263 | if (streq(lvalue, "OnLink")) |
c9778516 | 264 | r = sd_radv_prefix_set_onlink(p->radv_prefix, r); |
6e849e95 | 265 | else if (streq(lvalue, "AddressAutoconfiguration")) |
c9778516 YW |
266 | r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r); |
267 | if (r < 0) { | |
268 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue); | |
269 | return 0; | |
270 | } | |
6e849e95 PF |
271 | |
272 | p = NULL; | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
277 | int config_parse_prefix_lifetime(const char *unit, | |
278 | const char *filename, | |
279 | unsigned line, | |
280 | const char *section, | |
281 | unsigned section_line, | |
282 | const char *lvalue, | |
283 | int ltype, | |
284 | const char *rvalue, | |
285 | void *data, | |
286 | void *userdata) { | |
287 | Network *network = userdata; | |
fcbf4cb7 | 288 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; |
6e849e95 PF |
289 | usec_t usec; |
290 | int r; | |
291 | ||
292 | assert(filename); | |
293 | assert(section); | |
294 | assert(lvalue); | |
295 | assert(rvalue); | |
296 | assert(data); | |
297 | ||
298 | r = prefix_new_static(network, filename, section_line, &p); | |
299 | if (r < 0) | |
d96edb2c | 300 | return log_oom(); |
6e849e95 PF |
301 | |
302 | r = parse_sec(rvalue, &usec); | |
303 | if (r < 0) { | |
d96edb2c | 304 | log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); |
6e849e95 PF |
305 | return 0; |
306 | } | |
307 | ||
308 | /* a value of 0xffffffff represents infinity */ | |
309 | if (streq(lvalue, "PreferredLifetimeSec")) | |
310 | r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix, | |
311 | DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
312 | else if (streq(lvalue, "ValidLifetimeSec")) | |
313 | r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix, | |
314 | DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
c9778516 YW |
315 | if (r < 0) { |
316 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue); | |
317 | return 0; | |
318 | } | |
6e849e95 PF |
319 | |
320 | p = NULL; | |
321 | ||
322 | return 0; | |
323 | } | |
091214b6 | 324 | |
bd6379ec SS |
325 | int config_parse_prefix_assign( |
326 | const char *unit, | |
327 | const char *filename, | |
328 | unsigned line, | |
329 | const char *section, | |
330 | unsigned section_line, | |
331 | const char *lvalue, | |
332 | int ltype, | |
333 | const char *rvalue, | |
334 | void *data, | |
335 | void *userdata) { | |
336 | ||
337 | Network *network = userdata; | |
338 | _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; | |
339 | int r; | |
340 | ||
341 | assert(filename); | |
342 | assert(section); | |
343 | assert(lvalue); | |
344 | assert(rvalue); | |
345 | assert(data); | |
346 | ||
347 | r = prefix_new_static(network, filename, section_line, &p); | |
348 | if (r < 0) | |
d96edb2c | 349 | return log_oom(); |
bd6379ec SS |
350 | |
351 | r = parse_boolean(rvalue); | |
352 | if (r < 0) { | |
d96edb2c | 353 | log_syntax(unit, LOG_WARNING, filename, line, r, |
bd6379ec SS |
354 | "Failed to parse %s=, ignoring assignment: %s", |
355 | lvalue, rvalue); | |
356 | return 0; | |
357 | } | |
358 | ||
359 | p->assign = r; | |
360 | p = NULL; | |
361 | ||
362 | return 0; | |
363 | } | |
364 | ||
203d4df5 SS |
365 | int config_parse_route_prefix(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 | ||
376 | Network *network = userdata; | |
95081e08 | 377 | _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; |
203d4df5 SS |
378 | uint8_t prefixlen = 64; |
379 | union in_addr_union in6addr; | |
380 | int r; | |
381 | ||
382 | assert(filename); | |
383 | assert(section); | |
384 | assert(lvalue); | |
385 | assert(rvalue); | |
386 | assert(data); | |
387 | ||
388 | r = route_prefix_new_static(network, filename, section_line, &p); | |
389 | if (r < 0) | |
d96edb2c | 390 | return log_oom(); |
203d4df5 SS |
391 | |
392 | r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); | |
393 | if (r < 0) { | |
d96edb2c | 394 | log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue); |
203d4df5 SS |
395 | return 0; |
396 | } | |
397 | ||
c9778516 YW |
398 | r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen); |
399 | if (r < 0) { | |
400 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m"); | |
401 | return 0; | |
402 | } | |
203d4df5 SS |
403 | |
404 | p = NULL; | |
405 | ||
406 | return 0; | |
407 | } | |
408 | ||
409 | int config_parse_route_prefix_lifetime(const char *unit, | |
410 | const char *filename, | |
411 | unsigned line, | |
412 | const char *section, | |
413 | unsigned section_line, | |
414 | const char *lvalue, | |
415 | int ltype, | |
416 | const char *rvalue, | |
417 | void *data, | |
418 | void *userdata) { | |
419 | Network *network = userdata; | |
95081e08 | 420 | _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; |
203d4df5 SS |
421 | usec_t usec; |
422 | int r; | |
423 | ||
424 | assert(filename); | |
425 | assert(section); | |
426 | assert(lvalue); | |
427 | assert(rvalue); | |
428 | assert(data); | |
429 | ||
430 | r = route_prefix_new_static(network, filename, section_line, &p); | |
431 | if (r < 0) | |
d96edb2c | 432 | return log_oom(); |
203d4df5 SS |
433 | |
434 | r = parse_sec(rvalue, &usec); | |
435 | if (r < 0) { | |
d96edb2c | 436 | log_syntax(unit, LOG_WARNING, filename, line, r, |
095b3a7d | 437 | "Route lifetime is invalid, ignoring assignment: %s", rvalue); |
203d4df5 SS |
438 | return 0; |
439 | } | |
440 | ||
441 | /* a value of 0xffffffff represents infinity */ | |
442 | r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC)); | |
c9778516 YW |
443 | if (r < 0) { |
444 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
445 | "Failed to set route lifetime, ignoring assignment: %m"); | |
446 | return 0; | |
447 | } | |
203d4df5 SS |
448 | |
449 | p = NULL; | |
450 | ||
451 | return 0; | |
452 | } | |
453 | ||
c555a358 PF |
454 | static int radv_get_ip6dns(Network *network, struct in6_addr **dns, |
455 | size_t *n_dns) { | |
456 | _cleanup_free_ struct in6_addr *addresses = NULL; | |
457 | size_t i, n_addresses = 0, n_allocated = 0; | |
458 | ||
459 | assert(network); | |
460 | assert(dns); | |
461 | assert(n_dns); | |
462 | ||
463 | for (i = 0; i < network->n_dns; i++) { | |
464 | union in_addr_union *addr; | |
465 | ||
e77bd3fd | 466 | if (network->dns[i]->family != AF_INET6) |
c555a358 PF |
467 | continue; |
468 | ||
e77bd3fd | 469 | addr = &network->dns[i]->address; |
c555a358 PF |
470 | |
471 | if (in_addr_is_null(AF_INET6, addr) || | |
472 | in_addr_is_link_local(AF_INET6, addr) || | |
473 | in_addr_is_localhost(AF_INET6, addr)) | |
474 | continue; | |
475 | ||
476 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) | |
477 | return -ENOMEM; | |
478 | ||
479 | addresses[n_addresses++] = addr->in6; | |
480 | } | |
481 | ||
482 | if (addresses) { | |
ae2a15bc | 483 | *dns = TAKE_PTR(addresses); |
c555a358 PF |
484 | |
485 | *n_dns = n_addresses; | |
486 | } | |
487 | ||
488 | return n_addresses; | |
489 | } | |
490 | ||
491 | static int radv_set_dns(Link *link, Link *uplink) { | |
492 | _cleanup_free_ struct in6_addr *dns = NULL; | |
c555a358 | 493 | usec_t lifetime_usec; |
fd3ef936 | 494 | size_t n_dns; |
c555a358 PF |
495 | int r; |
496 | ||
497 | if (!link->network->router_emit_dns) | |
498 | return 0; | |
499 | ||
500 | if (link->network->router_dns) { | |
fd3ef936 YW |
501 | struct in6_addr *p; |
502 | ||
503 | dns = new(struct in6_addr, link->network->n_router_dns); | |
4e361acc | 504 | if (!dns) |
c555a358 PF |
505 | return -ENOMEM; |
506 | ||
fd3ef936 YW |
507 | p = dns; |
508 | for (size_t i = 0; i < link->network->n_router_dns; i++) | |
509 | if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) { | |
510 | if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address)) | |
511 | *(p++) = link->ipv6ll_address; | |
512 | } else | |
513 | *(p++) = link->network->router_dns[i]; | |
514 | ||
515 | n_dns = p - dns; | |
c555a358 PF |
516 | lifetime_usec = link->network->router_dns_lifetime_usec; |
517 | ||
518 | goto set_dns; | |
519 | } | |
520 | ||
521 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
522 | ||
523 | r = radv_get_ip6dns(link->network, &dns, &n_dns); | |
524 | if (r > 0) | |
525 | goto set_dns; | |
526 | ||
527 | if (uplink) { | |
4e361acc | 528 | if (!uplink->network) { |
349a981d PF |
529 | log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us"); |
530 | return 0; | |
531 | } | |
532 | ||
c555a358 PF |
533 | r = radv_get_ip6dns(uplink->network, &dns, &n_dns); |
534 | if (r > 0) | |
535 | goto set_dns; | |
536 | } | |
537 | ||
538 | return 0; | |
539 | ||
540 | set_dns: | |
541 | return sd_radv_set_rdnss(link->radv, | |
542 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
543 | dns, n_dns); | |
544 | } | |
545 | ||
546 | static int radv_set_domains(Link *link, Link *uplink) { | |
5e2a51d5 | 547 | OrderedSet *search_domains; |
c555a358 | 548 | usec_t lifetime_usec; |
5e2a51d5 | 549 | _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */ |
c555a358 PF |
550 | |
551 | if (!link->network->router_emit_domains) | |
552 | return 0; | |
553 | ||
554 | search_domains = link->network->router_search_domains; | |
555 | lifetime_usec = link->network->router_dns_lifetime_usec; | |
556 | ||
557 | if (search_domains) | |
558 | goto set_domains; | |
559 | ||
560 | lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; | |
561 | ||
562 | search_domains = link->network->search_domains; | |
563 | if (search_domains) | |
564 | goto set_domains; | |
565 | ||
566 | if (uplink) { | |
4e361acc | 567 | if (!uplink->network) { |
349a981d PF |
568 | log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us"); |
569 | return 0; | |
570 | } | |
571 | ||
c555a358 PF |
572 | search_domains = uplink->network->search_domains; |
573 | if (search_domains) | |
574 | goto set_domains; | |
575 | } | |
576 | ||
577 | return 0; | |
578 | ||
579 | set_domains: | |
5e2a51d5 ZJS |
580 | s = ordered_set_get_strv(search_domains); |
581 | if (!s) | |
582 | return log_oom(); | |
583 | ||
c555a358 PF |
584 | return sd_radv_set_dnssl(link->radv, |
585 | DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), | |
5e2a51d5 | 586 | s); |
c555a358 PF |
587 | |
588 | } | |
589 | ||
590 | int radv_emit_dns(Link *link) { | |
591 | Link *uplink; | |
592 | int r; | |
593 | ||
594 | uplink = manager_find_uplink(link->manager, link); | |
595 | ||
596 | r = radv_set_dns(link, uplink); | |
597 | if (r < 0) | |
598 | log_link_warning_errno(link, r, "Could not set RA DNS: %m"); | |
599 | ||
600 | r = radv_set_domains(link, uplink); | |
601 | if (r < 0) | |
602 | log_link_warning_errno(link, r, "Could not set RA Domains: %m"); | |
603 | ||
604 | return 0; | |
605 | } | |
606 | ||
091214b6 | 607 | int radv_configure(Link *link) { |
95081e08 | 608 | RoutePrefix *q; |
091214b6 | 609 | Prefix *p; |
95081e08 | 610 | int r; |
091214b6 PF |
611 | |
612 | assert(link); | |
613 | assert(link->network); | |
614 | ||
615 | r = sd_radv_new(&link->radv); | |
616 | if (r < 0) | |
617 | return r; | |
618 | ||
619 | r = sd_radv_attach_event(link->radv, NULL, 0); | |
620 | if (r < 0) | |
621 | return r; | |
622 | ||
623 | r = sd_radv_set_mac(link->radv, &link->mac); | |
624 | if (r < 0) | |
625 | return r; | |
626 | ||
627 | r = sd_radv_set_ifindex(link->radv, link->ifindex); | |
628 | if (r < 0) | |
629 | return r; | |
630 | ||
631 | r = sd_radv_set_managed_information(link->radv, link->network->router_managed); | |
632 | if (r < 0) | |
633 | return r; | |
634 | ||
635 | r = sd_radv_set_other_information(link->radv, link->network->router_other_information); | |
636 | if (r < 0) | |
637 | return r; | |
638 | ||
301a2fb9 PF |
639 | /* a value of 0xffffffff represents infinity, 0x0 means this host is |
640 | not a router */ | |
091214b6 | 641 | r = sd_radv_set_router_lifetime(link->radv, |
945e3225 | 642 | DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC)); |
091214b6 PF |
643 | if (r < 0) |
644 | return r; | |
645 | ||
646 | if (link->network->router_lifetime_usec > 0) { | |
647 | r = sd_radv_set_preference(link->radv, | |
648 | link->network->router_preference); | |
649 | if (r < 0) | |
650 | return r; | |
651 | } | |
652 | ||
61c0ef4f | 653 | if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) { |
56a23cb4 | 654 | LIST_FOREACH(prefixes, p, link->network->static_prefixes) { |
d601b566 | 655 | r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); |
59ea6e57 LP |
656 | if (r == -EEXIST) |
657 | continue; | |
658 | if (r == -ENOEXEC) { | |
659 | log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section."); | |
660 | continue; | |
661 | } | |
662 | if (r < 0) | |
56a23cb4 PF |
663 | return r; |
664 | } | |
203d4df5 | 665 | |
95081e08 YW |
666 | LIST_FOREACH(route_prefixes, q, link->network->static_route_prefixes) { |
667 | r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false); | |
203d4df5 SS |
668 | if (r == -EEXIST) |
669 | continue; | |
670 | if (r < 0) | |
671 | return r; | |
672 | } | |
091214b6 PF |
673 | } |
674 | ||
fd3ef936 | 675 | return 0; |
091214b6 | 676 | } |
ca5ad760 | 677 | |
1633c457 | 678 | int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, |
1d596fde YW |
679 | uint32_t lifetime_preferred, uint32_t lifetime_valid) { |
680 | _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; | |
681 | int r; | |
682 | ||
683 | assert(link); | |
85b6a811 YW |
684 | |
685 | if (!link->radv) | |
686 | return 0; | |
1d596fde YW |
687 | |
688 | r = sd_radv_prefix_new(&p); | |
689 | if (r < 0) | |
690 | return r; | |
691 | ||
692 | r = sd_radv_prefix_set_prefix(p, prefix, prefix_len); | |
693 | if (r < 0) | |
694 | return r; | |
695 | ||
696 | r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred); | |
697 | if (r < 0) | |
698 | return r; | |
699 | ||
700 | r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid); | |
701 | if (r < 0) | |
702 | return r; | |
703 | ||
704 | r = sd_radv_add_prefix(link->radv, p, true); | |
705 | if (r < 0 && r != -EEXIST) | |
706 | return r; | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
ca5ad760 YW |
711 | int config_parse_radv_dns( |
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) { | |
722 | ||
723 | Network *n = data; | |
ca5ad760 YW |
724 | int r; |
725 | ||
726 | assert(filename); | |
727 | assert(lvalue); | |
728 | assert(rvalue); | |
729 | ||
d96edb2c | 730 | for (const char *p = rvalue;;) { |
ca5ad760 YW |
731 | _cleanup_free_ char *w = NULL; |
732 | union in_addr_union a; | |
733 | ||
734 | r = extract_first_word(&p, &w, NULL, 0); | |
735 | if (r == -ENOMEM) | |
736 | return log_oom(); | |
737 | if (r < 0) { | |
d96edb2c | 738 | log_syntax(unit, LOG_WARNING, filename, line, r, |
ca5ad760 YW |
739 | "Failed to extract word, ignoring: %s", rvalue); |
740 | return 0; | |
741 | } | |
742 | if (r == 0) | |
d96edb2c | 743 | return 0; |
ca5ad760 | 744 | |
fd3ef936 YW |
745 | if (streq(w, "_link_local")) |
746 | a = IN_ADDR_NULL; | |
747 | else { | |
748 | r = in_addr_from_string(AF_INET6, w, &a); | |
749 | if (r < 0) { | |
d96edb2c | 750 | log_syntax(unit, LOG_WARNING, filename, line, r, |
fd3ef936 YW |
751 | "Failed to parse DNS server address, ignoring: %s", w); |
752 | continue; | |
753 | } | |
ca5ad760 | 754 | |
fd3ef936 | 755 | if (in_addr_is_null(AF_INET6, &a)) { |
d96edb2c | 756 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
fd3ef936 YW |
757 | "DNS server address is null, ignoring: %s", w); |
758 | continue; | |
759 | } | |
760 | } | |
ca5ad760 | 761 | |
fd3ef936 YW |
762 | struct in6_addr *m; |
763 | m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr)); | |
764 | if (!m) | |
765 | return log_oom(); | |
ca5ad760 | 766 | |
fd3ef936 YW |
767 | m[n->n_router_dns++] = a.in6; |
768 | n->router_dns = m; | |
ca5ad760 | 769 | } |
ca5ad760 YW |
770 | } |
771 | ||
772 | int config_parse_radv_search_domains( | |
773 | const char *unit, | |
774 | const char *filename, | |
775 | unsigned line, | |
776 | const char *section, | |
777 | unsigned section_line, | |
778 | const char *lvalue, | |
779 | int ltype, | |
780 | const char *rvalue, | |
781 | void *data, | |
782 | void *userdata) { | |
783 | ||
784 | Network *n = data; | |
ca5ad760 YW |
785 | int r; |
786 | ||
787 | assert(filename); | |
788 | assert(lvalue); | |
789 | assert(rvalue); | |
790 | ||
d96edb2c | 791 | for (const char *p = rvalue;;) { |
ca5ad760 YW |
792 | _cleanup_free_ char *w = NULL, *idna = NULL; |
793 | ||
794 | r = extract_first_word(&p, &w, NULL, 0); | |
795 | if (r == -ENOMEM) | |
796 | return log_oom(); | |
797 | if (r < 0) { | |
d96edb2c | 798 | log_syntax(unit, LOG_WARNING, filename, line, r, |
ca5ad760 YW |
799 | "Failed to extract word, ignoring: %s", rvalue); |
800 | return 0; | |
801 | } | |
802 | if (r == 0) | |
d96edb2c | 803 | return 0; |
ca5ad760 YW |
804 | |
805 | r = dns_name_apply_idna(w, &idna); | |
806 | if (r < 0) { | |
d96edb2c | 807 | log_syntax(unit, LOG_WARNING, filename, line, r, |
ca5ad760 YW |
808 | "Failed to apply IDNA to domain name '%s', ignoring: %m", w); |
809 | continue; | |
810 | } else if (r == 0) | |
811 | /* transfer ownership to simplify subsequent operations */ | |
812 | idna = TAKE_PTR(w); | |
813 | ||
814 | r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); | |
815 | if (r < 0) | |
d96edb2c | 816 | return log_oom(); |
ca5ad760 YW |
817 | |
818 | r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); | |
819 | if (r < 0) | |
d96edb2c | 820 | return log_oom(); |
ca5ad760 | 821 | } |
ca5ad760 YW |
822 | } |
823 | ||
824 | static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = { | |
825 | [RADV_PREFIX_DELEGATION_NONE] = "no", | |
826 | [RADV_PREFIX_DELEGATION_STATIC] = "static", | |
827 | [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6", | |
828 | [RADV_PREFIX_DELEGATION_BOTH] = "yes", | |
829 | }; | |
830 | ||
831 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( | |
832 | radv_prefix_delegation, | |
833 | RADVPrefixDelegation, | |
834 | RADV_PREFIX_DELEGATION_BOTH); | |
835 | ||
c9778516 YW |
836 | DEFINE_CONFIG_PARSE_ENUM(config_parse_router_prefix_delegation, |
837 | radv_prefix_delegation, | |
838 | RADVPrefixDelegation, | |
839 | "Invalid router prefix delegation"); | |
ca5ad760 YW |
840 | |
841 | int config_parse_router_preference(const char *unit, | |
842 | const char *filename, | |
843 | unsigned line, | |
844 | const char *section, | |
845 | unsigned section_line, | |
846 | const char *lvalue, | |
847 | int ltype, | |
848 | const char *rvalue, | |
849 | void *data, | |
850 | void *userdata) { | |
851 | Network *network = userdata; | |
852 | ||
853 | assert(filename); | |
854 | assert(section); | |
855 | assert(lvalue); | |
856 | assert(rvalue); | |
857 | assert(data); | |
858 | ||
859 | if (streq(rvalue, "high")) | |
860 | network->router_preference = SD_NDISC_PREFERENCE_HIGH; | |
861 | else if (STR_IN_SET(rvalue, "medium", "normal", "default")) | |
862 | network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; | |
863 | else if (streq(rvalue, "low")) | |
864 | network->router_preference = SD_NDISC_PREFERENCE_LOW; | |
865 | else | |
d96edb2c YW |
866 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
867 | "Invalid router preference, ignoring assignment: %s", rvalue); | |
ca5ad760 YW |
868 | |
869 | return 0; | |
870 | } |