8e766630f0
This drops a good number of type-specific _cleanup_ macros, and patches all users to just use the generic ones. In most recent code we abstained from defining type-specific macros, and this basically removes all those added already, with the exception of the really low-level ones. Having explicit macros for this is not too useful, as the expression without the extra macro is generally just 2ch wider. We should generally emphesize generic code, unless there are really good reasons for specific code, hence let's follow this in this case too. Note that _cleanup_free_ and similar really low-level, libc'ish, Linux API'ish macros continue to be defined, only the really high-level OO ones are dropped. From now on this should really be the rule: for really low-level stuff, such as memory allocation, fd handling and so one, go ahead and define explicit per-type macros, but for high-level, specific program code, just use the generic _cleanup_() macro directly, in order to keep things simple and as readable as possible for the uninitiated. Note that before this patch some of the APIs (notable libudev ones) were already used with the high-level macros at some places and with the generic _cleanup_ macro at others. With this patch we hence unify on the latter.
497 lines
15 KiB
C
497 lines
15 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
/***
|
|
This file is part of systemd.
|
|
|
|
Copyright (C) 2017 Intel Corporation. All rights reserved.
|
|
***/
|
|
|
|
#include <netinet/icmp6.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "networkd-address.h"
|
|
#include "networkd-manager.h"
|
|
#include "networkd-radv.h"
|
|
#include "parse-util.h"
|
|
#include "sd-radv.h"
|
|
#include "string-util.h"
|
|
|
|
int config_parse_router_prefix_delegation(
|
|
const char *unit,
|
|
const char *filename,
|
|
unsigned line,
|
|
const char *section,
|
|
unsigned section_line,
|
|
const char *lvalue,
|
|
int ltype,
|
|
const char *rvalue,
|
|
void *data,
|
|
void *userdata) {
|
|
|
|
Network *network = userdata;
|
|
int d;
|
|
|
|
assert(filename);
|
|
assert(section);
|
|
assert(lvalue);
|
|
assert(rvalue);
|
|
assert(data);
|
|
|
|
if (streq(rvalue, "static"))
|
|
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_STATIC;
|
|
else if (streq(rvalue, "dhcpv6"))
|
|
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_DHCP6;
|
|
else {
|
|
d = parse_boolean(rvalue);
|
|
if (d > 0)
|
|
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_BOTH;
|
|
else
|
|
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
|
|
|
|
if (d < 0)
|
|
log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router prefix delegation '%s' is invalid, ignoring assignment: %m", rvalue);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_parse_router_preference(const char *unit,
|
|
const char *filename,
|
|
unsigned line,
|
|
const char *section,
|
|
unsigned section_line,
|
|
const char *lvalue,
|
|
int ltype,
|
|
const char *rvalue,
|
|
void *data,
|
|
void *userdata) {
|
|
Network *network = userdata;
|
|
|
|
assert(filename);
|
|
assert(section);
|
|
assert(lvalue);
|
|
assert(rvalue);
|
|
assert(data);
|
|
|
|
if (streq(rvalue, "high"))
|
|
network->router_preference = SD_NDISC_PREFERENCE_HIGH;
|
|
else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
|
|
network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
|
|
else if (streq(rvalue, "low"))
|
|
network->router_preference = SD_NDISC_PREFERENCE_LOW;
|
|
else
|
|
log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void prefix_free(Prefix *prefix) {
|
|
if (!prefix)
|
|
return;
|
|
|
|
if (prefix->network) {
|
|
LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
|
|
assert(prefix->network->n_static_prefixes > 0);
|
|
prefix->network->n_static_prefixes--;
|
|
|
|
if (prefix->section)
|
|
hashmap_remove(prefix->network->prefixes_by_section,
|
|
prefix->section);
|
|
}
|
|
|
|
prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
|
|
|
|
free(prefix);
|
|
}
|
|
|
|
int prefix_new(Prefix **ret) {
|
|
Prefix *prefix = NULL;
|
|
|
|
prefix = new0(Prefix, 1);
|
|
if (!prefix)
|
|
return -ENOMEM;
|
|
|
|
if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
|
|
return -ENOMEM;
|
|
|
|
*ret = TAKE_PTR(prefix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int prefix_new_static(Network *network, const char *filename,
|
|
unsigned section_line, Prefix **ret) {
|
|
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
|
_cleanup_(prefix_freep) Prefix *prefix = NULL;
|
|
int r;
|
|
|
|
assert(network);
|
|
assert(ret);
|
|
assert(!!filename == (section_line > 0));
|
|
|
|
if (filename) {
|
|
r = network_config_section_new(filename, section_line, &n);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (section_line) {
|
|
prefix = hashmap_get(network->prefixes_by_section, n);
|
|
if (prefix) {
|
|
*ret = TAKE_PTR(prefix);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
r = prefix_new(&prefix);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (filename) {
|
|
prefix->section = TAKE_PTR(n);
|
|
|
|
r = hashmap_put(network->prefixes_by_section, prefix->section,
|
|
prefix);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
prefix->network = network;
|
|
LIST_APPEND(prefixes, network->static_prefixes, prefix);
|
|
network->n_static_prefixes++;
|
|
|
|
*ret = TAKE_PTR(prefix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_parse_prefix(const char *unit,
|
|
const char *filename,
|
|
unsigned line,
|
|
const char *section,
|
|
unsigned section_line,
|
|
const char *lvalue,
|
|
int ltype,
|
|
const char *rvalue,
|
|
void *data,
|
|
void *userdata) {
|
|
|
|
Network *network = userdata;
|
|
_cleanup_(prefix_freep) Prefix *p = NULL;
|
|
uint8_t prefixlen = 64;
|
|
union in_addr_union in6addr;
|
|
int r;
|
|
|
|
assert(filename);
|
|
assert(section);
|
|
assert(lvalue);
|
|
assert(rvalue);
|
|
assert(data);
|
|
|
|
r = prefix_new_static(network, filename, section_line, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
|
|
if (r < 0) {
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
|
|
return 0;
|
|
}
|
|
|
|
if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
|
|
return -EADDRNOTAVAIL;
|
|
|
|
log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
|
|
|
|
p = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_parse_prefix_flags(const char *unit,
|
|
const char *filename,
|
|
unsigned line,
|
|
const char *section,
|
|
unsigned section_line,
|
|
const char *lvalue,
|
|
int ltype,
|
|
const char *rvalue,
|
|
void *data,
|
|
void *userdata) {
|
|
Network *network = userdata;
|
|
_cleanup_(prefix_freep) Prefix *p = NULL;
|
|
int r, val;
|
|
|
|
assert(filename);
|
|
assert(section);
|
|
assert(lvalue);
|
|
assert(rvalue);
|
|
assert(data);
|
|
|
|
r = prefix_new_static(network, filename, section_line, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = parse_boolean(rvalue);
|
|
if (r < 0) {
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
|
|
return 0;
|
|
}
|
|
|
|
val = r;
|
|
|
|
if (streq(lvalue, "OnLink"))
|
|
r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
|
|
else if (streq(lvalue, "AddressAutoconfiguration"))
|
|
r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
p = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int config_parse_prefix_lifetime(const char *unit,
|
|
const char *filename,
|
|
unsigned line,
|
|
const char *section,
|
|
unsigned section_line,
|
|
const char *lvalue,
|
|
int ltype,
|
|
const char *rvalue,
|
|
void *data,
|
|
void *userdata) {
|
|
Network *network = userdata;
|
|
_cleanup_(prefix_freep) Prefix *p = NULL;
|
|
usec_t usec;
|
|
int r;
|
|
|
|
assert(filename);
|
|
assert(section);
|
|
assert(lvalue);
|
|
assert(rvalue);
|
|
assert(data);
|
|
|
|
r = prefix_new_static(network, filename, section_line, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = parse_sec(rvalue, &usec);
|
|
if (r < 0) {
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
|
|
return 0;
|
|
}
|
|
|
|
/* a value of 0xffffffff represents infinity */
|
|
if (streq(lvalue, "PreferredLifetimeSec"))
|
|
r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
|
|
DIV_ROUND_UP(usec, USEC_PER_SEC));
|
|
else if (streq(lvalue, "ValidLifetimeSec"))
|
|
r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
|
|
DIV_ROUND_UP(usec, USEC_PER_SEC));
|
|
if (r < 0)
|
|
return r;
|
|
|
|
p = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
|
|
size_t *n_dns) {
|
|
_cleanup_free_ struct in6_addr *addresses = NULL;
|
|
size_t i, n_addresses = 0, n_allocated = 0;
|
|
|
|
assert(network);
|
|
assert(dns);
|
|
assert(n_dns);
|
|
|
|
for (i = 0; i < network->n_dns; i++) {
|
|
union in_addr_union *addr;
|
|
|
|
if (network->dns[i].family != AF_INET6)
|
|
continue;
|
|
|
|
addr = &network->dns[i].address;
|
|
|
|
if (in_addr_is_null(AF_INET6, addr) ||
|
|
in_addr_is_link_local(AF_INET6, addr) ||
|
|
in_addr_is_localhost(AF_INET6, addr))
|
|
continue;
|
|
|
|
if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
|
|
return -ENOMEM;
|
|
|
|
addresses[n_addresses++] = addr->in6;
|
|
}
|
|
|
|
if (addresses) {
|
|
*dns = TAKE_PTR(addresses);
|
|
|
|
*n_dns = n_addresses;
|
|
}
|
|
|
|
return n_addresses;
|
|
}
|
|
|
|
static int radv_set_dns(Link *link, Link *uplink) {
|
|
_cleanup_free_ struct in6_addr *dns = NULL;
|
|
size_t n_dns;
|
|
usec_t lifetime_usec;
|
|
int r;
|
|
|
|
if (!link->network->router_emit_dns)
|
|
return 0;
|
|
|
|
if (link->network->router_dns) {
|
|
dns = newdup(struct in6_addr, link->network->router_dns,
|
|
link->network->n_router_dns);
|
|
if (dns == NULL)
|
|
return -ENOMEM;
|
|
|
|
n_dns = link->network->n_router_dns;
|
|
lifetime_usec = link->network->router_dns_lifetime_usec;
|
|
|
|
goto set_dns;
|
|
}
|
|
|
|
lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
|
|
|
|
r = radv_get_ip6dns(link->network, &dns, &n_dns);
|
|
if (r > 0)
|
|
goto set_dns;
|
|
|
|
if (uplink) {
|
|
if (uplink->network == NULL) {
|
|
log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
|
|
return 0;
|
|
}
|
|
|
|
r = radv_get_ip6dns(uplink->network, &dns, &n_dns);
|
|
if (r > 0)
|
|
goto set_dns;
|
|
}
|
|
|
|
return 0;
|
|
|
|
set_dns:
|
|
return sd_radv_set_rdnss(link->radv,
|
|
DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
|
|
dns, n_dns);
|
|
}
|
|
|
|
static int radv_set_domains(Link *link, Link *uplink) {
|
|
char **search_domains;
|
|
usec_t lifetime_usec;
|
|
|
|
if (!link->network->router_emit_domains)
|
|
return 0;
|
|
|
|
search_domains = link->network->router_search_domains;
|
|
lifetime_usec = link->network->router_dns_lifetime_usec;
|
|
|
|
if (search_domains)
|
|
goto set_domains;
|
|
|
|
lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
|
|
|
|
search_domains = link->network->search_domains;
|
|
if (search_domains)
|
|
goto set_domains;
|
|
|
|
if (uplink) {
|
|
if (uplink->network == NULL) {
|
|
log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
|
|
return 0;
|
|
}
|
|
|
|
search_domains = uplink->network->search_domains;
|
|
if (search_domains)
|
|
goto set_domains;
|
|
}
|
|
|
|
return 0;
|
|
|
|
set_domains:
|
|
return sd_radv_set_dnssl(link->radv,
|
|
DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
|
|
search_domains);
|
|
|
|
}
|
|
|
|
int radv_emit_dns(Link *link) {
|
|
Link *uplink;
|
|
int r;
|
|
|
|
uplink = manager_find_uplink(link->manager, link);
|
|
|
|
r = radv_set_dns(link, uplink);
|
|
if (r < 0)
|
|
log_link_warning_errno(link, r, "Could not set RA DNS: %m");
|
|
|
|
r = radv_set_domains(link, uplink);
|
|
if (r < 0)
|
|
log_link_warning_errno(link, r, "Could not set RA Domains: %m");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int radv_configure(Link *link) {
|
|
int r;
|
|
Prefix *p;
|
|
|
|
assert(link);
|
|
assert(link->network);
|
|
|
|
r = sd_radv_new(&link->radv);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_radv_attach_event(link->radv, NULL, 0);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_radv_set_mac(link->radv, &link->mac);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_radv_set_ifindex(link->radv, link->ifindex);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* a value of 0xffffffff represents infinity, 0x0 means this host is
|
|
not a router */
|
|
r = sd_radv_set_router_lifetime(link->radv,
|
|
DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC));
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (link->network->router_lifetime_usec > 0) {
|
|
r = sd_radv_set_preference(link->radv,
|
|
link->network->router_preference);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if (IN_SET(link->network->router_prefix_delegation,
|
|
RADV_PREFIX_DELEGATION_STATIC,
|
|
RADV_PREFIX_DELEGATION_BOTH)) {
|
|
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
|
|
r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
|
|
if (r != -EEXIST && r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return radv_emit_dns(link);
|
|
}
|