merge: branch 'ndisc_evict'

ndisc: add option to evict oldest prefix when maximum is reached

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2366
This commit is contained in:
Scrooge McDuck 2026-02-21 21:38:35 +00:00
commit 498a9efecd
9 changed files with 98 additions and 17 deletions

2
NEWS
View file

@ -8,6 +8,8 @@ subject to change and not guaranteed to be compatible with
the later release.
USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* A new "prefix-delegation.evict-oldest" configuration option is available to
evict old prefixes rather than dropping new ones when the limit is reached.
* Unify the versioning to use everywhere the scheme with the -rcX or -dev
suffixes when appropriate. This affects, for example, the URL and filename
of the release tarball and the version reported by nmcli and the daemon.

View file

@ -13026,19 +13026,21 @@ _dev_ipac6_grace_period_start(NMDevice *self, guint32 timeout_sec, gboolean forc
static void
_dev_ipac6_start(NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
NMConnection *connection;
NMSettingIP6Config *s_ip = NULL;
NMNDiscNodeType node_type;
NMUtilsStableType stable_type;
const char *stable_id;
int max_addresses;
int router_solicitations;
int router_solicitation_interval;
guint32 ra_timeout;
guint32 default_ra_timeout;
NMUtilsIPv6IfaceId iid;
gboolean is_token;
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
NMConnection *connection;
NMSettingIP6Config *s_ip = NULL;
NMSettingPrefixDelegation *s_pd;
bool pd_evict_oldest = FALSE;
NMNDiscNodeType node_type;
NMUtilsStableType stable_type;
const char *stable_id;
int max_addresses;
int router_solicitations;
int router_solicitation_interval;
guint32 ra_timeout;
guint32 default_ra_timeout;
NMUtilsIPv6IfaceId iid;
gboolean is_token;
if (priv->ipac6_data.state == NM_DEVICE_IP_STATE_NONE)
_dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_PENDING);
@ -13083,6 +13085,10 @@ _dev_ipac6_start(NMDevice *self)
stable_id = _prop_get_connection_stable_id(self, connection, &stable_type);
s_pd = nm_device_get_applied_setting(self, NM_TYPE_SETTING_PREFIX_DELEGATION);
if (s_pd)
pd_evict_oldest = nm_setting_prefix_delegation_get_evict_oldest(s_pd);
{
const NMNDiscConfig config = {
.l3cfg = nm_device_get_l3cfg(self),
@ -13096,6 +13102,7 @@ _dev_ipac6_start(NMDevice *self)
.router_solicitation_interval = router_solicitation_interval,
.ra_timeout = ra_timeout,
.ip6_privacy = _prop_get_ipv6_ip6_privacy(self, connection),
.pd_evict_oldest = pd_evict_oldest,
};
priv->ipac6_data.ndisc = nm_lndp_ndisc_new(&config);

View file

@ -664,8 +664,33 @@ nm_ndisc_add_address(NMNDisc *ndisc,
* what the kernel does, because it considers *all* addresses (including
* static and other temporary addresses).
**/
if (rdata->addresses->len >= priv->config.max_addresses)
return FALSE;
if (rdata->addresses->len >= priv->config.max_addresses) {
guint oldest_idx = 0;
gint64 oldest_expiry = G_MAXINT64;
/* In PD mode with evict_oldest enabled, and the new address is still
* preferred (active), find and evict the address with shortest remaining
* lifetime to make room for the new prefix. */
if (from_ra || !priv->config.pd_evict_oldest
|| new_item->expiry_preferred_msec <= NM_NDISC_EXPIRY_BASE_TIMESTAMP) {
return FALSE;
}
for (i = 0; i < rdata->addresses->len; i++) {
NMNDiscAddress *item = &nm_g_array_index(rdata->addresses, NMNDiscAddress, i);
if (item->expiry_msec >= oldest_expiry)
continue;
oldest_expiry = item->expiry_msec;
oldest_idx = i;
}
_LOGI("add_address: max_addresses=%u reached, evicting oldest "
"(expiry=%" G_GINT64_FORMAT " ms)",
priv->config.max_addresses,
oldest_expiry);
g_array_remove_index(rdata->addresses, oldest_idx);
}
if (new_item->expiry_msec <= now_msec)
return FALSE;

View file

@ -183,6 +183,7 @@ typedef struct {
NMSettingIP6ConfigAddrGenMode addr_gen_mode;
NMNDiscNodeType node_type;
NMSettingIP6ConfigPrivacy ip6_privacy;
bool pd_evict_oldest;
} NMNDiscConfig;
struct _NMNDiscPrivate;

View file

@ -2127,6 +2127,7 @@ global:
nm_setting_geneve_new;
nm_setting_ip4_config_clat_get_type;
nm_setting_ip4_config_get_clat;
nm_setting_prefix_delegation_get_evict_oldest;
nm_utils_wifi_6ghz_freqs;
nm_utils_wifi_freq_to_band;
nm_wifi_band_get_type;

View file

@ -2366,6 +2366,10 @@
<setting name="prefix-delegation"
gtype="NMSettingPrefixDelegation"
>
<property name="evict-oldest"
dbus-type="b"
gprop-type="gboolean"
/>
<property name="subnet-id"
dbus-type="x"
gprop-type="gint64"

View file

@ -18,7 +18,7 @@
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SUBNET_ID, );
NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SUBNET_ID, PROP_EVICT_OLDEST, );
/**
* NMSettingPrefixDelegation:
@ -30,6 +30,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_SUBNET_ID, );
struct _NMSettingPrefixDelegation {
NMSetting parent;
gint64 subnet_id;
bool evict_oldest;
};
struct _NMSettingPrefixDelegationClass {
@ -56,6 +57,22 @@ nm_setting_prefix_delegation_get_subnet_id(NMSettingPrefixDelegation *setting)
return setting->subnet_id;
}
/**
* nm_setting_prefix_delegation_get_evict_oldest:
* @setting: the #NMSettingPrefixDelegation
*
* Returns: whether to evict oldest addresses when adding new ones
*
* Since: 1.58
**/
gboolean
nm_setting_prefix_delegation_get_evict_oldest(NMSettingPrefixDelegation *setting)
{
g_return_val_if_fail(NM_IS_SETTING_PREFIX_DELEGATION(setting), FALSE);
return setting->evict_oldest;
}
/*****************************************************************************/
static void
@ -108,6 +125,26 @@ nm_setting_prefix_delegation_class_init(NMSettingPrefixDelegationClass *klass)
NMSettingPrefixDelegation,
subnet_id);
/**
* NMSettingPrefixDelegation:evict-oldest:
*
* If %TRUE, when the maximum number of addresses is reached and a new
* prefix is delegated, the address with the shortest remaining lifetime
* will be evicted to make room for the new prefix. This is useful for
* scenarios where upstream prefixes change frequently (e.g., unstable
* connections), ensuring the newest working prefix is always advertised.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_boolean(properties_override,
obj_properties,
NM_SETTING_PREFIX_DELEGATION_EVICT_OLDEST,
PROP_EVICT_OLDEST,
FALSE,
NM_SETTING_PARAM_NONE,
NMSettingPrefixDelegation,
evict_oldest);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit(setting_class,

View file

@ -25,7 +25,8 @@ G_BEGIN_DECLS
#define NM_SETTING_PREFIX_DELEGATION_SETTING_NAME "prefix-delegation"
#define NM_SETTING_PREFIX_DELEGATION_SUBNET_ID "subnet-id"
#define NM_SETTING_PREFIX_DELEGATION_SUBNET_ID "subnet-id"
#define NM_SETTING_PREFIX_DELEGATION_EVICT_OLDEST "evict-oldest"
typedef struct _NMSettingPrefixDelegationClass NMSettingPrefixDelegationClass;
@ -35,6 +36,8 @@ NM_AVAILABLE_IN_1_54
NMSetting *nm_setting_prefix_delegation_new(void);
NM_AVAILABLE_IN_1_54
gint64 nm_setting_prefix_delegation_get_subnet_id(NMSettingPrefixDelegation *setting);
NM_AVAILABLE_IN_1_58
gboolean nm_setting_prefix_delegation_get_evict_oldest(NMSettingPrefixDelegation *setting);
G_END_DECLS

View file

@ -516,5 +516,6 @@
#define DESCRIBE_DOC_NM_SETTING_LOOPBACK_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with external-ids for OVS.")
#define DESCRIBE_DOC_NM_SETTING_OVS_OTHER_CONFIG_DATA N_("A dictionary of key/value pairs with other_config settings for OVS. See also \"other_config\" in the \"ovs-vswitchd.conf.db\" manual for the keys that OVS supports.")
#define DESCRIBE_DOC_NM_SETTING_PREFIX_DELEGATION_EVICT_OLDEST N_("If TRUE, when the maximum number of addresses is reached and a new prefix is delegated, the address with the shortest remaining lifetime will be evicted to make room for the new prefix. This is useful for scenarios where upstream prefixes change frequently (e.g., unstable connections), ensuring the newest working prefix is always advertised.")
#define DESCRIBE_DOC_NM_SETTING_PREFIX_DELEGATION_SUBNET_ID N_("The subnet ID to use on the interface from the prefix delegation received via an upstream interface. Set to a value between 0 and 0xffffffff (2^32 - 1) to indicate a specific subnet ID; or set to -1 to automatically choose an available subnet ID.")
#define DESCRIBE_DOC_NM_SETTING_VETH_PEER N_("This property specifies the peer interface name of the veth. This property is mandatory.")