summary refs log tree commit diff
path: root/src/platform/nmp-object.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/nmp-object.h')
-rw-r--r--src/platform/nmp-object.h559
1 files changed, 442 insertions, 117 deletions
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index b69680f6..41fd08cb 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -21,8 +21,9 @@
 #ifndef __NMP_OBJECT_H__
 #define __NMP_OBJECT_H__
 
+#include "nm-utils/nm-obj.h"
+#include "nm-utils/nm-dedup-multi.h"
 #include "nm-platform.h"
-#include "nm-multi-index.h"
 
 struct udev_device;
 
@@ -47,36 +48,42 @@ typedef enum { /*< skip >*/
  * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT.
  *
  * Of one index type, there can be multiple indexes or not.
- * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX there
+ * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX there
  * are multiple instances (for different route/addresses, v4/v6, per-ifindex).
  *
  * But one object, can only be indexed by one particular index of a
  * type. For example, a certain address instance is only indexed by
- * the index NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX with
+ * the index NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX with
  * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible.
  * */
 typedef enum { /*< skip >*/
 	NMP_CACHE_ID_TYPE_NONE,
 
-	/* all the objects of a certain type */
+	/* all the objects of a certain type.
+	 *
+	 * This index is special. It is the only one that contains *all* object.
+	 * Other indexes may consider some object as non "partitionable", hence
+	 * they don't track all objects.
+	 *
+	 * Hence, this index type is used when looking at all objects (still
+	 * partitioned by type).
+	 *
+	 * Also, note that links may be considered invisible. This index type
+	 * expose all links, even invisible ones. For addresses/routes, this
+	 * distiction doesn't exist, as all addresses/routes that are alive
+	 * are visible as well. */
 	NMP_CACHE_ID_TYPE_OBJECT_TYPE,
 
 	/* index for the link objects by ifname. */
 	NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
 
-	/* all the visible objects of a certain type */
-	NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY,
+	/* indeces for the visible default-routes, ignoring ifindex.
+	 * This index only contains two partitions: all visible default-routes,
+	 * separate for IPv4 and IPv6. */
+	NMP_CACHE_ID_TYPE_DEFAULT_ROUTES,
 
-	/* indeces for the visible routes, ignoring ifindex. */
-	NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT,
-	NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT,
-
-	/* all the visible addresses/routes (by object-type) for an ifindex. */
-	NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX,
-
-	/* three indeces for the visible routes, per ifindex. */
-	NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT,
-	NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT,
+	/* all the addresses/routes (by object-type) for an ifindex. */
+	NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX,
 
 	/* Consider all the destination fields of a route, that is, the ID without the ifindex
 	 * and gateway (meaning: network/plen,metric).
@@ -86,65 +93,21 @@ typedef enum { /*< skip >*/
 	 * sends one RTM_NEWADDR notification without notifying about the deletion. We detect
 	 * that by having this index to contain overlapping routes which require special
 	 * cache-resync. */
-	NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4,
-	NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6,
+	NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
 
 	__NMP_CACHE_ID_TYPE_MAX,
 	NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1,
 } NMPCacheIdType;
 
-typedef struct _NMPCacheId NMPCacheId;
-
-struct _NMPCacheId {
-	union {
-		NMMultiIndexId base;
-		guint8 _id_type; /* NMPCacheIdType as guint8 */
-		struct _nm_packed {
-			/* NMP_CACHE_ID_TYPE_OBJECT_TYPE */
-			/* NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY */
-			/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */
-			/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */
-			guint8 _id_type;
-			guint8 obj_type; /* NMPObjectType as guint8 */
-		} object_type;
-		struct _nm_packed {
-			/* NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX */
-			/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT */
-			/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT */
-			guint8 _id_type;
-			guint8 obj_type; /* NMPObjectType as guint8 */
-			int _misaligned_ifindex;
-		} object_type_by_ifindex;
-		struct _nm_packed {
-			/* NMP_CACHE_ID_TYPE_LINK_BY_IFNAME */
-			guint8 _id_type;
-			char ifname_short[IFNAMSIZ - 1]; /* don't include the trailing NUL so the struct fits in 4 bytes. */
-		} link_by_ifname;
-		struct _nm_packed {
-			/* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4 */
-			guint8 _id_type;
-			guint8 plen;
-			guint32 _misaligned_metric;
-			guint32 _misaligned_network;
-		} routes_by_destination_ip4;
-		struct _nm_packed {
-			/* NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6 */
-			guint8 _id_type;
-			guint8 plen;
-			guint32 _misaligned_metric;
-			struct in6_addr _misaligned_network;
-		} routes_by_destination_ip6;
-	};
-};
-
 typedef struct {
+	NMDedupMultiObjClass parent;
+	const char *obj_type_name;
+	int sizeof_data;
+	int sizeof_public;
 	NMPObjectType obj_type;
 	int addr_family;
 	int rtm_gettype;
-	int sizeof_data;
-	int sizeof_public;
 	NMPlatformSignalIdType signal_type_id;
-	const char *obj_type_name;
 	const char *signal_type;
 
 	const guint8 *supported_cache_ids;
@@ -152,13 +115,9 @@ typedef struct {
 	/* Only for NMPObjectLnk* types. */
 	NMLinkType lnk_link_type;
 
-	/* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index,
-	 * initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */
-	gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id);
-
+	void (*cmd_obj_hash_update) (const NMPObject *obj, NMHashState *h);
 	int (*cmd_obj_cmp) (const NMPObject *obj1, const NMPObject *obj2);
 	void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src);
-	void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src);
 	void (*cmd_obj_dispose) (NMPObject *obj);
 	gboolean (*cmd_obj_is_alive) (const NMPObject *obj);
 	gboolean (*cmd_obj_is_visible) (const NMPObject *obj);
