diff --git a/man/systemd.network.xml b/man/systemd.network.xml index e5647f7dae..e46f506f0f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1261,6 +1261,14 @@ IPv6Token=prefixstable:2002:da8:1:: unset. + + Type= + + Specifies Routing Policy Database (RPDB) rule type. Takes one of blackhole, + unreachable or prohibit. + + + diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index be7c076c61..ccf7867d4f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -161,6 +161,7 @@ RoutingPolicyRule.InvertRule, config_parse_routing_policy_rule_in RoutingPolicyRule.Family, config_parse_routing_policy_rule_family, 0, 0 RoutingPolicyRule.User, config_parse_routing_policy_rule_uid_range, 0, 0 RoutingPolicyRule.SuppressPrefixLength, config_parse_routing_policy_rule_suppress_prefixlen, 0, 0 +RoutingPolicyRule.Type, config_parse_routing_policy_rule_type, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 4e4c347c7b..8c9565bda9 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -15,10 +15,20 @@ #include "networkd-util.h" #include "parse-util.h" #include "socket-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #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", +}; + +assert_cc(__FR_ACT_MAX <= UINT8_MAX); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fr_act_type, int); + RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) { if (!rule) return NULL; @@ -56,6 +66,7 @@ static int routing_policy_rule_new(RoutingPolicyRule **ret) { .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, + .type = FR_ACT_TO_TBL, }; *ret = rule; @@ -126,6 +137,7 @@ static int routing_policy_rule_copy(RoutingPolicyRule *dest, RoutingPolicyRule * dest->to_prefixlen = src->to_prefixlen; dest->invert_rule = src->invert_rule; dest->tos = src->tos; + dest->type = src->type; dest->fwmark = src->fwmark; dest->fwmask = src->fwmask; dest->priority = src->priority; @@ -158,6 +170,7 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash24_compress_boolean(rule->invert_rule, state); siphash24_compress(&rule->tos, sizeof(rule->tos), state); + siphash24_compress(&rule->type, sizeof(rule->type), state); siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state); siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state); siphash24_compress(&rule->priority, sizeof(rule->priority), state); @@ -213,6 +226,10 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro if (r != 0) return r; + r = CMP(a->type, b->type); + if (r != 0) + return r; + r = CMP(a->fwmark, b->fwmark); if (r != 0) return r; @@ -475,6 +492,12 @@ static int routing_policy_rule_set_netlink_message(RoutingPolicyRule *rule, sd_n return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m"); } + if (rule->type != FR_ACT_TO_TBL) { + r = sd_rtnl_message_routing_policy_rule_set_fib_type(m, rule->type); + if (r < 0) + return log_link_error_errno(link, r, "Could not append FIB rule type attribute: %m"); + } + return 0; } @@ -801,7 +824,13 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos); if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m"); + log_warning_errno(r, "rtnl: could not get FIB rule TOS, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_routing_policy_rule_get_fib_type(message, &tmp->type); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FIB rule type, ignoring: %m"); return 0; } @@ -1415,6 +1444,45 @@ int config_parse_routing_policy_rule_suppress_prefixlen( return 0; } +int config_parse_routing_policy_rule_type( + 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, t; + + 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(); + + t = fr_act_type_from_string(rvalue); + if (t < 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Could not parse FIB rule type \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n->type = (uint8_t) t; + n = NULL; + + return 0; +} + static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) { if (section_is_invalid(rule->section)) return -EINVAL; @@ -1494,6 +1562,13 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) { space = true; } + if (rule->type != 0) { + fprintf(f, "%stype=%hhu", + space ? " " : "", + rule->type); + space = true; + } + if (rule->priority != 0) { fprintf(f, "%spriority=%"PRIu32, space ? " " : "", @@ -1670,6 +1745,12 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { log_warning_errno(r, "Failed to parse RPDB rule TOS, ignoring: %s", b); continue; } + } else if (streq(a, "type")) { + r = safe_atou8(b, &rule->type); + if (r < 0) { + log_warning_errno(r, "Failed to parse RPDB rule type, ignoring: %s", b); + continue; + } } else if (streq(a, "table")) { r = safe_atou32(b, &rule->table); if (r < 0) { diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index baf086f25e..1b574452e2 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -23,7 +23,10 @@ typedef struct RoutingPolicyRule { bool invert_rule; uint8_t tos; + uint8_t type; uint8_t protocol; + uint8_t to_prefixlen; + uint8_t from_prefixlen; uint32_t table; uint32_t fwmark; @@ -32,8 +35,6 @@ typedef struct RoutingPolicyRule { AddressFamily address_family; /* Specified by Family= */ int family; /* Automatically determined by From= or To= */ - unsigned char to_prefixlen; - unsigned char from_prefixlen; char *iif; char *oif; @@ -71,3 +72,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_invert); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_family); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_uid_range); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_suppress_prefixlen); +CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_type); diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c index 8d87cdf9c9..4fed4fe8d9 100644 --- a/src/network/test-routing-policy-rule.c +++ b/src/network/test-routing-policy-rule.c @@ -57,34 +57,34 @@ int main(int argc, char **argv) { test_setup_logging(LOG_DEBUG); test_rule_serialization("basic parsing", - "RULE=family=AF_INET from=1.2.3.4/32 to=2.3.4.5/32 tos=5 priority=10 fwmark=1/2 invert_rule=yes table=10", NULL); + "RULE=family=AF_INET from=1.2.3.4/32 to=2.3.4.5/32 tos=5 type=1 priority=10 fwmark=1/2 invert_rule=yes table=10", NULL); test_rule_serialization("ignored values", "RULE=something=to=ignore from=1.2.3.4/32 from=1.2.3.4/32" - " \t to=2.3.4.5/24 to=2.3.4.5/32 tos=5 fwmark=2 fwmark=1 table=10 table=20", - "RULE=family=AF_INET from=1.2.3.4/32 to=2.3.4.5/32 tos=5 fwmark=1 invert_rule=no table=20"); + " \t to=2.3.4.5/24 to=2.3.4.5/32 tos=5 type=1 fwmark=2 fwmark=1 table=10 table=20", + "RULE=family=AF_INET from=1.2.3.4/32 to=2.3.4.5/32 tos=5 type=1 fwmark=1 invert_rule=no table=20"); test_rule_serialization("ipv6", - "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 invert_rule=yes table=6", NULL); + "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 type=1 invert_rule=yes table=6", NULL); - assert_se(asprintf(&p, "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 invert_rule=no table=%d", RT_TABLE_MAIN) >= 0); + assert_se(asprintf(&p, "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 type=1 invert_rule=no table=%d", RT_TABLE_MAIN) >= 0); test_rule_serialization("default table", "RULE=from=1::2/64 to=2::3/64", p); test_rule_serialization("incoming interface", "RULE=from=1::2/64 to=2::3/64 table=1 iif=lo", - "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 iif=lo invert_rule=no table=1"); + "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 type=1 iif=lo invert_rule=no table=1"); test_rule_serialization("outgoing interface", - "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 oif=eth0 invert_rule=no table=1", NULL); + "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 type=1 oif=eth0 invert_rule=no table=1", NULL); test_rule_serialization("freeing interface names", - "RULE=from=1::2/64 to=2::3/64 family=AF_INET6 iif=e0 iif=e1 oif=e0 oif=e1 table=1", - "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 iif=e1 oif=e1 invert_rule=no table=1"); + "RULE=from=1::2/64 to=2::3/64 family=AF_INET6 type=1 iif=e0 iif=e1 oif=e0 oif=e1 table=1", + "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 type=1 iif=e1 oif=e1 invert_rule=no table=1"); test_rule_serialization("ignoring invalid family", "RULE=from=1::2/64 to=2::3/64 family=AF_UNSEPC family=AF_INET table=1", - "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 invert_rule=no table=1"); + "RULE=family=AF_INET6 from=1::2/64 to=2::3/64 type=1 invert_rule=no table=1"); return 0; } diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 611d5bb7f7..a4da64bd00 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -275,6 +275,7 @@ InvertRule= Family= SuppressPrefixLength= User= +Type= [IPv6SendRA] RouterPreference= DNSLifetimeSec=