mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
Merge pull request #25166 from yuwata/network-router-preference
network: adjust route metric based on router preference
This commit is contained in:
@@ -2518,8 +2518,12 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
|
||||
<varlistentry>
|
||||
<term><varname>RouteMetric=</varname></term>
|
||||
<listitem>
|
||||
<para>Set the routing metric for the routes received in the Router Advertisement. Takes an
|
||||
unsigned integer in the range 0…4294967295. Defaults to 1024.</para>
|
||||
<para>Set the routing metric for the routes received in the Router Advertisement. Takes an unsigned
|
||||
integer in the range 0…4294967295, or three unsigned integer separated with <literal>:</literal>,
|
||||
in that case the first one is used when the router preference is high, the second is for medium
|
||||
preference, and the last is for low preference
|
||||
(<literal><replaceable>high</replaceable>:<replaceable>medium</replaceable>:<replaceable>low</replaceable></literal>).
|
||||
Defaults to <literal>512:1024:2048</literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ int config_parse_dhcp(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dhcp_or_ra_route_metric(
|
||||
int config_parse_dhcp_route_metric(
|
||||
const char* unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
@@ -322,7 +322,7 @@ int config_parse_dhcp_or_ra_route_metric(
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
|
||||
assert(IN_SET(ltype, AF_UNSPEC, AF_INET));
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
@@ -338,16 +338,15 @@ int config_parse_dhcp_or_ra_route_metric(
|
||||
network->dhcp_route_metric = metric;
|
||||
network->dhcp_route_metric_set = true;
|
||||
break;
|
||||
case AF_INET6:
|
||||
network->ipv6_accept_ra_route_metric = metric;
|
||||
network->ipv6_accept_ra_route_metric_set = true;
|
||||
break;
|
||||
case AF_UNSPEC:
|
||||
/* For backward compatibility. */
|
||||
if (!network->dhcp_route_metric_set)
|
||||
network->dhcp_route_metric = metric;
|
||||
if (!network->ipv6_accept_ra_route_metric_set)
|
||||
network->ipv6_accept_ra_route_metric = metric;
|
||||
if (!network->ipv6_accept_ra_route_metric_set) {
|
||||
network->ipv6_accept_ra_route_metric_high = metric;
|
||||
network->ipv6_accept_ra_route_metric_medium = metric;
|
||||
network->ipv6_accept_ra_route_metric_low = metric;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
@@ -356,6 +355,64 @@ int config_parse_dhcp_or_ra_route_metric(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_ipv6_accept_ra_route_metric(
|
||||
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 = ASSERT_PTR(userdata);
|
||||
uint32_t metric_high, metric_medium, metric_low;
|
||||
int r, s, t;
|
||||
|
||||
assert(filename);
|
||||
assert(rvalue);
|
||||
|
||||
if (safe_atou32(rvalue, &metric_low) >= 0)
|
||||
metric_high = metric_medium = metric_low;
|
||||
else {
|
||||
_cleanup_free_ char *high = NULL, *medium = NULL, *low = NULL;
|
||||
const char *p = rvalue;
|
||||
|
||||
r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &high, &medium, &low, NULL);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r != 3 || !isempty(p)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r < 0 ? r : 0,
|
||||
"Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou32(high, &metric_high);
|
||||
s = safe_atou32(medium, &metric_medium);
|
||||
t = safe_atou32(low, &metric_low);
|
||||
if (r < 0 || s < 0 || t < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r < 0 ? r : s < 0 ? s : t,
|
||||
"Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (metric_high >= metric_medium || metric_medium >= metric_low) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Invalid RouteTable=%s, ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
network->ipv6_accept_ra_route_metric_high = metric_high;
|
||||
network->ipv6_accept_ra_route_metric_medium = metric_medium;
|
||||
network->ipv6_accept_ra_route_metric_low = metric_low;
|
||||
network->ipv6_accept_ra_route_metric_set = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dhcp_use_dns(
|
||||
const char* unit,
|
||||
const char *filename,
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#define UPLINK_INDEX_SELF -2 /* the interface itself is uplink */
|
||||
|
||||
#define DHCP_ROUTE_METRIC 1024
|
||||
#define IPV6RA_ROUTE_METRIC_HIGH 512
|
||||
#define IPV6RA_ROUTE_METRIC_MEDIUM 1024
|
||||
#define IPV6RA_ROUTE_METRIC_LOW 2048
|
||||
#define DHCP6PD_ROUTE_METRIC 256
|
||||
|
||||
typedef struct Link Link;
|
||||
@@ -89,7 +92,8 @@ const char *dhcp_option_data_type_to_string(DHCPOptionDataType d) _const_;
|
||||
DHCPOptionDataType dhcp_option_data_type_from_string(const char *d) _pure_;
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_metric);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_metric);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_route_metric);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
|
||||
|
||||
@@ -143,6 +143,28 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ndisc_set_route_priority(Link *link, Route *route) {
|
||||
assert(link);
|
||||
assert(route);
|
||||
|
||||
if (route->priority_set)
|
||||
return; /* explicitly configured. */
|
||||
|
||||
switch (route->pref) {
|
||||
case SD_NDISC_PREFERENCE_LOW:
|
||||
route->priority = link->network->ipv6_accept_ra_route_metric_low;
|
||||
break;
|
||||
case SD_NDISC_PREFERENCE_MEDIUM:
|
||||
route->priority = link->network->ipv6_accept_ra_route_metric_medium;
|
||||
break;
|
||||
case SD_NDISC_PREFERENCE_HIGH:
|
||||
route->priority = link->network->ipv6_accept_ra_route_metric_high;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_(route_freep) Route *route = in;
|
||||
struct in6_addr router;
|
||||
@@ -160,8 +182,7 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
|
||||
route->provider.in6 = router;
|
||||
if (!route->table_set)
|
||||
route->table = link_get_ipv6_accept_ra_route_table(link);
|
||||
if (!route->priority_set)
|
||||
route->priority = link->network->ipv6_accept_ra_route_metric;
|
||||
ndisc_set_route_priority(link, route);
|
||||
if (!route->protocol_set)
|
||||
route->protocol = RTPROT_RA;
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ DHCPv4.UserClass, config_parse_dhcp_user_or_vendor_cl
|
||||
DHCPv4.IAID, config_parse_iaid, AF_INET, 0
|
||||
DHCPv4.DUIDType, config_parse_network_duid_type, 0, 0
|
||||
DHCPv4.DUIDRawData, config_parse_network_duid_rawdata, 0, 0
|
||||
DHCPv4.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET, 0
|
||||
DHCPv4.RouteMetric, config_parse_dhcp_route_metric, AF_INET, 0
|
||||
DHCPv4.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET, 0
|
||||
DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
|
||||
DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
|
||||
@@ -277,7 +277,7 @@ IPv6AcceptRA.UseDomains, config_parse_ipv6_accept_ra_use_dom
|
||||
IPv6AcceptRA.UseMTU, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_mtu)
|
||||
IPv6AcceptRA.DHCPv6Client, config_parse_ipv6_accept_ra_start_dhcp6_client, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
|
||||
IPv6AcceptRA.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET6, 0
|
||||
IPv6AcceptRA.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0
|
||||
IPv6AcceptRA.RouteMetric, config_parse_ipv6_accept_ra_route_metric, 0, 0
|
||||
IPv6AcceptRA.RouterAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_router)
|
||||
IPv6AcceptRA.RouterDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_router)
|
||||
IPv6AcceptRA.PrefixAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_prefix)
|
||||
@@ -559,7 +559,7 @@ DHCP.UserClass, config_parse_dhcp_user_or_vendor_cl
|
||||
DHCP.IAID, config_parse_iaid, AF_INET, 0
|
||||
DHCP.DUIDType, config_parse_network_duid_type, 0, 0
|
||||
DHCP.DUIDRawData, config_parse_network_duid_rawdata, 0, 0
|
||||
DHCP.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_UNSPEC, 0
|
||||
DHCP.RouteMetric, config_parse_dhcp_route_metric, AF_UNSPEC, 0
|
||||
DHCP.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET, 0
|
||||
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
|
||||
DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
|
||||
@@ -567,7 +567,7 @@ DHCP.RapidCommit, config_parse_bool,
|
||||
DHCP.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0
|
||||
DHCPv4.UseDomainName, config_parse_dhcp_use_domains, AF_INET, 0
|
||||
DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
|
||||
DHCPv6.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0
|
||||
DHCPv6.RouteMetric, config_parse_ipv6_accept_ra_route_metric, AF_INET6, 0
|
||||
DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0
|
||||
DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id)
|
||||
DHCPv6PrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce)
|
||||
|
||||
@@ -481,7 +481,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
.ipv6_accept_ra_use_onlink_prefix = true,
|
||||
.ipv6_accept_ra_use_mtu = true,
|
||||
.ipv6_accept_ra_route_table = RT_TABLE_MAIN,
|
||||
.ipv6_accept_ra_route_metric = DHCP_ROUTE_METRIC,
|
||||
.ipv6_accept_ra_route_metric_high = IPV6RA_ROUTE_METRIC_HIGH,
|
||||
.ipv6_accept_ra_route_metric_medium = IPV6RA_ROUTE_METRIC_MEDIUM,
|
||||
.ipv6_accept_ra_route_metric_low = IPV6RA_ROUTE_METRIC_LOW,
|
||||
.ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
|
||||
|
||||
.can_termination = -1,
|
||||
|
||||
@@ -315,7 +315,9 @@ struct Network {
|
||||
IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client;
|
||||
uint32_t ipv6_accept_ra_route_table;
|
||||
bool ipv6_accept_ra_route_table_set;
|
||||
uint32_t ipv6_accept_ra_route_metric;
|
||||
uint32_t ipv6_accept_ra_route_metric_high;
|
||||
uint32_t ipv6_accept_ra_route_metric_medium;
|
||||
uint32_t ipv6_accept_ra_route_metric_low;
|
||||
bool ipv6_accept_ra_route_metric_set;
|
||||
Set *ndisc_deny_listed_router;
|
||||
Set *ndisc_allow_listed_router;
|
||||
|
||||
10
test/test-network/conf/25-veth-bridge.network
Normal file
10
test/test-network/conf/25-veth-bridge.network
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=client-p
|
||||
Name=router-high-p
|
||||
Name=router-low-p
|
||||
|
||||
[Network]
|
||||
Bridge=bridge99
|
||||
IPv6AcceptRA=no
|
||||
IPv6SendRA=yes
|
||||
9
test/test-network/conf/25-veth-client.netdev
Normal file
9
test/test-network/conf/25-veth-client.netdev
Normal file
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[NetDev]
|
||||
Name=client
|
||||
Kind=veth
|
||||
MACAddress=12:34:56:78:9a:00
|
||||
|
||||
[Peer]
|
||||
Name=client-p
|
||||
MACAddress=12:34:56:78:9b:00
|
||||
10
test/test-network/conf/25-veth-client.network
Normal file
10
test/test-network/conf/25-veth-client.network
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=client
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=yes
|
||||
|
||||
[IPv6AcceptRA]
|
||||
UseDNS=no
|
||||
UseDomains=no
|
||||
9
test/test-network/conf/25-veth-router-high.netdev
Normal file
9
test/test-network/conf/25-veth-router-high.netdev
Normal file
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[NetDev]
|
||||
Name=router-high
|
||||
Kind=veth
|
||||
MACAddress=12:34:56:78:9a:99
|
||||
|
||||
[Peer]
|
||||
Name=router-high-p
|
||||
MACAddress=12:34:56:78:9b:99
|
||||
17
test/test-network/conf/25-veth-router-high.network
Normal file
17
test/test-network/conf/25-veth-router-high.network
Normal file
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=router-high
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
IPv6SendRA=yes
|
||||
|
||||
[IPv6SendRA]
|
||||
RouterPreference=high
|
||||
EmitDNS=no
|
||||
EmitDomains=no
|
||||
|
||||
[IPv6Prefix]
|
||||
Prefix=2002:da8:1:99::/64
|
||||
PreferredLifetimeSec=1000s
|
||||
ValidLifetimeSec=2100s
|
||||
9
test/test-network/conf/25-veth-router-low.netdev
Normal file
9
test/test-network/conf/25-veth-router-low.netdev
Normal file
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[NetDev]
|
||||
Name=router-low
|
||||
Kind=veth
|
||||
MACAddress=12:34:56:78:9a:98
|
||||
|
||||
[Peer]
|
||||
Name=router-low-p
|
||||
MACAddress=12:34:56:78:9b:98
|
||||
17
test/test-network/conf/25-veth-router-low.network
Normal file
17
test/test-network/conf/25-veth-router-low.network
Normal file
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=router-low
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
IPv6SendRA=yes
|
||||
|
||||
[IPv6SendRA]
|
||||
RouterPreference=low
|
||||
EmitDNS=no
|
||||
EmitDomains=no
|
||||
|
||||
[IPv6Prefix]
|
||||
Prefix=2002:da8:1:98::/64
|
||||
PreferredLifetimeSec=1000s
|
||||
ValidLifetimeSec=2100s
|
||||
@@ -907,6 +907,16 @@ class Utilities():
|
||||
|
||||
self.assertNotRegex(output, address_regex)
|
||||
|
||||
def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100):
|
||||
for i in range(timeout_sec):
|
||||
if i > 0:
|
||||
time.sleep(1)
|
||||
output = check_output(f'ip {ipv} route show dev {link} table {table}')
|
||||
if re.search(route_regex, output):
|
||||
break
|
||||
|
||||
self.assertRegex(output, route_regex)
|
||||
|
||||
def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
|
||||
if not shutil.which('selinuxenabled'):
|
||||
print('## Checking NetLabel skipped: selinuxenabled command not found.')
|
||||
@@ -4338,6 +4348,55 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
|
||||
self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
|
||||
|
||||
def test_router_preference(self):
|
||||
copy_network_unit('25-veth-client.netdev',
|
||||
'25-veth-router-high.netdev',
|
||||
'25-veth-router-low.netdev',
|
||||
'26-bridge.netdev',
|
||||
'25-veth-bridge.network',
|
||||
'25-veth-client.network',
|
||||
'25-veth-router-high.network',
|
||||
'25-veth-router-low.network',
|
||||
'25-bridge99.network')
|
||||
start_networkd()
|
||||
self.wait_online(['client-p:enslaved',
|
||||
'router-high:degraded', 'router-high-p:enslaved',
|
||||
'router-low:degraded', 'router-low-p:enslaved',
|
||||
'bridge99:routable'])
|
||||
|
||||
networkctl_reconfigure('client')
|
||||
self.wait_online(['client:routable'])
|
||||
|
||||
self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
|
||||
self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
|
||||
self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv='-6', timeout_sec=10)
|
||||
self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv='-6', timeout_sec=10)
|
||||
|
||||
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
|
||||
print(output)
|
||||
self.assertIn('pref high', output)
|
||||
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
|
||||
print(output)
|
||||
self.assertIn('pref low', output)
|
||||
|
||||
with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
|
||||
f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
|
||||
|
||||
networkctl_reload()
|
||||
self.wait_online(['client:routable'])
|
||||
|
||||
self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
|
||||
self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
|
||||
self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv='-6', timeout_sec=10)
|
||||
self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv='-6', timeout_sec=10)
|
||||
|
||||
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
|
||||
print(output)
|
||||
self.assertIn('pref high', output)
|
||||
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
|
||||
print(output)
|
||||
self.assertIn('pref low', output)
|
||||
|
||||
class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user