@@ -166,10 +125,11 @@ typedef struct {
 
 	/* functions that operate on NMPlatformObject */
 	void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src);
-	gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2);
-	guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj);
+	int (*cmd_plobj_id_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2);
+	void (*cmd_plobj_id_hash_update) (const NMPlatformObject *obj, NMHashState *h);
 	const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size);
 	const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj, char *buf, gsize len);
+	void (*cmd_plobj_hash_update) (const NMPlatformObject *obj, NMHashState *h);
 	int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2);
 } NMPClass;
 
@@ -182,7 +142,7 @@ typedef struct {
 		bool is_in_netlink;
 
 		/* Additional data that depends on the link-type (IFLA_INFO_DATA) */
-		NMPObject *lnk;
+		const NMPObject *lnk;
 	} netlink;
 
 	struct {
@@ -266,9 +226,10 @@ typedef struct {
 } NMPObjectIP6Route;
 
 struct _NMPObject {
-	const NMPClass *_class;
-	int _ref_count;
-	bool is_cached;
+	union {
+		NMDedupMultiObj parent;
+		const NMPClass *_class;
+	};
 	union {
 		NMPlatformObject        object;
 
@@ -326,8 +287,6 @@ NMP_CLASS_IS_VALID (const NMPClass *klass)
 	    && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0;
 }
 
-#define NMP_REF_COUNT_STACKINIT (G_MAXINT)
-
 static inline NMPObject *
 NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj)
 {
@@ -336,7 +295,7 @@ NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj)
 	obj = plobj
 	      ? (NMPObject *) ( &(((char *) plobj)[-((int) G_STRUCT_OFFSET (NMPObject, object))]) )
 	      : NULL;
-	nm_assert (!obj || (obj->_ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class)));
+	nm_assert (!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class)));
 	return obj;
 }
 #define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST ((const NMPlatformObject *) (plobj)))
