]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: Add L3MasterDevice= into routing policy
authorNick Cao <nickcao@nichi.co>
Wed, 17 Jan 2024 21:28:15 +0000 (16:28 -0500)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 19 Jan 2024 00:17:50 +0000 (00:17 +0000)
man/systemd.network.xml
src/network/networkd-network-gperf.gperf
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
test/fuzz/fuzz-unit-file/directives-all.service
test/test-network/conf/25-fibrule-l3mdev.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index 35c897af398efdcf94091db15e64bc7a9669fe6a..ef4a0fd4305278ea578aa24839a906e54f183bea 100644 (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>
index fd1f26c79803c015d4a9abdb3b5062d9b9ba725e..f0650a0c8869f8995544986bc366093579270e05 100644 (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
index 914e288aeccca924ffac3fc9a2dc957b94cb0d89..e8e154dd60671f42ad88062a82e82679fe30400a 100644 (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;
 }
 
index b6ce2fa19c42ecffce97de0f74cf1ff51a873d9b..85016bbc8a3cc8e70a8f03f079e40050e7aa2408 100644 (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);
index 93307c0bbd1d899f52d1839dec16594f76885791..18d9c3b30f651553222837b7884be7ca9d7e5ac3 100644 (file)
@@ -496,6 +496,7 @@ KernelVersion=
 Key=
 Kind=
 L2MissNotification=
+L3MasterDevice=
 L3MissNotification=
 LACPTransmitRate=
 LLDP=
diff --git a/test/test-network/conf/25-fibrule-l3mdev.network b/test/test-network/conf/25-fibrule-l3mdev.network
new file mode 100644 (file)
index 0000000..a1afcd2
--- /dev/null
@@ -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
index 7bf2f5014530413d9e01c03dfd2b00046694b6c2..3bb0166f4eee6304f3c82efe4617f7d224f56c8c 100755 (executable)
@@ -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')