network: Add L3MasterDevice= into routing policy

This commit is contained in:
Nick Cao 2024-01-17 16:28:15 -05:00 committed by Luca Boccassi
parent 12af3f1834
commit 4be1fc8443
7 changed files with 112 additions and 20 deletions

View file

@ -1629,6 +1629,18 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>L3MasterDevice=</varname></term>
<listitem>
<para>A boolean. Specifies whether the rule is to direct lookups to the tables associated with
level 3 master devices (also known as Virtual Routing and Forwarding or VRF devices).
For further details see <ulink url="https://docs.kernel.org/networking/vrf.html">
Virtual Routing and Forwarding (VRF)</ulink>. Defaults to false.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>SourcePort=</varname></term>
<listitem>
@ -1951,7 +1963,7 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
<command>ip route show table <replaceable>num</replaceable></command>. If unset and
<varname>Type=</varname> is <literal>local</literal>, <literal>broadcast</literal>,
<literal>anycast</literal>, or <literal>nat</literal>, then <literal>local</literal> is used.
In other cases, defaults to <literal>main</literal>.</para>
In other cases, defaults to <literal>main</literal>. Ignored if <varname>L3MasterDevice=</varname> is true.</para>
<xi:include href="version-info.xml" xpointer="v230"/>
</listitem>

View file

@ -180,6 +180,7 @@ RoutingPolicyRule.IPProtocol, config_parse_routing_policy_rule_ip
RoutingPolicyRule.SourcePort, config_parse_routing_policy_rule_port_range, 0, 0
RoutingPolicyRule.DestinationPort, config_parse_routing_policy_rule_port_range, 0, 0
RoutingPolicyRule.InvertRule, config_parse_routing_policy_rule_invert, 0, 0
RoutingPolicyRule.L3MasterDevice, config_parse_routing_policy_rule_l3mdev, 0, 0
RoutingPolicyRule.Family, config_parse_routing_policy_rule_family, 0, 0
RoutingPolicyRule.User, config_parse_routing_policy_rule_uid_range, 0, 0
RoutingPolicyRule.SuppressInterfaceGroup, config_parse_routing_policy_rule_suppress_ifgroup, 0, 0

View file