@@ -345,7 +304,7 @@ static inline gboolean
 NMP_OBJECT_IS_VALID (const NMPObject *obj)
 {
 	nm_assert (!obj || (   obj
-	                    && obj->_ref_count > 0
+	                    && obj->parent._ref_count > 0
 	                    && NMP_CLASS_IS_VALID (obj->_class)));
 
 	/* There isn't really much to check. Either @obj is NULL, or we must
@@ -358,7 +317,7 @@ NMP_OBJECT_IS_STACKINIT (const NMPObject *obj)
 {
 	nm_assert (!obj || NMP_OBJECT_IS_VALID (obj));
 
-	return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT;
+	return obj && obj->parent._ref_count == NM_OBJ_REF_COUNT_STACKINIT;
 }
 
 static inline const NMPClass *
@@ -377,65 +336,264 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 	return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN;
 }
 
-
+#define NMP_OBJECT_CAST_LINK(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_LINK); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->link : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IP_ROUTE(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IP4_ROUTE(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_IP6_ROUTE(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \
+		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \
+	})
 
 const NMPClass *nmp_class_from_type (NMPObjectType obj_type);
 
-NMPObject *nmp_object_ref (NMPObject *object);
-void nmp_object_unref (NMPObject *object);
+static inline const NMPObject *
+nmp_object_ref (const NMPObject *obj)
+{
+	if (!obj) {
+		/* for convenience, allow NULL. */
+		return NULL;
+	}
+
+	/* ref and unref accept const pointers. NMPObject is supposed to be shared
+	 * and kept immutable. Disallowing to take/retrun a reference to a const
+	 * NMPObject is cumbersome, because callers are precisely expected to
+	 * keep a ref on the otherwise immutable object. */
+	g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL);
+	g_return_val_if_fail (obj->parent._ref_count != NM_OBJ_REF_COUNT_STACKINIT, NULL);
+
+	return (const NMPObject *) nm_dedup_multi_obj_ref ((const NMDedupMultiObj *) obj);
+}
+
+static inline const NMPObject *
+nmp_object_unref (const NMPObject *obj)
+{
+	nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj);
+	return NULL;
+}
+
+#define nm_clear_nmp_object(ptr) \
+	({ \
+		typeof (ptr) _ptr = (ptr); \
+		typeof (*_ptr) _pptr; \
+		gboolean _changed = FALSE; \
+		\
+		if (   _ptr \
+		    && (_pptr = *_ptr)) { \
+			*_ptr = NULL; \
+			nmp_object_unref (_pptr); \
+			_changed = TRUE; \
+		} \
+		_changed; \
+	})
+
 NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob);
 NMPObject *nmp_object_new_link (int ifindex);
 
 const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj);
+
+static inline NMPObject *
+nmp_object_stackinit_obj (NMPObject *obj, const NMPObject *src)
+{
+	return obj == src
+	         ? obj
+	         : (NMPObject *) nmp_object_stackinit (obj, NMP_OBJECT_GET_TYPE (src), &src->object);
+}
+
 const NMPObject *nmp_object_stackinit_id  (NMPObject *obj, const NMPObject *src);
 const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex);
 const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address);
-const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, guint8 plen);
-const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric);
-const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric);
+const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address);
 
 const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size);
+void nmp_object_hash_update (const NMPObject *obj, NMHashState *h);
 int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2);
 gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2);
 void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only);
 NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only);
-gboolean nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2);
+
+int nmp_object_id_cmp (const NMPObject *obj1, const NMPObject *obj2);
+void nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h);
 guint nmp_object_id_hash (const NMPObject *obj);
+
+static inline gboolean
+nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2)
+{
+	return nmp_object_id_cmp (obj1, obj2) == 0;
+}
+
 gboolean nmp_object_is_alive (const NMPObject *obj);
 gboolean nmp_object_is_visible (const NMPObject *obj);
 
-void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev);
+void _nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev);
 
-#define nm_auto_nmpobj __attribute__((cleanup(_nm_auto_nmpobj_cleanup)))
 static inline void
-_nm_auto_nmpobj_cleanup (NMPObject **pobj)
+_nm_auto_nmpobj_cleanup (gpointer p)
 {
-	nmp_object_unref (*pobj);
+	nmp_object_unref (*((const NMPObject **) p));
 }
