]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-radv.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / network / networkd-radv.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
091214b6
PF
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"
c555a358 25#include "networkd-manager.h"
091214b6 26#include "networkd-radv.h"
6e849e95 27#include "parse-util.h"
091214b6 28#include "sd-radv.h"
6e849e95
PF
29#include "string-util.h"
30
9c3b99fe
YW
31int 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
56a23cb4
PF
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
6e849e95
PF
70int 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
100void 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
119int 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
ae2a15bc 129 *ret = TAKE_PTR(prefix);
6e849e95
PF
130
131 return 0;
132}
133
134int prefix_new_static(Network *network, const char *filename,
135 unsigned section_line, Prefix **ret) {
136 _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
137 _cleanup_prefix_free_ Prefix *prefix = NULL;
138 int r;
139
140 assert(network);
141 assert(ret);
142 assert(!!filename == (section_line > 0));
143
144 if (filename) {
145 r = network_config_section_new(filename, section_line, &n);
146 if (r < 0)
147 return r;
148
149 if (section_line) {
150 prefix = hashmap_get(network->prefixes_by_section, n);
151 if (prefix) {
1cc6c93a 152 *ret = TAKE_PTR(prefix);
6e849e95
PF
153
154 return 0;
155 }
156 }
157 }
158
159 r = prefix_new(&prefix);
160 if (r < 0)
161 return r;
162
163 if (filename) {
1cc6c93a 164 prefix->section = TAKE_PTR(n);
6e849e95
PF
165
166 r = hashmap_put(network->prefixes_by_section, prefix->section,
167 prefix);
168 if (r < 0)
169 return r;
170 }
171
172 prefix->network = network;
173 LIST_APPEND(prefixes, network->static_prefixes, prefix);
174 network->n_static_prefixes++;
175
1cc6c93a 176 *ret = TAKE_PTR(prefix);
6e849e95
PF
177
178 return 0;
179}
180
181int config_parse_prefix(const char *unit,
182 const char *filename,
183 unsigned line,
184 const char *section,
185 unsigned section_line,
186 const char *lvalue,
187 int ltype,
188 const char *rvalue,
189 void *data,
190 void *userdata) {
191
192 Network *network = userdata;
193 _cleanup_prefix_free_ Prefix *p = NULL;
194 uint8_t prefixlen = 64;
195 union in_addr_union in6addr;
196 int r;
197
198 assert(filename);
199 assert(section);
200 assert(lvalue);
201 assert(rvalue);
202 assert(data);
203
204 r = prefix_new_static(network, filename, section_line, &p);
205 if (r < 0)
206 return r;
207
208 r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
209 if (r < 0) {
210 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
211 return 0;
212 }
213
214 if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
215 return -EADDRNOTAVAIL;
216
217 log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
218
219 p = NULL;
220
221 return 0;
222}
223
224int config_parse_prefix_flags(const char *unit,
225 const char *filename,
226 unsigned line,
227 const char *section,
228 unsigned section_line,
229 const char *lvalue,
230 int ltype,
231 const char *rvalue,
232 void *data,
233 void *userdata) {
234 Network *network = userdata;
235 _cleanup_prefix_free_ Prefix *p = NULL;
236 int r, val;
237
238 assert(filename);
239 assert(section);
240 assert(lvalue);
241 assert(rvalue);
242 assert(data);
243
244 r = prefix_new_static(network, filename, section_line, &p);
245 if (r < 0)
246 return r;
247
248 r = parse_boolean(rvalue);
249 if (r < 0) {
250 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
251 return 0;
252 }
253
254 val = r;
255
256 if (streq(lvalue, "OnLink"))
257 r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
258 else if (streq(lvalue, "AddressAutoconfiguration"))
259 r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
260 if (r < 0)
261 return r;
262
263 p = NULL;
264
265 return 0;
266}
267
268int config_parse_prefix_lifetime(const char *unit,
269 const char *filename,
270 unsigned line,
271 const char *section,
272 unsigned section_line,
273 const char *lvalue,
274 int ltype,
275 const char *rvalue,
276 void *data,
277 void *userdata) {
278 Network *network = userdata;
279 _cleanup_prefix_free_ Prefix *p = NULL;
280 usec_t usec;
281 int r;
282
283 assert(filename);
284 assert(section);
285 assert(lvalue);
286 assert(rvalue);
287 assert(data);
288
289 r = prefix_new_static(network, filename, section_line, &p);
290 if (r < 0)
291 return r;
292
293 r = parse_sec(rvalue, &usec);
294 if (r < 0) {
295 log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
296 return 0;
297 }
298
299 /* a value of 0xffffffff represents infinity */
300 if (streq(lvalue, "PreferredLifetimeSec"))
301 r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
302 DIV_ROUND_UP(usec, USEC_PER_SEC));
303 else if (streq(lvalue, "ValidLifetimeSec"))
304 r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
305 DIV_ROUND_UP(usec, USEC_PER_SEC));
306 if (r < 0)
307 return r;
308
309 p = NULL;
310
311 return 0;
312}
091214b6 313
c555a358
PF
314static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
315 size_t *n_dns) {
316 _cleanup_free_ struct in6_addr *addresses = NULL;
317 size_t i, n_addresses = 0, n_allocated = 0;
318
319 assert(network);
320 assert(dns);
321 assert(n_dns);
322
323 for (i = 0; i < network->n_dns; i++) {
324 union in_addr_union *addr;
325
326 if (network->dns[i].family != AF_INET6)
327 continue;
328
329 addr = &network->dns[i].address;
330
331 if (in_addr_is_null(AF_INET6, addr) ||
332 in_addr_is_link_local(AF_INET6, addr) ||
333 in_addr_is_localhost(AF_INET6, addr))
334 continue;
335
336 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
337 return -ENOMEM;
338
339 addresses[n_addresses++] = addr->in6;
340 }
341
342 if (addresses) {
ae2a15bc 343 *dns = TAKE_PTR(addresses);
c555a358
PF
344
345 *n_dns = n_addresses;
346 }
347
348 return n_addresses;
349}
350
351static int radv_set_dns(Link *link, Link *uplink) {
352 _cleanup_free_ struct in6_addr *dns = NULL;
353 size_t n_dns;
354 usec_t lifetime_usec;
355 int r;
356
357 if (!link->network->router_emit_dns)
358 return 0;
359
360 if (link->network->router_dns) {
361 dns = newdup(struct in6_addr, link->network->router_dns,
362 link->network->n_router_dns);
363 if (dns == NULL)
364 return -ENOMEM;
365
366 n_dns = link->network->n_router_dns;
367 lifetime_usec = link->network->router_dns_lifetime_usec;
368
369 goto set_dns;
370 }
371
372 lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
373
374 r = radv_get_ip6dns(link->network, &dns, &n_dns);
375 if (r > 0)
376 goto set_dns;
377
378 if (uplink) {
349a981d
PF
379 if (uplink->network == NULL) {
380 log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
381 return 0;
382 }
383
c555a358
PF
384 r = radv_get_ip6dns(uplink->network, &dns, &n_dns);
385 if (r > 0)
386 goto set_dns;
387 }
388
389 return 0;
390
391 set_dns:
392 return sd_radv_set_rdnss(link->radv,
393 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
394 dns, n_dns);
395}
396
397static int radv_set_domains(Link *link, Link *uplink) {
398 char **search_domains;
399 usec_t lifetime_usec;
400
401 if (!link->network->router_emit_domains)
402 return 0;
403
404 search_domains = link->network->router_search_domains;
405 lifetime_usec = link->network->router_dns_lifetime_usec;
406
407 if (search_domains)
408 goto set_domains;
409
410 lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
411
412 search_domains = link->network->search_domains;
413 if (search_domains)
414 goto set_domains;
415
416 if (uplink) {
349a981d
PF
417 if (uplink->network == NULL) {
418 log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
419 return 0;
420 }
421
c555a358
PF
422 search_domains = uplink->network->search_domains;
423 if (search_domains)
424 goto set_domains;
425 }
426
427 return 0;
428
429 set_domains:
430 return sd_radv_set_dnssl(link->radv,
431 DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
432 search_domains);
433
434}
435
436int radv_emit_dns(Link *link) {
437 Link *uplink;
438 int r;
439
440 uplink = manager_find_uplink(link->manager, link);
441
442 r = radv_set_dns(link, uplink);
443 if (r < 0)
444 log_link_warning_errno(link, r, "Could not set RA DNS: %m");
445
446 r = radv_set_domains(link, uplink);
447 if (r < 0)
448 log_link_warning_errno(link, r, "Could not set RA Domains: %m");
449
450 return 0;
451}
452
091214b6
PF
453int radv_configure(Link *link) {
454 int r;
455 Prefix *p;
456
457 assert(link);
458 assert(link->network);
459
460 r = sd_radv_new(&link->radv);
461 if (r < 0)
462 return r;
463
464 r = sd_radv_attach_event(link->radv, NULL, 0);
465 if (r < 0)
466 return r;
467
468 r = sd_radv_set_mac(link->radv, &link->mac);
469 if (r < 0)
470 return r;
471
472 r = sd_radv_set_ifindex(link->radv, link->ifindex);
473 if (r < 0)
474 return r;
475
476 r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
477 if (r < 0)
478 return r;
479
480 r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
481 if (r < 0)
482 return r;
483
301a2fb9
PF
484 /* a value of 0xffffffff represents infinity, 0x0 means this host is
485 not a router */
091214b6 486 r = sd_radv_set_router_lifetime(link->radv,
945e3225 487 DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC));
091214b6
PF
488 if (r < 0)
489 return r;
490
491 if (link->network->router_lifetime_usec > 0) {
492 r = sd_radv_set_preference(link->radv,
493 link->network->router_preference);
494 if (r < 0)
495 return r;
496 }
497
56a23cb4
PF
498 if (IN_SET(link->network->router_prefix_delegation,
499 RADV_PREFIX_DELEGATION_STATIC,
500 RADV_PREFIX_DELEGATION_BOTH)) {
501 LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
d601b566 502 r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
56a23cb4
PF
503 if (r != -EEXIST && r < 0)
504 return r;
505 }
091214b6
PF
506 }
507
c555a358 508 return radv_emit_dns(link);
091214b6 509}