summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2024-11-05 03:39:31 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2024-11-05 18:05:00 +0100
commit451c2baf30f50b95d73e648058c7c2348dbf0c31 (patch)
tree38ddc600818d9f98d876d2c61db88523d008b661 /src/network
parentnetwork: merge link_foreignize_config() and link_drop_foreign_config() (diff)
downloadsystemd-451c2baf30f50b95d73e648058c7c2348dbf0c31.tar.xz
systemd-451c2baf30f50b95d73e648058c7c2348dbf0c31.zip
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
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkd-dhcp-prefix-delegation.c22
-rw-r--r--src/network/networkd-dhcp-prefix-delegation.h2
-rw-r--r--src/network/networkd-dhcp4.c82
-rw-r--r--src/network/networkd-dhcp4.h1
-rw-r--r--src/network/networkd-dhcp6.c20
-rw-r--r--src/network/networkd-dhcp6.h2
-rw-r--r--src/network/networkd-ipv4ll.c38
-rw-r--r--src/network/networkd-ipv4ll.h2
-rw-r--r--src/network/networkd-link.c46
-rw-r--r--src/network/networkd-ndisc.c84
-rw-r--r--src/network/networkd-ndisc.h1
-rw-r--r--src/network/networkd-radv.c16
-rw-r--r--src/network/networkd-radv.h1
13 files changed, 275 insertions, 42 deletions
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.c b/src/network/networkd-link.c
index 8bd32976bd..8f925f895e 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 677ddc6b1c..ee1e09dd69 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-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_;