+#define nm_auto_nmpobj nm_auto(_nm_auto_nmpobj_cleanup)
 
 typedef struct _NMPCache NMPCache;
 
 typedef void (*NMPCachePreHook) (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
 typedef gboolean (*NMPObjectMatchFn) (const NMPObject *obj, gpointer user_data);
 
-gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b);
-guint nmp_cache_id_hash (const NMPCacheId *id);
-NMPCacheId *nmp_cache_id_clone (const NMPCacheId *id);
-void nmp_cache_id_destroy (NMPCacheId *id);
+const NMDedupMultiEntry *nmp_cache_lookup_entry (const NMPCache *cache,
+                                                 const NMPObject *obj);
+const NMDedupMultiEntry *nmp_cache_lookup_entry_with_idx_type (const NMPCache *cache,
+                                                               NMPCacheIdType cache_id_type,
+                                                               const NMPObject *obj);
+const NMDedupMultiEntry *nmp_cache_lookup_entry_link (const NMPCache *cache,
+                                                      int ifindex);
+const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache,
+                                       const NMPObject *obj);
+const NMPObject *nmp_cache_lookup_link (const NMPCache *cache,
+                                        int ifindex);
+
+typedef struct _NMPLookup NMPLookup;
+
+struct _NMPLookup {
+	NMPCacheIdType cache_id_type;
+	NMPObject selector_obj;
+};
+
+const NMDedupMultiHeadEntry *nmp_cache_lookup_all (const NMPCache *cache,
+                                                   NMPCacheIdType cache_id_type,
+                                                   const NMPObject *select_obj);
+
+static inline const NMDedupMultiHeadEntry *
+nmp_cache_lookup (const NMPCache *cache,
+                  const NMPLookup *lookup)
+{
+	return nmp_cache_lookup_all (cache, lookup->cache_id_type, &lookup->selector_obj);
+}
+
+const NMPLookup *nmp_lookup_init_obj_type (NMPLookup *lookup,
+                                           NMPObjectType obj_type);
+const NMPLookup *nmp_lookup_init_link_by_ifname (NMPLookup *lookup,
+                                                 const char *ifname);
+const NMPLookup *nmp_lookup_init_addrroute (NMPLookup *lookup,
+                                            NMPObjectType obj_type,
+                                            int ifindex);
+const NMPLookup *nmp_lookup_init_route_default (NMPLookup *lookup,
+                                                NMPObjectType obj_type);
+const NMPLookup *nmp_lookup_init_route_by_weak_id (NMPLookup *lookup,
+                                                   const NMPObject *obj);
+const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup,
+                                                       in_addr_t network,
+                                                       guint plen,
+                                                       guint32 metric,
+                                                       guint8 tos);
+const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup,
+                                                       const struct in6_addr *network,
+                                                       guint plen,
+                                                       guint32 metric,
+                                                       const struct in6_addr *src,
+                                                       guint8 src_plen);
+
+GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry,
+                                   NMPObjectType obj_type,
+                                   gboolean visible_only);
+
+static inline gboolean
+nmp_cache_iter_next (NMDedupMultiIter *iter, const NMPObject **out_obj)
+{
+	gboolean has_next;
+
+	has_next = nm_dedup_multi_iter_next (iter);
+	nm_assert (!has_next || NMP_OBJECT_IS_VALID (iter->current->obj));
+	if (out_obj)
+		*out_obj = has_next ? iter->current->obj : NULL;
+	return has_next;
+}
+
+static inline gboolean
+nmp_cache_iter_next_link (NMDedupMultiIter *iter, const NMPlatformLink **out_obj)
+{
+	gboolean has_next;
 
-NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only);
-NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex);
-NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex);
-NMPCacheId *nmp_cache_id_init_link_by_ifname (NMPCacheId *id, const char *ifname);
-NMPCacheId *nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, guint32 network, guint8 plen, guint32 metric);
-NMPCacheId *nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, const struct in6_addr *network, guint8 plen, guint32 metric);
+	has_next = nm_dedup_multi_iter_next (iter);
+	nm_assert (!has_next || NMP_OBJECT_GET_TYPE (iter->current->obj) == NMP_OBJECT_TYPE_LINK);
+	if (out_obj)
+		*out_obj = has_next ? &(((const NMPObject *) iter->current->obj)->link) : NULL;
+	return has_next;
+}
 
