]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-radv.c
Merge pull request #7582 from pfl/dhcp6_prefix_delegation
[thirdparty/systemd.git] / src / network / networkd-radv.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2017 Intel Corporation. All rights reserved.
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <netinet/icmp6.h>
22 #include <arpa/inet.h>
23
24 #include "networkd-address.h"
25 #include "networkd-manager.h"
26 #include "networkd-radv.h"
27 #include "parse-util.h"
28 #include "sd-radv.h"
29 #include "string-util.h"
30
31 int config_parse_router_prefix_delegation(const char *unit,
32 const char *filename,
33 unsigned line,
34 const char *section,
35 unsigned section_line,
36 const char *lvalue,
37 int ltype,
38 const char *rvalue,
39 void *data,
40 void *userdata) {
41 Network *network = userdata;
42 int d;
43
44 assert(filename);
45 assert(section);
46 assert(lvalue);
47 assert(rvalue);
48 assert(data);
49
50 if (streq(rvalue, "static"))
51 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_STATIC;
52 else if (streq(rvalue, "dhcpv6"))
53 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_DHCP6;
54 else {
55 d = parse_boolean(rvalue);
56 if (d > 0)
57 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_BOTH;
58 else
59 network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
60
61 if (d < 0)
62 log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router prefix delegation '%s' is invalid, ignoring assignment: %m", rvalue);
63 }
64
65 return 0;
66 }
67
68 int config_parse_router_preference(const char *unit,
69 const char *filename,
70 unsigned line,
71 const char *section,
72 unsigned section_line,
73 const char *lvalue,
74 int ltype,
75 const char *rvalue,
76 void *data,
77 void *userdata) {
78 Network *network = userdata;
79
80 assert(filename);
81 assert(section);
82 assert(lvalue);
83 assert(rvalue);
84 assert(data);
85
86 if (streq(rvalue, "high"))
87 network->router_preference = SD_NDISC_PREFERENCE_HIGH;
88 else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
89 network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
90 else if (streq(rvalue, "low"))
91 network->router_preference = SD_NDISC_PREFERENCE_LOW;
92 else
93 log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
94
95 return 0;
96 }
97
98 void prefix_free(Prefix *prefix) {
99 if (!prefix)
100 return;
101
102 if (prefix->network) {
103 LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
104 assert(prefix->network->n_static_prefixes > 0);
105 prefix->network->n_static_prefixes--;
106
107 if (prefix->section)
108 hashmap_remove(prefix->network->prefixes_by_section,
109 prefix->section);
110 }
111
112 prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
113
114 free(prefix);
115 }
116
117 int prefix_new(Prefix **ret) {
118 Prefix *prefix = NULL;
119
120 prefix = new0(Prefix, 1);
121 if (!prefix)
122 return -ENOMEM;
123
124 if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
125 return -ENOMEM;
126
127 *ret = prefix;
128 prefix = NULL;
129
130 return 0;
131 }
132
133 int prefix_new_static(Network *network, const char *filename,
134 unsigned section_line, Prefix **ret) {
135 _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
136 _cleanup_prefix_free_ Prefix *prefix = NULL;
137 int r;
138
139 assert(network);
140 assert(ret);
141 assert(!!filename == (section_line > 0));
142
143 if (filename) {
144 r = network_config_section_new(filename, section_line, &n);
145 if (r < 0)
146 return r;
147
148 if (section_line) {
149 prefix = hashmap_get(network->prefixes_by_section, n);
150 if (prefix) {
151 *ret = prefix;
152 prefix = NULL;
153
154 return 0;
155 }
156 }
157 }
158
159 r = prefix_new(&prefix);
160 if (r < 0)
161 return r;
162
163 if (filename) {
164 prefix->section = n;
165 n = NULL;
166
167 r = hashmap_put(network->prefixes_by_section, prefix->section,
168 prefix);
169 if (r < 0)
170 return r;
171 }
172
173 prefix->network = network;
174 LIST_APPEND(prefixes, network->static_prefixes, prefix);
175 network->n_static_prefixes++;
176
177 *ret = prefix;
178 prefix = NULL;
179
180 return 0;
181 }
182
183 int config_parse_prefix(const char *unit,
184 const char *filename,
185 unsigned line,
186 const char *section,
187 unsigned section_line,
188 const char *lvalue,
189 int ltype,
190 const char *rvalue,
191 void *data,
192 void *userdata) {
193
194 Network *network = userdata;
195 _cleanup_prefix_free_ Prefix *p = NULL;
196 uint8_t prefixlen = 64;
197 union in_addr_union in6addr;
198 int r;
199
200 assert(filename);
201 assert(section);
202 assert(lvalue);
203 assert(rvalue);
204 assert(data);
205
206 r = prefix_new_static(network, filename, section_line, &p);
207 if (r < 0)
208 return r;
209
210 r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
211 if (r < 0) {
212 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
213 return 0;
214 }
215
216 if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
217 return -EADDRNOTAVAIL;
218
219 log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
220
221 p = NULL;
222
223 return 0;
224 }
225
226 int config_parse_prefix_flags(const char *unit,
227 const char *filename,
228 unsigned line,
229 const char *section,
230 unsigned section_line,
231 const char *lvalue,
232 int ltype,
233 const char *rvalue,
234 void *data,
235 void *userdata) {
236 Network *network = userdata;
237 _cleanup_prefix_free_ Prefix *p = NULL;
238 int r, val;
239
240 assert(filename);
241 assert(section);
242 assert(lvalue);
243 assert(rvalue);
244 assert(data);
245
246 r = prefix_new_static(network, filename, section_line, &p);
247 if (r < 0)
248 return r;
249
250 r = parse_boolean(rvalue);
251 if (r < 0) {
252 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
253 return 0;
254 }
255
256 val = r;
257
258 if (streq(lvalue, "OnLink"))
259 r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
260 else if (streq(lvalue, "AddressAutoconfiguration"))
261 r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
262 if (r < 0)
263 return r;
264
265 p = NULL;
266
267 return 0;
268 }
269
270 int config_parse_prefix_lifetime(const char *unit,
271 const char *filename,
272 unsigned line,
273 const char *section,
274 unsigned section_line,
275 const char *lvalue,
276 int ltype,
277 const char *rvalue,
278 void *data,
279 void *userdata) {
280 Network *network = userdata;
281 _cleanup_prefix_free_ Prefix *p = NULL;
282 usec_t usec;
283 int r;
284
285 assert(filename);
286 assert(section);
287 assert(lvalue);
288 assert(rvalue);
289 assert(data);
290
291 r = prefix_new_static(network, filename, section_line, &p);
292 if (r < 0)
293 return r;
294
295 r = parse_sec(rvalue, &usec);
296 if (r < 0) {
297 log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
298 return 0;
299 }
300
301 /* a value of 0xffffffff represents infinity */
302 if (streq(lvalue, "PreferredLifetimeSec"))
303 r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
304 DIV_ROUND_UP(usec, USEC_PER_SEC));
305 else if (streq(lvalue, "ValidLifetimeSec"))
306 r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
307 DIV_ROUND_UP(usec, USEC_PER_SEC));
308 if (r < 0)
309 return r;
310
311 p = NULL;
312
313 return 0;
314 }
315
316 static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
317 size_t *n_dns) {
318 _cleanup_free_ struct in6_addr *addresses = NULL;
319 size_t i, n_addresses = 0, n_allocated = 0;
320
321 assert(network);
322 assert(dns);
323 assert(n_dns);
324
325 for (i = 0; i < network->n_dns; i++) {
326 union in_addr_union *addr;
327
328 if (network->dns[i].family != AF_INET6)
329 continue;
330
331 addr = &network->dns[i].address;
332
333 if (in_addr_is_null(AF_INET6, addr) ||
334 in_addr_is_link_local(AF_INET6, addr) ||
335 in_addr_is_localhost(AF_INET6, addr))
336 continue;
337
338 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
339 return -ENOMEM;
340
341 addresses[n_addresses++] = addr->in6;
342 }
343
344 if (addresses) {
345 *dns = addresses;
346 addresses = NULL;
347
348 *n_dns = n_addresses;
349 }
350
351 return n_addresses;
352 }
353
354 static int radv_set_dns(Link *link, Link *uplink) {
355 _cleanup_free_ struct in6_addr *dns = NULL;
356 size_t n_dns;
357 usec_t lifetime_usec;
358 int r;
359
360 if (!link->network->router_emit_dns)
361 return 0;
362
363 if (link->network->router_dns) {
364 dns = newdup(struct in6_addr, link->network->router_dns,
365 link->network->n_router_dns);
366 if (dns == NULL)
367 return -ENOMEM;
368
369 n_dns = link->network->n_router_dns;
370 lifetime_usec = link->network->router_dns_lifetime_usec;
371
372 goto set_dns;
373 }
374
375 lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
376
377 r = radv_get_ip6dns(link->network, &dns, &n_dns);
378 if (r > 0)
379 goto set_dns;
380
381 if (uplink) {
382 if (uplink->network == NULL) {
383 log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
384 return 0;
385 }
386
387 r = radv_get_ip6dns(uplink->network, &dns, &n_dns);
388 if (r > 0)
389 goto set_dns;
390 }
391
392 return 0;
393
394 set_dns:
395 return sd_radv_set_rdnss(link->radv,
396 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
397 dns, n_dns);
398 }
399
400 static int radv_set_domains(Link *link, Link *uplink) {
401 char **search_domains;
402 usec_t lifetime_usec;
403
404 if (!link->network->router_emit_domains)
405 return 0;
406
407 search_domains = link->network->router_search_domains;
408 lifetime_usec = link->network->router_dns_lifetime_usec;
409
410 if (search_domains)
411 goto set_domains;
412
413 lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
414
415 search_domains = link->network->search_domains;
416 if (search_domains)
417 goto set_domains;
418
419 if (uplink) {
420 if (uplink->network == NULL) {
421 log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
422 return 0;
423 }
424
425 search_domains = uplink->network->search_domains;
426 if (search_domains)
427 goto set_domains;
428 }
429
430 return 0;
431
432 set_domains:
433 return sd_radv_set_dnssl(link->radv,
434 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
435 search_domains);
436
437 }
438
439 int radv_emit_dns(Link *link) {
440 Link *uplink;
441 int r;
442
443 uplink = manager_find_uplink(link->manager, link);
444
445 r = radv_set_dns(link, uplink);
446 if (r < 0)
447 log_link_warning_errno(link, r, "Could not set RA DNS: %m");
448
449 r = radv_set_domains(link, uplink);
450 if (r < 0)
451 log_link_warning_errno(link, r, "Could not set RA Domains: %m");
452
453 return 0;
454 }
455
456 int radv_configure(Link *link) {
457 int r;
458 Prefix *p;
459
460 assert(link);
461 assert(link->network);
462
463 r = sd_radv_new(&link->radv);
464 if (r < 0)
465 return r;
466
467 r = sd_radv_attach_event(link->radv, NULL, 0);
468 if (r < 0)
469 return r;
470
471 r = sd_radv_set_mac(link->radv, &link->mac);
472 if (r < 0)
473 return r;
474
475 r = sd_radv_set_ifindex(link->radv, link->ifindex);
476 if (r < 0)
477 return r;
478
479 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
480 if (r < 0)
481 return r;
482
483 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
484 if (r < 0)
485 return r;
486
487 /* a value of 0xffffffff represents infinity, 0x0 means this host is
488 not a router */
489 r = sd_radv_set_router_lifetime(link->radv,
490 DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC));
491 if (r < 0)
492 return r;
493
494 if (link->network->router_lifetime_usec > 0) {
495 r = sd_radv_set_preference(link->radv,
496 link->network->router_preference);
497 if (r < 0)
498 return r;
499 }
500
501 if (IN_SET(link->network->router_prefix_delegation,
502 RADV_PREFIX_DELEGATION_STATIC,
503 RADV_PREFIX_DELEGATION_BOTH)) {
504 LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
505 r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
506 if (r != -EEXIST && r < 0)
507 return r;
508 }
509 }
510
511 return radv_emit_dns(link);
512 }