1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 10:25:37 +03:00

Merge pull request #16585 from yuwata/network-dhcp6-fixes

network: fix several DHCP6 PD issues, and cleanup DHCP4 and NDISC
This commit is contained in:
Lennart Poettering 2020-07-29 15:59:11 +02:00 committed by GitHub
commit 0cfb490fe9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1896 additions and 1370 deletions

View File

@ -809,16 +809,6 @@
<literal>false</literal>. See the [IPv6PrefixDelegation] and the [IPv6Prefix] sections for more
configuration options.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6PDSubnetId=</varname></term>
<listitem><para>Configure a specific subnet ID on the interface from a (previously) received prefix delegation.
You can either set "auto" (the default) or a specific subnet ID
(as defined in <ulink url="https://tools.ietf.org/html/rfc4291#section-2.5.4">RFC 4291</ulink>, section 2.5.4),
in which case the allowed value is hexadecimal, from 0 to 0x7fffffffffffffff inclusive.
This option is only effective when used together with <varname>IPv6PrefixDelegation=</varname>
and the corresponding configuration on the upstream interface.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6MTUBytes=</varname></term>
<listitem><para>Configures IPv6 maximum transmission unit (MTU).
@ -1893,25 +1883,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>AssignAcquiredDelegatedPrefixAddress=</varname></term>
<listitem>
<para>Takes a boolean. Specifies whether to add an address from the delegated prefixes which are received
from the WAN interface by the <varname>IPv6PrefixDelegation=</varname>. When true (on LAN interfce), the EUI-64
algorithm will be used to form an interface identifier from the delegated prefixes. Defaults to true.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>AssignAcquiredDelegatedPrefixToken=</varname></term>
<listitem>
<para>Specifies an optional address generation mode for <varname>AssignAcquiredDelegatedPrefixAddress=</varname>.
Takes an IPv6 address. When set, the lower bits of the supplied address are combined with the upper bits of a
delegatad prefix received from the WAN interface by the <varname>IPv6PrefixDelegation=</varname> prefixes to
form a complete address.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PrefixDelegationHint=</varname></term>
<listitem>
@ -1966,6 +1937,48 @@
</variablelist>
</refsect1>
<refsect1>
<title>[DHCPv6PrefixDelegation] Section Options</title>
<para>The [DHCPv6PrefixDelegation] section configures delegated prefix assigned by DHCPv6 server.
The settings in this section are used only when <varname>IPv6PrefixDelegation=</varname> setting is
enabled, or set to <literal>dhcp6</literal>.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>SubnetId=</varname></term>
<listitem>
<para>Configure a specific subnet ID on the interface from a (previously) received prefix
delegation. You can either set "auto" (the default) or a specific subnet ID (as defined in
<ulink url="https://tools.ietf.org/html/rfc4291#section-2.5.4">RFC 4291</ulink>, section
2.5.4), in which case the allowed value is hexadecimal, from 0 to 0x7fffffffffffffff
inclusive. This option is only effective when used together with
<varname>IPv6PrefixDelegation=</varname> and the corresponding configuration on the upstream
interface.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Assign=</varname></term>
<listitem>
<para>Takes a boolean. Specifies whether to add an address from the delegated prefixes which
are received from the WAN interface by the <varname>IPv6PrefixDelegation=</varname>. When
true (on LAN interfce), the EUI-64 algorithm will be used to form an interface identifier
from the delegated prefixes. Defaults to true.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Token=</varname></term>
<listitem>
<para>Specifies an optional address generation mode for <varname>Assign=</varname>. Takes an
IPv6 address. When set, the lower bits of the supplied address are combined with the upper
bits of a delegatad prefix received from the WAN interface by the
<varname>IPv6PrefixDelegation=</varname> prefixes to form a complete address.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[IPv6AcceptRA] Section Options</title>
<para>The [IPv6AcceptRA] section configures the IPv6 Router Advertisement (RA) client, if it is enabled

View File

@ -125,6 +125,17 @@ void address_free(Address *address) {
if (address->link && !address->acd) {
set_remove(address->link->addresses, address);
set_remove(address->link->addresses_foreign, address);
set_remove(address->link->static_addresses, address);
if (address->link->dhcp_address == address)
address->link->dhcp_address = NULL;
if (address->link->dhcp_address_old == address)
address->link->dhcp_address_old = NULL;
set_remove(address->link->dhcp6_addresses, address);
set_remove(address->link->dhcp6_addresses_old, address);
set_remove(address->link->dhcp6_pd_addresses, address);
set_remove(address->link->dhcp6_pd_addresses_old, address);
set_remove(address->link->ndisc_addresses, address);
set_remove(address->link->ndisc_addresses_old, address);
if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address))
memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr));
@ -205,7 +216,7 @@ static int address_compare_func(const Address *a1, const Address *a2) {
}
}
DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);
bool address_equal(Address *a1, Address *a2) {
if (a1 == a2)
@ -344,11 +355,8 @@ int address_update(
int r;
assert(address);
assert(address->link);
assert(cinfo);
assert_return(address->link, 1);
if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
ready = address_is_ready(address);
@ -356,18 +364,27 @@ int address_update(
address->scope = scope;
address->cinfo = *cinfo;
if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0;
link_update_operstate(address->link, true);
link_check_ready(address->link);
if (!ready &&
address_is_ready(address) &&
address->family == AF_INET6 &&
in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) {
if (!ready && address_is_ready(address)) {
if (address->callback) {
r = address->callback(address);
if (r < 0)
return r;
}
r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
if (r < 0)
return r;
if (address->family == AF_INET6 &&
in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
IN6_IS_ADDR_UNSPECIFIED(&address->link->ipv6ll_address) > 0) {
r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
if (r < 0)
return r;
}
}
return 0;
@ -586,9 +603,11 @@ int address_configure(
Address *address,
Link *link,
link_netlink_message_handler_t callback,
bool update) {
bool update,
Address **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
Address *a;
int r;
assert(address);
@ -609,6 +628,13 @@ int address_configure(
if (r < 0)
return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m");
if (DEBUG_LOGGING) {
_cleanup_free_ char *str = NULL;
(void) in_addr_to_string(address->family, &address->in_addr, &str);
log_link_debug(link, "%s address: %s", update ? "Updating" : "Configuring", strna(str));
}
if (update)
r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
link->ifindex, address->family);
@ -690,9 +716,9 @@ int address_configure(
link_ref(link);
if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer))
r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, NULL);
r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a);
else
r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a);
if (r < 0) {
address_release(address);
return log_link_error_errno(link, r, "Could not add address: %m");
@ -712,6 +738,9 @@ int address_configure(
log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
}
if (ret)
*ret = a;
return 1;
}

View File

@ -20,6 +20,7 @@ typedef struct Address Address;
typedef struct Network Network;
typedef struct Link Link;
typedef struct NetworkConfigSection NetworkConfigSection;
typedef int (*address_ready_callback_t)(Address *address);
struct Address {
Network *network;
@ -47,6 +48,9 @@ struct Address {
bool autojoin:1;
AddressFamily duplicate_address_detection;
/* Called when address become ready */
address_ready_callback_t callback;
sd_ipv4acd *acd;
LIST_FIELDS(Address, addresses);
@ -60,7 +64,7 @@ int address_get(Link *link, int family, const union in_addr_union *in_addr, unsi
bool address_exists(Link *link, int family, const union in_addr_union *in_addr);
int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo);
int address_drop(Address *address);
int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update);
int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret);
int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback);
bool address_equal(Address *a1, Address *a2);
bool address_is_ready(const Address *a);

View File

