summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuca Boccassi <bluca@debian.org>2024-08-21 12:46:37 +0200
committerGitHub <noreply@github.com>2024-08-21 12:46:37 +0200
commitbdf75118bade008b6a465173c02933eb377aef0d (patch)
treeb7b519b1410be6844a65f15699dfe67164852127
parentMerge pull request #34018 from yuwata/network-address-label (diff)
parenttest-network: add test for ManageForeignRoutingPolicyRules= (diff)
downloadsystemd-bdf75118bade008b6a465173c02933eb377aef0d.tar.xz
systemd-bdf75118bade008b6a465173c02933eb377aef0d.zip
Merge pull request #34049 from yuwata/network-routing-policy-rule
network: further rework for routing policy rule
-rw-r--r--man/systemd.network.xml18
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c28
-rw-r--r--src/network/networkd-json.c2
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-queue.c12
-rw-r--r--src/network/networkd-routing-policy-rule.c532
-rw-r--r--src/network/networkd-routing-policy-rule.h10
-rw-r--r--src/systemd/sd-netlink.h1
-rw-r--r--test/test-network/conf/25-routing-policy-rule-test1.network16
-rw-r--r--test/test-network/conf/networkd-manage-foreign-rules-no.conf3
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py98
11 files changed, 536 insertions, 185 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 6f6746b13b..734a4f7c0b 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1729,6 +1729,18 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
</varlistentry>
<varlistentry>
+ <term><varname>GoTo=</varname></term>
+ <listitem>
+ <para>Specifies the target priority used by <literal>goto</literal> type of rule. Takes an integer
+ in the range 1…4294967295. This must be larger than the priority of this rule specified in
+ <varname>Priority=</varname>. When specified, <varname>Type=goto</varname> is implied. This is
+ mandatory when <varname>Type=goto</varname>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>IncomingInterface=</varname></term>
<listitem>
<para>Specifies incoming device to match. If the interface is loopback, the rule only matches
@@ -1853,8 +1865,10 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
<term><varname>Type=</varname></term>
<listitem>
<para>Specifies Routing Policy Database (RPDB) rule type. Takes one of
- <literal>blackhole</literal>, <literal>unreachable</literal> or <literal>prohibit</literal>.
- </para>
+ <literal>table</literal>, <literal>goto</literal>, <literal>nop</literal>,
+ <literal>blackhole</literal>, <literal>unreachable</literal>, or <literal>prohibit</literal>.
+ When <literal>goto</literal>, the target priority must be specified in <varname>GoTo=</varname>.
+ Defaults to <literal>table</literal>.</para>
<xi:include href="version-info.xml" xpointer="v248"/>
</listitem>
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 49d000d47e..25bcbd9640 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -928,6 +928,34 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint3
return 0;
}
+int sd_netlink_message_read_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t *ret) {
+ void *attr_data;
+ bool net_byteorder;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U64);
+ if (r < 0)
+ return r;
+
+ r = netlink_message_read_internal(m, attr_type, &attr_data, &net_byteorder);
+ if (r < 0)
+ return r;
+
+ if ((size_t) r < sizeof(uint64_t))
+ return -EIO;
+
+ if (ret) {
+ if (net_byteorder)
+ *ret = be64toh(*(uint64_t *) attr_data);
+ else
+ *ret = *(uint64_t *) attr_data;
+ }
+
+ return 0;
+}
+
int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *data) {
void *attr_data;
int r;
diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c
index 0a57e6aee0..4eafc62c7e 100644
--- a/src/network/networkd-json.c
+++ b/src/network/networkd-json.c
@@ -308,7 +308,7 @@ static int routing_policy_rule_append_json(RoutingPolicyRule *rule, sd_json_vari
SD_JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
SD_JSON_BUILD_PAIR_UNSIGNED("TOS", rule->tos),
SD_JSON_BUILD_PAIR_UNSIGNED("Type", rule->type),
- SD_JSON_BUILD_PAIR_STRING("TypeString", fr_act_type_full_to_string(rule->type)),
+ SD_JSON_BUILD_PAIR_STRING("TypeString", fr_act_type_to_string(rule->type)),
SD_JSON_BUILD_PAIR_UNSIGNED("IPProtocol", rule->ipproto),
SD_JSON_BUILD_PAIR_STRING("IPProtocolString", ip_protocol_to_name(rule->ipproto)),
SD_JSON_BUILD_PAIR_UNSIGNED("Priority", rule->priority),
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index aa849fe535..b2794f9efd 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -178,6 +178,7 @@ Neighbor.LinkLayerAddress, config_parse_neighbor_lladdr,
Neighbor.MACAddress, config_parse_neighbor_lladdr, 0, 0 /* deprecated */
RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0
RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0
+RoutingPolicyRule.GoTo, config_parse_routing_policy_rule_goto, 0, 0
RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0
RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0
RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0
diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c
index 7943ab5fb0..dcb9bd0549 100644
--- a/src/network/networkd-queue.c
+++ b/src/network/networkd-queue.c
@@ -58,7 +58,11 @@ static void request_hash_func(const Request *req, struct siphash *state) {
siphash24_compress_typesafe(req->type, state);
- if (!IN_SET(req->type, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_ROUTE)) {
+ if (!IN_SET(req->type,
+ REQUEST_TYPE_NEXTHOP,
+ REQUEST_TYPE_ROUTE,
+ REQUEST_TYPE_ROUTING_POLICY_RULE)) {
+
siphash24_compress_boolean(req->link, state);
if (req->link)
siphash24_compress_typesafe(req->link->ifindex, state);
@@ -81,7 +85,11 @@ static int request_compare_func(const struct Request *a, const struct Request *b
if (r != 0)
return r;
- if (!IN_SET(a->type, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_ROUTE)) {
+ if (!IN_SET(a->type,
+ REQUEST_TYPE_NEXTHOP,
+ REQUEST_TYPE_ROUTE,
+ REQUEST_TYPE_ROUTING_POLICY_RULE)) {
+
r = CMP(!!a->link, !!b->link);
if (r != 0)
return r;
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index 8e7cc47118..44d85d000f 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -25,12 +25,6 @@
#include "user-util.h"
static const char *const fr_act_type_table[__FR_ACT_MAX] = {
- [FR_ACT_BLACKHOLE] = "blackhole",
- [FR_ACT_UNREACHABLE] = "unreachable",
- [FR_ACT_PROHIBIT] = "prohibit",
-};
-
-static const char *const fr_act_type_full_table[__FR_ACT_MAX] = {
[FR_ACT_TO_TBL] = "table",
[FR_ACT_GOTO] = "goto",
[FR_ACT_NOP] = "nop",
@@ -40,8 +34,7 @@ static const char *const fr_act_type_full_table[__FR_ACT_MAX] = {
};
assert_cc(__FR_ACT_MAX <= UINT8_MAX);
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fr_act_type, int);
-DEFINE_STRING_TABLE_LOOKUP_TO_STRING(fr_act_type_full, int);
+DEFINE_STRING_TABLE_LOOKUP(fr_act_type, int);
static RoutingPolicyRule* routing_policy_rule_detach_impl(RoutingPolicyRule *rule) {
assert(rule);
@@ -162,7 +155,7 @@ static int routing_policy_rule_new_static(Network *network, const char *filename
return 0;
}
-static int routing_policy_rule_dup(const RoutingPolicyRule *src, RoutingPolicyRule **ret) {
+static int routing_policy_rule_dup(const RoutingPolicyRule *src, int family, RoutingPolicyRule **ret) {
_cleanup_(routing_policy_rule_unrefp) RoutingPolicyRule *dest = NULL;
assert(src);
@@ -179,6 +172,9 @@ static int routing_policy_rule_dup(const RoutingPolicyRule *src, RoutingPolicyRu
dest->section = NULL;
dest->iif = dest->oif = NULL;
+ /* Set family. */
+ dest->family = family;
+
if (src->iif) {
dest->iif = strdup(src->iif);
if (!dest->iif)
@@ -209,7 +205,7 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
siphash24_compress_typesafe(rule->suppress_ifgroup, state);
siphash24_compress_typesafe(rule->suppress_prefixlen, state);
siphash24_compress_typesafe(rule->fwmask, state);
- /* FRA_TUN_ID */
+ siphash24_compress_typesafe(rule->tunnel_id, state);
/* fr_net (network namespace) */
siphash24_compress_typesafe(rule->l3mdev, state);
siphash24_compress_typesafe(rule->uid_range, state);
@@ -222,20 +218,22 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
siphash24_compress_typesafe(rule->from_prefixlen, state);
siphash24_compress_typesafe(rule->to_prefixlen, state);
siphash24_compress_typesafe(rule->tos, state);
- /* FRA_FLOW (IPv4 only) */
+ siphash24_compress_typesafe(rule->realms, state);
in_addr_hash_func(&rule->from, rule->family, state);
in_addr_hash_func(&rule->to, rule->family, state);
}
-static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) {
+static int routing_policy_rule_compare_func_full(const RoutingPolicyRule *a, const RoutingPolicyRule *b, bool all) {
int r;
assert(a);
assert(b);
- r = CMP(a->family, b->family);
- if (r != 0)
- return r;
+ if (all) {
+ r = CMP(a->family, b->family);
+ if (r != 0)
+ return r;
+ }
r = CMP(a->type, b->type);
if (r != 0)
@@ -245,9 +243,11 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
- r = CMP(a->priority, b->priority);
- if (r != 0)
- return r;
+ if (all) {
+ r = CMP(a->priority, b->priority);
+ if (r != 0)
+ return r;
+ }
r = strcmp_ptr(a->iif, b->iif);
if (r != 0)
@@ -273,6 +273,10 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
+ r = CMP(a->tunnel_id, b->tunnel_id);
+ if (r != 0)
+ return r;
+
r = CMP(a->l3mdev, b->l3mdev);
if (r != 0)
return r;
@@ -309,60 +313,124 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
- r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
+ r = CMP(a->realms, b->realms);
if (r != 0)
return r;
- r = memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
- if (r != 0)
- return r;
+ if (all) {
+ r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+ }
return 0;
}
-static bool routing_policy_rule_equal(const RoutingPolicyRule *rule1, const RoutingPolicyRule *rule2) {
- if (rule1 == rule2)
- return true;
+static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) {
+ return routing_policy_rule_compare_func_full(a, b, /* all = */ true);
+}
+
+static bool routing_policy_rule_equal(const RoutingPolicyRule *a, const RoutingPolicyRule *b, int family, uint32_t priority) {
+ assert(a);
+ assert(b);
- if (!rule1 || !rule2)
+ if (a->family != AF_UNSPEC && a->family != family)
+ return false;
+ if (b->family != AF_UNSPEC && b->family != family)
return false;
- return routing_policy_rule_compare_func(rule1, rule2) == 0;
+ if (a->priority_set && a->priority != priority)
+ return false;
+ if (b->priority_set && b->priority != priority)
+ return false;
+
+ return routing_policy_rule_compare_func_full(a, b, /* all = */ false) == 0;
}
-static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) {
+static bool routing_policy_rule_can_update(const RoutingPolicyRule *existing, const RoutingPolicyRule *requesting, int family) {
+ assert(existing);
+ assert(requesting);
+
+ if (!routing_policy_rule_equal(existing, requesting, family, existing->priority))
+ return false;
+
+ /* These flags cannot be updated. */
+ if ((existing->flags ^ requesting->flags) & (FIB_RULE_PERMANENT|FIB_RULE_INVERT))
+ return false;
+
+ /* GOTO target cannot be updated. */
+ if (existing->type == FR_ACT_GOTO && existing->priority_goto != requesting->priority_goto)
+ return false;
+
+ return true;
+}
+
+static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *in, int family, RoutingPolicyRule **ret) {
RoutingPolicyRule *rule;
assert(m);
assert(in);
+ assert(in->family == AF_UNSPEC || in->family == family);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (in->priority_set && in->family != AF_UNSPEC) {
+ rule = set_get(m->rules, in);
+ if (!rule)
+ return -ENOENT;
- rule = set_get(m->rules, in);
- if (rule) {
if (ret)
*ret = rule;
return 0;
}
- if (in->priority_set)
- return -ENOENT;
+ SET_FOREACH(rule, m->rules)
+ if (routing_policy_rule_equal(in, rule, family, rule->priority)) {
+ if (ret)
+ *ret = rule;
+ return 0;
+ }
- /* Also find rules configured without priority. */
- SET_FOREACH(rule, m->rules) {
- uint32_t priority;
- bool found;
+ return -ENOENT;
+}
- if (rule->priority_set)
- /* The rule is configured with priority. */
- continue;
+static int routing_policy_rule_get_request(Manager *m, const RoutingPolicyRule *in, int family, Request **ret) {
+ Request *req;
- priority = rule->priority;
- rule->priority = 0;
- found = routing_policy_rule_equal(rule, in);
- rule->priority = priority;
+ assert(m);
+ assert(in);
+ assert(in->family == AF_UNSPEC || in->family == family);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (in->priority_set && in->family != AF_UNSPEC) {
+ req = ordered_set_get(
+ m->request_queue,
+ &(Request) {
+ .type = REQUEST_TYPE_ROUTING_POLICY_RULE,
+ .userdata = (void*) in,
+ .hash_func = (hash_func_t) routing_policy_rule_hash_func,
+ .compare_func = (compare_func_t) routing_policy_rule_compare_func,
+ });
+ if (!req)
+ return -ENOENT;
- if (found) {
+ if (ret)
+ *ret = req;
+ return 0;
+ }
+
+ ORDERED_SET_FOREACH(req, m->request_queue) {
+
+ if (req->type != REQUEST_TYPE_ROUTING_POLICY_RULE)
+ continue;
+
+ RoutingPolicyRule *rule = ASSERT_PTR(req->userdata);
+ if (routing_policy_rule_equal(in, rule, family, rule->priority)) {
if (ret)
- *ret = rule;
+ *ret = req;
return 0;
}
}
@@ -416,6 +484,21 @@ static int routing_policy_rule_acquire_priority(Manager *manager, RoutingPolicyR
return r;
}
+ Request *req;
+ ORDERED_SET_FOREACH(req, manager->request_queue) {
+ if (req->type != REQUEST_TYPE_ROUTING_POLICY_RULE)
+ continue;
+
+ tmp = ASSERT_PTR(req->userdata);
+ if (tmp->family != rule->family)
+ continue;
+ if (tmp->priority == 0 || tmp->priority > 32765)
+ continue;
+ r = set_ensure_put(&priorities, NULL, UINT32_TO_PTR(tmp->priority));
+ if (r < 0)
+ return r;
+ }
+
ORDERED_HASHMAP_FOREACH(network, manager->networks)
HASHMAP_FOREACH(tmp, network->rules_by_section) {
if (tmp->family != AF_UNSPEC && tmp->family != rule->family)
@@ -429,11 +512,13 @@ static int routing_policy_rule_acquire_priority(Manager *manager, RoutingPolicyR
return r;
}
- for (priority = 32765; priority > 0; priority--)
+ /* priority must be smaller than goto target */
+ for (priority = rule->type == FR_ACT_GOTO ? rule->priority_goto - 1 : 32765; priority > 0; priority--)
if (!set_contains(priorities, UINT32_TO_PTR(priority)))
break;
rule->priority = priority;
+ rule->priority_set = true;
return 0;
}
@@ -584,33 +669,76 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule
if (r < 0)
return r;
+ if (rule->type == FR_ACT_GOTO) {
+ r = sd_netlink_message_append_u32(m, FRA_GOTO, rule->priority_goto);
+ if (r < 0)
+ return r;
+ }
+
+ if (rule->realms > 0) {
+ r = sd_netlink_message_append_u32(m, FRA_FLOW, rule->realms);
+ if (r < 0)
+ return r;
+ }
+
+ if (rule->tunnel_id > 0) {
+ r = sd_netlink_message_append_u64(m, FRA_TUN_ID, htobe64(rule->tunnel_id));
+ if (r < 0)
+ return r;
+ }
+
return 0;
}
-static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
int r;
assert(m);
+ assert(rreq);
+
+ Manager *manager = ASSERT_PTR(rreq->manager);
+ RoutingPolicyRule *rule = ASSERT_PTR(rreq->userdata);
r = sd_netlink_message_get_errno(m);
- if (r < 0)
- log_message_warning_errno(m, r, "Could not drop routing policy rule");
+ if (r < 0) {
+ log_message_full_errno(m,
+ (r == -ENOENT || !rule->manager) ? LOG_DEBUG : LOG_WARNING,
+ r, "Could not drop routing policy rule, ignoring");
+
+ if (rule->manager) {
+ /* If the rule cannot be removed, then assume the rule is already removed. */
+ log_routing_policy_rule_debug(rule, "Forgetting", NULL, manager);
+
+ Request *req;
+ if (routing_policy_rule_get_request(manager, rule, rule->family, &req) >= 0)
+ routing_policy_rule_enter_removed(req->userdata);
+
+ routing_policy_rule_detach(rule);
+ }
+ }
return 1;
}
-static int routing_policy_rule_remove(RoutingPolicyRule *rule) {
+static int routing_policy_rule_remove(RoutingPolicyRule *rule, Manager *manager) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(rule);
- assert(rule->manager);
- assert(rule->manager->rtnl);
assert(IN_SET(rule->family, AF_INET, AF_INET6));
+ assert(manager);
+ assert(manager->rtnl);
+
+ /* If the rule is remembered, then use the remembered object. */
+ (void) routing_policy_rule_get(manager, rule, rule->family, &rule);
+
+ /* We cannot remove rules with the permanent flag. */
+ if (FLAGS_SET(rule->flags, FIB_RULE_PERMANENT))
+ return 0;
- log_routing_policy_rule_debug(rule, "Removing", NULL, rule->manager);
+ log_routing_policy_rule_debug(rule, "Removing", NULL, manager);
- r = sd_rtnl_message_new_routing_policy_rule(rule->manager->rtnl, &m, RTM_DELRULE, rule->family);
+ r = sd_rtnl_message_new_routing_policy_rule(manager->rtnl, &m, RTM_DELRULE, rule->family);
if (r < 0)
return log_warning_errno(r, "Could not allocate netlink message: %m");
@@ -618,11 +746,10 @@ static int routing_policy_rule_remove(RoutingPolicyRule *rule) {
if (r < 0)
return log_warning_errno(r, "Could not create netlink message: %m");
- r = netlink_call_async(rule->manager->rtnl, NULL, m,
- routing_policy_rule_remove_handler,
- NULL, NULL);
+ r = manager_remove_request_add(manager, rule, routing_policy_rule,
+ manager->rtnl, m, routing_policy_rule_remove_handler);
if (r < 0)
- return log_warning_errno(r, "Could not send netlink message: %m");
+ return log_warning_errno(r, "Could not queue rtnetlink message: %m");
routing_policy_rule_enter_removing(rule);
return 0;
@@ -653,6 +780,23 @@ static int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, Re
return request_call_netlink_async(link->manager->rtnl, m, req);
}
+static void manager_unmark_routing_policy_rule(Manager *m, const RoutingPolicyRule *rule, int family) {
+ RoutingPolicyRule *existing;
+
+ assert(m);
+ assert(rule);
+ assert(rule->family == AF_UNSPEC || rule->family == family);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (routing_policy_rule_get(m, rule, family, &existing) < 0)
+ return;
+
+ if (!routing_policy_rule_can_update(existing, rule, rule->family))
+ return;
+
+ routing_policy_rule_unmark(existing);
+}
+
static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Link *except) {
RoutingPolicyRule *rule;
Link *link;
@@ -685,22 +829,12 @@ static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Li
continue;
HASHMAP_FOREACH(rule, link->network->rules_by_section) {
- RoutingPolicyRule *existing;
-
- if (IN_SET(rule->family, AF_INET, AF_INET6)) {
- if (routing_policy_rule_get(m, rule, &existing) >= 0)
- routing_policy_rule_unmark(existing);
- } else {
- /* The case Family=both. */
- rule->family = AF_INET;
- if (routing_policy_rule_get(m, rule, &existing) >= 0)
- routing_policy_rule_unmark(existing);
-
- rule->family = AF_INET6;
- if (routing_policy_rule_get(m, rule, &existing) >= 0)
- routing_policy_rule_unmark(existing);
-
- rule->family = AF_UNSPEC;
+ if (IN_SET(rule->family, AF_INET, AF_INET6))
+ manager_unmark_routing_policy_rule(m, rule, rule->family);
+ else {
+ assert(rule->address_family == ADDRESS_FAMILY_YES);
+ manager_unmark_routing_policy_rule(m, rule, AF_INET);
+ manager_unmark_routing_policy_rule(m, rule, AF_INET6);
}
}
}
@@ -718,7 +852,7 @@ int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const L
if (!routing_policy_rule_is_marked(rule))
continue;
- RET_GATHER(r, routing_policy_rule_remove(rule));
+ RET_GATHER(r, routing_policy_rule_remove(rule, m));
}
return r;
@@ -741,10 +875,12 @@ void link_foreignize_routing_policy_rules(Link *link) {
}
static int routing_policy_rule_process_request(Request *req, Link *link, RoutingPolicyRule *rule) {
+ RoutingPolicyRule *existing;
int r;
assert(req);
assert(link);
+ assert(link->manager);
assert(rule);
if (!link_is_ready_to_configure(link, false))
@@ -755,6 +891,9 @@ static int routing_policy_rule_process_request(Request *req, Link *link, Routing
return log_link_warning_errno(link, r, "Failed to configure routing policy rule: %m");
routing_policy_rule_enter_configuring(rule);
+ if (routing_policy_rule_get(link->manager, rule, rule->family, &existing) >= 0)
+ routing_policy_rule_enter_configuring(existing);
+
return 1;
}
@@ -769,16 +908,24 @@ static int static_routing_policy_rule_configure_handler(
assert(m);
assert(link);
+ assert(rule);
r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
+ if (r == -EEXIST) {
+ RoutingPolicyRule *existing;
+
+ if (routing_policy_rule_get(link->manager, rule, rule->family, &existing) >= 0) {
+ existing->source = rule->source;
+ routing_policy_rule_enter_configured(existing);
+ }
+ } else if (r < 0) {
log_link_message_warning_errno(link, m, r, "Could not add routing policy rule");
link_enter_failed(link);
return 1;
}
if (link->static_routing_policy_rule_messages == 0) {
- log_link_debug(link, "Routing policy rule configured");
+ log_link_debug(link, "Routing policy rule configured.");
link->static_routing_policy_rules_configured = true;
link_check_ready(link);
}
@@ -786,37 +933,44 @@ static int static_routing_policy_rule_configure_handler(
return 1;
}
-static int link_request_routing_policy_rule(Link *link, RoutingPolicyRule *rule) {
- RoutingPolicyRule *existing;
+static int link_request_routing_policy_rule(Link *link, const RoutingPolicyRule *rule, int family) {
+ _cleanup_(routing_policy_rule_unrefp) RoutingPolicyRule *tmp = NULL;
+ RoutingPolicyRule *existing = NULL;
int r;
assert(link);
assert(link->manager);
assert(rule);
assert(rule->source != NETWORK_CONFIG_SOURCE_FOREIGN);
+ assert(rule->family == AF_UNSPEC || rule->family == family);
+ assert(IN_SET(family, AF_INET, AF_INET6));
- if (routing_policy_rule_get(link->manager, rule, &existing) < 0) {
- _cleanup_(routing_policy_rule_unrefp) RoutingPolicyRule *tmp = NULL;
+ if (routing_policy_rule_get_request(link->manager, rule, family, NULL) >= 0)
+ return 0; /* already requested, skipping. */
- r = routing_policy_rule_dup(rule, &tmp);
- if (r < 0)
- return r;
+ r = routing_policy_rule_dup(rule, family, &tmp);
+ if (r < 0)
+ return r;
+ if (routing_policy_rule_get(link->manager, tmp, family, &existing) < 0) {
r = routing_policy_rule_acquire_priority(link->manager, tmp);
if (r < 0)
return r;
+ } else {
+ /* Copy priority from existing rule. */
+ if (!tmp->priority_set) {
+ tmp->priority_set = true;
+ tmp->priority = existing->priority;
+ }
- r = routing_policy_rule_attach(link->manager, tmp);
- if (r < 0)
- return r;
-
- existing = tmp;
- } else
- existing->source = rule->source;
+ /* Copy state for logging below. */
+ tmp->state = existing->state;
+ }
- log_routing_policy_rule_debug(existing, "Requesting", link, link->manager);
+ log_routing_policy_rule_debug(tmp, "Requesting", link, link->manager);
r = link_queue_request_safe(link, REQUEST_TYPE_ROUTING_POLICY_RULE,
- existing, NULL,
+ tmp,
+ routing_policy_rule_unref,
routing_policy_rule_hash_func,
routing_policy_rule_compare_func,
routing_policy_rule_process_request,
@@ -826,27 +980,31 @@ static int link_request_routing_policy_rule(Link *link, RoutingPolicyRule *rule)
if (r <= 0)
return r;
- routing_policy_rule_enter_requesting(existing);
+ routing_policy_rule_enter_requesting(tmp);
+ if (existing)
+ routing_policy_rule_enter_requesting(existing);
+
+ TAKE_PTR(tmp);
return 1;
}
-static int link_request_static_routing_policy_rule(Link *link, RoutingPolicyRule *rule) {
+static int link_request_static_routing_policy_rule(Link *link, const RoutingPolicyRule *rule) {
int r;
if (IN_SET(rule->family, AF_INET, AF_INET6))
- return link_request_routing_policy_rule(link, rule);
+ return link_request_routing_policy_rule(link, rule, rule->family);
- rule->family = AF_INET;
- r = link_request_routing_policy_rule(link, rule);
- if (r < 0) {
- rule->family = AF_UNSPEC;
+ assert(rule->address_family == ADDRESS_FAMILY_YES);
+
+ r = link_request_routing_policy_rule(link, rule, AF_INET);
+ if (r < 0)
return r;
- }
- rule->family = AF_INET6;
- r = link_request_routing_policy_rule(link, rule);
- rule->family = AF_UNSPEC;
- return r;
+ r = link_request_routing_policy_rule(link, rule, AF_INET6);
+ if (r < 0)
+ return r;
+
+ return 0;
}
int link_request_static_routing_policy_rules(Link *link) {
@@ -868,7 +1026,7 @@ int link_request_static_routing_policy_rules(Link *link) {
link->static_routing_policy_rules_configured = true;
link_check_ready(link);
} else {
- log_link_debug(link, "Requesting routing policy rules");
+ log_link_debug(link, "Requesting routing policy rules.");
link_set_state(link, LINK_STATE_CONFIGURING);
}
@@ -888,8 +1046,8 @@ static const RoutingPolicyRule kernel_rules[] = {
static bool routing_policy_rule_is_created_by_kernel(const RoutingPolicyRule *rule) {
assert(rule);
- for (size_t i = 0; i < ELEMENTSOF(kernel_rules); i++)
- if (routing_policy_rule_equal(rule, &kernel_rules[i]))
+ FOREACH_ELEMENT(i, kernel_rules)
+ if (routing_policy_rule_equal(rule, i, i->family, i->priority))
return true;
return false;
@@ -897,8 +1055,9 @@ static bool routing_policy_rule_is_created_by_kernel(const RoutingPolicyRule *ru
int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(routing_policy_rule_unrefp) RoutingPolicyRule *tmp = NULL;
+ bool adjust_protocol = false, is_new = false;
RoutingPolicyRule *rule = NULL;
- bool adjust_protocol = false;
+ Request *req = NULL;
uint16_t type;
int r;
@@ -961,12 +1120,6 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
}
}
- r = sd_rtnl_message_routing_policy_rule_get_flags(message, &tmp->flags);
- if (r < 0) {
- log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m");
- return 0;
- }
-
r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m");
@@ -1079,47 +1232,81 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
if (r >= 0)
tmp->suppress_ifgroup = (int32_t) suppress_ifgroup;
+ r = sd_netlink_message_read_u64(message, FRA_TUN_ID, &tmp->tunnel_id);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_TUN_ID attribute, ignoring: %m");
+ return 0;
+ }
+ if (r >= 0)
+ tmp->tunnel_id = be64toh(tmp->tunnel_id);
+
+ r = sd_netlink_message_read_u32(message, FRA_FLOW, &tmp->realms);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_FLOW attribute, ignoring: %m");
+ return 0;
+ }
+
if (adjust_protocol)
/* As .network files does not have setting to specify protocol, we can assume the
* protocol of the received rule is RTPROT_KERNEL or RTPROT_STATIC. */
tmp->protocol = routing_policy_rule_is_created_by_kernel(tmp) ? RTPROT_KERNEL : RTPROT_STATIC;
- (void) routing_policy_rule_get(m, tmp, &rule);
+ (void) routing_policy_rule_get(m, tmp, tmp->family, &rule);
+ (void) routing_policy_rule_get_request(m, tmp, tmp->family, &req);
- switch (type) {
- case RTM_NEWRULE:
- if (rule) {
- routing_policy_rule_enter_configured(rule);
- log_routing_policy_rule_debug(rule, "Received remembered", NULL, m);
- } else if (!m->manage_foreign_rules) {
- routing_policy_rule_enter_configured(tmp);
- log_routing_policy_rule_debug(tmp, "Ignoring received", NULL, m);
- } else {
- routing_policy_rule_enter_configured(tmp);
- log_routing_policy_rule_debug(tmp, "Remembering", NULL, m);
- r = routing_policy_rule_attach(m, tmp);
- if (r < 0) {
- log_warning_errno(r, "Could not remember foreign rule, ignoring: %m");
- return 0;
- }
- }
- break;
- case RTM_DELRULE:
+ if (type == RTM_DELRULE) {
if (rule) {
routing_policy_rule_enter_removed(rule);
- if (rule->state == 0) {
- log_routing_policy_rule_debug(rule, "Forgetting", NULL, m);
- routing_policy_rule_detach(rule);
- } else
- log_routing_policy_rule_debug(rule, "Removed", NULL, m);
+ log_routing_policy_rule_debug(rule, "Forgetting removed", NULL, m);
+ routing_policy_rule_detach(rule);
} else
log_routing_policy_rule_debug(tmp, "Kernel removed unknown", NULL, m);
- break;
- default:
- assert_not_reached();
+ if (req)
+ routing_policy_rule_enter_removed(req->userdata);
+
+ return 0;
+ }
+
+ if (!rule) {
+ if (!req && !m->manage_foreign_rules) {
+ routing_policy_rule_enter_configured(tmp);
+ log_routing_policy_rule_debug(tmp, "Ignoring received", NULL, m);
+ return 0;
+ }
+
+ /* If we did not know the rule, then save it. */
+ r = routing_policy_rule_attach(m, tmp);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to save received routing policy rule, ignoring: %m");
+ return 0;
+ }
+
+ rule = tmp;
+ is_new = true;
}
+ /* Also update information that cannot be obtained through netlink notification. */
+ if (req && req->waiting_reply) {
+ RoutingPolicyRule *req_rule = ASSERT_PTR(req->userdata);
+
+ rule->source = req_rule->source;
+ }
+
+ /* Then, update miscellaneous info from netlink notification. */
+ r = sd_rtnl_message_routing_policy_rule_get_flags(message, &rule->flags);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: received rule message without valid flag, ignoring: %m");
+
+ r = sd_netlink_message_read_u32(message, FRA_GOTO, &rule->priority_goto);
+ if (r < 0 && r != -ENODATA)
+ log_debug_errno(r, "rtnl: could not get FRA_GOTO attribute, ignoring: %m");
+
+ routing_policy_rule_enter_configured(rule);
+ if (req)
+ routing_policy_rule_enter_configured(req->userdata);
+
+ log_routing_policy_rule_debug(rule, is_new ? "Remembering" : "Received remembered", NULL, m);
return 1;
}
@@ -1239,6 +1426,48 @@ int config_parse_routing_policy_rule_priority(
return 0;
}
+int config_parse_routing_policy_rule_goto(
+ 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_unref_or_set_invalidp) RoutingPolicyRule *n = NULL;
+ Network *network = ASSERT_PTR(userdata);
+ uint32_t priority;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = routing_policy_rule_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return log_oom();
+
+ r = safe_atou32(rvalue, &priority);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=%s, ignoring assignment: %m", lvalue, rvalue);
+ return 0;
+ }
+ if (priority <= 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid goto target priority, ignoring assignment.");
+ return 0;
+ }
+
+ n->type = FR_ACT_GOTO;
+ n->priority_goto = priority;
+
+ TAKE_PTR(n);
+ return 0;
+}
+
int config_parse_routing_policy_rule_table(
const char *unit,
const char *filename,
@@ -1795,6 +2024,23 @@ static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) {
if (rule->l3mdev)
rule->table = RT_TABLE_UNSPEC;
+ if (rule->type == FR_ACT_GOTO) {
+ if (rule->priority_goto <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Type=goto is specified but the target priority GoTo= is unspecified. "
+ "Ignoring [RoutingPolicyRule] section from line %u.",
+ rule->section->filename,
+ rule->section->line);
+
+ if (rule->priority_set && rule->priority >= rule->priority_goto)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: goto target priority %"PRIu32" must be larger than the priority of this rule %"PRIu32". "
+ "Ignoring [RoutingPolicyRule] section from line %u.",
+ rule->section->filename,
+ rule->priority_goto, rule->priority,
+ rule->section->line);
+ }
+
return 0;
}
diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h
index 7047cc4e9b..c7a0512b44 100644
--- a/src/network/networkd-routing-policy-rule.h
+++ b/src/network/networkd-routing-policy-rule.h
@@ -35,12 +35,12 @@ typedef struct RoutingPolicyRule {
union in_addr_union to; /* FRA_DST */
union in_addr_union from; /* FRA_SRC */
char *iif; /* FRA_IIFNAME */
- /* FRA_GOTO */
+ uint32_t priority_goto; /* FRA_GOTO */
bool priority_set;
uint32_t priority; /* FRA_PRIORITY */
uint32_t fwmark; /* FRA_FWMARK */
- /* FRA_FLOW */
- /* FRA_TUN_ID */
+ uint32_t realms; /* FRA_FLOW (IPv4 only) */
+ uint64_t tunnel_id; /* FRA_TUN_ID */
int32_t suppress_ifgroup; /* FRA_SUPPRESS_IFGROUP */
int32_t suppress_prefixlen; /* FRA_SUPPRESS_PREFIXLEN */
uint32_t table; /* FRA_TABLE, also used in struct fib_rule_hdr */
@@ -54,7 +54,8 @@ typedef struct RoutingPolicyRule {
struct fib_rule_port_range dport; /* FRA_DPORT_RANGE */
} RoutingPolicyRule;
-const char* fr_act_type_full_to_string(int t) _const_;
+int fr_act_type_from_string(const char *s) _pure_;
+const char* fr_act_type_to_string(int t) _const_;
RoutingPolicyRule* routing_policy_rule_ref(RoutingPolicyRule *rule);
RoutingPolicyRule* routing_policy_rule_unref(RoutingPolicyRule *rule);
@@ -81,6 +82,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_table);
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_goto);
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);
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index 34db2bbf1c..b091725510 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -112,6 +112,7 @@ int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type,
int sd_netlink_message_read_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t *data);
int sd_netlink_message_read_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t *data);
int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t *data);
+int sd_netlink_message_read_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t *ret);
int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *data);
int sd_netlink_message_read_cache_info(sd_netlink_message *m, uint16_t attr_type, struct ifa_cacheinfo *info);
int sd_netlink_message_read_in_addr(sd_netlink_message *m, uint16_t attr_type, struct in_addr *data);
diff --git a/test/test-network/conf/25-routing-policy-rule-test1.network b/test/test-network/conf/25-routing-policy-rule-test1.network
index 7d6e17cf6c..2cec4334fd 100644
--- a/test/test-network/conf/25-routing-policy-rule-test1.network
+++ b/test/test-network/conf/25-routing-policy-rule-test1.network
@@ -27,7 +27,23 @@ Priority=101
Family=both
[RoutingPolicyRule]
+Type=table
IncomingInterface=test1
From=0.0.0.0/8
Table=10
Priority=102
+
+[RoutingPolicyRule]
+Type=goto
+IncomingInterface=test1
+From=10.0.0.0/16
+Priority=103
+GoTo=111
+Table=11
+
+[RoutingPolicyRule]
+Type=nop
+IncomingInterface=test1
+From=10.1.0.0/16
+Priority=104
+Table=12
diff --git a/test/test-network/conf/networkd-manage-foreign-rules-no.conf b/test/test-network/conf/networkd-manage-foreign-rules-no.conf
new file mode 100644
index 0000000000..b376889fc8
--- /dev/null
+++ b/test/test-network/conf/networkd-manage-foreign-rules-no.conf
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
+ManageForeignRoutingPolicyRules=no
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 3cf743dec2..fae7b58fcc 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -645,7 +645,7 @@ def flush_routing_policy_rules():
have = True
print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
print(f'# {line}')
- words = line.replace('lookup [l3mdev-table]', 'l3mdev').split()
+ words = line.replace('lookup [l3mdev-table]', 'l3mdev').replace('[detached]', '').split()
priority = words[0].rstrip(':')
call(f'ip -{ipv} rule del priority {priority} ' + ' '.join(words[1:]))
@@ -3210,43 +3210,80 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertNotRegex(output, '192.168.0.1')
self.assertRegex(output, routable_map[carrier])
- def test_routing_policy_rule(self):
- copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
- start_networkd()
- self.wait_online('test1:degraded')
-
+ def check_routing_policy_rule_test1(self):
output = check_output('ip rule list iif test1 priority 111')
print(output)
- self.assertRegex(output, '111:')
- self.assertRegex(output, 'from 192.168.100.18')
- self.assertRegex(output, r'tos (0x08|throughput)\s')
- self.assertRegex(output, 'iif test1')
- self.assertRegex(output, 'oif test1')
- self.assertRegex(output, 'lookup 7')
+ self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
- output = check_output('ip rule list iif test1 priority 101')
+ output = check_output('ip -6 rule list iif test1 priority 100')
print(output)
- self.assertRegex(output, '101:')
- self.assertRegex(output, 'from all')
- self.assertRegex(output, 'iif test1')
- self.assertRegex(output, 'lookup 9')
+ self.assertIn('100: from all iif test1 lookup 8', output)
- output = check_output('ip -6 rule list iif test1 priority 100')
+ output = check_output('ip rule list iif test1 priority 101')
print(output)
- self.assertRegex(output, '100:')
- self.assertRegex(output, 'from all')
- self.assertRegex(output, 'iif test1')
- self.assertRegex(output, 'lookup 8')
+ self.assertIn('101: from all iif test1 lookup 9', output)
output = check_output('ip rule list iif test1 priority 102')
print(output)
- self.assertRegex(output, '102:')
- self.assertRegex(output, 'from 0.0.0.0/8')
- self.assertRegex(output, 'iif test1')
- self.assertRegex(output, 'lookup 10')
+ self.assertIn('102: from 0.0.0.0/8 iif test1 lookup 10', output)
+
+ output = check_output('ip rule list iif test1 priority 103')
+ print(output)
+ self.assertIn('103: from 10.0.0.0/16 iif test1 lookup 11 goto 111', output)
+ output = check_output('ip rule list iif test1 priority 104')
+ print(output)
+ self.assertIn('104: from 10.1.0.0/16 iif test1 lookup 12 nop', output)
+
+ def check_routing_policy_rule_dummy98(self):
+ output = check_output('ip rule list table 8')
+ print(output)
+ self.assertRegex(output, r'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
+
+ def _test_routing_policy_rule(self, manage_foreign_routes):
+ if not manage_foreign_routes:
+ copy_networkd_conf_dropin('networkd-manage-foreign-rules-no.conf')
+ copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
+
+ stop_networkd()
+
+ check_output('ip -4 rule add priority 20001 table 9999 from 10.10.0.0/16')
+ check_output('ip -6 rule add priority 20001 table 9999 from 2001:db8:0:1::/64')
+
+ start_networkd()
+ self.wait_online('test1:degraded')
+
+ self.check_routing_policy_rule_test1()
check_json(networkctl_json())
+ output = check_output('ip -4 rule list priority 20001 table 9999 from 10.10.0.0/16')
+ print(output)
+ if manage_foreign_routes:
+ self.assertEqual(output, '')
+ else:
+ self.assertIn(output, '20001: from 10.10.0.0/16 lookup 9999')
+ check_output('ip -4 rule del priority 20001 table 9999 from 10.10.0.0/16')
+
+ output = check_output('ip -6 rule list priority 20001 table 9999 from 2001:db8:0:1::/64')
+ print(output)
+ if manage_foreign_routes:
+ self.assertEqual(output, '')
+ else:
+ self.assertIn(output, '20001: from 2001:db8:0:1::/64 lookup 9999')
+ check_output('ip -6 rule del priority 20001 table 9999 from 2001:db8:0:1::/64')
+
+ def test_routing_policy_rule(self):
+ first = True
+ for manage_foreign_routes in [True, False]:
+ if first:
+ first = False
+ else:
+ self.tearDown()
+
+ print(f'### test_routing_policy_rule(manage_foreign_routes={manage_foreign_routes})')
+ with self.subTest(manage_foreign_routes=manage_foreign_routes):
+ self._test_routing_policy_rule(manage_foreign_routes)
+
def test_routing_policy_rule_issue_11280(self):
copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
'25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
@@ -3255,13 +3292,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
restart_networkd(show_logs=(trial > 0))
self.wait_online('test1:degraded', 'dummy98:degraded')
- output = check_output('ip rule list table 7')
- print(output)
- self.assertRegex(output, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
-
- output = check_output('ip rule list table 8')
- print(output)
- self.assertRegex(output, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
+ self.check_routing_policy_rule_test1()
+ self.check_routing_policy_rule_dummy98()
def test_routing_policy_rule_reconfigure(self):
copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')