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