summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-02-09 17:40:09 +0100
committerThomas Haller <thaller@redhat.com>2018-02-09 17:40:09 +0100
commitfa41e5852cebb3ba5f6dde880ef792d361cc7fe9 (patch)
tree66340a955600ddd74885d60698c2f64aebc32981
parent86a18b2df2dafdd6cdb120e07509d473d498a591 (diff)
parent6d8a636563b0ddbf2b484473210bf3bcf41b47a7 (diff)
downloadNetworkManager-fa41e5852cebb3ba5f6dde880ef792d361cc7fe9.tar.gz
core: merge branch 'th/ip6-temp-addr-sync-rh1542609'
https://github.com/NetworkManager/NetworkManager/pull/65 https://bugzilla.redhat.com/show_bug.cgi?id=1542609
-rw-r--r--src/devices/nm-device.c58
-rw-r--r--src/ndisc/nm-ndisc.c13
-rw-r--r--src/nm-core-utils.c52
-rw-r--r--src/nm-core-utils.h11
-rw-r--r--src/nm-ip6-config.c2
-rw-r--r--src/platform/nm-linux-platform.c7
-rw-r--r--src/platform/nm-platform.c293
-rw-r--r--src/platform/nm-platform.h8
-rw-r--r--src/platform/nmp-object.c61
-rw-r--r--src/platform/nmp-object.h10
10 files changed, 348 insertions, 167 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index ea2c426e19..8b99fc0ba8 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -2816,6 +2816,8 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) {
const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter.current->obj);
NMNDiscAddress *ndisc_addr;
+ guint32 lifetime, preferred;
+ gint32 base;
if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
continue;
@@ -2827,12 +2829,28 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
if (addr->plen != 64)
continue;
+ /* resolve the timestamps relative to a new base.
+ *
+ * Note that for convenience, platform @addr might have timestamp and/or
+ * lifetime unset. We don't allow that flexibility for ndisc and require
+ * well defined timestamps. */
+ if (addr->timestamp) {
+ nm_assert (addr->timestamp < G_MAXINT32);
+ base = addr->timestamp;
+ } else
+ base = now;
+
+ lifetime = nm_utils_lifetime_get (addr->timestamp, addr->lifetime, addr->preferred,
+ base, &preferred);
+ if (!lifetime)
+ continue;
+
g_array_set_size (addresses, addresses->len+1);
ndisc_addr = &g_array_index (addresses, NMNDiscAddress, addresses->len-1);
ndisc_addr->address = addr->address;
- ndisc_addr->timestamp = addr->timestamp;
- ndisc_addr->lifetime = addr->lifetime;
- ndisc_addr->preferred = addr->preferred;
+ ndisc_addr->timestamp = base;
+ ndisc_addr->lifetime = lifetime;
+ ndisc_addr->preferred = preferred;
}
len = nm_ip6_config_get_num_nameservers (priv->ip6_config);
@@ -11511,6 +11529,7 @@ queued_ip6_config_change (gpointer user_data)
NMDevicePrivate *priv;
GSList *iter;
gboolean need_ipv6ll = FALSE;
+ NMPlatform *platform;
g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
@@ -11537,14 +11556,24 @@ queued_ip6_config_change (gpointer user_data)
} else
update_ip_config (self, AF_INET6, FALSE);
- if (priv->state < NM_DEVICE_STATE_DEACTIVATING
- && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) {
+ if ( priv->state < NM_DEVICE_STATE_DEACTIVATING
+ && (platform = nm_device_get_platform (self))
+ && nm_platform_link_get (platform, priv->ifindex)) {
/* Handle DAD failures */
- for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) {
- const NMPlatformIP6Address *addr = iter->data;
-
- if (addr->addr_source >= NM_IP_CONFIG_SOURCE_USER)
+ for (iter = priv->dad6_failed_addrs; iter; iter = iter->next) {
+ const NMPObject *obj = iter->data;
+ const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (obj);
+ const NMPlatformIP6Address *addr2;
+
+ addr2 = NMP_OBJECT_CAST_IP6_ADDRESS (nm_platform_lookup_obj (platform,
+ NMP_CACHE_ID_TYPE_OBJECT_TYPE,
+ obj));
+ if ( addr2
+ && ( NM_FLAGS_HAS (addr2->n_ifa_flags, IFA_F_SECONDARY)
+ || !NM_FLAGS_HAS (addr2->n_ifa_flags, IFA_F_DADFAILED))) {
+ /* the address still/again exists and is not in DADFAILED state. Skip it. */
continue;
+ }
_LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address",
nm_platform_ip6_address_to_string (addr, NULL, 0));
@@ -11567,7 +11596,7 @@ queued_ip6_config_change (gpointer user_data)
check_and_add_ipv6ll_addr (self);
}
- g_slist_free_full (priv->dad6_failed_addrs, g_free);
+ g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
priv->dad6_failed_addrs = NULL;
/* Check if DAD is still pending */
@@ -11623,12 +11652,13 @@ device_ipx_changed (NMPlatform *platform,
case NMP_OBJECT_TYPE_IP6_ADDRESS:
addr = platform_object;
- if ( priv->state > NM_DEVICE_STATE_DISCONNECTED
+ if ( !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_SECONDARY)
+ && priv->state > NM_DEVICE_STATE_DISCONNECTED
&& priv->state < NM_DEVICE_STATE_DEACTIVATING
&& ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
|| (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))) {
- priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs,
- g_memdup (addr, sizeof (NMPlatformIP6Address)));
+ priv->dad6_failed_addrs = g_slist_prepend (priv->dad6_failed_addrs,
+ (gpointer) nmp_object_ref (NMP_OBJECT_UP_CAST (addr)));
}
/* fall through */
case NMP_OBJECT_TYPE_IP6_ROUTE:
@@ -14684,7 +14714,7 @@ finalize (GObject *object)
g_free (priv->hw_addr_perm);
g_free (priv->hw_addr_initial);
g_slist_free (priv->pending_actions);
- g_slist_free_full (priv->dad6_failed_addrs, g_free);
+ g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
g_clear_pointer (&priv->physical_port_id, g_free);
g_free (priv->udi);
g_free (priv->iface);
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index a02e4b7692..ede655dd95 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -89,7 +89,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
);
enum {
- CONFIG_CHANGED,
+ CONFIG_RECEIVED,
RA_TIMEOUT,
LAST_SIGNAL
};
@@ -235,7 +235,7 @@ static void
_emit_config_change (NMNDisc *self, NMNDiscConfigMap changed)
{
_config_changed_log (self, changed);
- g_signal_emit (self, signals[CONFIG_CHANGED], 0,
+ g_signal_emit (self, signals[CONFIG_RECEIVED], 0,
_data_complete (&NM_NDISC_GET_PRIVATE (self)->rdata),
(guint) changed);
}
@@ -349,6 +349,9 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new)
NMNDiscDataInternal *rdata = &priv->rdata;
guint i;
+ nm_assert (new);
+ nm_assert (new->timestamp > 0 && new->timestamp < G_MAXINT32);
+
for (i = 0; i < rdata->addresses->len; i++) {
NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
@@ -936,7 +939,7 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed)
get_exp (str_exp, now_ns, gateway));
}
for (i = 0; i < rdata->addresses->len; i++) {
- NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i);
+ const NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i);
inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr));
_LOGD (" address %s exp %s", addrstr,
@@ -1003,7 +1006,7 @@ clean_addresses (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *
rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata;
for (i = 0; i < rdata->addresses->len; ) {
- NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
+ const NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
if (item->lifetime != NM_NDISC_INFINITY) {
gint32 expiry = get_expiry (item);
@@ -1378,7 +1381,7 @@ nm_ndisc_class_init (NMNDiscClass *klass)
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
- signals[CONFIG_CHANGED] =
+ signals[CONFIG_RECEIVED] =
g_signal_new (NM_NDISC_CONFIG_RECEIVED,
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_FIRST,
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index 3a5118508b..f3e9e5f75e 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -3891,12 +3891,11 @@ nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
return t;
}
-gboolean
+guint32
nm_utils_lifetime_get (guint32 timestamp,
guint32 lifetime,
guint32 preferred,
gint32 now,
- guint32 *out_lifetime,
guint32 *out_preferred)
{
guint32 t_lifetime, t_preferred;
@@ -3904,38 +3903,39 @@ nm_utils_lifetime_get (guint32 timestamp,
nm_assert (now >= 0);
if (timestamp == 0 && lifetime == 0) {
- /* We treat lifetime==0 && timestamp == 0 addresses as permanent addresses to allow easy
+ /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy
* creation of such addresses (without requiring to set the lifetime fields to
* NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us
* to drop an address will have timestamp set.
*/
- NM_SET_OUT (out_lifetime, NM_PLATFORM_LIFETIME_PERMANENT);
NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT);
- g_return_val_if_fail (preferred == 0, TRUE);
- } else {
- if (now <= 0)
- now = nm_utils_get_monotonic_timestamp_s ();
- t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
- if (!t_lifetime) {
- NM_SET_OUT (out_lifetime, 0);
- NM_SET_OUT (out_preferred, 0);
- return FALSE;
- }
- t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
+ g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT);
+ return NM_PLATFORM_LIFETIME_PERMANENT;
+ }
- NM_SET_OUT (out_lifetime, t_lifetime);
- NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime));
+ if (now <= 0)
+ now = nm_utils_get_monotonic_timestamp_s ();
- /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
- * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
- * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
- */
- g_return_val_if_fail ( timestamp != 0
- || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT
- && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
- g_return_val_if_fail (t_preferred <= t_lifetime, TRUE);
+ t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
+ if (!t_lifetime) {
+ NM_SET_OUT (out_preferred, 0);
+ return 0;
}
- return TRUE;
+
+ t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
+
+ NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime));
+
+ /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
+ * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
+ * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
+ */
+ g_return_val_if_fail ( timestamp != 0
+ || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT
+ && preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime);
+ g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime);
+
+ return t_lifetime;
}
const char *
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index 2f6585e717..38f20b0522 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -408,12 +408,11 @@ guint32 nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
guint32 duration,
gint32 now);
-gboolean nm_utils_lifetime_get (guint32 timestamp,
- guint32 lifetime,
- guint32 preferred,
- gint32 now,
- guint32 *out_lifetime,
- guint32 *out_preferred);
+guint32 nm_utils_lifetime_get (guint32 timestamp,
+ guint32 lifetime,
+ guint32 preferred,
+ gint32 now,
+ guint32 *out_preferred);
gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr);
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 692182e183..3fb1c8675a 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -566,7 +566,7 @@ nm_ip6_config_commit (const NMIP6Config *self,
ifindex,
route_table_sync);
- nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE);
+ nm_platform_ip6_address_sync (platform, ifindex, addresses, FALSE);
if (!nm_platform_ip_route_sync (platform,
AF_INET6,
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 0128362864..1e141eeb90 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -4829,6 +4829,12 @@ link_refresh (NMPlatform *platform, int ifindex)
return !!nm_platform_link_get_obj (platform, ifindex, TRUE);
}
+static void
+refresh_all (NMPlatform *platform, NMPObjectType obj_type)
+{
+ do_request_one_type (platform, obj_type);
+}
+
static gboolean
link_set_netns (NMPlatform *platform,
int ifindex,
@@ -7196,6 +7202,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_add = link_add;
platform_class->link_delete = link_delete;
+ platform_class->refresh_all = refresh_all;
platform_class->link_refresh = link_refresh;
platform_class->link_set_netns = link_set_netns;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 67051f7769..9ac6a646e1 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1118,6 +1118,21 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex)
}
/**
+ * nm_platform_refresh_all:
+ * @self: platform instance
+ * @obj_type: The object type to request.
+ *
+ * Resync and re-request all objects from kernel of a certain @obj_type.
+ */
+void
+nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type)
+{
+ _CHECK_SELF_VOID (self, klass);
+
+ klass->refresh_all (self, obj_type);
+}
+
+/**
* nm_platform_link_refresh:
* @self: platform instance
* @ifindex: Interface index
@@ -3110,35 +3125,68 @@ nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr addr
return NMP_OBJECT_CAST_IP6_ADDRESS (obj);
}
-static int
-array_ip6_address_position (const GPtrArray *addresses,
- const NMPlatformIP6Address *address,
- gint32 now,
- gboolean ignore_ll)
-{
- guint len = addresses ? addresses->len : 0;
- guint i, pos;
-
- nm_assert (!ignore_ll || !IN6_IS_ADDR_LINKLOCAL (&address->address));
-
- for (i = 0, pos = 0; i < len; i++) {
- NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]);
-
- if ( IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address)
- && candidate->plen == address->plen
- && nm_utils_lifetime_get (candidate->timestamp,
- candidate->lifetime,
- candidate->preferred,
- now,
- NULL,
- NULL))
- return pos;
-
- if (!ignore_ll || !IN6_IS_ADDR_LINKLOCAL (&candidate->address))
- pos++;
+static gboolean
+_addr_array_clean_expired (int addr_family, int ifindex, GPtrArray *array, guint32 now, GHashTable **idx)
+{
+ guint i;
+ gboolean any_addrs = FALSE;
+
+ nm_assert_addr_family (addr_family);
+ nm_assert (ifindex > 0);
+ nm_assert (now > 0);
+
+ if (!array)
+ return FALSE;
+
+ /* remove all addresses that are already expired. */
+ for (i = 0; i < array->len; i++) {
+ const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS (array->pdata[i]);
+
+#if NM_MORE_ASSERTS > 10
+ nm_assert (a);
+ nm_assert (a->ifindex == ifindex);
+ {
+ const NMPObject *o = NMP_OBJECT_UP_CAST (a);
+ guint j;
+
+ nm_assert (NMP_OBJECT_GET_CLASS (o)->addr_family == addr_family);
+ for (j = i + 1; j < array->len; j++) {
+ const NMPObject *o2 = array->pdata[j];
+
+ nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_GET_TYPE (o2));
+ nm_assert (!nmp_object_id_equal (o, o2));
+ }
+ }
+#endif
+
+ if (NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_SECONDARY)) {
+ /* temporary addresses are never added explicitly by NetworkManager but
+ * kernel adds them via mngtempaddr flag.
+ *
+ * We drop them from this list. */
+ goto clear_and_next;
+ }
+
+ if (!nm_utils_lifetime_get (a->timestamp, a->lifetime, a->preferred,
+ now, NULL))
+ goto clear_and_next;
+
+ if (idx) {
+ if (G_UNLIKELY (!*idx)) {
+ *idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
+ (GEqualFunc) nmp_object_id_equal);
+ }
+ if (!g_hash_table_add (*idx, (gpointer) NMP_OBJECT_UP_CAST (a)))
+ nm_assert_not_reached ();
+ }
+ any_addrs = TRUE;
+ continue;
+
+clear_and_next:
+ nmp_object_unref (g_steal_pointer (&array->pdata[i]));
}
- return -1;
+ return any_addrs;
}
static gboolean
@@ -3322,39 +3370,8 @@ nm_platform_ip4_address_sync (NMPlatform *self,
_CHECK_SELF (self, klass, FALSE);
- if (known_addresses) {
- /* remove all addresses that are already expired. */
- for (i = 0; i < known_addresses->len; i++) {
- const NMPObject *o;
-
- o = known_addresses->pdata[i];
- nm_assert (o);
-
- known_address = NMP_OBJECT_CAST_IP4_ADDRESS (known_addresses->pdata[i]);
-
- if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
- now, NULL, NULL))
- goto delete_and_next;
-
- if (G_UNLIKELY (!known_addresses_idx)) {
- known_addresses_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
- (GEqualFunc) nmp_object_id_equal);
- }
- if (!g_hash_table_insert (known_addresses_idx, (gpointer) o, (gpointer) o)) {
- /* duplicate? Keep only the first instance. */
- goto delete_and_next;
- }
-
- continue;
-delete_and_next:
- nmp_object_unref (o);
- known_addresses->pdata[i] = NULL;
- }
-
- if ( !known_addresses_idx
- || g_hash_table_size (known_addresses_idx) == 0)
- known_addresses = NULL;
- }
+ if (!_addr_array_clean_expired (AF_INET, ifindex, known_addresses, now, &known_addresses_idx))
+ known_addresses = NULL;
plat_addresses = nm_platform_lookup_clone (self,
nmp_lookup_init_object (&lookup,
@@ -3451,8 +3468,9 @@ delete_and_next:
known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o);
- if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
- now, &lifetime, &preferred))
+ lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+ now, &preferred);
+ if (!lifetime)
goto delete_and_next2;
if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen,
@@ -3474,9 +3492,15 @@ delete_and_next2:
* nm_platform_ip6_address_sync:
* @self: platform instance
* @ifindex: Interface index
- * @known_addresses: List of IPv6 addresses, as NMPObject. The list
- * is not modified.
- * @keep_link_local: Don't remove link-local address
+ * @known_addresses: List of addresses. The list will be modified and only
+ * addresses that were successfully added will be kept in the list.
+ * That means, expired addresses and addresses that could not be added
+ * will be dropped.
+ * Hence, the input argument @known_addresses is also an output argument
+ * telling which addresses were succesfully added.
+ * Addresses are removed by unrefing the instance via nmp_object_unref()
+ * and leaving a NULL tombstone.
+ * @full_sync: Also remove link-local and temporary addresses.
*
* A convenience function to synchronize addresses for a specific interface
* with the least possible disturbance. It simply removes addresses that are
@@ -3487,60 +3511,114 @@ delete_and_next2:
gboolean
nm_platform_ip6_address_sync (NMPlatform *self,
int ifindex,
- const GPtrArray *known_addresses,
- gboolean keep_link_local)
+ GPtrArray *known_addresses,
+ gboolean full_sync)
{
gs_unref_ptrarray GPtrArray *plat_addresses = NULL;
- NMPlatformIP6Address *address;
gint32 now = nm_utils_get_monotonic_timestamp_s ();
- int i, position;
+ guint i_plat, i_know;
+ gs_unref_hashtable GHashTable *known_addresses_idx = NULL;
NMPLookup lookup;
guint32 ifa_flags;
- gboolean remove = FALSE;
- /* @plat_addresses and @known_addresses are in increasing priority order */
+ if (!_addr_array_clean_expired (AF_INET6, ifindex, known_addresses, now, &known_addresses_idx))
+ known_addresses = NULL;
+
+ /* @plat_addresses is in decreasing priority order (highest priority addresses first), contrary to
+ * @known_addresses which is in increasing priority order (lowest priority addresses first). */
plat_addresses = nm_platform_lookup_clone (self,
nmp_lookup_init_object (&lookup,
NMP_OBJECT_TYPE_IP6_ADDRESS,
ifindex),
NULL, NULL);
- if (plat_addresses) {
- /* First, remove unknown addresses from platform */
- for (i = 0; i < plat_addresses->len; i++) {
- address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]);
- if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address))
- continue;
-
- position = array_ip6_address_position (known_addresses, address, now, FALSE);
- if (position < 0) {
- nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen);
- g_ptr_array_remove_index (plat_addresses, i);
- i--;
+ if (plat_addresses) {
+ gboolean delete_remaining;
+ guint known_addresses_len;
+
+ known_addresses_len = known_addresses ? known_addresses->len : 0;
+
+ /* First, compare every address whether it is still a "known address", that is, whether
+ * to keep it or to delete it.
+ *
+ * If we don't find a matching valid address in @known_addresses, we will delete
+ * plat_addr.
+ *
+ * Certain addresses, like link-local or temporary addresses, are ignored by this function
+ * if not run with full_sync.
+ *
+ * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */
+ for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) {
+ const NMPObject *plat_obj = plat_addresses->pdata[i_plat];
+ const NMPObject *know_obj;
+ const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj);
+
+ if ( NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_SECONDARY)
+ || IN6_IS_ADDR_LINKLOCAL (&plat_addr->address)) {
+ if (!full_sync) {
+ /* just mark as handled, without actually deleting the address. */
+ goto clear_and_next;
+ }
+ } else if (known_addresses_idx) {
+ know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj);
+ if ( know_obj
+ && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) {
+ /* technically, plen is not part of the ID for IPv6 addresses and thus
+ * @plat_addr is essentially the same address as @know_addr (regrading
+ * its identity, not its other attributes).
+ * However, we cannot modify an existing addresses' plen without
+ * removing and readding it. Thus, only keep plat_addr, if the plen
+ * matches.
+ *
+ * keep this one, and continue */
+ continue;
+ }
}
- }
- /* Start from addresses with lower priority and remove them if they are
- * in a wrong position. Removing an address also removes all addresses
- * with higher priority. We ignore link-local addresses when determining
- * positions.
- */
- for (i = 0, position = 0; i < plat_addresses->len; i++) {
- address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]);
+ nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
+clear_and_next:
+ nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat]));
+ }
- if (IN6_IS_ADDR_LINKLOCAL (&address->address))
+ /* Next, we must preserve the priority of the routes. That is, source address
+ * selection will choose addresses in the order as they are reported by kernel.
+ * Note that the order in @plat_addresses of the remaining matches is highest
+ * priority first.
+ * We need to compare this to the order in @known_addresses (which has lowest
+ * priority first).
+ *
+ * If we find a first discrepancy, we need to delete all remaining addresses
+ * from that point on, because below we must re-add all the addresses in the
+ * right order to get their priority right. */
+ i_plat = plat_addresses->len;
+ i_know = 0;
+ delete_remaining = FALSE;
+ while (i_plat > 0) {
+ const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]);
+
+ if (!plat_addr)
continue;
- if ( remove
- || position != array_ip6_address_position (known_addresses,
- address,
- now,
- TRUE)) {
- nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen);
- remove = TRUE;
+ if (!delete_remaining) {
+ for (; i_know < known_addresses_len; i_know++) {
+ const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]);
+
+ if (!know_addr)
+ continue;
+
+ if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) {
+ /* we have a match. Mark address as handled. */
+ i_know++;
+ goto next_plat;
+ }
+ break;
+ }
+ delete_remaining = TRUE;
}
- position++;
+ nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
+next_plat:
+ ;
}
}
@@ -3554,18 +3632,15 @@ nm_platform_ip6_address_sync (NMPlatform *self,
/* Add missing addresses. New addresses are added by kernel with top
* priority.
*/
- for (i = 0; i < known_addresses->len; i++) {
- const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]);
+ for (i_know = 0; i_know < known_addresses->len; i_know++) {
+ const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]);
guint32 lifetime, preferred;
- if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_SECONDARY)) {
- /* Kernel manages these */
+ if (!known_address)
continue;
- }
- if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
- now, &lifetime, &preferred))
- continue;
+ lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+ now, &preferred);
if (!nm_platform_ip6_address_add (self, ifindex, known_address->address,
known_address->plen, known_address->peer_address,
@@ -3593,7 +3668,7 @@ nm_platform_ip_address_flush (NMPlatform *self,
if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET))
success &= nm_platform_ip4_address_sync (self, ifindex, NULL);
if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6))
- success &= nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE);
+ success &= nm_platform_ip6_address_sync (self, ifindex, NULL, TRUE);
return success;
}
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 1f4230c6c4..3cbf8c9990 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -719,6 +719,8 @@ typedef struct {
gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value);
char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path);
+ void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type);
+
gboolean (*link_add) (NMPlatform *,
const char *name,
NMLinkType type,
@@ -1033,6 +1035,8 @@ gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char
const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */);
int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname);
+void nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type);
+
const NMPObject *nm_platform_link_get_obj (NMPlatform *self,
int ifindex,
gboolean visible_only);
@@ -1256,8 +1260,8 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self,
guint32 flags);
gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address);
gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen);
-gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresse);
-gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GPtrArray *known_addresses, gboolean keep_link_local);
+gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses);
+gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync);
gboolean nm_platform_ip_address_flush (NMPlatform *self,
int addr_family,
int ifindex);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index dd5d1a229c..4df64e608c 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -1768,6 +1768,54 @@ nmp_cache_lookup_link_full (const NMPCache *cache,
/*****************************************************************************/
+static NMDedupMultiIdxMode
+_obj_get_add_mode (const NMPObject *obj)
+{
+ /* new objects are usually appended to the list. Except for
+ * addresses, which are prepended during `ip address add`.
+ *
+ * Actually, for routes it is more complicated, because depending on
+ * `ip route append`, `ip route replace`, `ip route prepend`, the object
+ * will be added at the tail, at the front, or even replace an element
+ * in the list. However, that is handled separately by nmp_cache_update_netlink_route()
+ * and of no concern here. */
+ if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj),
+ NMP_OBJECT_TYPE_IP4_ADDRESS,
+ NMP_OBJECT_TYPE_IP6_ADDRESS))
+ return NM_DEDUP_MULTI_IDX_MODE_PREPEND;
+ return NM_DEDUP_MULTI_IDX_MODE_APPEND;
+}
+
+static void
+_idxcache_update_order_for_dump (NMPCache *cache,
+ const NMDedupMultiEntry *entry)
+{
+ const NMPClass *klass;
+ const guint8 *i_idx_type;
+ const NMDedupMultiEntry *entry2;
+
+ nm_dedup_multi_entry_reorder (entry, NULL, TRUE);
+
+ klass = NMP_OBJECT_GET_CLASS (entry->obj);
+ for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) {
+ NMPCacheIdType id_type = *i_idx_type;
+
+ if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE)
+ continue;
+
+ entry2 = nm_dedup_multi_index_lookup_obj (cache->multi_idx,
+ _idx_type_get (cache, id_type),
+ entry->obj);
+ if (!entry2)
+ continue;
+
+ nm_assert (entry2 != entry);
+ nm_assert (entry2->obj == entry->obj);
+
+ nm_dedup_multi_entry_reorder (entry2, NULL, TRUE);
+ }
+}
+
static void
_idxcache_update_other_cache_ids (NMPCache *cache,
NMPCacheIdType cache_id_type,
@@ -1827,7 +1875,7 @@ _idxcache_update_other_cache_ids (NMPCache *cache,
obj_new,
is_dump
? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
- : NM_DEDUP_MULTI_IDX_MODE_APPEND,
+ : _obj_get_add_mode (obj_new),
is_dump
? NULL
: entry_order,
@@ -1905,7 +1953,7 @@ _idxcache_update (NMPCache *cache,
obj_new,
is_dump
? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
- : NM_DEDUP_MULTI_IDX_MODE_APPEND,
+ : _obj_get_add_mode (obj_new),
NULL,
entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING,
NULL,
@@ -2162,6 +2210,8 @@ nmp_cache_update_netlink (NMPCache *cache,
}
if (nmp_object_equal (obj_old, obj_hand_over)) {
+ if (is_dump)
+ _idxcache_update_order_for_dump (cache, entry_old);
nm_dedup_multi_entry_set_dirty (entry_old, FALSE);
NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old));
return NMP_CACHE_OPS_UNCHANGED;
@@ -2235,6 +2285,8 @@ nmp_cache_update_netlink_route (NMPCache *cache,
}
if (nmp_object_equal (entry_old->obj, obj_hand_over)) {
+ if (is_dump)
+ _idxcache_update_order_for_dump (cache, entry_old);
nm_dedup_multi_entry_set_dirty (entry_old, FALSE);
goto update_done;
}
@@ -2258,9 +2310,8 @@ update_done:
* properly find @obj_replaced. */
resync_required = FALSE;
entry_replace = NULL;
- if (is_dump) {
+ if (is_dump)
goto out;
- }
if (!entry_new) {
if ( NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE)
@@ -2275,6 +2326,8 @@ update_done:
goto out;
}
+ /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID
+ * index. For all other indexes their order becomes messed up. */
entry_cur = _lookup_entry_with_idx_type (cache,
NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
entry_new->obj);
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 8414c1a57f..8c36e2e3d4 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -730,6 +730,16 @@ const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform,
NMPCacheIdType cache_id_type,
const NMPObject *obj);
+static inline const NMPObject *
+nm_platform_lookup_obj (NMPlatform *platform,
+ NMPCacheIdType cache_id_type,
+ const NMPObject *obj)
+{
+ return nm_dedup_multi_entry_get_obj (nm_platform_lookup_entry (platform,
+ cache_id_type,
+ obj));
+}
+
static inline const NMDedupMultiHeadEntry *
nm_platform_lookup_obj_type (NMPlatform *platform,
NMPObjectType obj_type)