diff --git a/man/systemd.network.xml b/man/systemd.network.xml index accc824e4d..95c56b0ae3 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1139,6 +1139,16 @@ A boolean. Specifies whether the rule to be inverted. Defaults to false. + + Family= + + Takes a special value ipv4, ipv6, or + both. By default, the address family is determined by the address + specified in To= or From=. If neither + To= nor From= are specified, then defaults to + ipv4. + + diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 8c7e5db4bb..d4d108ad25 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -122,6 +122,7 @@ RoutingPolicyRule.IPProtocol, config_parse_routing_policy_rule_ip_prot 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.Family, config_parse_routing_policy_rule_family, 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 1494727ead..5edc2444a7 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -11,6 +11,7 @@ #include "networkd-routing-policy-rule.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-util.h" #include "parse-util.h" #include "socket-util.h" #include "string-util.h" @@ -569,11 +570,45 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl } int routing_policy_rule_section_verify(RoutingPolicyRule *rule) { + int r; + if (section_is_invalid(rule->section)) return -EINVAL; - if (rule->family == AF_UNSPEC) + if ((rule->family == AF_INET && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) || + (rule->family == AF_INET6 && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: address family specified by Family= conflicts with the address " + "specified by To= or From=. Ignoring [RoutingPolicyRule] section from line %u.", + rule->section->filename, rule->section->line); + + if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6)) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule6 = NULL; + + assert(rule->family == AF_UNSPEC); + + /* When Family=both, we need to copy the section, AF_INET and AF_INET6. */ + + r = routing_policy_rule_new_static(rule->network, NULL, 0, &rule6); + if (r < 0) + return r; + + r = routing_policy_rule_copy(rule6, rule); + if (r < 0) + return r; + rule->family = AF_INET; + rule6->family = AF_INET6; + + TAKE_PTR(rule6); + } + + if (rule->family == AF_UNSPEC) { + if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) + rule->family = AF_INET6; + else + rule->family = AF_INET; + } return 0; } @@ -973,6 +1008,46 @@ int config_parse_routing_policy_rule_invert( return 0; } +int config_parse_routing_policy_rule_family( + 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; + AddressFamily a; + 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 r; + + a = routing_policy_rule_address_family_from_string(rvalue); + if (a < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid address family '%s', ignoring.", rvalue); + return 0; + } + + n->address_family = a; + n = NULL; + + return 0; +} + static int routing_policy_rule_read_full_file(const char *state_file, char **ret) { _cleanup_free_ char *s = NULL; size_t size; diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 8ddb85307c..6b8e310227 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -36,7 +36,8 @@ struct RoutingPolicyRule { uint32_t fwmask; uint32_t priority; - int family; + AddressFamily address_family; /* Specified by Family= */ + int family; /* Automatically determined by From= or To= */ unsigned char to_prefixlen; unsigned char from_prefixlen; @@ -77,3 +78,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_device); 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); +CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_family); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 2f56b4c7f1..3fd26836c6 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -24,8 +24,15 @@ static const char * const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = [ADDRESS_FAMILY_FALLBACK_IPV4] = "ipv4-fallback", }; +static const char * const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = { + [ADDRESS_FAMILY_YES] = "both", + [ADDRESS_FAMILY_IPV4] = "ipv4", + [ADDRESS_FAMILY_IPV6] = "ipv6", +}; + DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family, AddressFamily, ADDRESS_FAMILY_YES); DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(link_local_address_family, AddressFamily, ADDRESS_FAMILY_YES); +DEFINE_STRING_TABLE_LOOKUP(routing_policy_rule_address_family, AddressFamily); DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family, link_local_address_family, AddressFamily, "Failed to parse option"); diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index c96c2fd9fa..3a57819f39 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -32,6 +32,9 @@ AddressFamily address_family_from_string(const char *s) _pure_; const char *link_local_address_family_to_string(AddressFamily b) _const_; AddressFamily link_local_address_family_from_string(const char *s) _pure_; +const char *routing_policy_rule_address_family_to_string(AddressFamily b) _const_; +AddressFamily routing_policy_rule_address_family_from_string(const char *s) _pure_; + int kernel_route_expiration_supported(void); int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 7be38b0f34..848d4bd187 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -208,6 +208,7 @@ SourcePort= DestinationPort= IPProtocol= InvertRule= +Family= [IPv6PrefixDelegation] RouterPreference= DNSLifetimeSec=