Merge pull request #34049 from yuwata/network-routing-policy-rule

network: further rework for routing policy rule
This commit is contained in:
Luca Boccassi
2024-08-21 12:46:37 +02:00
committed by GitHub
11 changed files with 543 additions and 192 deletions
+16 -2
View File
@@ -1728,6 +1728,18 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
</listitem>
</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>
@@ -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>
@@ -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;
+1 -1
View File
@@ -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),
+1
View File
@@ -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
+10 -2
View File
@@ -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;
+395 -149
View File
@@ -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,62 +313,126 @@ 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;
if (!rule1 || !rule2)
return false;
return routing_policy_rule_compare_func(rule1, rule2) == 0;
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 int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) {
static bool routing_policy_rule_equal(const RoutingPolicyRule *a, const RoutingPolicyRule *b, int family, uint32_t priority) {
assert(a);
assert(b);
if (a->family != AF_UNSPEC && a->family != family)
return false;
if (b->family != AF_UNSPEC && b->family != family)
return false;
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 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;
/* Also find rules configured without priority. */
SET_FOREACH(rule, m->rules) {
uint32_t priority;
bool found;
if (rule->priority_set)
/* The rule is configured with priority. */
continue;
priority = rule->priority;
rule->priority = 0;
found = routing_policy_rule_equal(rule, in);
rule->priority = priority;
if (found) {
SET_FOREACH(rule, m->rules)
if (routing_policy_rule_equal(in, rule, family, rule->priority)) {
if (ret)
*ret = rule;
return 0;
}
return -ENOENT;
}
static int routing_policy_rule_get_request(Manager *m, const RoutingPolicyRule *in, int family, Request **ret) {
Request *req;
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 (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 = req;
return 0;
}
}
return -ENOENT;
@@ -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);
log_routing_policy_rule_debug(rule, "Removing", NULL, rule->manager);
/* If the rule is remembered, then use the remembered object. */
(void) routing_policy_rule_get(manager, rule, rule->family, &rule);
r = sd_rtnl_message_new_routing_policy_rule(rule->manager->rtnl, &m, RTM_DELRULE, rule->family);
/* 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, manager);
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;
/* Copy state for logging below. */
tmp->state = existing->state;
}
existing = tmp;
} else
existing->source = rule->source;
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;
}
+6 -4
View File
@@ -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);
+1
View File
@@ -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);
@@ -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
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Network]
ManageForeignRoutingPolicyRules=no
+66 -34
View File
@@ -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')
output = check_output('ip rule list iif test1 priority 101')
print(output)
self.assertRegex(output, '101:')
self.assertRegex(output, 'from all')
self.assertRegex(output, 'iif test1')
self.assertRegex(output, 'lookup 9')
self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
output = check_output('ip -6 rule list iif test1 priority 100')
print(output)
self.assertRegex(output, '100:')
self.assertRegex(output, 'from all')
self.assertRegex(output, 'iif test1')
self.assertRegex(output, 'lookup 8')
self.assertIn('100: from all iif test1 lookup 8', output)
output = check_output('ip rule list iif test1 priority 101')
print(output)
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')