From: Yu Watanabe Date: Tue, 5 Nov 2024 02:39:31 +0000 (+0900) Subject: network: keep dynamic configurations as possible as we can on reconfigure X-Git-Tag: v257-rc1~18^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=451c2baf30f50b95d73e648058c7c2348dbf0c31;p=thirdparty%2Fsystemd.git network: keep dynamic configurations as possible as we can on reconfigure E.g. when a .network file is updated, but DHCP setting is unchanged, it is not necessary to drop acquired DHCP lease. So, let's not stop DHCP client and friends in link_reconfigure_impl(), but stop them later when we know they are not necessary anymore. Still DHCP clients and friends are stopped and leases are dropped when the explicit reconfiguration is requested --- diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index 1f80513007c..0819ed54f13 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 0d68a51d0f6..099e1444a54 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 f22bc3954ca..ca4e2a59261 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 b3fe0272fc0..401ff59f694 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 1eb5138d5e0..88e7c0a263d 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 81267c255f6..89915a19ac1 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 648f20d596c..094cee6a148 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 fa53bd2b39a..f380afe9ece 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.c b/src/network/networkd-link.c index 8bd32976bd8..8f925f895ee 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1117,6 +1117,28 @@ static int link_drop_static_config(Link *link) { return r; } +static int link_drop_dynamic_config(Link *link, Network *network) { + int r; + + assert(link); + 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. */ + + 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) { int r; @@ -1364,13 +1386,6 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) { 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_UNCONDITIONALLY)) { /* Remove all static configurations. Note, dynamic configurations are dropped by @@ -1379,15 +1394,30 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) { r = link_drop_static_config(link); if (r < 0) return r; + + /* 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); /* Then, apply new .network file */ diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 677ddc6b1cc..ee1e09dd698 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 2ee07d3f727..bc5d277c5b1 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-radv.c b/src/network/networkd-radv.c index 8cff0237a96..a284e2c47eb 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 ccdd656449d..afde1fcfe23 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/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 61a4933d652..c7d75d8a670 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -6704,6 +6704,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '--dhcp-option=option6:ntp-server,[2600::ff]') networkctl_reload() + networkctl_reconfigure('veth99') # Release previously acquired lease and start new DHCPv6 handshake. self.wait_online('veth99:routable', 'veth-peer:routable') # checking address