-const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len);
-GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id);
-const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj);
-const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex);
+#define nmp_cache_iter_for_each(iter, head, obj) \
+	for (nm_dedup_multi_iter_init ((iter), \
+	                               (head)); \
+	     nmp_cache_iter_next ((iter), (obj)); \
+	     )
 
-const NMPObject *nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route);
+#define nmp_cache_iter_for_each_link(iter, head, obj) \
+	for (nm_dedup_multi_iter_init ((iter), \
+	                               (head)); \
+	     nmp_cache_iter_next_link ((iter), (obj)); \
+	     )
 
 const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache,
                                              int ifindex,
@@ -444,10 +602,8 @@ const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache,
                                              NMLinkType link_type,
                                              NMPObjectMatchFn match_fn,
                                              gpointer user_data);
-GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache,
-                                          NMPCacheId *cache_id,
-                                          GHashTable *hash);
 
+gboolean nmp_cache_link_connected_for_slave (int ifindex_master, const NMPObject *slave);
 gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave);
 const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave);
 
@@ -455,13 +611,182 @@ gboolean nmp_cache_use_udev_get (const NMPCache *cache);
 
 void ASSERT_nmp_cache_is_consistent (const NMPCache *cache);
 
-NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-
-NMPCache *nmp_cache_new (gboolean use_udev);
+NMPCacheOpsType nmp_cache_remove (NMPCache *cache,
+                                  const NMPObject *obj_needle,
+                                  gboolean equals_by_ptr,
+                                  gboolean only_dirty,
+                                  const NMPObject **out_obj_old);
+NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache,
+                                          const NMPObject *obj_needle,
+                                          const NMPObject **out_obj_old,
+                                          const NMPObject **out_obj_new);
+NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache,
+                                          NMPObject *obj_hand_over,
+                                          gboolean is_dump,
+                                          const NMPObject **out_obj_old,
+                                          const NMPObject **out_obj_new);
+NMPCacheOpsType nmp_cache_update_netlink_route (NMPCache *cache,
+                                                NMPObject *obj_hand_over,
+                                                gboolean is_dump,
+                                                guint16 nlmsgflags,
+                                                const NMPObject **out_obj_old,
+                                                const NMPObject **out_obj_new,
+                                                const NMPObject **out_obj_replace,
+                                                gboolean *out_resync_required);
+NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache,
+                                            int ifindex,
+                                            struct udev_device *udevice,
+                                            const NMPObject **out_obj_old,
+                                            const NMPObject **out_obj_new);
+NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache,
+                                                        int ifindex,
+                                                        const NMPObject **out_obj_old,
+                                                        const NMPObject **out_obj_new);
+
+void nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type);
+
+NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev);
 void nmp_cache_free (NMPCache *cache);
 