@ -20,38 +20,58 @@
#include "sysctl-util.h"
#include "web-util.h"
static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback);
static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback);
static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback);
static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, link_netlink_message_handler_t callback);
static int dhcp4_update_address(Link *link, bool announce);
static int dhcp4_remove_all(Link *link);
static int dhcp4_release_old_lease(Link *link, bool force);
static void dhcp4_release_old_lease(Link *link) {
struct in_addr address = {}, address_old = {};
static int dhcp4_address_callback(Address *address) {
assert(address);
assert(address->link);
/* Do not call this callback again. */
address->callback = NULL;
return dhcp4_release_old_lease(address->link, true);
}
static int dhcp4_release_old_lease(Link *link, bool force) {
Route *route;
Iterator i;
int k, r = 0;
assert(link);
if (!link->dhcp_lease_old)
return;
if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old))
return 0;
assert(link->dhcp_lease);
if (!force && (link->dhcp_address && !address_is_ready(link->dhcp_address))) {
log_link_debug(link, "New DHCPv4 address is not ready. The old lease will be removed later.");
link->dhcp_address->callback = dhcp4_address_callback;
return 0;
}
(void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old);
(void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
log_link_debug(link, "Removing old DHCPv4 address and routes.");
(void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false, NULL);
(void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false, NULL);
(void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false, NULL);
if (!in4_addr_equal(&address_old, &address))
(void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL);
link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
link_dirty(link);
SET_FOREACH(route, link->dhcp_routes_old, i) {
k = route_remove(route, link, NULL);
if (k < 0)
r = k;
}
if (link->dhcp_address_old) {
k = address_remove(link->dhcp_address_old, link, NULL);
if (k < 0)
r = k;
}
return r;
}
static void dhcp4_check_ready(Link *link) {
int r;
if (link->network->dhcp_send_decline && !link->dhcp4_address_bind)
return;
@ -59,8 +79,14 @@ static void dhcp4_check_ready(Link *link) {
return;
link->dhcp4_configured = true;
/* New address and routes are configured now. Let's release old lease. */
dhcp4_release_old_lease(link);
r = dhcp4_release_old_lease(link, false);
if (r < 0) {
link_enter_failed(link);
return;
}
link_check_ready(link);
}
@ -124,27 +150,25 @@ static bool link_prefixroute(Link *link) {
link->manager->dhcp4_prefix_root_cannot_set_table;
}
static int dhcp_route_configure(Route **route, Link *link) {
static int dhcp_route_configure(Route *route, Link *link) {
Route *ret;
int r;
assert(route);
assert(*route);
assert(link);
if (set_contains(link->dhcp_routes, *route))
return 0;
r = route_configure(*route, link, dhcp4_route_handler);
if (r <= 0)
return r;
r = route_configure(route, link, dhcp4_route_handler, &ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set DHCPv4 route: %m");
link->dhcp4_messages++;
r = set_put(link->dhcp_routes, *route);
r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, ret);
if (r < 0)
return r;
return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m");
(void) set_remove(link->dhcp_routes_old, ret);
TAKE_PTR(*route);
return 0;
}
@ -187,7 +211,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) {
route->priority = link->network->dhcp_route_metric;
route->table = table;
r = dhcp_route_configure(&route, link);
r = dhcp_route_configure(route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set route to DNS server: %m");
}
@ -231,6 +255,7 @@ static int link_set_dhcp_routes(Link *link) {
struct in_addr address;
int r, n, i;
uint32_t table;
Route *rt;
assert(link);
@ -245,12 +270,11 @@ static int link_set_dhcp_routes(Link *link) {
* the addresses now, let's not configure the routes either. */
return 0;
r = set_ensure_allocated(&link->dhcp_routes, &route_hash_ops);
if (r < 0)
return log_oom();
/* Clear old entries in case the set was already allocated */
set_clear(link->dhcp_routes);
while ((rt = set_steal_first(link->dhcp_routes))) {
r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m");
}
table = link_get_dhcp_route_table(link);
@ -265,7 +289,7 @@ static int link_set_dhcp_routes(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not create prefix route: %m");
r = dhcp_route_configure(&prefix_route, link);
r = dhcp_route_configure(prefix_route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set prefix route: %m");
}
@ -316,7 +340,7 @@ static int link_set_dhcp_routes(Link *link) {
if (set_contains(link->dhcp_routes, route))
continue;
r = dhcp_route_configure(&route, link);
r = dhcp_route_configure(route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set route: %m");
}
@ -356,7 +380,7 @@ static int link_set_dhcp_routes(Link *link) {
route_gw->table = table;
route_gw->mtu = link->network->dhcp_route_mtu;
r = dhcp_route_configure(&route_gw, link);
r = dhcp_route_configure(route_gw, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set host route: %m");
@ -372,12 +396,11 @@ static int link_set_dhcp_routes(Link *link) {
route->table = table;
route->mtu = link->network->dhcp_route_mtu;
r = dhcp_route_configure(&route, link);
r = dhcp_route_configure(route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set router: %m");
}
Route *rt;
LIST_FOREACH(routes, rt, link->network->static_routes) {
if (!rt->gateway_from_dhcp)
continue;
@ -387,272 +410,15 @@ static int link_set_dhcp_routes(Link *link) {
rt->gw.in = router[0];
r = route_configure(rt, link, dhcp4_route_handler);
r = dhcp_route_configure(rt, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set gateway: %m");
if (r > 0)
link->dhcp4_messages++;
}
}
return link_set_dns_routes(link, &address);
}
static int dhcp_route_remove(Route *route, Link *link, link_netlink_message_handler_t callback) {
int r;
r = route_remove(route, link, callback);
if (r < 0)
return r;
if (callback)
link->dhcp4_remove_messages++;
return 0;
}
static int dhcp_remove_routes(
Link *link,
sd_dhcp_lease *lease,
const struct in_addr *address,
bool remove_all,
link_netlink_message_handler_t callback) {
_cleanup_free_ sd_dhcp_route **routes = NULL;
uint32_t table;
int n, i, r;
assert(link);
assert(address);
if (!link->network->dhcp_use_routes)
return 0;
n = sd_dhcp_lease_get_routes(lease, &routes);
if (IN_SET(n, 0, -ENODATA))
return 0;
else if (n < 0)
return log_link_error_errno(link, n, "DHCP error: Failed to get routes: %m");
table = link_get_dhcp_route_table(link);
for (i = 0; i < n; i++) {
_cleanup_(route_freep) Route *route = NULL;
r = route_new(&route);
if (r < 0)
return log_oom();
route->family = AF_INET;
assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
route->priority = link->network->dhcp_route_metric;
route->table = table;
route->scope = route_scope_from_address(route, address);
if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
route->prefsrc.in = *address;
if (!remove_all && set_contains(link->dhcp_routes, route))
continue;
r = dhcp_route_remove(route, link, callback);
if (r < 0)
return r;
}
return n;
}
static int dhcp_remove_router(
Link *link,
sd_dhcp_lease *lease,
const struct in_addr *address,
bool remove_all,
link_netlink_message_handler_t callback) {
_cleanup_(route_freep) Route *route_gw = NULL, *route = NULL;
const struct in_addr *router;
uint32_t table;
int r;
assert(link);
assert(address);
if (!link->network->dhcp_use_gateway)
return 0;
r = sd_dhcp_lease_get_router(lease, &router);
if (IN_SET(r, 0, -ENODATA)) {
log_link_debug(link, "DHCP: No gateway received from DHCP server.");
return 0;
} else if (r < 0)
return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
else if (in4_addr_is_null(&router[0])) {
log_link_info(link, "DHCP: Received gateway is null, ignoring.");
return 0;
}
table = link_get_dhcp_route_table(link);
r = route_new(&route_gw);
if (r < 0)
return log_oom();
route_gw->family = AF_INET;
route_gw->dst.in = router[0];
route_gw->dst_prefixlen = 32;
route_gw->prefsrc.in = *address;
route_gw->scope = RT_SCOPE_LINK;
route_gw->protocol = RTPROT_DHCP;
route_gw->priority = link->network->dhcp_route_metric;
route_gw->table = table;
if (remove_all || !set_contains(link->dhcp_routes, route_gw)) {
r = dhcp_route_remove(route_gw, link, callback);
if (r < 0)
return r;
}
r = route_new(&route);
if (r < 0)
return log_oom();
route->family = AF_INET;
route->gw.in = router[0];
route->prefsrc.in = *address;
route->protocol = RTPROT_DHCP;
route->priority = link->network->dhcp_route_metric;
route->table = table;
if (remove_all || !set_contains(link->dhcp_routes, route)) {
r = dhcp_route_remove(route, link, callback);
if (r < 0)
return r;
}
Route *rt;
LIST_FOREACH(routes, rt, link->network->static_routes) {
if (!rt->gateway_from_dhcp)
continue;
if (rt->family != AF_INET)
continue;
if (!remove_all && in4_addr_equal(router, &rt->gw.in))
continue;
r = dhcp_route_remove(rt, link, callback);
if (r < 0)
return r;
}
return 0;
}
static int dhcp_remove_dns_routes(
Link *link,
sd_dhcp_lease *lease,
const struct in_addr *address,
bool remove_all,
link_netlink_message_handler_t callback) {
const struct in_addr *dns;
uint32_t table;
int i, n, r;
assert(link);
assert(lease);
assert(link->network);
if (!link->network->dhcp_use_dns ||
!link->network->dhcp_routes_to_dns)
return 0;
n = sd_dhcp_lease_get_dns(lease, &dns);
if (IN_SET(n, 0, -ENODATA))
return 0;
if (n < 0)
return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %m");
table = link_get_dhcp_route_table(link);
for (i = 0; i < n; i ++) {
_cleanup_(route_freep) Route *route = NULL;
r = route_new(&route);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET;
route->dst.in = dns[i];
route->dst_prefixlen = 32;
route->prefsrc.in = *address;
route->scope = RT_SCOPE_LINK;
route->protocol = RTPROT_DHCP;
route->priority = link->network->dhcp_route_metric;
route->table = table;
if (!remove_all && set_contains(link->dhcp_routes, route))
continue;
r = dhcp_route_remove(route, link, callback);
if (r < 0)
return r;
}
if (!link_prefixroute(link)) {
_cleanup_(route_freep) Route *prefix_route = NULL;
r = dhcp_prefix_route_from_lease(lease, table, address, &prefix_route);
if (r < 0)
return log_link_warning_errno(link, r, "Could not create prefix route: %m");
if (remove_all || !set_contains(link->dhcp_routes, prefix_route)) {
r = dhcp_route_remove(prefix_route, link, callback);
if (r < 0)
return r;
}
}
return 0;
}
static int dhcp_remove_address(
Link *link, sd_dhcp_lease *lease,
const struct in_addr *address,
link_netlink_message_handler_t callback) {
_cleanup_(address_freep) Address *a = NULL;
struct in_addr netmask;
int r;
assert(link);
assert(address);
if (in4_addr_is_null(address))
return 0;
r = address_new(&a);
if (r < 0)
return log_oom();
a->family = AF_INET;
a->in_addr.in = *address;
if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
r = address_remove(a, link, callback);
if (r < 0)
return r;
if (callback)
link->dhcp4_remove_messages++;
return 0;
}
static int dhcp_reset_mtu(Link *link) {
uint16_t mtu;
int r;
@ -756,37 +522,33 @@ static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
}
static int dhcp4_remove_all(Link *link) {
struct in_addr address;
int r;
Route *route;
Iterator i;
int k, r = 0;
assert(link);
assert(link->dhcp_lease);
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get DHCPv4 address: %m");
SET_FOREACH(route, link->dhcp_routes, i) {
k = route_remove(route, link, dhcp4_remove_route_handler);
if (k < 0)
r = k;
else
link->dhcp4_remove_messages++;
}
r = dhcp_remove_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler);
if (r < 0)
return r;
if (link->dhcp_address) {
k = address_remove(link->dhcp_address, link, dhcp4_remove_address_handler);
if (k < 0)
r = k;
else
link->dhcp4_remove_messages++;
}
r = dhcp_remove_router(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler);
if (r < 0)
return r;
r = dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler);
if (r < 0)
return r;
r = dhcp_remove_address(link, link->dhcp_lease, &address, dhcp4_remove_address_handler);
if (r < 0)
return r;
return 0;
return r;
}
static int dhcp_lease_lost(Link *link) {
int r;
int k, r;
assert(link);
assert(link->dhcp_lease);
@ -796,24 +558,26 @@ static int dhcp_lease_lost(Link *link) {
link->dhcp4_configured = false;
/* dhcp_lease_lost() may be called during renewing IP address. */
dhcp4_release_old_lease(link);
k = dhcp4_release_old_lease(link, true);
if (k < 0)
r = k;
r = dhcp4_remove_all(link);
if (r < 0)
return r;
k = dhcp4_remove_all(link);
if (k < 0)
r = k;
r = dhcp_reset_mtu(link);
if (r < 0)
return r;
k = dhcp_reset_mtu(link);
if (k < 0)
r = k;
r = dhcp_reset_hostname(link);
if (r < 0)
return r;
k = dhcp_reset_hostname(link);
if (k < 0)
r = k;
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link_dirty(link);
return 0;
return r;
}
static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
@ -975,6 +739,7 @@ static int dhcp4_update_address(Link *link, bool announce) {
uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
struct in_addr address, netmask;
unsigned prefixlen;
Address *ret;
int r;
assert(link);
@ -1049,9 +814,13 @@ static int dhcp4_update_address(Link *link, bool announce) {
/* allow reusing an existing address and simply update its lifetime
* in case it already exists */
r = address_configure(addr, link, dhcp4_address_handler, true);
r = address_configure(addr, link, dhcp4_address_handler, true, &ret);
if (r < 0)
return r;
return log_link_error_errno(link, r, "Failed to set DHCPv4 address: %m");
if (!address_equal(link->dhcp_address, ret))
link->dhcp_address_old = link->dhcp_address;
link->dhcp_address = ret;
return 0;
}
@ -1151,32 +920,11 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
int r;
link->dhcp_lease_old = TAKE_PTR(link->dhcp_lease);
/* On IP address change, to keep the connectability, we would like to assign new address and
* routes, and then release old lease. There are two possible success paths:
*
* 1. new address and routes are configured.
* -> handled by dhcp_release_old_lease() in dhcp4_route_handler().
* 2. new address is configured and no route is requested.
* -> handled by dhcp_release_old_lease() in dhcp4_address_handler().
*
* On error in assigning new address and routes, then the link always enters to the failed
* state. And link_enter_failed() leads to the DHCP client to be stopped. So,
* dhcp_release_old_lease() will be also called by link_stop_clients().
*/
r = dhcp_lease_acquired(client, link);
if (r < 0) {
/* If it fails, then the new address is not configured yet.
* So, let's simply drop the old lease. */
sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp_lease = TAKE_PTR(link->dhcp_lease_old);
if (r < 0)
(void) dhcp_lease_lost(link);
return r;
}
return 0;
return r;
}
static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) {

File diff suppressed because it is too large Load Diff

View File

@ -17,17 +17,26 @@ typedef enum DHCP6ClientStartMode {
typedef struct Link Link;
typedef struct Manager Manager;
bool dhcp6_get_prefix_delegation(Link *link);
int dhcp6_request_prefix_delegation(Link *link);
typedef struct DHCP6DelegatedPrefix {
struct in6_addr prefix; /* Prefix assigned to the link */
struct in6_addr pd_prefix; /* PD prefix provided by DHCP6 lease */
Link *link;
} DHCP6DelegatedPrefix;
DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
bool link_dhcp6_pd_is_enabled(Link *link);
int dhcp6_pd_remove(Link *link);
int dhcp6_configure(Link *link);
int dhcp6_request_address(Link *link, int ir);
int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
int dhcp6_request_prefix_delegation(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_delegated_prefix_token);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_subnet_id);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_token);
const char* dhcp6_client_start_mode_to_string(DHCP6ClientStartMode i) _const_;
DHCP6ClientStartMode dhcp6_client_start_mode_from_string(const char *s) _pure_;

View File

@ -17,7 +17,7 @@ static int ipv4ll_address_lost(Link *link) {
assert(link);
link->ipv4ll_address = false;
link->ipv4ll_address_configured = false;
r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
if (r < 0)
@ -47,7 +47,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
int r;
assert(link);
assert(!link->ipv4ll_address);
assert(!link->ipv4ll_address_configured);
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
@ -57,7 +57,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
} else if (r >= 0)
(void) manager_rtnl_process_address(rtnl, m, link->manager);
link->ipv4ll_address = true;
link->ipv4ll_address_configured = true;
link_check_ready(link);
return 1;
@ -71,7 +71,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
assert(ll);
assert(link);
link->ipv4ll_address = false;
link->ipv4ll_address_configured = false;
r = sd_ipv4ll_get_address(ll, &address);
if (r == -ENOENT)
@ -92,7 +92,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen);
ll_addr->scope = RT_SCOPE_LINK;
r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
r = address_configure(ll_addr, link, ipv4ll_address_handler, false, NULL);
if (r < 0)
return r;

View File

@ -691,7 +691,6 @@ static void link_free_engines(Link *link) {
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp_routes = set_free(link->dhcp_routes);
link->lldp = sd_lldp_unref(link->lldp);
@ -699,6 +698,7 @@ static void link_free_engines(Link *link) {
link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll);
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
link->ndisc = sd_ndisc_unref(link->ndisc);
link->radv = sd_radv_unref(link->radv);
}
@ -711,17 +711,32 @@ static Link *link_free(Link *link) {
link_ntp_settings_clear(link);
link_dns_settings_clear(link);
link->routes = set_free_with_destructor(link->routes, route_free);
link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
link->routes = set_free(link->routes);
link->routes_foreign = set_free(link->routes_foreign);
link->dhcp_routes = set_free(link->dhcp_routes);
link->dhcp_routes_old = set_free(link->dhcp_routes_old);
link->dhcp6_routes = set_free(link->dhcp6_routes);
link->dhcp6_routes_old = set_free(link->dhcp6_routes_old);
link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes);
link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old);
link->ndisc_routes = set_free(link->ndisc_routes);
link->ndisc_routes_old = set_free(link->ndisc_routes_old);
link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free);
link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free);
link->nexthops = set_free(link->nexthops);
link->nexthops_foreign = set_free(link->nexthops_foreign);
link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free);
link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free);
link->neighbors = set_free(link->neighbors);
link->neighbors_foreign = set_free(link->neighbors_foreign);
link->addresses = set_free_with_destructor(link->addresses, address_free);
link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free);
link->addresses = set_free(link->addresses);
link->addresses_foreign = set_free(link->addresses_foreign);
link->static_addresses = set_free(link->static_addresses);
link->dhcp6_addresses = set_free(link->dhcp6_addresses);
link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old);
link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses);
link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old);
link->ndisc_addresses = set_free(link->ndisc_addresses);
link->ndisc_addresses_old = set_free(link->ndisc_addresses_old);
while ((address = link->pool_addresses)) {
LIST_REMOVE(addresses, link->pool_addresses, address);
@ -834,6 +849,12 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) {
r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
}
if (link_dhcp6_pd_is_enabled(link)) {
k = dhcp6_pd_remove(link);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
}
if (link->ndisc) {
k = sd_ndisc_stop(link->ndisc);
if (k < 0)
@ -1037,12 +1058,13 @@ int link_request_set_routes(Link *link) {
assert(link);
assert(link->network);
assert(link->addresses_configured);
assert(link->address_messages == 0);
assert(link->state != _LINK_STATE_INVALID);
link->static_routes_configured = false;
if (!link->addresses_ready)
return 0;
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
/* During configuring addresses, the link lost its carrier. As networkd is dropping
* the addresses now, let's not configure the routes either. */
@ -1061,7 +1083,7 @@ int link_request_set_routes(Link *link) {
if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
continue;
r = route_configure(rt, link, route_handler);
r = route_configure(rt, link, route_handler, NULL);
if (r < 0)
return log_link_warning_errno(link, r, "Could not set routes: %m");
if (r > 0)
@ -1082,12 +1104,14 @@ int link_request_set_routes(Link *link) {
void link_check_ready(Link *link) {
Address *a;
Iterator i;
int r;
assert(link);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) {
log_link_debug(link, "%s(): link is in failed or linger state.", __func__);
if (link->state == LINK_STATE_CONFIGURED)
return;
if (link->state != LINK_STATE_CONFIGURING) {
log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state));
return;
}
@ -1113,15 +1137,6 @@ void link_check_ready(Link *link) {
return;
}
if (!link->addresses_ready) {
link->addresses_ready = true;
r = link_request_set_routes(link);
if (r < 0)
link_enter_failed(link);
log_link_debug(link, "%s(): static addresses are configured. Configuring static routes.", __func__);
return;
}
if (!link->static_routes_configured) {
log_link_debug(link, "%s(): static routes are not configured.", __func__);
return;
@ -1149,7 +1164,7 @@ void link_check_ready(Link *link) {
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address) {
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) {
log_link_debug(link, "%s(): IPv4LL is not configured.", __func__);
return;
}
@ -1160,17 +1175,19 @@ void link_check_ready(Link *link) {
return;
}
if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) && set_isempty(link->addresses)) {
log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no address is assigned yet.", __func__);
if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && set_isempty(link->ndisc_addresses) &&
!(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) {
log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no dynamic address is assigned yet.", __func__);
return;
}
if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || dhcp6_get_prefix_delegation(link) || link_ipv6_accept_ra_enabled(link)) {
if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || link_ipv6_accept_ra_enabled(link)) {
if (!link->dhcp4_configured &&
!(link->dhcp6_address_configured && link->dhcp6_route_configured) &&
!(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) &&
!(link->ndisc_addresses_configured && link->ndisc_routes_configured) &&
!(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address)) {
!(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) {
/* When DHCP or RA is enabled, at least one protocol must provide an address, or
* an IPv4ll fallback address must be configured. */
log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
@ -1189,8 +1206,7 @@ void link_check_ready(Link *link) {
}
}
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
link_enter_configured(link);
return;
}
@ -1222,6 +1238,50 @@ static int link_request_set_neighbors(Link *link) {
return 0;
}
static int link_set_bridge_fdb(Link *link) {
FdbEntry *fdb_entry;
int r;
LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
r = fdb_entry_configure(link, fdb_entry);
if (r < 0)
return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
}
return 0;
}
static int static_address_ready_callback(Address *address) {
Address *a;
Iterator i;
Link *link;
assert(address);
assert(address->link);
link = address->link;
if (!link->addresses_configured)
return 0;
SET_FOREACH(a, link->static_addresses, i)
if (!address_is_ready(a)) {
_cleanup_free_ char *str = NULL;
(void) in_addr_to_string(a->family, &a->in_addr, &str);
log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen);
return 0;
}
/* This should not be called again */
SET_FOREACH(a, link->static_addresses, i)
a->callback = NULL;
link->addresses_ready = true;
return link_request_set_routes(link);
}
static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@ -1247,23 +1307,50 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
(void) manager_rtnl_process_address(rtnl, m, link->manager);
if (link->address_messages == 0) {
Address *a;
log_link_debug(link, "Addresses set");
link->addresses_configured = true;
link_check_ready(link);
/* When all static addresses are already ready, then static_address_ready_callback()
* will not be called automatically. So, call it here. */
a = set_first(link->static_addresses);
if (!a) {
log_link_warning(link, "No static address is stored.");
link_enter_failed(link);
return 1;
}
if (!a->callback) {
log_link_warning(link, "Address ready callback is not set.");
link_enter_failed(link);
return 1;
}
r = a->callback(a);
if (r < 0)
link_enter_failed(link);
}
return 1;
}
static int link_set_bridge_fdb(Link *link) {
FdbEntry *fdb_entry;
static int static_address_configure(Address *address, Link *link, bool update) {
Address *ret;
int r;
LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
r = fdb_entry_configure(link, fdb_entry);
if (r < 0)
return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
}
assert(address);
assert(link);
r = address_configure(address, link, address_handler, update, &ret);
if (r < 0)
return log_link_warning_errno(link, r, "Could not configure static address: %m");
link->address_messages++;
r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to store static address: %m");
ret->callback = static_address_ready_callback;
return 0;
}
@ -1302,16 +1389,12 @@ static int link_request_set_addresses(Link *link) {
else
update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
r = address_configure(ad, link, address_handler, update);
r = static_address_configure(ad, link, update);
if (r < 0)
return log_link_warning_errno(link, r, "Could not set addresses: %m");
if (r > 0)
link->address_messages++;
return r;
}
if (IN_SET(link->network->router_prefix_delegation,
RADV_PREFIX_DELEGATION_STATIC,
RADV_PREFIX_DELEGATION_BOTH))
if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC)
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
_cleanup_(address_freep) Address *address = NULL;
@ -1320,22 +1403,20 @@ static int link_request_set_addresses(Link *link) {
r = address_new(&address);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate address: %m");
return log_oom();
r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
if (r < 0)
return r;
return log_link_warning_errno(link, r, "Could not get RA prefix: %m");
r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
if (r < 0)
return r;
return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
address->family = AF_INET6;
r = address_configure(address, link, address_handler, true);
r = static_address_configure(address, link, true);
if (r < 0)
return log_link_warning_errno(link, r, "Could not set addresses: %m");
if (r > 0)
link->address_messages++;
return r;
}
LIST_FOREACH(labels, label, link->network->address_labels) {
@ -1346,8 +1427,7 @@ static int link_request_set_addresses(Link *link) {
link->address_label_messages++;
}
/* now that we can figure out a default address for the dhcp server,
start it */
/* now that we can figure out a default address for the dhcp server, start it */
if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) {
r = dhcp4_server_configure(link);
if (r < 0)
@ -1357,7 +1437,10 @@ static int link_request_set_addresses(Link *link) {
if (link->address_messages == 0) {
link->addresses_configured = true;
link_check_ready(link);
link->addresses_ready = true;
r = link_request_set_routes(link);
if (r < 0)
return r;
} else {
log_link_debug(link, "Setting addresses");
link_set_state(link, LINK_STATE_CONFIGURING);
@ -1612,12 +1695,14 @@ static int link_acquire_ipv6_conf(Link *link) {
r = dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
if (r < 0 && r != -EBUSY)
return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
else
log_link_debug(link, "Acquiring DHCPv6 lease");
}
(void) dhcp6_request_prefix_delegation(link);
r = dhcp6_request_prefix_delegation(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m");
return 0;
}
@ -4235,7 +4320,6 @@ int link_save(Link *link) {
if (link->network) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;
const char *dhcp_domainname = NULL, *p;
sd_dhcp6_lease *dhcp6_lease = NULL;
bool space;
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
@ -4247,12 +4331,6 @@ int link_save(Link *link) {
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
if (r < 0 && r != -ENOMSG)
log_link_debug_errno(link, r, "Failed to get DHCPv6 lease: %m");
}
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
/************************************************************/
@ -4269,7 +4347,7 @@ int link_save(Link *link) {
link->dhcp_lease,
link->network->dhcp_use_dns,
SD_DHCP_LEASE_DNS,
dhcp6_lease,
link->dhcp6_lease,
link->network->dhcp6_use_dns,
sd_dhcp6_lease_get_dns,
NULL);
@ -4293,7 +4371,7 @@ int link_save(Link *link) {
link->dhcp_lease,
link->network->dhcp_use_ntp,
SD_DHCP_LEASE_NTP,
dhcp6_lease,
link->dhcp6_lease,
link->network->dhcp6_use_ntp,
sd_dhcp6_lease_get_ntp_addrs,
sd_dhcp6_lease_get_ntp_fqdn);
@ -4312,8 +4390,8 @@ int link_save(Link *link) {
(void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
(void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
}
if (dhcp6_lease)
(void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
if (link->dhcp6_lease)
(void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains);
}
fputs("DOMAINS=", f);

View File

@ -87,6 +87,7 @@ typedef struct Link {
Set *addresses;
Set *addresses_foreign;
Set *static_addresses;
Set *neighbors;
Set *neighbors_foreign;
Set *routes;
@ -95,32 +96,20 @@ typedef struct Link {
Set *nexthops_foreign;
sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
Set *dhcp_routes;
sd_dhcp_lease *dhcp_lease;
Address *dhcp_address, *dhcp_address_old;
Set *dhcp_routes, *dhcp_routes_old;
char *lease_file;
uint32_t original_mtu;
unsigned dhcp4_messages;
unsigned dhcp4_remove_messages;
unsigned dhcp6_address_messages;
unsigned dhcp6_route_messages;
unsigned dhcp6_pd_address_messages;
unsigned dhcp6_pd_route_messages;
bool dhcp4_route_failed:1;
bool dhcp4_route_retrying:1;
bool dhcp4_configured:1;
bool dhcp4_address_bind:1;
bool dhcp6_address_configured:1;
bool dhcp6_route_configured:1;
bool dhcp6_pd_address_configured:1;
bool dhcp6_pd_route_configured:1;
unsigned ndisc_addresses_messages;
unsigned ndisc_routes_messages;
bool ndisc_addresses_configured:1;
bool ndisc_routes_configured:1;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address:1;
bool ipv4ll_address_configured:1;
bool addresses_configured:1;
bool addresses_ready:1;
@ -141,10 +130,30 @@ typedef struct Link {
sd_ndisc *ndisc;
Set *ndisc_rdnss;
Set *ndisc_dnssl;
Set *ndisc_addresses, *ndisc_addresses_old;
Set *ndisc_routes, *ndisc_routes_old;
unsigned ndisc_addresses_messages;
unsigned ndisc_routes_messages;
bool ndisc_addresses_configured:1;
bool ndisc_routes_configured:1;
sd_radv *radv;
sd_dhcp6_client *dhcp6_client;
sd_dhcp6_lease *dhcp6_lease;
Set *dhcp6_addresses, *dhcp6_addresses_old;
Set *dhcp6_routes, *dhcp6_routes_old;
Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old;
Set *dhcp6_pd_routes, *dhcp6_pd_routes_old;
unsigned dhcp6_address_messages;
unsigned dhcp6_route_messages;
unsigned dhcp6_pd_address_messages;
unsigned dhcp6_pd_route_messages;
bool dhcp6_address_configured:1;
bool dhcp6_route_configured:1;
bool dhcp6_pd_address_configured:1;
bool dhcp6_pd_route_configured:1;
bool dhcp6_pd_prefixes_assigned:1;
/* This is about LLDP reception */
sd_lldp *lldp;

View File

@ -857,8 +857,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
valid_str ? "for " : "forever", strempty(valid_str));
}
/* address_update() logs internally, so we don't need to. */
(void) address_update(address, flags, scope, &cinfo);
/* address_update() logs internally, so we don't need to here. */
r = address_update(address, flags, scope, &cinfo);
if (r < 0)
link_enter_failed(link);
break;
@ -1821,27 +1823,20 @@ int manager_new(Manager **ret) {
}
void manager_free(Manager *m) {
struct in6_addr *a;
AddressPool *pool;
Link *link;
Iterator i;
if (!m)
return;
free(m->state_file);
while ((a = hashmap_first_key(m->dhcp6_prefixes)))
(void) dhcp6_prefix_remove(m, a);
m->dhcp6_prefixes = hashmap_free(m->dhcp6_prefixes);
while ((link = hashmap_steal_first(m->links))) {
if (link->dhcp6_client)
(void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link);
HASHMAP_FOREACH(link, m->links, i)
(void) link_stop_clients(link, true);
link_unref(link);
}
m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free);
m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref);
@ -1857,9 +1852,9 @@ void manager_free(Manager *m) {
/* routing_policy_rule_free() access m->rules and m->rules_foreign.
* So, it is necessary to set NULL after the sets are freed. */
m->rules = set_free_with_destructor(m->rules, routing_policy_rule_free);
m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free);
set_free_with_destructor(m->rules_saved, routing_policy_rule_free);
m->rules = set_free(m->rules);
m->rules_foreign = set_free(m->rules_foreign);
set_free(m->rules_saved);
sd_netlink_unref(m->rtnl);
sd_netlink_unref(m->genl);

View File

@ -44,6 +44,7 @@ struct Manager {
Hashmap *netdevs;
OrderedHashmap *networks;
Hashmap *dhcp6_prefixes;
Set *dhcp6_pd_prefixes;
LIST_HEAD(AddressPool, address_pools);
usec_t network_dirs_ts_usec;

View File

@ -35,6 +35,79 @@
#define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
static int ndisc_remove_old(Link *link, bool force);
static int ndisc_address_callback(Address *address) {
Address *a;
Iterator i;
assert(address);
assert(address->link);
/* Make this called only once */
SET_FOREACH(a, address->link->ndisc_addresses, i)
a->callback = NULL;
return ndisc_remove_old(address->link, true);
}
static int ndisc_remove_old(Link *link, bool force) {
Address *address;
Route *route;
NDiscDNSSL *dnssl;
NDiscRDNSS *rdnss;
Iterator i;
int k, r = 0;
assert(link);
if (!force) {
bool set_callback = !set_isempty(link->ndisc_addresses);
if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured)
return 0;
SET_FOREACH(address, link->ndisc_addresses, i)
if (address_is_ready(address)) {
set_callback = false;
break;
}
if (set_callback) {
SET_FOREACH(address, link->ndisc_addresses, i)
address->callback = ndisc_address_callback;
return 0;
}
}
if (!set_isempty(link->ndisc_addresses_old) || !set_isempty(link->ndisc_routes_old))
log_link_debug(link, "Removing old NDisc addresses and routes.");
link_dirty(link);
SET_FOREACH(address, link->ndisc_addresses_old, i) {
k = address_remove(address, link, NULL);
if (k < 0)
r = k;
}
SET_FOREACH(route, link->ndisc_routes_old, i) {
k = route_remove(route, link, NULL);
if (k < 0)
r = k;
}
SET_FOREACH(rdnss, link->ndisc_rdnss, i)
if (rdnss->marked)
free(set_remove(link->ndisc_rdnss, rdnss));
SET_FOREACH(dnssl, link->ndisc_dnssl, i)
if (dnssl->marked)
free(set_remove(link->ndisc_dnssl, dnssl));
return r;
}
static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@ -56,6 +129,13 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
if (link->ndisc_routes_messages == 0) {
log_link_debug(link, "NDisc routes set.");
link->ndisc_routes_configured = true;
r = ndisc_remove_old(link, false);
if (r < 0) {
link_enter_failed(link);
return 1;
}
link_check_ready(link);
}
@ -84,6 +164,13 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
if (link->ndisc_addresses_messages == 0) {
log_link_debug(link, "NDisc SLAAC addresses set.");
link->ndisc_addresses_configured = true;
r = ndisc_remove_old(link, false);
if (r < 0) {
link_enter_failed(link);
return 1;
}
r = link_request_set_routes(link);
if (r < 0) {
link_enter_failed(link);
@ -94,6 +181,50 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
return 1;
}
static int ndisc_route_configure(Route *route, Link *link) {
Route *ret;
int r;
assert(route);
assert(link);
r = route_configure(route, link, ndisc_route_handler, &ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set NDisc route: %m");
link->ndisc_routes_messages++;
r = set_ensure_put(&link->ndisc_routes, &route_hash_ops, ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store NDisc route: %m");
(void) set_remove(link->ndisc_routes_old, ret);
return 0;
}
static int ndisc_address_configure(Address *address, Link *link) {
Address *ret;
int r;
assert(address);
assert(link);
r = address_configure(address, link, ndisc_address_handler, true, &ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m");
link->ndisc_addresses_messages++;
r = set_ensure_put(&link->ndisc_addresses, &address_hash_ops, ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m");
(void) set_remove(link->ndisc_addresses_old, ret);
return 0;
}
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
union in_addr_union gateway;
@ -155,11 +286,9 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->lifetime = time_now + lifetime * USEC_PER_SEC;
route->mtu = mtu;
r = route_configure(route, link, ndisc_route_handler);
r = ndisc_route_configure(route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set default route: %m");
if (r > 0)
link->ndisc_routes_messages++;
Route *route_gw;
LIST_FOREACH(routes, route_gw, link->network->static_routes) {
@ -171,11 +300,9 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route_gw->gw = gateway;
r = route_configure(route_gw, link, ndisc_route_handler);
r = ndisc_route_configure(route_gw, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set gateway: %m");
if (r > 0)
link->ndisc_routes_messages++;
}
return 0;
@ -387,11 +514,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
address->in_addr.in6 = *a;
r = address_configure(address, link, ndisc_address_handler, true);
r = ndisc_address_configure(address, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set SLAAC address: %m");
if (r > 0)
link->ndisc_addresses_messages++;
}
return 0;
@ -435,11 +560,9 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
r = route_configure(route, link, ndisc_route_handler);
r = ndisc_route_configure(route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set prefix route: %m");;
if (r > 0)
link->ndisc_routes_messages++;
return 0;
}
@ -494,11 +617,9 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_link_error_errno(link, r, "Failed to get route address: %m");
r = route_configure(route, link, ndisc_route_handler);
r = ndisc_route_configure(route, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set additional route: %m");
if (r > 0)
link->ndisc_routes_messages++;
return 0;
}
@ -511,12 +632,19 @@ static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
return memcmp(&a->address, &b->address, sizeof(a->address));
}
DEFINE_PRIVATE_HASH_OPS(ndisc_rdnss_hash_ops, NDiscRDNSS, ndisc_rdnss_hash_func, ndisc_rdnss_compare_func);
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
ndisc_rdnss_hash_ops,
NDiscRDNSS,
ndisc_rdnss_hash_func,
ndisc_rdnss_compare_func,
free);
static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
uint32_t lifetime;
const struct in6_addr *a;
NDiscRDNSS *rdnss;
usec_t time_now;
Iterator i;
int n, r;
assert(link);
@ -534,28 +662,27 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
if (n < 0)
return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
for (int i = 0; i < n; i++) {
SET_FOREACH(rdnss, link->ndisc_rdnss, i)
rdnss->marked = true;
if (lifetime == 0)
return 0;
if (n >= (int) NDISC_RDNSS_MAX) {
log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX);
n = NDISC_RDNSS_MAX;
}
for (int j = 0; j < n; j++) {
_cleanup_free_ NDiscRDNSS *x = NULL;
NDiscRDNSS d = {
.address = a[i],
}, *y;
.address = a[j],
};
if (lifetime == 0) {
(void) set_remove(link->ndisc_rdnss, &d);
link_dirty(link);
continue;
}
y = set_get(link->ndisc_rdnss, &d);
if (y) {
y->valid_until = time_now + lifetime * USEC_PER_SEC;
continue;
}
ndisc_vacuum(link);
if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
log_link_warning(link, "Too many RDNSS records per link, ignoring.");
rdnss = set_get(link->ndisc_rdnss, &d);
if (rdnss) {
rdnss->marked = false;
rdnss->valid_until = time_now + lifetime * USEC_PER_SEC;
continue;
}
@ -564,7 +691,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
return log_oom();
*x = (NDiscRDNSS) {
.address = a[i],
.address = a[j],
.valid_until = time_now + lifetime * USEC_PER_SEC,
};
@ -572,8 +699,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_oom();
assert(r > 0);
link_dirty(link);
}
return 0;
@ -587,13 +712,20 @@ static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
}
DEFINE_PRIVATE_HASH_OPS(ndisc_dnssl_hash_ops, NDiscDNSSL, ndisc_dnssl_hash_func, ndisc_dnssl_compare_func);
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
ndisc_dnssl_hash_ops,
NDiscDNSSL,
ndisc_dnssl_hash_func,
ndisc_dnssl_compare_func,
free);
static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
_cleanup_strv_free_ char **l = NULL;
uint32_t lifetime;
usec_t time_now;
char **i;
NDiscDNSSL *dnssl;
Iterator i;
char **j;
int r;
assert(link);
@ -611,32 +743,31 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
STRV_FOREACH(i, l) {
_cleanup_free_ NDiscDNSSL *s = NULL;
NDiscDNSSL *x;
SET_FOREACH(dnssl, link->ndisc_dnssl, i)
dnssl->marked = true;
s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
if (lifetime == 0)
return 0;
if (strv_length(l) >= NDISC_DNSSL_MAX) {
log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX);
STRV_FOREACH(j, l + NDISC_DNSSL_MAX)
*j = mfree(*j);
}
STRV_FOREACH(j, l) {
_cleanup_free_ NDiscDNSSL *s = NULL;
s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
if (!s)
return log_oom();
strcpy(NDISC_DNSSL_DOMAIN(s), *i);
strcpy(NDISC_DNSSL_DOMAIN(s), *j);
if (lifetime == 0) {
(void) set_remove(link->ndisc_dnssl, s);
link_dirty(link);
continue;
}
x = set_get(link->ndisc_dnssl, s);
if (x) {
x->valid_until = time_now + lifetime * USEC_PER_SEC;
continue;
}
ndisc_vacuum(link);
if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
log_link_warning(link, "Too many DNSSL records per link, ignoring.");
dnssl = set_get(link->ndisc_dnssl, s);
if (dnssl) {
dnssl->marked = false;
dnssl->valid_until = time_now + lifetime * USEC_PER_SEC;
continue;
}
@ -646,8 +777,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_oom();
assert(r > 0);
link_dirty(link);
}
return 0;
@ -736,6 +865,8 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
Address *address;
Route *route;
uint64_t flags;
int r;
@ -744,6 +875,23 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
assert(link->manager);
assert(rt);
link->ndisc_addresses_configured = false;
link->ndisc_routes_configured = false;
link_dirty(link);
while ((address = set_steal_first(link->ndisc_addresses))) {
r = set_ensure_put(&link->ndisc_addresses_old, &address_hash_ops, address);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store old NDisc SLAAC address: %m");
}
while ((route = set_steal_first(link->ndisc_routes))) {
r = set_ensure_put(&link->ndisc_routes_old, &route_hash_ops, route);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store old NDisc route: %m");
}
r = sd_ndisc_router_get_flags(rt, &flags);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA flags: %m");
@ -757,10 +905,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
if (r < 0 && r != -EBUSY)
return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
else {
else
log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
r = 0;
}
}
r = ndisc_router_process_default(link, rt);
@ -770,7 +916,33 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return r;
return r;
if (link->ndisc_addresses_messages == 0)
link->ndisc_addresses_configured = true;
else {
log_link_debug(link, "Setting SLAAC addresses.");
/* address_handler calls link_request_set_routes() and link_request_set_nexthop().
* Before they are called, the related flags must be cleared. Otherwise, the link
* becomes configured state before routes are configured. */
link->static_routes_configured = false;
link->static_nexthops_configured = false;
}
if (link->ndisc_routes_messages == 0)
link->ndisc_routes_configured = true;
else
log_link_debug(link, "Setting NDisc routes.");
r = ndisc_remove_old(link, false);
if (r < 0)
return r;
if (link->ndisc_addresses_configured && link->ndisc_routes_configured)
link_check_ready(link);
else
link_set_state(link, LINK_STATE_CONFIGURING);
return 0;
}
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
@ -785,44 +957,20 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r
switch (event) {
case SD_NDISC_EVENT_ROUTER:
link->ndisc_addresses_configured = false;
link->ndisc_routes_configured = false;
r = ndisc_router_handler(link, rt);
if (r < 0) {
link_enter_failed(link);
return;
}
if (link->ndisc_addresses_messages == 0)
link->ndisc_addresses_configured = true;
else {
log_link_debug(link, "Setting SLAAC addresses.");
/* address_handler calls link_request_set_routes() and link_request_set_nexthop().
* Before they are called, the related flags must be cleared. Otherwise, the link
* becomes configured state before routes are configured. */
link->static_routes_configured = false;
link->static_nexthops_configured = false;
}
if (link->ndisc_routes_messages == 0)
link->ndisc_routes_configured = true;
else
log_link_debug(link, "Setting NDisc routes.");
if (link->ndisc_addresses_configured && link->ndisc_routes_configured)
link_check_ready(link);
else
link_set_state(link, LINK_STATE_CONFIGURING);
break;
case SD_NDISC_EVENT_TIMEOUT:
log_link_debug(link, "NDisc handler get timeout event");
link->ndisc_addresses_configured = true;
link->ndisc_routes_configured = true;
link_check_ready(link);
if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) {
link->ndisc_addresses_configured = true;
link->ndisc_routes_configured = true;
link_check_ready(link);
}
break;
default:
assert_not_reached("Unknown NDisc event");
@ -862,6 +1010,7 @@ void ndisc_vacuum(Link *link) {
NDiscDNSSL *d;
Iterator i;
usec_t time_now;
bool updated = false;
assert(link);
@ -872,14 +1021,17 @@ void ndisc_vacuum(Link *link) {
SET_FOREACH(r, link->ndisc_rdnss, i)
if (r->valid_until < time_now) {
free(set_remove(link->ndisc_rdnss, r));
link_dirty(link);
updated = true;
}
SET_FOREACH(d, link->ndisc_dnssl, i)
if (d->valid_until < time_now) {
free(set_remove(link->ndisc_dnssl, d));
link_dirty(link);
updated = true;
}
if (updated)
link_dirty(link);
}
void ndisc_flush(Link *link) {
@ -887,8 +1039,8 @@ void ndisc_flush(Link *link) {
/* Removes all RDNSS and DNSSL entries, without exception */
link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
link->ndisc_rdnss = set_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free(link->ndisc_dnssl);
}
int ipv6token_new(IPv6Token **ret) {

View File

@ -24,11 +24,15 @@ typedef enum IPv6AcceptRAStartDHCP6Client {
} IPv6AcceptRAStartDHCP6Client;
typedef struct NDiscRDNSS {
/* Used when GC'ing old DNS servers when configuration changes. */
bool marked;
usec_t valid_until;
struct in6_addr address;
} NDiscRDNSS;
typedef struct NDiscDNSSL {
/* Used when GC'ing old domains when configuration changes. */
bool marked;
usec_t valid_until;
/* The domain name follows immediately. */
} NDiscDNSSL;

View File

@ -247,7 +247,7 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
}
DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func);
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
Neighbor neighbor, *existing;

View File

@ -213,8 +213,6 @@ DHCPv6.UserClass, config_parse_dhcp_user_class,
DHCPv6.VendorClass, config_parse_dhcp_vendor_class, 0, offsetof(Network, dhcp6_vendor_class)
DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options)
DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
DHCPv6.AssignAcquiredDelegatedPrefixAddress, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign_prefix)
DHCPv6.AssignAcquiredDelegatedPrefixToken, config_parse_dhcp6_delegated_prefix_token, 0, 0
DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_without_ra)
DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options)
@ -271,7 +269,9 @@ BridgeVLAN.PVID, config_parse_brvlan_pvid,
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, offsetof(Network, router_prefix_delegation)
Network.IPv6PDSubnetId, config_parse_router_prefix_subnet_id, 0, 0
DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp6_pd_subnet_id, 0, offsetof(Network, dhcp6_pd_subnet_id)
DHCPv6PrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign)
DHCPv6PrefixDelegation.Token, config_parse_dhcp6_pd_token, 0, offsetof(Network, dhcp6_pd_token)
IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)

View File

@ -420,7 +420,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp6_use_ntp = true,
.dhcp6_use_dns = true,
.dhcp6_pd_assign_prefix = true,
.dhcp6_pd_subnet_id = -1,
.dhcp6_pd_assign = true,
.dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
.dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
@ -429,7 +430,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp_server_emit_router = true,
.dhcp_server_emit_timezone = true,
.router_prefix_subnet_id = -1,
.router_emit_dns = true,
.router_emit_domains = true,

View File

@ -184,7 +184,6 @@ struct Network {
/* IPv6 prefix delegation support */
RADVPrefixDelegation router_prefix_delegation;
int64_t router_prefix_subnet_id;
usec_t router_lifetime_usec;
uint8_t router_preference;
bool router_managed;
@ -198,8 +197,11 @@ struct Network {
bool dhcp6_force_pd_other_information; /* Start DHCPv6 PD also when 'O'
RA flag is set, see RFC 7084,
WPD-4 */
bool dhcp6_pd_assign_prefix;
union in_addr_union dhcp6_delegation_prefix_token;
/* DHCPv6 Prefix Delegation support */
int64_t dhcp6_pd_subnet_id;
bool dhcp6_pd_assign;
union in_addr_union dhcp6_pd_token;
/* Bridge Support */
int use_bpdu;

View File

@ -650,10 +650,7 @@ int radv_configure(Link *link) {
return r;
}
if (IN_SET(link->network->router_prefix_delegation,
RADV_PREFIX_DELEGATION_STATIC,
RADV_PREFIX_DELEGATION_BOTH)) {
if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) {
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
if (r == -EEXIST)
@ -673,13 +670,12 @@ int radv_configure(Link *link) {
if (r < 0)
return r;
}
}
return 0;
}
int radv_add_prefix(Link *link, struct in6_addr *prefix, uint8_t prefix_len,
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
uint32_t lifetime_preferred, uint32_t lifetime_valid) {
_cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
int r;
@ -870,46 +866,3 @@ int config_parse_router_preference(const char *unit,
return 0;
}
int config_parse_router_prefix_subnet_id(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;
uint64_t t;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue) || streq(rvalue, "auto")) {
network->router_prefix_subnet_id = -1;
return 0;
}
r = safe_atoux64(rvalue, &t);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (t > INT64_MAX) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Invalid subnet id '%s', ignoring assignment.",
rvalue);
return 0;
}
network->router_prefix_subnet_id = (int64_t)t;
return 0;
}