@ -164,6 +164,8 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
in_addr_hash_func(&rule->from, rule->family, state);
siphash24_compress_typesafe(rule->from_prefixlen, state);
siphash24_compress_boolean(rule->l3mdev, state);
in_addr_hash_func(&rule->to, rule->family, state);
siphash24_compress_typesafe(rule->to_prefixlen, state);
@ -212,6 +214,10 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
r = CMP(a->l3mdev, b->l3mdev);
if (r != 0)
return r;
r = CMP(a->to_prefixlen, b->to_prefixlen);
if (r != 0)
return r;
@ -476,19 +482,19 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule
return r;
}
if (rule->table < 256) {
if (rule->l3mdev)
r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
else if (rule->table < 256)
r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
if (r < 0)
return r;
} else {
else {
r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
if (r < 0)
return r;
}
if (r < 0)
return r;
if (rule->fwmark > 0) {
r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
@ -544,6 +550,12 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule
return r;
}
if (rule->l3mdev) {
r = sd_netlink_message_append_u8(m, FRA_L3MDEV, 1);
if (r < 0)
return r;
}
if (rule->suppress_prefixlen >= 0) {
r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen);
if (r < 0)
@ -853,20 +865,17 @@ int link_request_static_routing_policy_rules(Link *link) {
static const RoutingPolicyRule kernel_rules[] = {
{ .family = AF_INET, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, },
{ .family = AF_INET, .priority_set = true, .priority = 1000, .table = RT_TABLE_UNSPEC, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, .l3mdev = true },
{ .family = AF_INET, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, },
{ .family = AF_INET, .priority_set = true, .priority = 32767, .table = RT_TABLE_DEFAULT, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, },
{ .family = AF_INET6, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, },
{ .family = AF_INET6, .priority_set = true, .priority = 1000, .table = RT_TABLE_UNSPEC, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, .l3mdev = true },
{ .family = AF_INET6, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, },
};
static bool routing_policy_rule_is_created_by_kernel(const RoutingPolicyRule *rule) {
assert(rule);
if (rule->l3mdev > 0)
/* Currently, [RoutingPolicyRule] does not explicitly set FRA_L3MDEV. So, if the flag
* is set, it is safe to treat the rule as created by kernel. */
return true;
for (size_t i = 0; i < ELEMENTSOF(kernel_rules); i++)
if (routing_policy_rule_equal(rule, &kernel_rules[i]))
return true;
@ -1016,11 +1025,13 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
return 0;
}
r = sd_netlink_message_read_u8(message, FRA_L3MDEV, &tmp->l3mdev);
uint8_t l3mdev = 0;
r = sd_netlink_message_read_u8(message, FRA_L3MDEV, &l3mdev);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_L3MDEV attribute, ignoring: %m");
return 0;
}
tmp->l3mdev = l3mdev != 0;
r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport);
if (r < 0 && r != -ENODATA) {
@ -1502,6 +1513,44 @@ int config_parse_routing_policy_rule_invert(
return 0;
}
int config_parse_routing_policy_rule_l3mdev(
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) {
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = routing_policy_rule_new_static(network, filename, section_line, &n);
if (r < 0)
return log_oom();
r = parse_boolean(rvalue);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule l3mdev, ignoring: %s", rvalue);
return 0;
}
n->l3mdev = r;
TAKE_PTR(n);
return 0;
}
int config_parse_routing_policy_rule_family(
const char *unit,
const char *filename,
@ -1734,12 +1783,6 @@ static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) {
/* rule->family can be AF_UNSPEC only when Family=both. */
}
/* Currently, [RoutingPolicyRule] does not have a setting to set FRA_L3MDEV flag. Please also
* update routing_policy_rule_is_created_by_kernel() when a new setting which sets the flag is
* added in the future. */
if (rule->l3mdev > 0)
assert_not_reached();
return 0;
}

View file

@ -22,6 +22,7 @@ typedef struct RoutingPolicyRule {
bool invert_rule;
bool priority_set;
bool l3mdev; /* FRA_L3MDEV */
uint8_t tos;
uint8_t type;
@ -29,7 +30,6 @@ typedef struct RoutingPolicyRule {
uint8_t protocol; /* FRA_PROTOCOL */
uint8_t to_prefixlen;
uint8_t from_prefixlen;
uint8_t l3mdev; /* FRA_L3MDEV */
uint32_t table;
uint32_t fwmark;
@ -80,6 +80,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_fwmark_mask);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_priority);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_device);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_l3mdev);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_port_range);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_ip_protocol);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_invert);

View file

@ -496,6 +496,7 @@ KernelVersion=
Key=
Kind=
L2MissNotification=
L3MasterDevice=
L3MissNotification=
LACPTransmitRate=
LLDP=

View file

@ -0,0 +1,15 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
[Network]
IPv6AcceptRA=no
[RoutingPolicyRule]
Priority=1500
L3MasterDevice=true
[RoutingPolicyRule]
Priority=2000
L3MasterDevice=true
Type=unreachable

View file

@ -199,6 +199,14 @@ def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
return f
def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
def f(func):
rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev')
call_quiet('ip rule del not from 192.168.100.19 l3mdev')
return func if rc == 0 else unittest.expectedFailure(func)
return f
def expectedFailureIfNexthopIsNotAvailable():
def f(func):
rc = call_quiet('ip nexthop list')
@ -3106,6 +3114,17 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'tcp')
self.assertRegex(output, 'lookup 7')
@expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
def test_routing_policy_rule_l3mdev(self):
copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
start_networkd()
self.wait_online(['test1:degraded'])
output = check_output('ip rule')
print(output)
self.assertIn('1500: from all lookup [l3mdev-table]', output)
self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output)
@expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
def test_routing_policy_rule_uidrange(self):
copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')