+static inline void
+ASSERT_nmp_cache_ops (const NMPCache *cache,
+                      NMPCacheOpsType ops_type,
+                      const NMPObject *obj_old,
+                      const NMPObject *obj_new)
+{
+#if NM_MORE_ASSERTS
+	nm_assert (cache);
+	nm_assert (obj_old || obj_new);
+	nm_assert (!obj_old || (   NMP_OBJECT_IS_VALID (obj_old)
+	                        && !NMP_OBJECT_IS_STACKINIT (obj_old)
+	                        && nmp_object_is_alive (obj_old)));
+	nm_assert (!obj_new || (   NMP_OBJECT_IS_VALID (obj_new)
+	                        && !NMP_OBJECT_IS_STACKINIT (obj_new)
+	                        && nmp_object_is_alive (obj_new)));
+
+	switch (ops_type) {
+	case NMP_CACHE_OPS_UNCHANGED:
+		nm_assert (obj_old == obj_new);
+		break;
+	case NMP_CACHE_OPS_ADDED:
+		nm_assert (!obj_old && obj_new);
+		break;
+	case NMP_CACHE_OPS_UPDATED:
+		nm_assert (obj_old &&  obj_new && obj_old != obj_new);
+		break;
+	case NMP_CACHE_OPS_REMOVED:
+		nm_assert (obj_old && !obj_new);
+		break;
+	default:
+		nm_assert_not_reached ();
+	}
+
+	nm_assert (obj_new == NULL || obj_old == NULL || nmp_object_id_equal (obj_new, obj_old));
+	nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_old) == NMP_OBJECT_GET_CLASS (obj_new));
+
+	nm_assert (obj_new == nmp_cache_lookup_obj (cache, obj_new ?: obj_old));
+#endif
+}
+
+const NMDedupMultiHeadEntry *nm_platform_lookup_all (NMPlatform *platform,
+                                                     NMPCacheIdType cache_id_type,
+                                                     const NMPObject *obj);
+
+const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform,
+                                                   NMPCacheIdType cache_id_type,
+                                                   const NMPObject *obj);
+
+static inline const NMDedupMultiHeadEntry *
+nm_platform_lookup_obj_type (NMPlatform *platform,
+                             NMPObjectType obj_type)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_obj_type (&lookup, obj_type);
+	return nm_platform_lookup (platform, &lookup);
+}
+
+static inline const NMDedupMultiHeadEntry *
+nm_platform_lookup_link_by_ifname (NMPlatform *platform,
+                                   const char *ifname)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_link_by_ifname (&lookup, ifname);
+	return nm_platform_lookup (platform, &lookup);
+}
+
+static inline const NMDedupMultiHeadEntry *
+nm_platform_lookup_addrroute (NMPlatform *platform,
+                              NMPObjectType obj_type,
+                              int ifindex)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_addrroute (&lookup, obj_type, ifindex);
+	return nm_platform_lookup (platform, &lookup);
+}
+
+static inline GPtrArray *
+nm_platform_lookup_addrroute_clone (NMPlatform *platform,
+                                    NMPObjectType obj_type,
+                                    int ifindex,
+                                    NMPObjectPredicateFunc predicate,
+                                    gpointer user_data)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_addrroute (&lookup, obj_type, ifindex);
+	return nm_platform_lookup_clone (platform, &lookup, predicate, user_data);
+}
+
+static inline const NMDedupMultiHeadEntry *
+nm_platform_lookup_route_default (NMPlatform *platform,
+                                  NMPObjectType obj_type)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_route_default (&lookup, obj_type);
+	return nm_platform_lookup (platform, &lookup);
+}
+
+static inline GPtrArray *
+nm_platform_lookup_route_default_clone (NMPlatform *platform,
+                                        NMPObjectType obj_type,
+                                        NMPObjectPredicateFunc predicate,
+                                        gpointer user_data)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_route_default (&lookup, obj_type);
+	return nm_platform_lookup_clone (platform, &lookup, predicate, user_data);
+}
+
+static inline const NMDedupMultiHeadEntry *
+nm_platform_lookup_ip4_route_by_weak_id (NMPlatform *platform,
+                                         in_addr_t network,
+                                         guint plen,
+                                         guint32 metric,
+                                         guint8 tos)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_ip4_route_by_weak_id (&lookup, network, plen, metric, tos);
+	return nm_platform_lookup (platform, &lookup);
+}
+
+static inline const NMDedupMultiHeadEntry *
+nm_platform_lookup_ip6_route_by_weak_id (NMPlatform *platform,
+                                         const struct in6_addr *network,
+                                         guint plen,
+                                         guint32 metric,
+                                         const struct in6_addr *src,
+                                         guint8 src_plen)
+{
+	NMPLookup lookup;
+
+	nmp_lookup_init_ip6_route_by_weak_id (&lookup, network, plen, metric, src, src_plen);
+	return nm_platform_lookup (platform, &lookup);
+}
+
 #endif /* __NMP_OBJECT_H__ */