diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index a43a831991..660c1f1824 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -121,6 +121,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_localhost_one(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + /* 127.0.0.1 */ + return be32toh(u->in.s_addr) == UINT32_C(0x7F000001); + + if (family == AF_INET6) + return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + + return -EAFNOSUPPORT; +} + bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) { return a->s6_addr32[0] == 0 && a->s6_addr32[1] == 0 && diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 0178391e5f..5de87a9539 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -49,6 +49,7 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +int in_addr_is_localhost_one(int family, const union in_addr_union *u); bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index ff80f185ce..44f77db3be 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -854,6 +854,10 @@ int link_drop_foreign_addresses(Link *link) { if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) continue; + /* Do not remove localhost address (127.0.0.1 and ::1) */ + if (link->flags & IFF_LOOPBACK && in_addr_is_localhost_one(address->family, &address->in_addr) > 0) + continue; + /* Ignore addresses we configured. */ if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN) continue; @@ -891,22 +895,19 @@ int link_drop_foreign_addresses(Link *link) { return r; } -int link_drop_addresses(Link *link) { +int link_drop_managed_addresses(Link *link) { Address *address; int k, r = 0; assert(link); SET_FOREACH(address, link->addresses) { - /* Ignore addresses not assigned yet or already removing. */ - if (!address_exists(address)) + /* Do not touch addresses managed by kernel or other tools. */ + if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN) continue; - /* Do not drop IPv6LL addresses assigned by the kernel here. They will be dropped in - * link_drop_ipv6ll_addresses() if IPv6LL addressing is disabled. */ - if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN && - address->family == AF_INET6 && - in6_addr_is_link_local(&address->in_addr.in6)) + /* Ignore addresses not assigned yet or already removing. */ + if (!address_exists(address)) continue; k = address_remove(address); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index c1e5b3ce3a..682f7e80e4 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -74,7 +74,7 @@ void address_set_broadcast(Address *a); DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free); -int link_drop_addresses(Link *link); +int link_drop_managed_addresses(Link *link); int link_drop_foreign_addresses(Link *link); int link_drop_ipv6ll_addresses(Link *link); void link_foreignize_addresses(Link *link); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index a8de644559..addf0d4afe 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -430,6 +430,8 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); + ndisc_flush(link); + k = sd_radv_stop(link->radv); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m"); @@ -892,24 +894,6 @@ static int link_new_bound_to_list(Link *link) { return 0; } -static int link_new_carrier_maps(Link *link) { - int r; - - r = link_new_bound_by_list(link); - if (r < 0) - return r; - - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - - r = link_new_bound_to_list(link); - if (r < 0) - return r; - - return link_handle_bound_to_list(link); -} - static void link_free_bound_to_list(Link *link) { bool updated = false; Link *bound_to; @@ -946,13 +930,6 @@ static void link_free_bound_by_list(Link *link) { link_dirty(link); } -static void link_free_carrier_maps(Link *link) { - assert(link); - - link_free_bound_to_list(link); - link_free_bound_by_list(link); -} - static int link_append_to_master(Link *link) { Link *master; int r; @@ -1012,7 +989,8 @@ static Link *link_drop(Link *link) { link_drop_requests(link); - link_free_carrier_maps(link); + link_free_bound_to_list(link); + link_free_bound_by_list(link); link_drop_from_master(link); @@ -1070,32 +1048,30 @@ static int link_drop_foreign_config(Link *link) { return r; } -static int link_drop_config(Link *link) { +static int link_drop_managed_config(Link *link) { int k, r; assert(link); assert(link->manager); - r = link_drop_routes(link); + r = link_drop_managed_routes(link); - k = link_drop_nexthops(link); + k = link_drop_managed_nexthops(link); if (k < 0 && r >= 0) r = k; - k = link_drop_addresses(link); + k = link_drop_managed_addresses(link); if (k < 0 && r >= 0) r = k; - k = link_drop_neighbors(link); + k = link_drop_managed_neighbors(link); if (k < 0 && r >= 0) r = k; - k = link_drop_routing_policy_rules(link); + k = link_drop_managed_routing_policy_rules(link); if (k < 0 && r >= 0) r = k; - ndisc_flush(link); - return r; } @@ -1119,6 +1095,10 @@ static int link_configure(Link *link) { link_set_state(link, LINK_STATE_CONFIGURING); + r = link_new_bound_to_list(link); + if (r < 0) + return r; + r = link_configure_traffic_control(link); if (r < 0) return r; @@ -1293,17 +1273,28 @@ static int link_reconfigure_impl(Link *link, bool force) { assert(link); + if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED)) + return 0; + r = link_get_network(link, &network); if (r < 0 && r != -ENOENT) return r; + if (link->state != LINK_STATE_UNMANAGED && !network) + /* If link is in initialized state, then link->network is also NULL. */ + force = true; + if (link->network == network && !force) return 0; - if (network) - log_link_info(link, "Reconfiguring with %s.", network->filename); - else - log_link_info(link, "Unmanaging interface."); + if (network) { + if (link->state == LINK_STATE_INITIALIZED) + log_link_info(link, "Configuring with %s.", network->filename); + else + log_link_info(link, "Reconfiguring with %s.", network->filename); + } else + log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO, + "Unmanaging interface."); /* Dropping old .network file */ r = link_stop_engines(link, false); @@ -1318,12 +1309,19 @@ static int link_reconfigure_impl(Link *link, bool force) { * link_drop_foreign_config() in link_configure(). */ link_foreignize_config(link); else { - r = link_drop_config(link); + /* Remove all managed configs. Note, foreign configs are removed in later by + * link_configure() -> link_drop_foreign_config() if the link is managed by us. */ + r = link_drop_managed_config(link); if (r < 0) return r; } - link_free_carrier_maps(link); + /* The bound_to map depends on .network file, hence it needs to be freed. But, do not free the + * bound_by map. Otherwise, if a link enters unmanaged state below, then its carrier state will + * not propagated to other interfaces anymore. Moreover, it is not necessary to recreate the + * map here, as it depends on .network files assigned to other links. */ + link_free_bound_to_list(link); + link_free_engines(link); link->network = network_unref(link->network); @@ -1337,10 +1335,6 @@ static int link_reconfigure_impl(Link *link, bool force) { link_update_operstate(link, true); link_dirty(link); - r = link_new_carrier_maps(link); - if (r < 0) - return r; - link_set_state(link, LINK_STATE_INITIALIZED); link->activated = false; @@ -1440,11 +1434,9 @@ int link_reconfigure_after_sleep(Link *link) { } static int link_initialized_and_synced(Link *link) { - Network *network; int r; assert(link); - assert(link->ifname); assert(link->manager); if (link->manager->test_mode) { @@ -1453,7 +1445,7 @@ static int link_initialized_and_synced(Link *link) { return 0; } - /* We may get called either from the asynchronous netlink callback, + /* This may get called either from the asynchronous netlink callback, * or directly from link_check_initialized() if running in a container. */ if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) return 0; @@ -1469,36 +1461,7 @@ static int link_initialized_and_synced(Link *link) { if (r < 0) return r; - if (!link->network) { - r = link_get_network(link, &network); - if (r == -ENOENT) { - link_set_state(link, LINK_STATE_UNMANAGED); - return 0; - } - if (r < 0) - return r; - - if (link->flags & IFF_LOOPBACK) { - if (network->link_local != ADDRESS_FAMILY_NO) - log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link"); - - if (network->dhcp != ADDRESS_FAMILY_NO) - log_link_debug(link, "Ignoring DHCP clients for loopback link"); - - if (network->dhcp_server) - log_link_debug(link, "Ignoring DHCP server for loopback link"); - } - - link->network = network_ref(network); - link_update_operstate(link, false); - link_dirty(link); - } - - r = link_new_bound_to_list(link); - if (r < 0) - return r; - - return link_configure(link); + return link_reconfigure_impl(link, /* force = */ false); } static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1706,7 +1669,7 @@ static int link_carrier_lost_impl(Link *link) { if (r < 0) ret = r; - r = link_drop_config(link); + r = link_drop_managed_config(link); if (r < 0 && ret >= 0) ret = r; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 4aab290bf8..4f13053082 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -416,13 +416,17 @@ int link_drop_foreign_neighbors(Link *link) { return r; } -int link_drop_neighbors(Link *link) { +int link_drop_managed_neighbors(Link *link) { Neighbor *neighbor; int k, r = 0; assert(link); SET_FOREACH(neighbor, link->neighbors) { + /* Do not touch nexthops managed by kernel or other tools. */ + if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + /* Ignore neighbors not assigned yet or already removing. */ if (!neighbor_exists(neighbor)) continue; diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index f31f58a4d4..ac1678de18 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -34,7 +34,7 @@ int neighbor_compare_func(const Neighbor *a, const Neighbor *b); void network_drop_invalid_neighbors(Network *network); -int link_drop_neighbors(Link *link); +int link_drop_managed_neighbors(Link *link); int link_drop_foreign_neighbors(Link *link); void link_foreignize_neighbors(Link *link); diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index e9e5d08557..b2bc0dc570 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -613,8 +613,8 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex if (nexthop->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, do not remove nexthops we configured. */ - if (foreign && nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN) + /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */ + if (foreign != (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN)) continue; /* Ignore nexthops not assigned yet or already removed. */ @@ -641,7 +641,7 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex } } -static int manager_drop_nexthops(Manager *manager) { +static int manager_drop_marked_nexthops(Manager *manager) { NextHop *nexthop; int k, r = 0; @@ -704,14 +704,14 @@ int link_drop_foreign_nexthops(Link *link) { manager_mark_nexthops(link->manager, /* foreign = */ true, NULL); - k = manager_drop_nexthops(link->manager); + k = manager_drop_marked_nexthops(link->manager); if (k < 0 && r >= 0) r = k; return r; } -int link_drop_nexthops(Link *link) { +int link_drop_managed_nexthops(Link *link) { NextHop *nexthop; int k, r = 0; @@ -723,6 +723,10 @@ int link_drop_nexthops(Link *link) { if (nexthop->protocol == RTPROT_KERNEL) continue; + /* Do not touch addresses managed by kernel or other tools. */ + if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + /* Ignore nexthops not assigned yet or already removing. */ if (!nexthop_exists(nexthop)) continue; @@ -734,7 +738,7 @@ int link_drop_nexthops(Link *link) { manager_mark_nexthops(link->manager, /* foreign = */ false, link); - k = manager_drop_nexthops(link->manager); + k = manager_drop_marked_nexthops(link->manager); if (k < 0 && r >= 0) r = k; diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 01b29ae560..16eb02f7eb 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -44,7 +44,7 @@ int nexthop_compare_func(const NextHop *a, const NextHop *b); void network_drop_invalid_nexthops(Network *network); -int link_drop_nexthops(Link *link); +int link_drop_managed_nexthops(Link *link); int link_drop_foreign_nexthops(Link *link); void link_foreignize_nexthops(Link *link); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 03021362b6..3a2e4490c1 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -788,8 +788,8 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce if (route->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, do not remove routes we configured. */ - if (foreign && route->source != NETWORK_CONFIG_SOURCE_FOREIGN) + /* When 'foreign' is true, mark only foreign routes, and vice versa. */ + if (foreign != (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)) continue; /* Do not touch dynamic routes. They will removed by dhcp_pd_prefix_lost() */ @@ -834,7 +834,7 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce } } -static int manager_drop_routes(Manager *manager) { +static int manager_drop_marked_routes(Manager *manager) { Route *route; int k, r = 0; @@ -955,14 +955,14 @@ int link_drop_foreign_routes(Link *link) { manager_mark_routes(link->manager, /* foreign = */ true, NULL); - k = manager_drop_routes(link->manager); + k = manager_drop_marked_routes(link->manager); if (k < 0 && r >= 0) r = k; return r; } -int link_drop_routes(Link *link) { +int link_drop_managed_routes(Link *link) { Route *route; int k, r = 0; @@ -973,6 +973,10 @@ int link_drop_routes(Link *link) { if (route_by_kernel(route)) continue; + /* Do not touch routes managed by kernel or other tools. */ + if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + if (!route_exists(route)) continue; @@ -983,7 +987,7 @@ int link_drop_routes(Link *link) { manager_mark_routes(link->manager, /* foreign = */ false, link); - k = manager_drop_routes(link->manager); + k = manager_drop_marked_routes(link->manager); if (k < 0 && r >= 0) r = k; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 3471008fee..ee8e36f83d 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -82,7 +82,7 @@ int route_remove(Route *route); int route_get(Manager *manager, Link *link, const Route *in, Route **ret); -int link_drop_routes(Link *link); +int link_drop_managed_routes(Link *link); int link_drop_foreign_routes(Link *link); void link_foreignize_routes(Link *link); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 1f3dfa53b0..a17d74e316 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -653,8 +653,8 @@ static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Li if (rule->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, do not remove rules we configured. */ - if (foreign && rule->source != NETWORK_CONFIG_SOURCE_FOREIGN) + /* When 'foreign' is true, mark only foreign rules, and vice versa. */ + if (foreign != (rule->source == NETWORK_CONFIG_SOURCE_FOREIGN)) continue; /* Ignore rules not assigned yet or already removing. */ diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 1ab147caae..2d1ce19165 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -71,7 +71,7 @@ int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const L static inline int manager_drop_foreign_routing_policy_rules(Manager *m) { return manager_drop_routing_policy_rules_internal(m, true, NULL); } -static inline int link_drop_routing_policy_rules(Link *link) { +static inline int link_drop_managed_routing_policy_rules(Link *link) { assert(link); return manager_drop_routing_policy_rules_internal(link->manager, false, link); } diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index ba16170393..9a2a839b40 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3893,7 +3893,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'NO-CARRIER') self.assertNotRegex(output, '192.168.0.15/24') - self.assertNotRegex(output, '192.168.0.16/24') + self.assertRegex(output, '192.168.0.16/24') # foreign address is kept print('### ip -6 route list table all dev bridge99') output = check_output('ip -6 route list table all dev bridge99')