mirror of
https://github.com/systemd/systemd
synced 2024-07-21 18:24:38 +00:00
parent
44e8cf303b
commit
6df0059441
|
@ -3191,6 +3191,16 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
|||
with the <varname>IPv6AcceptRA=</varname> setting described above:</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>UseRedirect=</varname></term>
|
||||
<listitem>
|
||||
<para>When true (the default), Redirect message sent by the current first-hop router will be
|
||||
accepted, and configures routes to redirected nodes will be configured.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Token=</varname></term>
|
||||
<listitem>
|
||||
|
|
|
@ -166,6 +166,7 @@ typedef struct Link {
|
|||
Set *ndisc_dnssl;
|
||||
Set *ndisc_captive_portals;
|
||||
Set *ndisc_pref64;
|
||||
Set *ndisc_redirects;
|
||||
unsigned ndisc_messages;
|
||||
bool ndisc_configured:1;
|
||||
|
||||
|
|
|
@ -378,6 +378,292 @@ static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
struct in6_addr gateway, destination;
|
||||
int r;
|
||||
|
||||
assert(rd);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_redirect_get_target_address(rd, &gateway);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_redirect_get_destination_address(rd, &destination);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->family = AF_INET6;
|
||||
if (!in6_addr_equal(&gateway, &destination)) {
|
||||
route->nexthop.gw.in6 = gateway;
|
||||
route->nexthop.family = AF_INET6;
|
||||
}
|
||||
route->dst.in6 = destination;
|
||||
route->dst_prefixlen = 128;
|
||||
|
||||
*ret = TAKE_PTR(route);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_request_redirect_route(Link *link, sd_ndisc_redirect *rd) {
|
||||
struct in6_addr router, sender;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->ndisc_default_router);
|
||||
assert(rd);
|
||||
|
||||
r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &router);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_redirect_get_sender_address(rd, &sender);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!in6_addr_equal(&sender, &router))
|
||||
return 0;
|
||||
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
r = ndisc_redirect_route_new(rd, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->protocol = RTPROT_REDIRECT;
|
||||
route->protocol_set = true; /* To make ndisc_request_route() not override the protocol. */
|
||||
|
||||
/* Redirect message does not have the lifetime, let's use the lifetime of the default router, and
|
||||
* update the lifetime of the redirect route every time when we receive RA. */
|
||||
return ndisc_request_route(route, link, link->ndisc_default_router);
|
||||
}
|
||||
|
||||
static int ndisc_remove_redirect_route(Link *link, sd_ndisc_redirect *rd) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(rd);
|
||||
|
||||
r = ndisc_redirect_route_new(rd, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return ndisc_remove_route(route, link);
|
||||
}
|
||||
|
||||
static void ndisc_redirect_hash_func(const sd_ndisc_redirect *x, struct siphash *state) {
|
||||
struct in6_addr dest = {};
|
||||
|
||||
assert(x);
|
||||
assert(state);
|
||||
|
||||
(void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest);
|
||||
|
||||
siphash24_compress_typesafe(dest, state);
|
||||
}
|
||||
|
||||
static int ndisc_redirect_compare_func(const sd_ndisc_redirect *x, const sd_ndisc_redirect *y) {
|
||||
struct in6_addr dest_x = {}, dest_y = {};
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
(void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest_x);
|
||||
(void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) y, &dest_y);
|
||||
|
||||
return memcmp(&dest_x, &dest_y, sizeof(dest_x));
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
ndisc_redirect_hash_ops,
|
||||
sd_ndisc_redirect,
|
||||
ndisc_redirect_hash_func,
|
||||
ndisc_redirect_compare_func,
|
||||
sd_ndisc_redirect_unref);
|
||||
|
||||
static int ndisc_redirect_equal(sd_ndisc_redirect *x, sd_ndisc_redirect *y) {
|
||||
struct in6_addr a, b;
|
||||
int r;
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
r = sd_ndisc_redirect_get_destination_address(x, &a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_redirect_get_destination_address(y, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!in6_addr_equal(&a, &b))
|
||||
return false;
|
||||
|
||||
r = sd_ndisc_redirect_get_target_address(x, &a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_redirect_get_target_address(y, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return in6_addr_equal(&a, &b);
|
||||
}
|
||||
|
||||
static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
|
||||
_cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *existing = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(rd);
|
||||
|
||||
existing = set_remove(link->ndisc_redirects, rd);
|
||||
if (!existing)
|
||||
return 0;
|
||||
|
||||
r = ndisc_redirect_equal(rd, existing);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return ndisc_remove_redirect_route(link, existing);
|
||||
}
|
||||
|
||||
static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
|
||||
struct in6_addr router, sender;
|
||||
usec_t lifetime_usec, now_usec;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(rd);
|
||||
|
||||
if (!link->network->ndisc_use_redirect)
|
||||
return 0;
|
||||
|
||||
/* Ignore all Redirect messages from non-default router. */
|
||||
|
||||
if (!link->ndisc_default_router)
|
||||
return 0;
|
||||
|
||||
r = sd_ndisc_router_get_lifetime_timestamp(link->ndisc_default_router, CLOCK_BOOTTIME, &lifetime_usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (lifetime_usec <= now_usec)
|
||||
return 0; /* The default router is outdated. Ignore the redirect message. */
|
||||
|
||||
r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &router);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_redirect_get_sender_address(rd, &sender);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!in6_addr_equal(&sender, &router))
|
||||
return 0; /* The redirect message is sent from a non-default router. */
|
||||
|
||||
/* OK, the Redirect message is sent from the current default router. */
|
||||
|
||||
r = ndisc_redirect_drop_conflict(link, rd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = set_ensure_put(&link->ndisc_redirects, &ndisc_redirect_hash_ops, rd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sd_ndisc_redirect_ref(rd);
|
||||
|
||||
return ndisc_request_redirect_route(link, rd);
|
||||
}
|
||||
|
||||
static int ndisc_router_update_redirect(Link *link) {
|
||||
int r, ret = 0;
|
||||
|
||||
assert(link);
|
||||
|
||||
/* Reconfigure redirect routes to update their lifetime. */
|
||||
|
||||
sd_ndisc_redirect *rd;
|
||||
SET_FOREACH(rd, link->ndisc_redirects) {
|
||||
r = ndisc_request_redirect_route(link, rd);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to update lifetime of the Redirect route: %m"));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ndisc_drop_redirect(Link *link, const struct in6_addr *router, bool remove) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
/* If the router is purged, then drop the redirect routes configured with the Redirect message sent
|
||||
* by the router. */
|
||||
|
||||
if (!router)
|
||||
return 0;
|
||||
|
||||
sd_ndisc_redirect *rd;
|
||||
SET_FOREACH(rd, link->ndisc_redirects) {
|
||||
struct in6_addr sender;
|
||||
|
||||
r = sd_ndisc_redirect_get_sender_address(rd, &sender);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!in6_addr_equal(&sender, router))
|
||||
continue;
|
||||
|
||||
if (remove) {
|
||||
r = ndisc_remove_redirect_route(link, rd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
sd_ndisc_redirect_unref(set_remove(link->ndisc_redirects, rd));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_update_redirect_sender(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(original_address);
|
||||
assert(current_address);
|
||||
|
||||
sd_ndisc_redirect *rd;
|
||||
SET_FOREACH(rd, link->ndisc_redirects) {
|
||||
struct in6_addr sender;
|
||||
|
||||
r = sd_ndisc_redirect_get_sender_address(rd, &sender);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!in6_addr_equal(&sender, original_address))
|
||||
continue;
|
||||
|
||||
r = sd_ndisc_redirect_set_sender_address(rd, current_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
struct in6_addr gateway;
|
||||
|
@ -1534,6 +1820,10 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
|
|||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
|
||||
|
||||
r = ndisc_drop_redirect(link, router, /* remove = */ false);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated Redirect messages, ignoring: %m"));
|
||||
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
|
@ -1744,6 +2034,7 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
|
|||
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
||||
struct in6_addr router;
|
||||
usec_t timestamp_usec;
|
||||
bool is_default;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
@ -1784,6 +2075,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
|||
r = ndisc_remember_default_router(link, rt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
is_default = r;
|
||||
|
||||
r = ndisc_start_dhcp6_client(link, rt);
|
||||
if (r < 0)
|
||||
|
@ -1813,6 +2105,17 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (is_default) {
|
||||
r = ndisc_router_update_redirect(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (sd_ndisc_router_get_lifetime(rt, NULL) <= 0) {
|
||||
r = ndisc_drop_redirect(link, &router, /* remove = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->ndisc_messages == 0)
|
||||
link->ndisc_configured = true;
|
||||
else
|
||||
|
@ -1875,6 +2178,10 @@ static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *n
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ndisc_update_redirect_sender(link, &original_address, ¤t_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
Route *route;
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
|
@ -1979,6 +2286,15 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, v
|
|||
}
|
||||
break;
|
||||
|
||||
case SD_NDISC_EVENT_REDIRECT:
|
||||
r = ndisc_redirect_handler(link, ASSERT_PTR(message));
|
||||
if (r < 0 && r != -EBADMSG) {
|
||||
log_link_warning_errno(link, r, "Failed to process Redirect message: %m");
|
||||
link_enter_failed(link);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case SD_NDISC_EVENT_TIMEOUT:
|
||||
log_link_debug(link, "NDisc handler get timeout event");
|
||||
if (link->ndisc_messages == 0) {
|
||||
|
@ -2114,6 +2430,7 @@ void ndisc_flush(Link *link) {
|
|||
link->ndisc_dnssl = set_free(link->ndisc_dnssl);
|
||||
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
|
||||
link->ndisc_pref64 = set_free(link->ndisc_pref64);
|
||||
link->ndisc_redirects = set_free(link->ndisc_redirects);
|
||||
}
|
||||
|
||||
static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
|
||||
|
|
|
@ -292,6 +292,7 @@ DHCPv6.RapidCommit, config_parse_bool,
|
|||
DHCPv6.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp6_netlabel)
|
||||
DHCPv6.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp6_send_release)
|
||||
DHCPv6.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp6_nft_set_context)
|
||||
IPv6AcceptRA.UseRedirect, config_parse_bool, 0, offsetof(Network, ndisc_use_redirect)
|
||||
IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ndisc_use_gateway)
|
||||
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_route_prefix)
|
||||
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_autonomous_prefix)
|
||||
|
|
|
@ -474,6 +474,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
|||
.ipv4_rp_filter = _IP_REVERSE_PATH_FILTER_INVALID,
|
||||
|
||||
.ndisc = -1,
|
||||
.ndisc_use_redirect = true,
|
||||
.ndisc_use_dns = true,
|
||||
.ndisc_use_gateway = true,
|
||||
.ndisc_use_captive_portal = true,
|
||||
|
|
|
@ -337,6 +337,7 @@ struct Network {
|
|||
|
||||
/* NDisc support */
|
||||
int ndisc;
|
||||
bool ndisc_use_redirect;
|
||||
bool ndisc_use_dns;
|
||||
bool ndisc_use_gateway;
|
||||
bool ndisc_use_route_prefix;
|
||||
|
|
Loading…
Reference in a new issue