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