diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index c953f89daa6..d66c04caa5d 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -489,9 +489,7 @@ int link_drop_unmanaged_neighbors(Link *link) { if (!neighbor_exists(neighbor)) continue; - /* Ignore foreign neighbors when KeepConfiguration=yes or static. */ - if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) + if (!link_should_mark_config(link, /* only_static = */ false, neighbor->source, RTPROT_STATIC)) continue; neighbor_mark(neighbor); diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index c962e882d86..d61ff6f4f28 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -924,17 +924,8 @@ int link_drop_nexthops(Link *link, bool only_static) { if (!nexthop_exists(nexthop)) continue; - if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN) { - if (only_static) - continue; - - /* Do not mark foreign nexthop when KeepConfiguration= is enabled. */ - if (link->network && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) - continue; - - } else if (nexthop->source != NETWORK_CONFIG_SOURCE_STATIC) - continue; /* Ignore dynamically configurad nexthops. */ + if (!link_should_mark_config(link, only_static, nexthop->source, nexthop->protocol)) + continue; /* Ignore nexthops bound to other links. */ if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex) diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e41b4c31be1..9498fa4ccc8 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -1476,24 +1476,8 @@ int link_drop_routes(Link *link, bool only_static) { if (!route_exists(route)) continue; - if (only_static) { - if (route->source != NETWORK_CONFIG_SOURCE_STATIC) - continue; - } else { - /* Ignore dynamically assigned routes. */ - if (!IN_SET(route->source, NETWORK_CONFIG_SOURCE_FOREIGN, NETWORK_CONFIG_SOURCE_STATIC)) - continue; - - if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN && link->network) { - if (route->protocol == RTPROT_STATIC && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) - continue; - - if (IN_SET(route->protocol, RTPROT_DHCP, RTPROT_RA, RTPROT_REDIRECT) && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) - continue; - } - } + if (!link_should_mark_config(link, only_static, route->source, route->protocol)) + continue; /* When we also mark foreign routes, do not mark routes assigned to other interfaces. * Otherwise, routes assigned to unmanaged interfaces will be dropped. diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 2cac730a417..7eb7548412d 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -822,22 +822,13 @@ int link_drop_routing_policy_rules(Link *link, bool only_static) { if (rule->protocol == RTPROT_KERNEL) continue; - if (only_static) { - /* When 'only_static' is true, mark only static rules. */ - if (rule->source != NETWORK_CONFIG_SOURCE_STATIC) - continue; - } else { - /* Do not mark foreign rules when KeepConfiguration= is enabled. */ - if (rule->source == NETWORK_CONFIG_SOURCE_FOREIGN && - link->network && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) - continue; - } - /* Ignore rules not assigned yet or already removing. */ if (!routing_policy_rule_exists(rule)) continue; + if (!link_should_mark_config(link, only_static, rule->source, rule->protocol)) + continue; + routing_policy_rule_mark(rule); } diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 5db89c60472..80cce4dcdc9 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -6,6 +6,7 @@ #include "escape.h" #include "logarithm.h" #include "networkd-link.h" +#include "networkd-network.h" #include "networkd-util.h" #include "parse-util.h" #include "string-table.h" @@ -113,6 +114,42 @@ DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_deprecated_address_family, AddressFa DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ip_masquerade_address_family, AddressFamily); DEFINE_STRING_TABLE_LOOKUP(dhcp_lease_server_type, sd_dhcp_lease_server_type_t); +bool link_should_mark_config(Link *link, bool only_static, NetworkConfigSource source, uint8_t protocol) { + /* Always mark static configs. */ + if (source == NETWORK_CONFIG_SOURCE_STATIC) + return true; + + /* When 'only_static' is true, do not mark other configs. */ + if (only_static) + return false; + + /* Always ignore dynamically assigned configs. */ + if (source != NETWORK_CONFIG_SOURCE_FOREIGN) + return false; + + /* When only_static is false, the logic is conditionalized with KeepConfiguration=. Hence, the + * interface needs to have a matching .network file. */ + assert(link); + assert(link->network); + + /* When KeepConfiguration=yes, keep all foreign configs. */ + if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_YES)) + return false; + + /* When static, keep all static configs. */ + if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC) && + protocol == RTPROT_STATIC) + return false; + + /* When dynamic, keep all dynamic configs. */ + if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC) && + IN_SET(protocol, RTPROT_DHCP, RTPROT_RA, RTPROT_REDIRECT)) + return false; + + /* Otherwise, mark the config. */ + return true; +} + int config_parse_ip_masquerade( const char *unit, const char *filename, diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index b424092ef64..2a3b5683fad 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -151,6 +151,8 @@ AddressFamily dhcp_deprecated_address_family_from_string(const char *s) _pure_; const char* dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type_t t) _const_; sd_dhcp_lease_server_type_t dhcp_lease_server_type_from_string(const char *s) _pure_; +bool link_should_mark_config(Link *link, bool only_static, NetworkConfigSource source, uint8_t protocol); + int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg); #define log_link_message_error_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_ERR, err, msg) #define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg) diff --git a/test/test-network/conf/24-keep-configuration-yes.network b/test/test-network/conf/24-keep-configuration-yes.network new file mode 100644 index 00000000000..e0cffae2a22 --- /dev/null +++ b/test/test-network/conf/24-keep-configuration-yes.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +KeepConfiguration=yes diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index a1f066c4bef..dc8bc395e4b 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4945,6 +4945,24 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'DNS: 192.168.42.1') self.assertRegex(output, 'Search Domains: one') + def test_keep_configuration_yes(self): + check_output('ip link add dummy98 type dummy') + check_output('ip link set dev dummy98 up') + check_output('ip address add 198.51.100.1/24 brd 198.51.100.255 dev dummy98') + check_output('ip route add 203.0.113.0/24 via 198.51.100.10 dev dummy98 proto boot') + + copy_network_unit('24-keep-configuration-yes.network') + start_networkd() + self.wait_online('dummy98:routable') + + output = check_output('ip address show dummy98') + print(output) + self.assertIn('inet 198.51.100.1/24 brd 198.51.100.255 scope global dummy98', output) + + output = check_output('ip -d -4 route show dev dummy98') + print(output) + self.assertIn('203.0.113.0/24 via 198.51.100.10 proto boot', output) + def test_keep_configuration_static(self): check_output('ip link add name dummy98 type dummy') check_output('ip address add 10.1.2.3/16 dev dummy98')