View File

@ -14,10 +14,10 @@ typedef struct Prefix Prefix;
typedef struct RoutePrefix RoutePrefix;
typedef enum RADVPrefixDelegation {
RADV_PREFIX_DELEGATION_NONE,
RADV_PREFIX_DELEGATION_STATIC,
RADV_PREFIX_DELEGATION_DHCP6,
RADV_PREFIX_DELEGATION_BOTH,
RADV_PREFIX_DELEGATION_NONE = 0,
RADV_PREFIX_DELEGATION_STATIC = 1 << 0,
RADV_PREFIX_DELEGATION_DHCP6 = 1 << 1,
RADV_PREFIX_DELEGATION_BOTH = RADV_PREFIX_DELEGATION_STATIC | RADV_PREFIX_DELEGATION_DHCP6,
_RADV_PREFIX_DELEGATION_MAX,
_RADV_PREFIX_DELEGATION_INVALID = -1,
} RADVPrefixDelegation;
@ -52,7 +52,7 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
int radv_emit_dns(Link *link);
int radv_configure(Link *link);
int radv_add_prefix(Link *link, struct in6_addr *prefix, uint8_t prefix_len,
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
uint32_t lifetime_preferred, uint32_t lifetime_valid);
const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
@ -60,7 +60,6 @@ RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_subnet_id);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);

