diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2024-11-06 09:57:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-06 09:57:56 +0100 |
commit | df69f29728f8324075951060217e7821a793fcb4 (patch) | |
tree | a3260bfb5b396eb9d6cdb63362b8743b5d4fe6c9 /src/network | |
parent | UKI: Introduce `.dtbauto` sections (#34855) (diff) | |
parent | network: introduce LINK_RECONFIGURE_CLEANLY flag (diff) | |
download | systemd-df69f29728f8324075951060217e7821a793fcb4.tar.xz systemd-df69f29728f8324075951060217e7821a793fcb4.zip |
network: reconfigure interface more gracefully (#35035)
split-out of #34989.
Diffstat (limited to 'src/network')
27 files changed, 507 insertions, 395 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index ca9e825b56..a689fa68bd 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -1384,7 +1384,7 @@ int link_drop_ipv6ll_addresses(Link *link) { return 0; } -int link_drop_foreign_addresses(Link *link) { +int link_drop_unmanaged_addresses(Link *link) { Address *address; int r = 0; @@ -1401,18 +1401,22 @@ int link_drop_foreign_addresses(Link *link) { 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; - /* Ignore addresses not assigned yet or already removing. */ if (!address_exists(address)) continue; - /* link_address_is_dynamic() is slightly heavy. Let's call the function only when KeepConfiguration= is set. */ - if (IN_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP, KEEP_CONFIGURATION_STATIC) && - link_address_is_dynamic(link, address) == (link->network->keep_configuration == KEEP_CONFIGURATION_DHCP)) - continue; + if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN) { + if (link->network->keep_configuration == KEEP_CONFIGURATION_YES) + continue; + + /* link_address_is_dynamic() is slightly heavy. Let's call the function only when + * KeepConfiguration=dhcp or static. */ + if (IN_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP, KEEP_CONFIGURATION_STATIC) && + link_address_is_dynamic(link, address) == (link->network->keep_configuration == KEEP_CONFIGURATION_DHCP)) + continue; + + } else if (address->source != NETWORK_CONFIG_SOURCE_STATIC) + continue; /* Ignore dynamically configurad addresses. */ address_mark(address); } @@ -1464,15 +1468,6 @@ int link_drop_static_addresses(Link *link) { return r; } -void link_foreignize_addresses(Link *link) { - Address *address; - - assert(link); - - SET_FOREACH(address, link->addresses) - address->source = NETWORK_CONFIG_SOURCE_FOREIGN; -} - int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) { int r; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index fc6f33b0f9..f8d6b6c3a1 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -113,9 +113,8 @@ bool link_check_addresses_ready(Link *link, NetworkConfigSource source); DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_unref); int link_drop_static_addresses(Link *link); -int link_drop_foreign_addresses(Link *link); +int link_drop_unmanaged_addresses(Link *link); int link_drop_ipv6ll_addresses(Link *link); -void link_foreignize_addresses(Link *link); bool link_address_is_dynamic(const Link *link, const Address *address); int link_get_address_full( diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index 1f80513007..0819ed54f1 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -1252,6 +1252,28 @@ int dhcp_request_prefix_delegation(Link *link) { dhcp6_pd_assign_subnet_prefixes(link, uplink); } +int link_drop_dhcp_pd_config(Link *link, Network *network) { + assert(link); + assert(network); + + if (!link_dhcp_pd_is_enabled(link)) + return 0; + + /* If will be disabled or at least one config is changed, then drop all configurations. */ + if (!network->dhcp_pd || + link->network->dhcp_pd_assign != network->dhcp_pd_assign || + (link->network->dhcp_pd_assign && + (link->network->dhcp_pd_manage_temporary_address != network->dhcp_pd_manage_temporary_address || + !set_equal(link->network->dhcp_pd_tokens, network->dhcp_pd_tokens))) || + link->network->dhcp_pd_subnet_id != network->dhcp_pd_subnet_id || + link->network->dhcp_pd_route_metric != network->dhcp_pd_route_metric || + link->network->dhcp_pd_uplink_index != network->dhcp_pd_uplink_index || + !streq_ptr(link->network->dhcp_pd_uplink_name, network->dhcp_pd_uplink_name)) + return dhcp_pd_remove(link, /* only_marked = */ false); + + return 0; +} + int config_parse_dhcp_pd_subnet_id( const char *unit, const char *filename, diff --git a/src/network/networkd-dhcp-prefix-delegation.h b/src/network/networkd-dhcp-prefix-delegation.h index 0d68a51d0f..099e1444a5 100644 --- a/src/network/networkd-dhcp-prefix-delegation.h +++ b/src/network/networkd-dhcp-prefix-delegation.h @@ -10,12 +10,14 @@ typedef struct Address Address; typedef struct Link Link; +typedef struct Network Network; bool link_dhcp_pd_is_enabled(Link *link); bool dhcp_pd_is_uplink(Link *link, Link *target, bool accept_auto); int dhcp_pd_find_uplink(Link *link, Link **ret); int dhcp_pd_remove(Link *link, bool only_marked); int dhcp_request_prefix_delegation(Link *link); +int link_drop_dhcp_pd_config(Link *link, Network *network); int dhcp4_pd_prefix_acquired(Link *uplink); int dhcp6_pd_prefix_acquired(Link *uplink); void dhcp_pd_prefix_lost(Link *uplink); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index f22bc3954c..ca4e2a5926 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1375,49 +1375,49 @@ static int dhcp4_set_client_identifier(Link *link) { return 0; } -static int dhcp4_find_dynamic_address(Link *link, struct in_addr *ret) { - Address *a; - +static int dhcp4_set_request_address(Link *link) { assert(link); assert(link->network); - assert(ret); - - if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) - return false; + assert(link->dhcp_client); + /* 1. Use already assigned address. */ + Address *a; SET_FOREACH(a, link->addresses) { - if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN) - continue; - if (a->family != AF_INET) + if (a->source != NETWORK_CONFIG_SOURCE_DHCP4) continue; - if (link_address_is_dynamic(link, a)) - break; - } - - if (!a) - return false; - *ret = a->in_addr.in; - return true; -} + assert(a->family == AF_INET); -static int dhcp4_set_request_address(Link *link) { - struct in_addr a; + log_link_debug(link, "DHCPv4 CLIENT: requesting previously acquired address %s.", + IN4_ADDR_TO_STRING(&a->in_addr.in)); + return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in); + } - assert(link); - assert(link->network); - assert(link->dhcp_client); + /* 2. If no address is assigned yet, use explicitly configured address. */ + if (in4_addr_is_set(&link->network->dhcp_request_address)) { + log_link_debug(link, "DHCPv4 CLIENT: requesting address %s specified by RequestAddress=.", + IN4_ADDR_TO_STRING(&link->network->dhcp_request_address)); + return sd_dhcp_client_set_request_address(link->dhcp_client, &link->network->dhcp_request_address); + } - a = link->network->dhcp_request_address; + /* 3. If KeepConfiguration=dhcp, use a foreign dynamic address. */ + if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) + return 0; - if (in4_addr_is_null(&a)) - (void) dhcp4_find_dynamic_address(link, &a); + SET_FOREACH(a, link->addresses) { + if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + if (a->family != AF_INET) + continue; + if (!link_address_is_dynamic(link, a)) + continue; - if (in4_addr_is_null(&a)) - return 0; + log_link_debug(link, "DHCPv4 CLIENT: requesting foreign dynamic address %s.", + IN4_ADDR_TO_STRING(&a->in_addr.in)); + return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in); + } - log_link_debug(link, "DHCPv4 CLIENT: requesting %s.", IN4_ADDR_TO_STRING(&a)); - return sd_dhcp_client_set_request_address(link->dhcp_client, &a); + return 0; } static bool link_needs_dhcp_broadcast(Link *link) { @@ -1833,6 +1833,26 @@ int link_request_dhcp4_client(Link *link) { return 0; } +int link_drop_dhcp4_config(Link *link, Network *network) { + int ret = 0; + + assert(link); + assert(network); + + if (!link_dhcp4_enabled(link)) + return 0; /* Currently DHCPv4 client is not enabled, there is nothing we need to drop. */ + + if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) + /* Currently enabled but will be disabled. Stop the client and drop the lease. */ + ret = sd_dhcp_client_stop(link->dhcp_client); + + /* Even if the client is currently enabled and also enabled in the new .network file, detailed + * settings for the client may be different. Let's unref() the client. But do not unref() the lease. + * it will be unref()ed later when a new lease is acquired. */ + link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client); + return ret; +} + int config_parse_dhcp_max_attempts( const char *unit, const char *filename, diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h index b3fe0272fc..401ff59f69 100644 --- a/src/network/networkd-dhcp4.h +++ b/src/network/networkd-dhcp4.h @@ -25,6 +25,7 @@ int dhcp4_lease_lost(Link *link); int dhcp4_check_ready(Link *link); int link_request_dhcp4_client(Link *link); +int link_drop_dhcp4_config(Link *link, Network *network); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 1eb5138d5e..88e7c0a263 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -841,6 +841,26 @@ int link_request_dhcp6_client(Link *link) { return 0; } +int link_drop_dhcp6_config(Link *link, Network *network) { + int ret = 0; + + assert(link); + assert(network); + + if (!link_dhcp6_enabled(link)) + return 0; /* Currently DHCPv6 client is not enabled, there is nothing we need to drop. */ + + if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6)) + /* Currently enabled but will be disabled. Stop the client and drop the lease. */ + ret = sd_dhcp6_client_stop(link->dhcp6_client); + + /* Even if the client is currently enabled and also enabled in the new .network file, detailed + * settings for the client may be different. Let's unref() the client. But do not unref() the lease. + * it will be unref()ed later when a new lease is acquired. */ + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + return ret; +} + int link_serialize_dhcp6_client(Link *link, FILE *f) { _cleanup_free_ char *duid = NULL; uint32_t iaid; diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index 81267c255f..89915a19ac 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -13,6 +13,7 @@ typedef enum DHCP6ClientStartMode { } DHCP6ClientStartMode; typedef struct Link Link; +typedef struct Network Network; bool link_dhcp6_with_address_enabled(Link *link); int dhcp6_check_ready(Link *link); @@ -21,6 +22,7 @@ int dhcp6_start(Link *link); int dhcp6_start_on_ra(Link *link, bool information_request); int link_request_dhcp6_client(Link *link); +int link_drop_dhcp6_config(Link *link, Network *network); int link_serialize_dhcp6_client(Link *link, FILE *f); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 648f20d596..094cee6a14 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -179,10 +179,21 @@ static int ipv4ll_set_address(Link *link) { assert(link->network); assert(link->ipv4ll); - if (!in4_addr_is_set(&link->network->ipv4ll_start_address)) - return 0; + /* 1. Use already assigned address. */ + Address *a; + SET_FOREACH(a, link->addresses) { + if (a->source != NETWORK_CONFIG_SOURCE_IPV4LL) + continue; + + assert(a->family == AF_INET); + return sd_ipv4ll_set_address(link->ipv4ll, &a->in_addr.in); + } - return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address); + /* 2. If no address is assigned yet, use explicitly configured address. */ + if (in4_addr_is_set(&link->network->ipv4ll_start_address)) + return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address); + + return 0; } int ipv4ll_configure(Link *link) { @@ -231,6 +242,27 @@ int ipv4ll_configure(Link *link) { return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager); } +int link_drop_ipv4ll_config(Link *link, Network *network) { + int ret = 0; + + assert(link); + assert(network); + + if (!link_ipv4ll_enabled(link)) + return 0; + + Network *saved = link->network; + link->network = network; + bool enabled = link_ipv4ll_enabled(link); + link->network = saved; + + if (!enabled) + ret = sd_ipv4ll_stop(link->ipv4ll); + + link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll); + return ret; +} + int ipv4ll_update_mac(Link *link) { assert(link); diff --git a/src/network/networkd-ipv4ll.h b/src/network/networkd-ipv4ll.h index fa53bd2b39..f380afe9ec 100644 --- a/src/network/networkd-ipv4ll.h +++ b/src/network/networkd-ipv4ll.h @@ -6,10 +6,12 @@ #define IPV4LL_ROUTE_METRIC 2048 typedef struct Link Link; +typedef struct Network Network; bool link_ipv4ll_enabled(Link *link); int ipv4ll_configure(Link *link); +int link_drop_ipv4ll_config(Link *link, Network *network); int ipv4ll_update_mac(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll); diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index cb114929fa..27ee855181 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -665,11 +665,9 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_ if (r == 0) return 1; /* Polkit will call us back */ - r = link_reconfigure(l, /* force = */ true); - if (r > 0) - r = link_save_and_clean_full(l, /* also_save_manager = */ true); - if (r < 0) - return r; + r = link_reconfigure_full(l, LINK_RECONFIGURE_UNCONDITIONALLY | LINK_RECONFIGURE_CLEANLY, message, /* counter = */ NULL); + if (r != 0) + return r; /* Will reply later when r > 0. */ return sd_bus_reply_method_return(message, NULL); } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 0e37618a88..080e6beff3 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -445,7 +445,7 @@ void link_enter_failed(Link *link) { } log_link_info(link, "Trying to reconfigure the interface."); - if (link_reconfigure(link, /* force = */ true) > 0) + if (link_reconfigure(link, LINK_RECONFIGURE_UNCONDITIONALLY) > 0) return; stop: @@ -1008,7 +1008,7 @@ static int link_drop_requests(Link *link) { /* If the request is already called, but its reply is not received, then we need to * drop the configuration (e.g. address) here. Note, if the configuration is known, - * it will be handled later by link_drop_foreign_addresses() or so. */ + * it will be handled later by link_drop_unmanaged_addresses() or so. */ if (req->waiting_reply && link->state != LINK_STATE_LINGER) switch (req->type) { case REQUEST_TYPE_ADDRESS: { @@ -1086,33 +1086,23 @@ static Link *link_drop(Link *link) { return link_unref(link); } -static int link_drop_foreign_config(Link *link) { +static int link_drop_unmanaged_config(Link *link) { int r; assert(link); + assert(link->state == LINK_STATE_CONFIGURING); assert(link->manager); - /* Drop foreign config, but ignore unmanaged, loopback, or critical interfaces. We do not want - * to remove loopback address or addresses used for root NFS. */ - - if (IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) - return 0; - if (FLAGS_SET(link->flags, IFF_LOOPBACK)) - return 0; - if (link->network->keep_configuration == KEEP_CONFIGURATION_YES) - return 0; - - r = link_drop_foreign_routes(link); - - RET_GATHER(r, link_drop_foreign_nexthops(link)); - RET_GATHER(r, link_drop_foreign_addresses(link)); - RET_GATHER(r, link_drop_foreign_neighbors(link)); - RET_GATHER(r, manager_drop_foreign_routing_policy_rules(link->manager)); + r = link_drop_unmanaged_routes(link); + RET_GATHER(r, link_drop_unmanaged_nexthops(link)); + RET_GATHER(r, link_drop_unmanaged_addresses(link)); + RET_GATHER(r, link_drop_unmanaged_neighbors(link)); + RET_GATHER(r, link_drop_unmanaged_routing_policy_rules(link)); return r; } -static int link_drop_managed_config(Link *link) { +static int link_drop_static_config(Link *link) { int r; assert(link); @@ -1127,15 +1117,26 @@ static int link_drop_managed_config(Link *link) { return r; } -static void link_foreignize_config(Link *link) { +static int link_drop_dynamic_config(Link *link, Network *network) { + int r; + assert(link); - assert(link->manager); + assert(network); + + /* Drop unnecessary dynamic configurations gracefully, e.g. drop DHCP lease in the case that + * previously DHCP=yes and now DHCP=no, but keep DHCP lease when DHCP setting is unchanged. */ - link_foreignize_routes(link); - link_foreignize_nexthops(link); - link_foreignize_addresses(link); - link_foreignize_neighbors(link); - link_foreignize_routing_policy_rules(link); + r = link_drop_ndisc_config(link, network); + RET_GATHER(r, link_drop_radv_config(link, network)); + RET_GATHER(r, link_drop_dhcp4_config(link, network)); + RET_GATHER(r, link_drop_dhcp6_config(link, network)); + RET_GATHER(r, link_drop_dhcp_pd_config(link, network)); + RET_GATHER(r, link_drop_ipv4ll_config(link, network)); + link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server); + link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx); /* TODO: keep the received neighbors. */ + link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx); + + return r; } static int link_configure(Link *link) { @@ -1252,7 +1253,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_drop_foreign_config(link); + r = link_drop_unmanaged_config(link); if (r < 0) return r; @@ -1320,7 +1321,31 @@ static int link_get_network(Link *link, Network **ret) { return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found."); } -int link_reconfigure_impl(Link *link, bool force) { +static void link_enter_unmanaged(Link *link) { + assert(link); + + if (link->state == LINK_STATE_UNMANAGED) + return; + + log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO, + "Unmanaging interface."); + + (void) link_stop_engines(link, /* may_keep_dhcp = */ false); + (void) link_drop_requests(link); + (void) link_drop_static_config(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); + link_set_state(link, LINK_STATE_UNMANAGED); +} + +int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) { Network *network = NULL; int r; @@ -1336,74 +1361,65 @@ int link_reconfigure_impl(Link *link, bool force) { return 0; r = link_get_network(link, &network); - if (r < 0 && r != -ENOENT) + if (r == -ENOENT) { + link_enter_unmanaged(link); + return 0; + } + if (r < 0) 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) + if (link->network == network && !FLAGS_SET(flags, LINK_RECONFIGURE_UNCONDITIONALLY)) return 0; - if (network) { - _cleanup_free_ char *joined = strv_join(network->dropins, ", "); - - if (link->state == LINK_STATE_INITIALIZED) - log_link_info(link, "Configuring with %s%s%s%s.", - network->filename, - isempty(joined) ? "" : " (dropins: ", - joined, - isempty(joined) ? "" : ")"); - else - log_link_info(link, "Reconfiguring with %s%s%s%s.", - network->filename, - isempty(joined) ? "" : " (dropins: ", - joined, - isempty(joined) ? "" : ")"); - } else - log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO, - "Unmanaging interface."); + _cleanup_free_ char *joined = strv_join(network->dropins, ", "); + if (link->network) + log_link_info(link, "Reconfiguring with %s%s%s%s.", + network->filename, + isempty(joined) ? "" : " (dropins: ", + joined, + isempty(joined) ? "" : ")"); + else + log_link_info(link, "Configuring with %s%s%s%s.", + network->filename, + isempty(joined) ? "" : " (dropins: ", + joined, + isempty(joined) ? "" : ")"); /* Dropping old .network file */ - r = link_stop_engines(link, false); - if (r < 0) - return r; - r = link_drop_requests(link); - if (r < 0) - return r; + if (FLAGS_SET(flags, LINK_RECONFIGURE_CLEANLY)) { + /* Remove all static configurations. Note, dynamic configurations are dropped by + * link_stop_engines(), and foreign configurations will be removed later by + * link_configure() -> link_drop_unmanaged_config(). */ + r = link_drop_static_config(link); + if (r < 0) + return r; - if (network && !force && network->keep_configuration != KEEP_CONFIGURATION_YES) - /* When a new/updated .network file is assigned, first make all configs (addresses, - * routes, and so on) foreign, and then drop unnecessary configs later by - * link_drop_foreign_config() in link_configure(). - * Note, when KeepConfiguration=yes, link_drop_foreign_config() does nothing. Hence, - * here we need to drop the configs such as addresses, routes, and so on configured by - * the previously assigned .network file. */ - link_foreignize_config(link); - else { - /* 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); + /* Stop DHCP client and friends, and drop dynamic configurations like DHCP address. */ + r = link_stop_engines(link, /* may_keep_dhcp = */ false); + if (r < 0) + return r; + + /* Free DHCP client and friends. */ + link_free_engines(link); + } else { + r = link_drop_dynamic_config(link, network); if (r < 0) return r; } + r = link_drop_requests(link); + if (r < 0) + return r; + /* 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); - if (!network) { - link_set_state(link, LINK_STATE_UNMANAGED); - return 0; - } - /* Then, apply new .network file */ link->network = network_ref(network); link_update_operstate(link, true); @@ -1419,63 +1435,14 @@ int link_reconfigure_impl(Link *link, bool force) { return 1; } -static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) { - int r; - - assert(link); - - r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state"); - if (r <= 0) - return r; - - r = link_reconfigure_impl(link, force); - if (r < 0) { - link_enter_failed(link); - return 0; - } - - return r; -} - -static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false); -} - -static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true); -} - -int link_reconfigure(Link *link, bool force) { - int r; - - assert(link); - - /* When link in pending or initialized state, then link_configure() will be called. To prevent - * the function from being called multiple times simultaneously, refuse to reconfigure the - * interface in these cases. */ - if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER)) - return 0; /* 0 means no-op. */ - - r = link_call_getlink(link, force ? link_force_reconfigure_handler : link_reconfigure_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to reconfigure interface: %m"); - link_enter_failed(link); - return r; - } - - if (force || link->state == LINK_STATE_FAILED) - link_set_state(link, LINK_STATE_INITIALIZED); - - return 1; /* 1 means the interface will be reconfigured. */ -} - -typedef struct ReconfigureData { +typedef struct LinkReconfigurationData { Link *link; - Manager *manager; + LinkReconfigurationFlag flags; sd_bus_message *message; -} ReconfigureData; + unsigned *counter; +} LinkReconfigurationData; -static ReconfigureData* reconfigure_data_free(ReconfigureData *data) { +static LinkReconfigurationData* link_reconfiguration_data_free(LinkReconfigurationData *data) { if (!data) return NULL; @@ -1485,56 +1452,72 @@ static ReconfigureData* reconfigure_data_free(ReconfigureData *data) { return mfree(data); } -DEFINE_TRIVIAL_CLEANUP_FUNC(ReconfigureData*, reconfigure_data_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(LinkReconfigurationData*, link_reconfiguration_data_free); -static void reconfigure_data_destroy_callback(ReconfigureData *data) { +static void link_reconfiguration_data_destroy_callback(LinkReconfigurationData *data) { int r; assert(data); - assert(data->manager); - assert(data->manager->reloading > 0); - assert(data->message); - data->manager->reloading--; - if (data->manager->reloading <= 0) { - r = sd_bus_reply_method_return(data->message, NULL); - if (r < 0) - log_warning_errno(r, "Failed to send reply for 'Reload' DBus method, ignoring: %m"); + if (data->message) { + if (data->counter) { + assert(*data->counter > 0); + (*data->counter)--; + } + + if (!data->counter || *data->counter <= 0) { + r = sd_bus_reply_method_return(data->message, NULL); + if (r < 0) + log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m"); + } } - reconfigure_data_free(data); + link_reconfiguration_data_free(data); } -static int reconfigure_handler_on_bus_method_reload(sd_netlink *rtnl, sd_netlink_message *m, ReconfigureData *data) { - assert(data); - assert(data->link); - return link_reconfigure_handler_internal(rtnl, m, data->link, /* force = */ false); +static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, LinkReconfigurationData *data) { + Link *link = ASSERT_PTR(ASSERT_PTR(data)->link); + int r; + + r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state"); + if (r <= 0) + return r; + + r = link_reconfigure_impl(link, data->flags); + if (r < 0) { + link_enter_failed(link); + return 0; + } + + return r; } -int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message) { +int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_message *message, unsigned *counter) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - _cleanup_(reconfigure_data_freep) ReconfigureData *data = NULL; + _cleanup_(link_reconfiguration_data_freep) LinkReconfigurationData *data = NULL; int r; assert(link); assert(link->manager); assert(link->manager->rtnl); - assert(message); - /* See comments in link_reconfigure() above. */ + /* When the link is in the pending or initialized state, link_reconfigure_impl() will be called later + * by link_initialized() or link_initialized_and_synced(). To prevent the function from being called + * multiple times simultaneously, let's refuse to reconfigure the interface here in such cases. */ if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER)) - return 0; + return 0; /* 0 means no-op. */ - data = new(ReconfigureData, 1); + data = new(LinkReconfigurationData, 1); if (!data) { r = -ENOMEM; goto failed; } - *data = (ReconfigureData) { + *data = (LinkReconfigurationData) { .link = link_ref(link), - .manager = link->manager, - .message = sd_bus_message_ref(message), + .flags = flags, + .message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */ + .counter = counter, }; r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK, link->ifindex); @@ -1542,18 +1525,19 @@ int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message) { goto failed; r = netlink_call_async(link->manager->rtnl, NULL, req, - reconfigure_handler_on_bus_method_reload, - reconfigure_data_destroy_callback, data); + link_reconfigure_handler, + link_reconfiguration_data_destroy_callback, data); if (r < 0) goto failed; TAKE_PTR(data); - link->manager->reloading++; + if (counter) + (*counter)++; if (link->state == LINK_STATE_FAILED) link_set_state(link, LINK_STATE_INITIALIZED); - return 0; + return 1; /* 1 means the interface will be reconfigured, and bus method will be replied later. */ failed: log_link_warning_errno(link, r, "Failed to reconfigure interface: %m"); @@ -1580,7 +1564,7 @@ static int link_initialized_and_synced(Link *link) { return r; } - return link_reconfigure_impl(link, /* force = */ false); + return link_reconfigure_impl(link, /* flags = */ 0); } static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1624,7 +1608,7 @@ static int link_initialized(Link *link, sd_device *device) { log_link_warning_errno(link, r, "Failed to manage SR-IOV PF and VF ports, ignoring: %m"); if (link->state != LINK_STATE_PENDING) - return link_reconfigure(link, /* force = */ false); + return link_reconfigure(link, /* flags = */ 0); log_link_debug(link, "udev initialized link"); @@ -1726,7 +1710,7 @@ int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t } static int link_carrier_gained(Link *link) { - bool force_reconfigure; + LinkReconfigurationFlag flags = 0; int r; assert(link); @@ -1752,11 +1736,12 @@ static int link_carrier_gained(Link *link) { * already assigned to the interface (in that case, the .network file does not have the SSID= * setting in the [Match] section), and the interface is already being configured. Of course, * there may exist another .network file with higher priority and a matching SSID= setting. But - * in that case, link_reconfigure_impl() can handle that without the force_reconfigure flag. + * in that case, link_reconfigure_impl() can handle that without any flags. * * For non-wireless interfaces, we have no way to detect the connected network change. So, - * setting force_reconfigure = false. Note, both ssid and previous_ssid are NULL in that case. */ - force_reconfigure = link->previous_ssid && !streq_ptr(link->previous_ssid, link->ssid); + * we do not set any flags here. Note, both ssid and previous_ssid are NULL in that case. */ + if (link->previous_ssid && !streq_ptr(link->previous_ssid, link->ssid)) + flags |= LINK_RECONFIGURE_UNCONDITIONALLY | LINK_RECONFIGURE_CLEANLY; link->previous_ssid = mfree(link->previous_ssid); /* AP and P2P-GO interfaces may have a new SSID - update the link properties in case a new .network @@ -1771,7 +1756,7 @@ static int link_carrier_gained(Link *link) { /* At this stage, both wlan and link information should be up-to-date. Hence, it is not necessary to * call RTM_GETLINK, NL80211_CMD_GET_INTERFACE, or NL80211_CMD_GET_STATION commands, and simply call * link_reconfigure_impl(). Note, link_reconfigure_impl() returns 1 when the link is reconfigured. */ - r = link_reconfigure_impl(link, force_reconfigure); + r = link_reconfigure_impl(link, flags); if (r != 0) return r; @@ -1808,7 +1793,7 @@ static int link_carrier_lost_impl(Link *link) { return ret; RET_GATHER(ret, link_stop_engines(link, false)); - RET_GATHER(ret, link_drop_managed_config(link)); + RET_GATHER(ret, link_drop_static_config(link)); return ret; } @@ -2866,7 +2851,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man if (manager->enumerating) return 0; - r = link_reconfigure_impl(link, /* force = */ false); + r = link_reconfigure_impl(link, /* flags = */ 0); if (r < 0) { log_link_warning_errno(link, r, "Failed to reconfigure interface: %m"); link_enter_failed(link); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 1c5c9ec709..ad8bac7ad6 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -42,6 +42,11 @@ typedef enum LinkState { _LINK_STATE_INVALID = -EINVAL, } LinkState; +typedef enum LinkReconfigurationFlag { + LINK_RECONFIGURE_UNCONDITIONALLY = 1 << 0, /* Reconfigure an interface even if .network file is unchanged. */ + LINK_RECONFIGURE_CLEANLY = 1 << 1, /* Drop all existing configs before reconfiguring. Otherwise, reuse existing configs as possible as we can. */ +} LinkReconfigurationFlag; + typedef struct Manager Manager; typedef struct Network Network; typedef struct NetDev NetDev; @@ -259,9 +264,11 @@ LinkState link_state_from_string(const char *s) _pure_; int link_request_stacked_netdevs(Link *link, NetDevLocalAddressType type); -int link_reconfigure_impl(Link *link, bool force); -int link_reconfigure(Link *link, bool force); -int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message); +int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags); +int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_message *message, unsigned *counter); +static inline int link_reconfigure(Link *link, LinkReconfigurationFlag flags) { + return link_reconfigure_full(link, flags, NULL, NULL); +} int link_check_initialized(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 38013994c8..47299e3b27 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -89,7 +89,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b log_debug("Coming back from suspend, reconfiguring all connections..."); HASHMAP_FOREACH(link, m->links_by_index) - (void) link_reconfigure(link, /* force = */ true); + (void) link_reconfigure(link, LINK_RECONFIGURE_UNCONDITIONALLY); return 0; } @@ -1221,12 +1221,9 @@ int manager_reload(Manager *m, sd_bus_message *message) { goto finish; } - HASHMAP_FOREACH(link, m->links_by_index) { - if (message) - (void) link_reconfigure_on_bus_method_reload(link, message); - else - (void) link_reconfigure(link, /* force = */ false); - } + HASHMAP_FOREACH(link, m->links_by_index) + (void) link_reconfigure_full(link, /* flags = */ 0, message, + /* counter = */ message ? &m->reloading : NULL); log_debug("Reloaded."); r = 0; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index d6e36575a2..5ab9c881f2 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -2668,6 +2668,90 @@ int link_request_ndisc(Link *link) { return 0; } +int link_drop_ndisc_config(Link *link, Network *network) { + int r, ret = 0; + + assert(link); + assert(network); + + if (!link_ndisc_enabled(link)) + return 0; /* Currently DHCPv4 client is not enabled, there is nothing we need to drop. */ + + Network *current = link->network; + link->network = network; + bool enabled = link_ndisc_enabled(link); + link->network = current; + + if (!enabled) { + /* Currently enabled but will be disabled. Stop the client and flush configs. */ + ret = ndisc_stop(link); + ndisc_flush(link); + link->ndisc = sd_ndisc_unref(link->ndisc); + return ret; + } + + /* Even if the client is currently enabled and also enabled in the new .network file, detailed + * settings for the client may be different. Let's unref() the client. */ + link->ndisc = sd_ndisc_unref(link->ndisc); + + /* Redirect messages will be ignored. Drop configurations based on the previously received redirect + * messages. */ + if (!network->ndisc_use_redirect) + (void) ndisc_drop_redirect(link, /* router = */ NULL); + + /* If one of the route setting is changed, drop all routes. */ + if (link->network->ndisc_use_gateway != network->ndisc_use_gateway || + link->network->ndisc_use_route_prefix != network->ndisc_use_route_prefix || + link->network->ndisc_use_onlink_prefix != network->ndisc_use_onlink_prefix || + link->network->ndisc_quickack != network->ndisc_quickack || + link->network->ndisc_route_metric_high != network->ndisc_route_metric_high || + link->network->ndisc_route_metric_medium != network->ndisc_route_metric_medium || + link->network->ndisc_route_metric_low != network->ndisc_route_metric_low || + !set_equal(link->network->ndisc_deny_listed_router, network->ndisc_deny_listed_router) || + !set_equal(link->network->ndisc_allow_listed_router, network->ndisc_allow_listed_router) || + !set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) || + !set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix) || + !set_equal(link->network->ndisc_deny_listed_route_prefix, network->ndisc_deny_listed_route_prefix) || + !set_equal(link->network->ndisc_allow_listed_route_prefix, network->ndisc_allow_listed_route_prefix)) { + Route *route; + SET_FOREACH(route, link->manager->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (route->nexthop.ifindex != link->ifindex) + continue; + + if (route->protocol == RTPROT_REDIRECT) + continue; /* redirect route is handled by ndisc_drop_redirect(). */ + + r = route_remove_and_cancel(route, link->manager); + if (r < 0) + RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC route, ignoring: %m")); + } + } + + /* If SLAAC address is disabled, drop all addresses. */ + if (!network->ndisc_use_autonomous_prefix || + !set_equal(link->network->ndisc_tokens, network->ndisc_tokens) || + !set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) || + !set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix)) { + Address *address; + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + r = address_remove_and_cancel(address, link); + if (r < 0) + RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC address, ignoring: %m")); + } + } + + if (!network->ndisc_use_mtu) + link->ndisc_mtu = 0; + + return ret; +} + int ndisc_stop(Link *link) { assert(link); diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 2ee07d3f72..bc5d277c5b 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -69,6 +69,7 @@ int ndisc_stop(Link *link); void ndisc_flush(Link *link); int link_request_ndisc(Link *link); +int link_drop_ndisc_config(Link *link, Network *network); int ndisc_reconfigure_address(Address *address, Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client); diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 6bfb6579e1..83275f2fc8 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -467,7 +467,7 @@ int neighbor_remove(Neighbor *neighbor, Link *link) { return 0; } -int link_drop_foreign_neighbors(Link *link) { +int link_drop_unmanaged_neighbors(Link *link) { Neighbor *neighbor; int r = 0; @@ -476,14 +476,15 @@ int link_drop_foreign_neighbors(Link *link) { /* First, mark all neighbors. */ SET_FOREACH(neighbor, link->neighbors) { - /* Do not remove neighbors we configured. */ - if (neighbor->source != NETWORK_CONFIG_SOURCE_FOREIGN) - continue; - /* Ignore neighbors not assigned yet or already removing. */ 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)) + continue; + neighbor_mark(neighbor); } @@ -495,6 +496,7 @@ int link_drop_foreign_neighbors(Link *link) { neighbor_unmark(existing); } + /* Finally, remove all marked neighbors. */ SET_FOREACH(neighbor, link->neighbors) { if (!neighbor_is_marked(neighbor)) continue; @@ -526,15 +528,6 @@ int link_drop_static_neighbors(Link *link) { return r; } -void link_foreignize_neighbors(Link *link) { - Neighbor *neighbor; - - assert(link); - - SET_FOREACH(neighbor, link->neighbors) - neighbor->source = NETWORK_CONFIG_SOURCE_FOREIGN; -} - int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL; Neighbor *neighbor = NULL; diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index c306e407d3..5dd43a98a0 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -36,8 +36,7 @@ int neighbor_remove(Neighbor *neighbor, Link *link); int network_drop_invalid_neighbors(Network *network); int link_drop_static_neighbors(Link *link); -int link_drop_foreign_neighbors(Link *link); -void link_foreignize_neighbors(Link *link); +int link_drop_unmanaged_neighbors(Link *link); int link_request_static_neighbors(Link *link); diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index f935831efe..e0fa4745ea 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -856,9 +856,10 @@ static bool nexthop_can_update(const NextHop *assigned_nexthop, const NextHop *r return true; } -static void link_mark_nexthops(Link *link, bool foreign) { +int link_drop_nexthops(Link *link, bool only_static) { NextHop *nexthop; Link *other; + int r = 0; assert(link); assert(link->manager); @@ -869,14 +870,21 @@ static void link_mark_nexthops(Link *link, bool foreign) { if (nexthop->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */ - if (nexthop->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC)) - continue; - /* Ignore nexthops not assigned yet or already removed. */ if (!nexthop_exists(nexthop)) continue; + if (only_static) { + if (nexthop->source != NETWORK_CONFIG_SOURCE_STATIC) + continue; + } else { + /* Do not mark foreign nexthop when KeepConfiguration= is enabled. */ + if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN && + link->network && + FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) + continue; + } + /* Ignore nexthops bound to other links. */ if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex) continue; @@ -886,7 +894,7 @@ static void link_mark_nexthops(Link *link, bool foreign) { /* Then, unmark all nexthops requested by active links. */ HASHMAP_FOREACH(other, link->manager->links_by_index) { - if (!foreign && other == link) + if (only_static && other == link) continue; if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) @@ -905,17 +913,8 @@ static void link_mark_nexthops(Link *link, bool foreign) { nexthop_unmark(existing); } } -} - -int link_drop_nexthops(Link *link, bool foreign) { - NextHop *nexthop; - int r = 0; - - assert(link); - assert(link->manager); - - link_mark_nexthops(link, foreign); + /* Finally, remove all marked nexthops. */ HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { if (!nexthop_is_marked(nexthop)) continue; @@ -926,22 +925,6 @@ int link_drop_nexthops(Link *link, bool foreign) { return r; } -void link_foreignize_nexthops(Link *link) { - NextHop *nexthop; - - assert(link); - assert(link->manager); - - link_mark_nexthops(link, /* foreign = */ false); - - HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { - if (!nexthop_is_marked(nexthop)) - continue; - - nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN; - } -} - static int nexthop_update_group(NextHop *nexthop, sd_netlink_message *message) { _cleanup_hashmap_free_free_ Hashmap *h = NULL; _cleanup_free_ struct nexthop_grp *group = NULL; diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index b918bc6ea3..b6184503f4 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -53,14 +53,13 @@ int nexthop_remove(NextHop *nexthop, Manager *manager); int network_drop_invalid_nexthops(Network *network); -int link_drop_nexthops(Link *link, bool foreign); -static inline int link_drop_foreign_nexthops(Link *link) { - return link_drop_nexthops(link, /* foreign = */ true); +int link_drop_nexthops(Link *link, bool only_static); +static inline int link_drop_unmanaged_nexthops(Link *link) { + return link_drop_nexthops(link, /* only_static = */ false); } static inline int link_drop_static_nexthops(Link *link) { - return link_drop_nexthops(link, /* foreign = */ false); + return link_drop_nexthops(link, /* only_static = */ true); } -void link_foreignize_nexthops(Link *link); int link_request_static_nexthops(Link *link, bool only_ipv4); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 8cff0237a9..a284e2c47e 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -645,6 +645,22 @@ int link_request_radv(Link *link) { return 0; } +int link_drop_radv_config(Link *link, Network *network) { + int ret = 0; + + assert(link); + assert(network); + + if (!link_radv_enabled(link)) + return 0; + + // FIXME: check detailed settings and do not stop if nothing changed. + // FIXME: save dynamic prefixes acquired by DHCP-PD. + ret = sd_radv_stop(link->radv); + link->radv = sd_radv_unref(link->radv); + return ret; +} + int radv_start(Link *link) { int r; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index ccdd656449..afde1fcfe2 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -68,6 +68,7 @@ int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_le usec_t lifetime_preferred_usec, usec_t lifetime_valid_usec); int link_request_radv(Link *link); +int link_drop_radv_config(Link *link, Network *network); const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_; RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 6769e6ce85..54d8c9f51f 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -1439,10 +1439,10 @@ static int link_unmark_route(Link *link, const Route *route, const RouteNextHop return 1; } -static int link_mark_routes(Link *link, bool foreign) { +int link_drop_routes(Link *link, bool only_static) { Route *route; Link *other; - int r; + int r = 0; assert(link); assert(link->manager); @@ -1453,31 +1453,35 @@ static int link_mark_routes(Link *link, bool foreign) { if (route_by_kernel(route)) continue; - /* When 'foreign' is true, mark only foreign routes, and vice versa. - * Note, do not touch dynamic routes. They will removed by when e.g. lease is lost. */ - if (route->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC)) - continue; - /* Ignore routes not assigned yet or already removed. */ if (!route_exists(route)) continue; - if (link->network) { - if (route->protocol == RTPROT_STATIC && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) + if (only_static) { + if (route->source != NETWORK_CONFIG_SOURCE_STATIC) continue; - - if (route->protocol == RTPROT_DHCP && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) + } 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_DHCP)) + continue; + } } - /* When we mark foreign routes, do not mark routes assigned to other interfaces. + /* When we also mark foreign routes, do not mark routes assigned to other interfaces. * Otherwise, routes assigned to unmanaged interfaces will be dropped. * Note, route_get_link() does not provide assigned link for routes with an unreachable type * or IPv4 multipath routes. So, the current implementation does not support managing such * routes by other daemon or so, unless ManageForeignRoutes=no. */ - if (foreign) { + if (!only_static) { Link *route_link; if (route_get_link(link->manager, route, &route_link) >= 0 && route_link != link) @@ -1489,7 +1493,7 @@ static int link_mark_routes(Link *link, bool foreign) { /* Then, unmark all routes requested by active links. */ HASHMAP_FOREACH(other, link->manager->links_by_index) { - if (!foreign && other == link) + if (only_static && other == link) continue; if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) @@ -1523,20 +1527,7 @@ static int link_mark_routes(Link *link, bool foreign) { } } - return 0; -} - -int link_drop_routes(Link *link, bool foreign) { - Route *route; - int r; - - assert(link); - assert(link->manager); - - r = link_mark_routes(link, foreign); - if (r < 0) - return r; - + /* Finally, remove all marked routes. */ SET_FOREACH(route, link->manager->routes) { if (!route_is_marked(route)) continue; @@ -1547,27 +1538,6 @@ int link_drop_routes(Link *link, bool foreign) { return r; } -int link_foreignize_routes(Link *link) { - Route *route; - int r; - - assert(link); - assert(link->manager); - - r = link_mark_routes(link, /* foreign = */ false); - if (r < 0) - return r; - - SET_FOREACH(route, link->manager->routes) { - if (!route_is_marked(route)) - continue; - - route->source = NETWORK_CONFIG_SOURCE_FOREIGN; - } - - return 0; -} - int network_add_ipv4ll_route(Network *network) { _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; unsigned section_line; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index b39e7e25d3..b0da5d80c7 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -102,14 +102,13 @@ int route_get_request(Manager *manager, const Route *route, Request **ret); bool route_can_update(const Route *existing, const Route *requesting); -int link_drop_routes(Link *link, bool foreign); +int link_drop_routes(Link *link, bool only_static); static inline int link_drop_static_routes(Link *link) { - return link_drop_routes(link, false); -} -static inline int link_drop_foreign_routes(Link *link) { return link_drop_routes(link, true); } -int link_foreignize_routes(Link *link); +static inline int link_drop_unmanaged_routes(Link *link) { + return link_drop_routes(link, false); +} int link_request_route( Link *link, diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index e2c76616dd..1a04af6359 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -800,21 +800,30 @@ static void manager_unmark_routing_policy_rule(Manager *m, const RoutingPolicyRu routing_policy_rule_unmark(existing); } -static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Link *except) { +int link_drop_routing_policy_rules(Link *link, bool only_static) { RoutingPolicyRule *rule; - Link *link; + int r = 0; - assert(m); + assert(link); + assert(link->manager); /* First, mark all existing rules. */ - SET_FOREACH(rule, m->rules) { + SET_FOREACH(rule, link->manager->rules) { /* Do not touch rules managed by kernel. */ if (rule->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, mark only foreign rules, and vice versa. */ - if (rule->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC)) - 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)) @@ -824,59 +833,36 @@ static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Li } /* Then, unmark all rules requested by active links. */ - HASHMAP_FOREACH(link, m->links_by_index) { - if (link == except) + Link *other; + HASHMAP_FOREACH(other, link->manager->links_by_index) { + if (only_static && other == link) continue; - if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) continue; - HASHMAP_FOREACH(rule, link->network->rules_by_section) { + HASHMAP_FOREACH(rule, other->network->rules_by_section) { if (IN_SET(rule->family, AF_INET, AF_INET6)) - manager_unmark_routing_policy_rule(m, rule, rule->family); + manager_unmark_routing_policy_rule(link->manager, 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); + manager_unmark_routing_policy_rule(link->manager, rule, AF_INET); + manager_unmark_routing_policy_rule(link->manager, rule, AF_INET6); } } } -} - -int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except) { - RoutingPolicyRule *rule; - int r = 0; - - assert(m); - manager_mark_routing_policy_rules(m, foreign, except); - - SET_FOREACH(rule, m->rules) { + /* Finally, remove all marked rules. */ + SET_FOREACH(rule, link->manager->rules) { if (!routing_policy_rule_is_marked(rule)) continue; - RET_GATHER(r, routing_policy_rule_remove(rule, m)); + RET_GATHER(r, routing_policy_rule_remove(rule, link->manager)); } return r; } -void link_foreignize_routing_policy_rules(Link *link) { - RoutingPolicyRule *rule; - - assert(link); - assert(link->manager); - - manager_mark_routing_policy_rules(link->manager, /* foreign = */ false, link); - - SET_FOREACH(rule, link->manager->rules) { - if (!routing_policy_rule_is_marked(rule)) - continue; - - rule->source = NETWORK_CONFIG_SOURCE_FOREIGN; - } -} - static int routing_policy_rule_process_request(Request *req, Link *link, RoutingPolicyRule *rule) { RoutingPolicyRule *existing; int r; diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 3b106c11d3..61609930db 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -63,15 +63,14 @@ void network_drop_invalid_routing_policy_rules(Network *network); int link_request_static_routing_policy_rules(Link *link); int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); -int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except); -static inline int manager_drop_foreign_routing_policy_rules(Manager *m) { - return manager_drop_routing_policy_rules_internal(m, true, NULL); + +int link_drop_routing_policy_rules(Link *link, bool only_static); +static inline int link_drop_unmanaged_routing_policy_rules(Link *link) { + return link_drop_routing_policy_rules(link, false); } static inline int link_drop_static_routing_policy_rules(Link *link) { - assert(link); - return manager_drop_routing_policy_rules_internal(link->manager, false, link); + return link_drop_routing_policy_rules(link, true); } -void link_foreignize_routing_policy_rules(Link *link); DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(RoutingPolicyRule, routing_policy_rule); diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c index ee63c3ec96..bc1d79adce 100644 --- a/src/network/networkd-wifi.c +++ b/src/network/networkd-wifi.c @@ -287,7 +287,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess * To make SSID= or other WiFi related settings in [Match] section work, let's try to * reconfigure the interface. */ if (link->ssid && link_has_carrier(link)) { - r = link_reconfigure_impl(link, /* force = */ false); + r = link_reconfigure_impl(link, /* flags = */ 0); if (r < 0) { log_link_warning_errno(link, r, "Failed to reconfigure interface: %m"); link_enter_failed(link); @@ -326,7 +326,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess } /* If necessary, reconfigure based on those new properties */ - r = link_reconfigure_impl(link, /* force = */ false); + r = link_reconfigure_impl(link, /* flags = */ 0); if (r < 0) { log_link_warning_errno(link, r, "Failed to reconfigure interface: %m"); link_enter_failed(link); |