View File

@ -144,6 +144,14 @@ void route_free(Route *route) {
if (route->link) {
set_remove(route->link->routes, route);
set_remove(route->link->routes_foreign, route);
set_remove(route->link->dhcp_routes, route);
set_remove(route->link->dhcp_routes_old, route);
set_remove(route->link->dhcp6_routes, route);
set_remove(route->link->dhcp6_routes_old, route);
set_remove(route->link->dhcp6_pd_routes, route);
set_remove(route->link->dhcp6_pd_routes_old, route);
set_remove(route->link->ndisc_routes, route);
set_remove(route->link->ndisc_routes_old, route);
}
ordered_set_free_free(route->multipath_routes);
@ -597,7 +605,8 @@ static int append_nexthops(Route *route, sd_netlink_message *req) {
int route_configure(
Route *route,
Link *link,
link_netlink_message_handler_t callback) {
link_netlink_message_handler_t callback,
Route **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
@ -803,6 +812,9 @@ int route_configure(
sd_event_source_unref(route->expire);
route->expire = TAKE_PTR(expire);
if (ret)
*ret = route;
return 1;
}

View File

@ -66,7 +66,7 @@ extern const struct hash_ops route_hash_ops;
int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback);
int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
int route_get(Link *link, Route *in, Route **ret);

View File

@ -227,7 +227,12 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
}
}
DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func);
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
routing_policy_rule_hash_ops,
RoutingPolicyRule,
routing_policy_rule_hash_func,
routing_policy_rule_compare_func,
routing_policy_rule_free);
int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {

View File

@ -53,7 +53,7 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
log_info("$ %s", cmd);
assert_se(system(cmd) == 0);
set_free_with_destructor(rules, routing_policy_rule_free);
set_free(rules);
}
int main(int argc, char **argv) {

View File

@ -130,10 +130,12 @@ SendOption=
RequestOptions=
UserClass=
VendorClass=
AssignAcquiredDelegatedPrefixAddress=
AssignAcquiredDelegatedPrefixToken=
SendVendorOption=
RouteMetric=
[DHCPv6PrefixDelegation]
SubnetId=
Assign=
Token=
[Route]
Destination=
Protocol=
@ -203,7 +205,6 @@ NTP=
DHCP=
Domains=
IPv6PrefixDelegation=
IPv6PDSubnetId=
VLAN=
DHCPServer=
BindCarrier=

View File

@ -0,0 +1,26 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
VRF=vrf99
Address=fdde:11:22::1/128
Address=fdde:11:33::1/64
Address=10.20.22.1/32
Address=10.20.33.1/24
[Address]
Address=fdde:11:44::1/128
AddPrefixRoute=no
[Address]
Address=fdde:11:55::1/64
AddPrefixRoute=no
[Address]
Address=10.20.44.1/32
AddPrefixRoute=no
[Address]
Address=10.20.55.1/24
AddPrefixRoute=no

View File

@ -0,0 +1,25 @@
[Match]
Name=test1
[Network]
IPv6AcceptRA=no
Address=fdde:12:22::1/128
Address=fdde:12:33::1/64
Address=10.21.22.1/32
Address=10.21.33.1/24
[Address]
Address=fdde:12:44::1/128
AddPrefixRoute=no
[Address]
Address=fdde:12:55::1/64
AddPrefixRoute=no
[Address]
Address=10.21.44.1/32
AddPrefixRoute=no
[Address]
Address=10.21.55.1/24
AddPrefixRoute=no

View File

@ -1717,6 +1717,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-qdisc-ingress-netem-compat.network',
'25-qdisc-pie.network',
'25-qdisc-qfq.network',
'25-prefix-route-with-vrf.network',
'25-prefix-route-without-vrf.network',
'25-route-ipv6-src.network',
'25-route-static.network',
'25-route-vrf.network',
@ -1729,6 +1731,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-veth-peer.network',
'25-veth.netdev',
'25-vrf.netdev',
'25-vrf.network',
'26-link-local-addressing-ipv6.network',
'routing-policy-rule-dummy98.network',
'routing-policy-rule-test1.network']
@ -1815,6 +1818,77 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertNotRegex(output, '192.168.100.10/24')
@expectedFailureIfModuleIsNotAvailable('vrf')
def test_prefix_route(self):
copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev',
'25-prefix-route-without-vrf.network', '11-dummy.netdev',
'25-vrf.netdev', '25-vrf.network')
for trial in range(2):
if trial == 0:
start_networkd()
else:
restart_networkd(3)
self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
output = check_output('ip route show table 42 dev dummy98')
print('### ip route show table 42 dev dummy98')
print(output)
self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
self.assertRegex(output, 'broadcast 10.20.33.0 proto kernel scope link src 10.20.33.1')
self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
self.assertRegex(output, 'broadcast 10.20.55.0 proto kernel scope link src 10.20.55.1')
self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
output = check_output('ip -6 route show table 42 dev dummy98')
print('### ip -6 route show table 42 dev dummy98')
print(output)
if trial == 0:
# Kernel's bug?
self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
#self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
self.assertRegex(output, 'ff00::/8 metric 256 pref medium')
print()
output = check_output('ip route show dev test1')
print('### ip route show dev test1')
print(output)
self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
output = check_output('ip route show table local dev test1')
print('### ip route show table local dev test1')
print(output)
self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
self.assertRegex(output, 'broadcast 10.21.33.0 proto kernel scope link src 10.21.33.1')
self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
self.assertRegex(output, 'broadcast 10.21.55.0 proto kernel scope link src 10.21.55.1')
self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
output = check_output('ip -6 route show dev test1')
print('### ip -6 route show dev test1')
print(output)
self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
output = check_output('ip -6 route show table local dev test1')
print('### ip -6 route show table local dev test1')
print(output)
self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
self.assertRegex(output, 'ff00::/8 metric 256 pref medium')
def test_configure_without_carrier(self):
copy_unit_to_networkd_unit_path('11-dummy.netdev')
start_networkd()