diff --git a/Makefile.am b/Makefile.am index f25fc02711..b97da487fc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -415,6 +415,7 @@ libnm_core_lib_h_pub_real = \ libnm_core_lib_h_pub_mkenums = \ libnm-core/nm-core-enum-types.h libnm_core_lib_h_priv = \ + shared/nm-utils/nm-dedup-multi.h \ shared/nm-utils/nm-enum-utils.h \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-udev-utils.h \ @@ -429,6 +430,7 @@ libnm_core_lib_h_priv = \ libnm-core/nm-setting-private.h \ libnm-core/nm-utils-private.h libnm_core_lib_c_real = \ + shared/nm-utils/nm-dedup-multi.c \ shared/nm-utils/nm-enum-utils.c \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-udev-utils.c \ @@ -1271,13 +1273,12 @@ $(src_libsystemd_nm_la_OBJECTS): $(libnm_core_lib_h_pub_mkenums) src_libNetworkManagerBase_la_CPPFLAGS = $(src_cppflags) src_libNetworkManagerBase_la_SOURCES = \ + \ src/nm-core-utils.c \ src/nm-core-utils.h \ src/nm-logging.c \ src/nm-logging.h \ \ - src/nm-multi-index.c \ - src/nm-multi-index.h \ src/NetworkManagerUtils.c \ src/NetworkManagerUtils.h \ \ @@ -1289,6 +1290,7 @@ src_libNetworkManagerBase_la_SOURCES = \ src/platform/nm-platform-utils.h \ src/platform/nm-platform.c \ src/platform/nm-platform.h \ + src/platform/nm-platform-private.h \ src/platform/nm-linux-platform.c \ src/platform/nm-linux-platform.h \ src/platform/wifi/wifi-utils-nl80211.c \ @@ -4444,6 +4446,7 @@ EXTRA_DIST += \ shared/nm-utils/c-list.h \ shared/nm-utils/gsystem-local-alloc.h \ shared/nm-utils/nm-glib.h \ + shared/nm-utils/nm-obj.h \ shared/nm-utils/nm-macros-internal.h \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-shared-utils.h \ diff --git a/clients/cli/utils.h b/clients/cli/utils.h index 1724d4391e..19145974ea 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -129,7 +129,10 @@ typedef enum { } G_STMT_END struct _NmcMetaGenericInfo { - const NMMetaType *meta_type; + union { + NMObjBaseInst parent; + const NMMetaType *meta_type; + }; NmcGenericInfoType info_type; const char *name; const char *name_header; diff --git a/clients/common/nm-meta-setting-desc.h b/clients/common/nm-meta-setting-desc.h index c1fd902210..e61b1fc4a6 100644 --- a/clients/common/nm-meta-setting-desc.h +++ b/clients/common/nm-meta-setting-desc.h @@ -20,6 +20,7 @@ #ifndef __NM_META_SETTING_DESC_H__ #define __NM_META_SETTING_DESC_H__ +#include "nm-utils/nm-obj.h" #include "nm-meta-setting.h" struct _NMDevice; @@ -284,7 +285,10 @@ enum { #define nm_meta_property_info_vpn_service_type (nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_VPN].properties[_NM_META_PROPERTY_TYPE_VPN_SERVICE_TYPE]) struct _NMMetaPropertyInfo { - const NMMetaType *meta_type; + union { + NMObjBaseInst parent; + const NMMetaType *meta_type; + }; const NMMetaSettingInfoEditor *setting_info; @@ -316,7 +320,10 @@ typedef struct _NMMetaSettingValidPartItem { } NMMetaSettingValidPartItem; struct _NMMetaSettingInfoEditor { - const NMMetaType *meta_type; + union { + NMObjBaseInst parent; + const NMMetaType *meta_type; + }; const NMMetaSettingInfo *general; const char *alias; const char *pretty_name; @@ -341,6 +348,7 @@ struct _NMMetaSettingInfoEditor { }; struct _NMMetaType { + NMObjBaseClass parent; const char *type_name; const char *(*get_name) (const NMMetaAbstractInfo *abstract_info, gboolean for_header); @@ -364,7 +372,10 @@ struct _NMMetaType { }; struct _NMMetaAbstractInfo { - const NMMetaType *meta_type; + union { + NMObjBaseInst parent; + const NMMetaType *meta_type; + }; }; extern const NMMetaType nm_meta_type_setting_info_editor; diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index c52948bd7b..2958cd90d9 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -4016,16 +4016,16 @@ _nm_utils_strstrdictkey_hash (gconstpointer a) if (((int) k->type) & ~STRSTRDICTKEY_ALL_SET) g_return_val_if_reached (0); - h = (h << 5) + h + k->type; + h = NM_HASH_COMBINE (h, k->type); if (k->type & STRSTRDICTKEY_ALL_SET) { p = (void *) k->data; for (; *p != '\0'; p++) - h = (h << 5) + h + *p; + h = NM_HASH_COMBINE (h, *p); if (k->type == STRSTRDICTKEY_ALL_SET) { /* the key contains two strings. Continue... */ - h = (h << 5) + h + '\0'; + h = NM_HASH_COMBINE (h, '\0'); for (p++; *p != '\0'; p++) - h = (h << 5) + h + *p; + h = NM_HASH_COMBINE (h, *p); } } } diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index b8c3dc8e12..db261adfec 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -60,6 +60,7 @@ #include "nm-setting-wireless-security.h" #include "nm-simple-connection.h" #include "nm-keyfile-internal.h" +#include "nm-utils/nm-dedup-multi.h" #include "test-general-enums.h" @@ -75,6 +76,302 @@ G_STATIC_ASSERT (sizeof (bool) <= sizeof (int)); /*****************************************************************************/ +typedef struct { + NMDedupMultiObj parent; + guint val; + guint other; +} DedupObj; + +static const NMDedupMultiObjClass dedup_obj_class; + +static DedupObj * +_dedup_obj_assert (const NMDedupMultiObj *obj) +{ + DedupObj *o; + + g_assert (obj); + o = (DedupObj *) obj; + g_assert (o->parent.klass == &dedup_obj_class); + g_assert (o->parent._ref_count > 0); + g_assert (o->val > 0); + return o; +} + +static const NMDedupMultiObj * +_dedup_obj_clone (const NMDedupMultiObj *obj) +{ + DedupObj *o, *o2; + + o = _dedup_obj_assert (obj); + o2 = g_slice_new0 (DedupObj); + o2->parent.klass = &dedup_obj_class; + o2->parent._ref_count = 1; + o2->val = o->val; + o2->other = o->other; + return (NMDedupMultiObj *) o2; +} + +static void +_dedup_obj_destroy (NMDedupMultiObj *obj) +{ + DedupObj *o = (DedupObj *) obj; + + nm_assert (o->parent._ref_count == 0); + o->parent._ref_count = 1; + o = _dedup_obj_assert (obj); + g_slice_free (DedupObj, o); +} + +static guint +_dedup_obj_full_hash (const NMDedupMultiObj *obj) +{ + const DedupObj *o; + + o = _dedup_obj_assert (obj); + return (o->val * 33) + o->other; +} + +static gboolean +_dedup_obj_full_equal (const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const DedupObj *o_a = _dedup_obj_assert (obj_a); + const DedupObj *o_b = _dedup_obj_assert (obj_b); + + return o_a->val == o_b->val + && o_a->other == o_b->other; +} + +static const NMDedupMultiObjClass dedup_obj_class = { + .obj_clone = _dedup_obj_clone, + .obj_destroy = _dedup_obj_destroy, + .obj_full_equality_allows_different_class = FALSE, + .obj_full_hash = _dedup_obj_full_hash, + .obj_full_equal = _dedup_obj_full_equal, +}; + +#define DEDUP_OBJ_INIT(val_val, other_other) \ + (&((DedupObj) { \ + .parent = { \ + .klass = &dedup_obj_class, \ + ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, \ + }, \ + .val = (val_val), \ + .other = (other_other), \ + })) + +typedef struct { + NMDedupMultiIdxType parent; + guint partition_size; + guint val_mod; +} DedupIdxType; + +static const NMDedupMultiIdxTypeClass dedup_idx_type_class; + +static const DedupIdxType * +_dedup_idx_assert (const NMDedupMultiIdxType *idx_type) +{ + DedupIdxType *t; + + g_assert (idx_type); + t = (DedupIdxType *) idx_type; + g_assert (t->parent.klass == &dedup_idx_type_class); + g_assert (t->partition_size > 0); + g_assert (t->val_mod > 0); + return t; +} + +static guint +_dedup_idx_obj_id_hash (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + const DedupIdxType *t; + const DedupObj *o; + guint h; + + t = _dedup_idx_assert (idx_type); + o = _dedup_obj_assert (obj); + + h = o->val / t->partition_size; + h = (h * 33) + (o->val % t->val_mod); + return h; +} + +static gboolean +_dedup_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const DedupIdxType *t; + const DedupObj *o_a; + const DedupObj *o_b; + + t = _dedup_idx_assert (idx_type); + o_a = _dedup_obj_assert (obj_a); + o_b = _dedup_obj_assert (obj_b); + + return (o_a->val / t->partition_size) == (o_b->val / t->partition_size) + && (o_a->val % t->val_mod) == (o_b->val % t->val_mod); +} + +static guint +_dedup_idx_obj_partition_hash (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + const DedupIdxType *t; + const DedupObj *o; + + t = _dedup_idx_assert (idx_type); + o = _dedup_obj_assert (obj); + + return o->val / t->partition_size; +} + +static gboolean +_dedup_idx_obj_partition_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const DedupIdxType *t; + const DedupObj *o_a; + const DedupObj *o_b; + + t = _dedup_idx_assert (idx_type); + o_a = _dedup_obj_assert (obj_a); + o_b = _dedup_obj_assert (obj_b); + + return (o_a->val / t->partition_size) == (o_b->val / t->partition_size); +} + +static const NMDedupMultiIdxTypeClass dedup_idx_type_class = { + .idx_obj_id_hash = _dedup_idx_obj_id_hash, + .idx_obj_id_equal = _dedup_idx_obj_id_equal, + .idx_obj_partition_hash = _dedup_idx_obj_partition_hash, + .idx_obj_partition_equal = _dedup_idx_obj_partition_equal, +}; + +static const DedupIdxType * +DEDUP_IDX_TYPE_INIT (DedupIdxType *idx_type, guint partition_size, guint val_mod) +{ + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, &dedup_idx_type_class); + idx_type->val_mod = val_mod; + idx_type->partition_size = partition_size; + return idx_type; +} + +static gboolean +_dedup_idx_add (NMDedupMultiIndex *idx, const DedupIdxType *idx_type, const DedupObj *obj, NMDedupMultiIdxMode mode, const NMDedupMultiEntry **out_entry) +{ + g_assert (idx); + _dedup_idx_assert ((NMDedupMultiIdxType *) idx_type); + if (obj) + _dedup_obj_assert ((NMDedupMultiObj *) obj); + return nm_dedup_multi_index_add (idx, (NMDedupMultiIdxType *) idx_type, + obj, mode, out_entry, NULL); +} + +static void +_dedup_head_entry_assert (const NMDedupMultiHeadEntry *entry) +{ + g_assert (entry); + g_assert (entry->len > 0); + g_assert (entry->len == c_list_length (&entry->lst_entries_head)); + g_assert (entry->idx_type); + g_assert (entry->is_head); +} + +static const DedupObj * +_dedup_entry_assert (const NMDedupMultiEntry *entry) +{ + g_assert (entry); + g_assert (!c_list_is_empty (&entry->lst_entries)); + g_assert (entry->head); + g_assert (!entry->is_head); + g_assert (entry->head != (gpointer) entry); + _dedup_head_entry_assert (entry->head); + return _dedup_obj_assert (entry->obj); +} + +static const DedupIdxType * +_dedup_entry_get_idx_type (const NMDedupMultiEntry *entry) +{ + _dedup_entry_assert (entry); + + g_assert (entry->head); + g_assert (entry->head->idx_type); + return _dedup_idx_assert (entry->head->idx_type); +} + +static void +_dedup_entry_assert_all (const NMDedupMultiEntry *entry, gssize expected_idx, const DedupObj *const*expected_obj) +{ + gsize n, i; + CList *iter; + + g_assert (entry); + _dedup_entry_assert (entry); + + g_assert (expected_obj); + n = NM_PTRARRAY_LEN (expected_obj); + + g_assert (n == c_list_length (&entry->lst_entries)); + + g_assert (expected_idx >= -1 && expected_idx < n); + g_assert (entry->head); + if (expected_idx == -1) + g_assert (entry->head == (gpointer) entry); + else + g_assert (entry->head != (gpointer) entry); + + i = 0; + c_list_for_each (iter, &entry->head->lst_entries_head) { + const NMDedupMultiEntry *entry_current = c_list_entry (iter, NMDedupMultiEntry, lst_entries); + const DedupObj *obj_current; + const DedupIdxType *idx_type = _dedup_entry_get_idx_type (entry_current); + + obj_current = _dedup_entry_assert (entry_current); + g_assert (obj_current); + g_assert (i < n); + if (expected_idx == i) + g_assert (entry_current == entry); + g_assert (idx_type->parent.klass->idx_obj_partition_equal (&idx_type->parent, + entry_current->obj, + c_list_entry (entry->head->lst_entries_head.next, NMDedupMultiEntry, lst_entries)->obj)); + i++; + } +} +#define _dedup_entry_assert_all(entry, expected_idx, ...) _dedup_entry_assert_all (entry, expected_idx, (const DedupObj *const[]) { __VA_ARGS__, NULL }) + +static void +test_dedup_multi (void) +{ + NMDedupMultiIndex *idx; + DedupIdxType IDX_20_3_a_stack; + const DedupIdxType *const IDX_20_3_a = DEDUP_IDX_TYPE_INIT (&IDX_20_3_a_stack, 20, 3); + const NMDedupMultiEntry *entry1; + + idx = nm_dedup_multi_index_new (); + + g_assert (_dedup_idx_add (idx, IDX_20_3_a, DEDUP_OBJ_INIT (1, 1), NM_DEDUP_MULTI_IDX_MODE_APPEND, &entry1)); + _dedup_entry_assert_all (entry1, 0, DEDUP_OBJ_INIT (1, 1)); + + g_assert (nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 1))); + g_assert (!nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 2))); + + g_assert (_dedup_idx_add (idx, IDX_20_3_a, DEDUP_OBJ_INIT (1, 2), NM_DEDUP_MULTI_IDX_MODE_APPEND, &entry1)); + _dedup_entry_assert_all (entry1, 0, DEDUP_OBJ_INIT (1, 2)); + + g_assert (!nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 1))); + g_assert (nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 2))); + + g_assert (_dedup_idx_add (idx, IDX_20_3_a, DEDUP_OBJ_INIT (2, 2), NM_DEDUP_MULTI_IDX_MODE_APPEND, &entry1)); + _dedup_entry_assert_all (entry1, 1, DEDUP_OBJ_INIT (1, 2), DEDUP_OBJ_INIT (2, 2)); + + nm_dedup_multi_index_unref (idx); +} + +/*****************************************************************************/ + static NMConnection * _connection_new_from_dbus (GVariant *dict, GError **error) { @@ -5782,7 +6079,7 @@ int main (int argc, char **argv) { nmtst_init (&argc, &argv, TRUE); - /* The tests */ + g_test_add_func ("/core/general/test_dedup_multi", test_dedup_multi); g_test_add_func ("/core/general/test_utils_str_utf8safe", test_utils_str_utf8safe); g_test_add_func ("/core/general/test_nm_in_set", test_nm_in_set); g_test_add_func ("/core/general/test_nm_in_strset", test_nm_in_strset); diff --git a/shared/nm-utils/c-list.h b/shared/nm-utils/c-list.h index 07e3f3cecd..557862b6ff 100644 --- a/shared/nm-utils/c-list.h +++ b/shared/nm-utils/c-list.h @@ -74,7 +74,7 @@ static inline void c_list_init(CList *what) { offsetof(_t, _m)) - offsetof(_t, _m))) /** - * c_list_is_linked() - check whether a entry is linked + * c_list_is_linked() - check whether an entry is linked * @what: entry to check, or NULL * * Return: True if @what is linked in a list, false if not. @@ -206,71 +206,22 @@ static inline void c_list_swap(CList *list1, CList *list2) { * This removes all the entries from @source and splice them into @target. * The order of the two lists is preserved and the source is appended * to the end of target. + * + * On return, the source list will be empty. */ static inline void c_list_splice(CList *target, CList *source) { - if (c_list_is_empty(source)) - return; + if (!c_list_is_empty(source)) { + /* attach the front of @source to the tail of @target */ + source->next->prev = target->prev; + target->prev->next = source->next; - /* attach the front of @source to the tail of @target */ - source->next->prev = target->prev; - target->prev->next = source->next; + /* attach the tail of @source to the front of @target */ + source->prev->next = target; + target->prev = source->prev; - /* attach the tail of @source to the front of @target */ - source->prev->next = target; - target->prev = source->prev; -} - -/** - * c_list_loop_first() - return first list element, or head if empty - * @list: list to operate on - * - * This is an O(1) accessor to the first list element. If the list is empty, - * this returns a pointer to the list head. Hence, this never returns NULL. - * - * Return: Pointer to first list element, or pointer to head if empty. - */ -static inline CList *c_list_loop_first(CList *list) { - return list->next; -} - -/** - * c_list_loop_last() - return last list element, or head if empty - * @list: list to operate on - * - * This is an O(1) accessor to the last list element. If the list is empty, - * this returns a pointer to the list head. Hence, this never returns NULL. - * - * Return: Pointer to last list element, or pointer to head if empty. - */ -static inline CList *c_list_loop_last(CList *list) { - return list->prev; -} - -/** - * c_list_loop_next() - return next list element, or head if none - * @what: list entry to operate on - * - * This is an O(1) accessor to the next list element. If @what is the list tail - * this will return a pointer to the list head. Hence, this never returns NULL. - * - * Return: Pointer to next list element, or pointer to head if none. - */ -static inline CList *c_list_loop_next(CList *what) { - return what->next; -} - -/** - * c_list_loop_prev() - return previous list element, or head if none - * @what: list entry to operate on - * - * This is an O(1) accessor to the previous list element. If @what is the list - * front this will return a pointer to the list head. Hence, this never returns - * NULL. - * - * Return: Pointer to previous list element, or pointer to head if none. - */ -static inline CList *c_list_loop_prev(CList *what) { - return what->prev; + /* clear source */ + *source = (CList)C_LIST_INIT(*source); + } } /** @@ -281,10 +232,10 @@ static inline CList *c_list_loop_prev(CList *what) { * This is a macro to use as for-loop to iterate an entire list. It is meant as * convenience macro. Feel free to code your own loop iterator. */ -#define c_list_for_each(_iter, _list) \ - for (_iter = c_list_loop_first(_list); \ - _iter != (_list); \ - _iter = c_list_loop_next(_iter)) +#define c_list_for_each(_iter, _list) \ + for (_iter = (_list)->next; \ + (_iter) != (_list); \ + _iter = (_iter)->next) /** @@ -302,10 +253,10 @@ static inline CList *c_list_loop_prev(CList *what) { * havoc if you remove other list entries. You better not modify anything but * the current list entry. */ -#define c_list_for_each_safe(_iter, _safe, _list) \ - for (_iter = c_list_loop_first(_list), _safe = c_list_loop_next(_iter); \ - _iter != (_list); \ - _iter = _safe, _safe = c_list_loop_next(_safe)) +#define c_list_for_each_safe(_iter, _safe, _list) \ + for (_iter = (_list)->next, _safe = (_iter)->next; \ + (_iter) != (_list); \ + _iter = (_safe), _safe = (_safe)->next) /** * c_list_for_each_entry() - loop over all list entries @@ -316,10 +267,10 @@ static inline CList *c_list_loop_prev(CList *what) { * This combines c_list_for_each() with c_list_entry(), making it easy to * iterate over a list of a specific type. */ -#define c_list_for_each_entry(_iter, _list, _m) \ - for (_iter = c_list_entry(c_list_loop_first(_list), __typeof__(*_iter), _m); \ - &_iter->_m != (_list); \ - _iter = c_list_entry(c_list_loop_next(&_iter->_m), __typeof__(*_iter), _m)) +#define c_list_for_each_entry(_iter, _list, _m) \ + for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m); \ + &(_iter)->_m != (_list); \ + _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m)) /** * c_list_for_each_entry_safe() - loop over all list entries, safe for removal @@ -331,12 +282,12 @@ static inline CList *c_list_loop_prev(CList *what) { * This combines c_list_for_each_safe() with c_list_entry(), making it easy to * iterate over a list of a specific type. */ -#define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \ - for (_iter = c_list_entry(c_list_loop_first(_list), __typeof__(*_iter), _m), \ - _safe = c_list_entry(c_list_loop_next(&_iter->_m), __typeof__(*_iter), _m);\ - &_iter->_m != (_list); \ - _iter = _safe, \ - _safe = c_list_entry(c_list_loop_next(&_safe->_m), __typeof__(*_iter), _m)) +#define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \ + for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ + _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ + &(_iter)->_m != (_list); \ + _iter = (_safe), \ + _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ /** * c_list_first() - return pointer to first element, or NULL if empty @@ -391,44 +342,54 @@ static inline CList *c_list_last(CList *list) { c_list_entry(c_list_last(_list), _t, _m) /** - * c_list_length() - return the number of linked entries, excluding the head + * c_list_length() - return number of linked entries, excluding the head * @list: list to operate on * - * Returns the number of entires in the list, excluding the list head - * @list. That is, for a list that is empty according to c_list_is_empty(), - * the returned length is 0. This requires to iterate the list and has - * thus O(n) runtime. + * Returns the number of entries in the list, excluding the list head @list. + * That is, for a list that is empty according to c_list_is_empty(), the + * returned length is 0. This requires to iterate the list and has thus O(n) + * runtime. * - * Return: the number of items in the list + * Note that this function is meant for debugging purposes only. If you need + * the list size during normal operation, you should maintain a counter + * separately. + * + * Return: Number of items in @list. */ -static inline size_t c_list_length(const CList *list) { - CList *iter; - size_t n = 0; +static inline unsigned long c_list_length(const CList *list) { + unsigned long n = 0; + const CList *iter; + + c_list_for_each(iter, list) + ++n; - c_list_for_each(iter, (CList *)list) - n++; return n; } /** - * c_list_contains() - whether an item is linked in a certain list + * c_list_contains() - check whether an entry is linked in a certain list * @list: list to operate on - * @what: the list entry to find + * @what: entry to look for * - * Searches @list whether @what is a linked entry of the list - * in O(n). For the head @list, this also returns True. + * This checks whether @what is linked into @list. This requires a linear + * search through the list, as such runs in O(n). Note that the list-head is + * considered part of the list, and hence this returns true if @what equals + * @list. * - * Return: True if @what is in @list + * Note that this function is meant for debugging purposes, and consistency + * checks. You should always be aware whether your objects are linked in a + * specific list. + * + * Return: True if @what is in @list, false otherwise. */ static inline _Bool c_list_contains(const CList *list, const CList *what) { - const CList *iter = list; + const CList *iter; - do { - if (iter == what) + c_list_for_each(iter, list) + if (what == iter) return 1; - iter = iter->next; - } while (iter != list); - return 0; + + return what == list; } #ifdef __cplusplus diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c new file mode 100644 index 0000000000..92dba89936 --- /dev/null +++ b/shared/nm-utils/nm-dedup-multi.c @@ -0,0 +1,1025 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dedup-multi.h" + +/*****************************************************************************/ + +typedef struct { + /* the stack-allocated lookup entry. It has a compatible + * memory layout with NMDedupMultiEntry and NMDedupMultiHeadEntry. + * + * It is recognizable by having lst_entries_sentinel.next set to NULL. + * Contrary to the other entries, which have lst_entries.next + * always non-NULL. + * */ + CList lst_entries_sentinel; + const NMDedupMultiObj *obj; + const NMDedupMultiIdxType *idx_type; + bool lookup_head; +} LookupEntry; + +struct _NMDedupMultiIndex { + int ref_count; + GHashTable *idx_entries; + GHashTable *idx_objs; +}; + +/*****************************************************************************/ + +static void +ASSERT_idx_type (const NMDedupMultiIdxType *idx_type) +{ + nm_assert (idx_type); +#if NM_MORE_ASSERTS > 10 + nm_assert (idx_type->klass); + nm_assert (idx_type->klass->idx_obj_id_hash); + nm_assert (idx_type->klass->idx_obj_id_equal); + nm_assert (!!idx_type->klass->idx_obj_partition_hash == !!idx_type->klass->idx_obj_partition_equal); + nm_assert (idx_type->lst_idx_head.next); +#endif +} + +void +nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, + const NMDedupMultiIdxTypeClass *klass) +{ + nm_assert (idx_type); + nm_assert (klass); + + memset (idx_type, 0, sizeof (*idx_type)); + idx_type->klass = klass; + c_list_init (&idx_type->lst_idx_head); + + ASSERT_idx_type (idx_type); +} + +/*****************************************************************************/ + +static NMDedupMultiEntry * +_entry_lookup_obj (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + const LookupEntry stack_entry = { + .obj = obj, + .idx_type = idx_type, + .lookup_head = FALSE, + }; + + ASSERT_idx_type (idx_type); + return g_hash_table_lookup (self->idx_entries, &stack_entry); +} + +static NMDedupMultiHeadEntry * +_entry_lookup_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + NMDedupMultiHeadEntry *head_entry; + const LookupEntry stack_entry = { + .obj = obj, + .idx_type = idx_type, + .lookup_head = TRUE, + }; + + ASSERT_idx_type (idx_type); + + if (!idx_type->klass->idx_obj_partition_equal) { + if (c_list_is_empty (&idx_type->lst_idx_head)) + head_entry = NULL; + else { + nm_assert (c_list_length (&idx_type->lst_idx_head) == 1); + head_entry = c_list_entry (idx_type->lst_idx_head.next, NMDedupMultiHeadEntry, lst_idx); + } + nm_assert (head_entry == g_hash_table_lookup (self->idx_entries, &stack_entry)); + return head_entry; + } + + return g_hash_table_lookup (self->idx_entries, &stack_entry); +} + +static void +_entry_unpack (const NMDedupMultiEntry *entry, + const NMDedupMultiIdxType **out_idx_type, + const NMDedupMultiObj **out_obj, + gboolean *out_lookup_head) +{ + const NMDedupMultiHeadEntry *head_entry; + const LookupEntry *lookup_entry; + + nm_assert (entry); + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (LookupEntry, lst_entries_sentinel) == G_STRUCT_OFFSET (NMDedupMultiEntry, lst_entries)); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, lst_entries) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, lst_entries_head)); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, obj) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, idx_type)); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, is_head) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, is_head)); + + if (!entry->lst_entries.next) { + /* the entry is stack-allocated by _entry_lookup(). */ + lookup_entry = (LookupEntry *) entry; + *out_obj = lookup_entry->obj; + *out_idx_type = lookup_entry->idx_type; + *out_lookup_head = lookup_entry->lookup_head; + } else if (entry->is_head) { + head_entry = (NMDedupMultiHeadEntry *) entry; + nm_assert (!c_list_is_empty (&head_entry->lst_entries_head)); + *out_obj = c_list_entry (head_entry->lst_entries_head.next, NMDedupMultiEntry, lst_entries)->obj; + *out_idx_type = head_entry->idx_type; + *out_lookup_head = TRUE; + } else { + *out_obj = entry->obj; + *out_idx_type = entry->head->idx_type; + *out_lookup_head = FALSE; + } + + nm_assert (NM_IN_SET (*out_lookup_head, FALSE, TRUE)); + ASSERT_idx_type (*out_idx_type); +} + +static guint +_dict_idx_entries_hash (const NMDedupMultiEntry *entry) +{ + const NMDedupMultiIdxType *idx_type; + const NMDedupMultiObj *obj; + gboolean lookup_head; + guint h; + + _entry_unpack (entry, &idx_type, &obj, &lookup_head); + + if (idx_type->klass->idx_obj_partition_hash) { + nm_assert (obj); + h = idx_type->klass->idx_obj_partition_hash (idx_type, obj); + } else + h = 1914869417; + + if (!lookup_head) + h = idx_type->klass->idx_obj_id_hash (idx_type, obj); + + h = NM_HASH_COMBINE (h, GPOINTER_TO_UINT (idx_type)); + h = NM_HASH_COMBINE (h, lookup_head); + return h; +} + +static gboolean +_dict_idx_entries_equal (const NMDedupMultiEntry *entry_a, + const NMDedupMultiEntry *entry_b) +{ + const NMDedupMultiIdxType *idx_type_a, *idx_type_b; + const NMDedupMultiObj *obj_a, *obj_b; + gboolean lookup_head_a, lookup_head_b; + + _entry_unpack (entry_a, &idx_type_a, &obj_a, &lookup_head_a); + _entry_unpack (entry_b, &idx_type_b, &obj_b, &lookup_head_b); + + if ( idx_type_a != idx_type_b + || lookup_head_a != lookup_head_b) + return FALSE; + if (!nm_dedup_multi_idx_type_partition_equal (idx_type_a, obj_a, obj_b)) + return FALSE; + if ( !lookup_head_a + && !nm_dedup_multi_idx_type_id_equal (idx_type_a, obj_a, obj_b)) + return FALSE; + return TRUE; +} + +/*****************************************************************************/ + +static gboolean +_add (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMDedupMultiEntry *entry, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry *entry_order, + NMDedupMultiHeadEntry *head_existing, + const NMDedupMultiEntry **out_entry, + const NMDedupMultiObj **out_obj_old) +{ + NMDedupMultiHeadEntry *head_entry; + const NMDedupMultiObj *obj_new, *obj_old; + gboolean add_head_entry = FALSE; + + nm_assert (self); + ASSERT_idx_type (idx_type); + nm_assert (obj); + nm_assert (NM_IN_SET (mode, + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE)); + nm_assert (!head_existing || head_existing->idx_type == idx_type); + nm_assert (({ + const NMDedupMultiHeadEntry *_h; + gboolean _ok = TRUE; + if (head_existing) { + _h = nm_dedup_multi_index_lookup_head (self, idx_type, obj); + if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) + _ok = (_h == NULL); + else + _ok = (_h == head_existing); + } + _ok; + })); + + if (entry) { + nm_dedup_multi_entry_set_dirty (entry, FALSE); + + nm_assert (!head_existing || entry->head == head_existing); + + if (entry_order) { + nm_assert (entry_order->head == entry->head); + nm_assert (c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); + nm_assert (c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); + } + + switch (mode) { + case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: + if (entry_order) { + if ( entry_order != entry + && entry->lst_entries.next != &entry_order->lst_entries) { + c_list_unlink (&entry->lst_entries); + c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); + } + } else { + if (entry->lst_entries.prev != &entry->head->lst_entries_head) { + c_list_unlink (&entry->lst_entries); + c_list_link_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + } + } + break; + case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE: + if (entry_order) { + if ( entry_order != entry + && entry->lst_entries.prev != &entry_order->lst_entries) { + c_list_unlink (&entry->lst_entries); + c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); + } + } else { + if (entry->lst_entries.next != &entry->head->lst_entries_head) { + c_list_unlink (&entry->lst_entries); + c_list_link_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + } + } + break; + case NM_DEDUP_MULTI_IDX_MODE_PREPEND: + case NM_DEDUP_MULTI_IDX_MODE_APPEND: + break; + }; + + if ( obj == entry->obj + || obj->klass->obj_full_equal (obj, + entry->obj)) { + NM_SET_OUT (out_entry, entry); + NM_SET_OUT (out_obj_old, nm_dedup_multi_obj_ref (entry->obj)); + return FALSE; + } + + obj_new = nm_dedup_multi_index_obj_intern (self, obj); + + obj_old = entry->obj; + entry->obj = obj_new; + + NM_SET_OUT (out_entry, entry); + if (out_obj_old) + *out_obj_old = obj_old; + else + nm_dedup_multi_obj_unref (obj_old); + return TRUE; + } + + if ( idx_type->klass->idx_obj_partitionable + && !idx_type->klass->idx_obj_partitionable (idx_type, obj)) { + /* this object cannot be partitioned by this idx_type. */ + nm_assert (!head_existing || head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING); + NM_SET_OUT (out_entry, NULL); + NM_SET_OUT (out_obj_old, NULL); + return FALSE; + } + + obj_new = nm_dedup_multi_index_obj_intern (self, obj); + + if (!head_existing) + head_entry = _entry_lookup_head (self, idx_type, obj_new); + else if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) + head_entry = NULL; + else + head_entry = head_existing; + + if (!head_entry) { + head_entry = g_slice_new0 (NMDedupMultiHeadEntry); + head_entry->is_head = TRUE; + head_entry->idx_type = idx_type; + c_list_init (&head_entry->lst_entries_head); + c_list_link_tail (&idx_type->lst_idx_head, &head_entry->lst_idx); + add_head_entry = TRUE; + } else + nm_assert (c_list_contains (&idx_type->lst_idx_head, &head_entry->lst_idx)); + + if (entry_order) { + nm_assert (!add_head_entry); + nm_assert (entry_order->head == head_entry); + nm_assert (c_list_contains (&head_entry->lst_entries_head, &entry_order->lst_entries)); + nm_assert (c_list_contains (&entry_order->lst_entries, &head_entry->lst_entries_head)); + } + + entry = g_slice_new0 (NMDedupMultiEntry); + entry->obj = obj_new; + entry->head = head_entry; + + switch (mode) { + case NM_DEDUP_MULTI_IDX_MODE_PREPEND: + case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: + if (entry_order) + c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); + else + c_list_link_front (&head_entry->lst_entries_head, &entry->lst_entries); + break; + default: + if (entry_order) + c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); + else + c_list_link_tail (&head_entry->lst_entries_head, &entry->lst_entries); + break; + }; + + idx_type->len++; + head_entry->len++; + + if ( add_head_entry + && !nm_g_hash_table_add (self->idx_entries, head_entry)) + nm_assert_not_reached (); + + if (!nm_g_hash_table_add (self->idx_entries, entry)) + nm_assert_not_reached (); + + NM_SET_OUT (out_entry, entry); + NM_SET_OUT (out_obj_old, NULL); + return TRUE; +} + +gboolean +nm_dedup_multi_index_add (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old) +{ + NMDedupMultiEntry *entry; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + g_return_val_if_fail (obj, FALSE); + g_return_val_if_fail (NM_IN_SET (mode, + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), + FALSE); + + entry = _entry_lookup_obj (self, idx_type, obj); + return _add (self, idx_type, obj, + entry, mode, + NULL, NULL, + out_entry, out_obj_old); +} + +/* nm_dedup_multi_index_add_full: + * @self: the index instance. + * @idx_type: the index handle for storing @obj. + * @obj: the NMDedupMultiObj instance to add. + * @mode: whether to append or prepend the new item. If @entry_order is given, + * the entry will be sorted after/before, instead of appending/prepending to + * the entire list. If a comparable object is already tracked, then it may + * still be resorted by specifying one of the "FORCE" modes. + * @entry_order: if not NULL, the new entry will be sorted before or after @entry_order. + * If given, @entry_order MUST be tracked by @self, and the object it points to MUST + * be in the same partition tracked by @idx_type. That is, they must have the same + * head_entry and it means, you must ensure that @entry_order and the created/modified + * entry will share the same head. + * @entry_existing: if not NULL, it safes a hash lookup of the entry where the + * object will be placed in. You can omit this, and it will be automatically + * detected (at the expense of an additional hash lookup). + * Basically, this is the result of nm_dedup_multi_index_lookup_obj(), + * with the pecularity that if you know that @obj is not yet tracked, + * you may specify %NM_DEDUP_MULTI_ENTRY_MISSING. + * @head_existing: an optional argument to safe a lookup for the head. If specified, + * it must be identical to nm_dedup_multi_index_lookup_head(), with the pecularity + * that if the head is not yet tracked, you may specify %NM_DEDUP_MULTI_HEAD_ENTRY_MISSING + * @out_entry: if give, return the added entry. This entry may have already exists (update) + * or be newly created. If @obj is not partitionable according to @idx_type, @obj + * is not to be added and it returns %NULL. + * @out_obj_old: if given, return the previously contained object. It only + * returns a object, if a matching entry was tracked previously, not if a + * new entry was created. Note that when passing @out_obj_old you obtain a reference + * to the boxed object and MUST return it with nm_dedup_multi_obj_unref(). + * + * Adds and object to the index. + * + * Return: %TRUE if anything changed, %FALSE if nothing changed. + */ +gboolean +nm_dedup_multi_index_add_full (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry *entry_order, + const NMDedupMultiEntry *entry_existing, + const NMDedupMultiHeadEntry *head_existing, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old) +{ + NMDedupMultiEntry *entry; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + g_return_val_if_fail (obj, FALSE); + g_return_val_if_fail (NM_IN_SET (mode, + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), + FALSE); + + if (entry_existing == NULL) + entry = _entry_lookup_obj (self, idx_type, obj); + else if (entry_existing == NM_DEDUP_MULTI_ENTRY_MISSING) { + nm_assert (!_entry_lookup_obj (self, idx_type, obj)); + entry = NULL; + } else { + nm_assert (entry_existing == _entry_lookup_obj (self, idx_type, obj)); + entry = (NMDedupMultiEntry *) entry_existing; + } + return _add (self, idx_type, obj, + entry, + mode, entry_order, + (NMDedupMultiHeadEntry *) head_existing, + out_entry, out_obj_old); +} + +/*****************************************************************************/ + +static void +_remove_entry (NMDedupMultiIndex *self, + NMDedupMultiEntry *entry, + gboolean *out_head_entry_removed) +{ + const NMDedupMultiObj *obj; + NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIdxType *idx_type; + + nm_assert (self); + nm_assert (entry); + nm_assert (entry->obj); + nm_assert (entry->head); + nm_assert (!c_list_is_empty (&entry->lst_entries)); + nm_assert (g_hash_table_lookup (self->idx_entries, entry) == entry); + + head_entry = (NMDedupMultiHeadEntry *) entry->head; + obj = entry->obj; + + nm_assert (head_entry); + nm_assert (head_entry->len > 0); + nm_assert (g_hash_table_lookup (self->idx_entries, head_entry) == head_entry); + + idx_type = (NMDedupMultiIdxType *) head_entry->idx_type; + ASSERT_idx_type (idx_type); + + nm_assert (idx_type->len >= head_entry->len); + if (--head_entry->len > 0) { + nm_assert (idx_type->len > 1); + idx_type->len--; + head_entry = NULL; + } + + NM_SET_OUT (out_head_entry_removed, head_entry != NULL); + + if (!g_hash_table_remove (self->idx_entries, entry)) + nm_assert_not_reached (); + + if ( head_entry + && !g_hash_table_remove (self->idx_entries, head_entry)) + nm_assert_not_reached (); + + c_list_unlink (&entry->lst_entries); + g_slice_free (NMDedupMultiEntry, entry); + + if (head_entry) { + nm_assert (c_list_is_empty (&head_entry->lst_entries_head)); + c_list_unlink (&head_entry->lst_idx); + g_slice_free (NMDedupMultiHeadEntry, head_entry); + } + + nm_dedup_multi_obj_unref (obj); +} + +static guint +_remove_head (NMDedupMultiIndex *self, + NMDedupMultiHeadEntry *head_entry, + gboolean remove_all /* otherwise just dirty ones */, + gboolean mark_survivors_dirty) +{ + guint n; + gboolean head_entry_removed; + CList *iter_entry, *iter_entry_safe; + + nm_assert (self); + nm_assert (head_entry); + nm_assert (head_entry->len > 0); + nm_assert (head_entry->len == c_list_length (&head_entry->lst_entries_head)); + nm_assert (g_hash_table_lookup (self->idx_entries, head_entry) == head_entry); + + n = 0; + c_list_for_each_safe (iter_entry, iter_entry_safe, &head_entry->lst_entries_head) { + NMDedupMultiEntry *entry; + + entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); + if ( remove_all + || entry->dirty) { + _remove_entry (self, + entry, + &head_entry_removed); + n++; + if (head_entry_removed) + break; + } else if (mark_survivors_dirty) + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } + + return n; +} + +static guint +_remove_idx_entry (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + gboolean remove_all /* otherwise just dirty ones */, + gboolean mark_survivors_dirty) +{ + guint n; + CList *iter_idx, *iter_idx_safe; + + nm_assert (self); + ASSERT_idx_type (idx_type); + + n = 0; + c_list_for_each_safe (iter_idx, iter_idx_safe, &idx_type->lst_idx_head) { + n += _remove_head (self, + c_list_entry (iter_idx, NMDedupMultiHeadEntry, lst_idx), + remove_all, mark_survivors_dirty); + } + return n; +} + +guint +nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, + gconstpointer entry) +{ + g_return_val_if_fail (self, 0); + + nm_assert (entry); + + if (!((NMDedupMultiEntry *) entry)->is_head) { + _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); + return 1; + } + return _remove_head (self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE); +} + +guint +nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + const NMDedupMultiEntry *entry; + + entry = nm_dedup_multi_index_lookup_obj (self, idx_type, obj); + if (!entry) + return 0; + _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); + return 1; +} + +guint +nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + const NMDedupMultiHeadEntry *entry; + + entry = nm_dedup_multi_index_lookup_head (self, idx_type, obj); + return entry + ? _remove_head (self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE) + : 0; +} + +guint +nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type) +{ + g_return_val_if_fail (self, 0); + g_return_val_if_fail (idx_type, 0); + + return _remove_idx_entry (self, idx_type, TRUE, FALSE); +} + +/*****************************************************************************/ + +/** + * nm_dedup_multi_index_lookup_obj: + * @self: the index cache + * @idx_type: the lookup index type + * @obj: the object to lookup. This means the match is performed + * according to NMDedupMultiIdxTypeClass's idx_obj_id_equal() + * of @idx_type. + * + * Returns: the cache entry or %NULL if the entry wasn't found. + */ +const NMDedupMultiEntry * +nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + g_return_val_if_fail (obj, FALSE); + + nm_assert (idx_type && idx_type->klass); + return _entry_lookup_obj (self, idx_type, obj); +} + +/** + * nm_dedup_multi_index_lookup_head: + * @self: the index cache + * @idx_type: the lookup index type + * @obj: the object to lookup, of type "const NMDedupMultiObj *". + * Depending on the idx_type, you *must* also provide a selector + * object, even when looking up the list head. That is, because + * the idx_type implementation may choose to partition the objects + * in distinct list, so you need a selector object to know which + * list head to lookup. + * + * Returns: the cache entry or %NULL if the entry wasn't found. + */ +const NMDedupMultiHeadEntry * +nm_dedup_multi_index_lookup_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + + return _entry_lookup_head (self, idx_type, obj); +} + +/*****************************************************************************/ + +void +nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + NMDedupMultiHeadEntry *head_entry; + CList *iter_entry; + + g_return_if_fail (self); + g_return_if_fail (idx_type); + + head_entry = _entry_lookup_head (self, idx_type, obj); + if (!head_entry) + return; + + c_list_for_each (iter_entry, &head_entry->lst_entries_head) { + NMDedupMultiEntry *entry; + + entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } +} + +void +nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type) +{ + CList *iter_idx, *iter_entry; + + g_return_if_fail (self); + g_return_if_fail (idx_type); + + c_list_for_each (iter_idx, &idx_type->lst_idx_head) { + NMDedupMultiHeadEntry *head_entry; + + head_entry = c_list_entry (iter_idx, NMDedupMultiHeadEntry, lst_idx); + c_list_for_each (iter_entry, &head_entry->lst_entries_head) { + NMDedupMultiEntry *entry; + + entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } + } +} + +/** + * nm_dedup_multi_index_dirty_remove_idx: + * @self: the index instance + * @idx_type: the index-type to select the objects. + * @mark_survivors_dirty: while the function removes all entries that are + * marked as dirty, if @set_dirty is true, the surviving objects + * will be marked dirty right away. + * + * Deletes all entries for @idx_type that are marked dirty. Only + * non-dirty objects survive. If @mark_survivors_dirty is set to TRUE, the survivors + * are marked as dirty right away. + * + * Returns: number of deleted entries. + */ +guint +nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + gboolean mark_survivors_dirty) +{ + g_return_val_if_fail (self, 0); + g_return_val_if_fail (idx_type, 0); + + return _remove_idx_entry (self, idx_type, FALSE, mark_survivors_dirty); +} + +/*****************************************************************************/ + +static guint +_dict_idx_objs_hash (const NMDedupMultiObj *obj) +{ + return obj->klass->obj_full_hash (obj); +} + +static gboolean +_dict_idx_objs_equal (const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const NMDedupMultiObjClass *klass; + + klass = obj_a->klass; + + /* if the class differs, but at least one of them supports calls with + * differing klass, choose it. + * + * Implementing a klass that can compare equality for multiple + * types is hard to get right. E.g. hash(), equal() and get_ref() + * must all agree so that instances of different types look identical. */ + if ( klass != obj_b->klass + && !klass->obj_full_equality_allows_different_class) { + klass = obj_b->klass; + if (!klass->obj_full_equality_allows_different_class) + return FALSE; + } + + return klass->obj_full_equal (obj_a, obj_b); +} + +void +nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj) +{ + nm_assert (self); + nm_assert (obj); + nm_assert (g_hash_table_lookup (self->idx_objs, obj) == obj); + nm_assert (((const NMDedupMultiObj *) obj)->_multi_idx == self); + + ((NMDedupMultiObj *) obj)->_multi_idx = NULL; + if (!g_hash_table_remove (self->idx_objs, obj)) + nm_assert_not_reached (); +} + +gconstpointer +nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj) +{ + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (obj, NULL); + + return g_hash_table_lookup (self->idx_objs, obj); +} + +gconstpointer +nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj) +{ + const NMDedupMultiObj *obj_new = obj; + const NMDedupMultiObj *obj_old; + + nm_assert (self); + nm_assert (obj_new); + + if (obj_new->_multi_idx == self) { + nm_assert (g_hash_table_lookup (self->idx_objs, obj_new) == obj_new); + nm_dedup_multi_obj_ref (obj_new); + return obj_new; + } + + obj_old = g_hash_table_lookup (self->idx_objs, obj_new); + nm_assert (obj_old != obj_new); + + if (obj_old) { + nm_assert (obj_old->_multi_idx == self); + nm_dedup_multi_obj_ref (obj_old); + return obj_old; + } + + if (nm_dedup_multi_obj_needs_clone (obj_new)) + obj_new = nm_dedup_multi_obj_clone (obj_new); + else + obj_new = nm_dedup_multi_obj_ref (obj_new); + + nm_assert (obj_new); + nm_assert (!obj_new->_multi_idx); + + if (!nm_g_hash_table_add (self->idx_objs, (gpointer) obj_new)) + nm_assert_not_reached (); + + ((NMDedupMultiObj *) obj_new)->_multi_idx = self; + return obj_new; +} + +const NMDedupMultiObj * +nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj) +{ + /* ref and unref accept const pointers. Objects 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. */ + + nm_assert (obj); + nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); + nm_assert (obj->_ref_count > 0); + + ((NMDedupMultiObj *) obj)->_ref_count++; + return obj; +} + +const NMDedupMultiObj * +nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj) +{ + if (obj) { + nm_assert (obj->_ref_count > 0); + nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); + +again: + if (--(((NMDedupMultiObj *) obj)->_ref_count) <= 0) { + if (obj->_multi_idx) { + /* restore the ref-count to 1 and release the object first + * from the index. Then, retry again to unref. */ + ((NMDedupMultiObj *) obj)->_ref_count++; + nm_dedup_multi_index_obj_release (obj->_multi_idx, obj); + nm_assert (obj->_ref_count == 1); + nm_assert (!obj->_multi_idx); + goto again; + } + + obj->klass->obj_destroy ((NMDedupMultiObj *) obj); + } + } + + return NULL; +} + +gboolean +nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj) +{ + nm_assert (obj); + + if ( obj->_multi_idx + || obj->_ref_count == NM_OBJ_REF_COUNT_STACKINIT) + return TRUE; + + if ( obj->klass->obj_needs_clone + && obj->klass->obj_needs_clone (obj)) + return TRUE; + + return FALSE; +} + +const NMDedupMultiObj * +nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj) +{ + const NMDedupMultiObj *o; + + nm_assert (obj); + + o = obj->klass->obj_clone (obj); + nm_assert (o); + nm_assert (o->_ref_count == 1); + return o; +} + +GPtrArray * +nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry, + NMDedupMultiFcnSelectPredicate predicate, + gpointer user_data) +{ + GPtrArray *result; + NMDedupMultiIter iter; + + if (!head_entry) + return NULL; + + result = g_ptr_array_new_full (head_entry->len, + (GDestroyNotify) nm_dedup_multi_obj_unref); + nm_dedup_multi_iter_for_each (&iter, head_entry) { + const NMDedupMultiObj *obj = iter.current->obj; + + if ( !predicate + || predicate (obj, user_data)) + g_ptr_array_add (result, (gpointer) nm_dedup_multi_obj_ref (obj)); + } + + if (result->len == 0) { + g_ptr_array_unref (result); + return NULL; + } + return result; +} + +/*****************************************************************************/ + +NMDedupMultiIndex * +nm_dedup_multi_index_new (void) +{ + NMDedupMultiIndex *self; + + self = g_slice_new0 (NMDedupMultiIndex); + self->ref_count = 1; + self->idx_entries = g_hash_table_new ((GHashFunc) _dict_idx_entries_hash, (GEqualFunc) _dict_idx_entries_equal); + self->idx_objs = g_hash_table_new ((GHashFunc) _dict_idx_objs_hash, (GEqualFunc) _dict_idx_objs_equal); + return self; +} + +NMDedupMultiIndex * +nm_dedup_multi_index_ref (NMDedupMultiIndex *self) +{ + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count > 0, NULL); + + self->ref_count++; + return self; +} + +NMDedupMultiIndex * +nm_dedup_multi_index_unref (NMDedupMultiIndex *self) +{ + GHashTableIter iter; + const NMDedupMultiIdxType *idx_type; + NMDedupMultiEntry *entry; + const NMDedupMultiObj *obj; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count > 0, NULL); + + if (--self->ref_count > 0) + return NULL; + +more: + g_hash_table_iter_init (&iter, self->idx_entries); + while (g_hash_table_iter_next (&iter, (gpointer *) &entry, NULL)) { + if (entry->is_head) + idx_type = ((NMDedupMultiHeadEntry *) entry)->idx_type; + else + idx_type = entry->head->idx_type; + _remove_idx_entry (self, (NMDedupMultiIdxType *) idx_type, TRUE, FALSE); + goto more; + } + + nm_assert (g_hash_table_size (self->idx_entries) == 0); + + g_hash_table_iter_init (&iter, self->idx_objs); + while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { + nm_assert (obj->_multi_idx == self); + ((NMDedupMultiObj * )obj)->_multi_idx = NULL; + } + g_hash_table_remove_all (self->idx_objs); + + g_hash_table_unref (self->idx_entries); + g_hash_table_unref (self->idx_objs); + + g_slice_free (NMDedupMultiIndex, self); + return NULL; +} diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h new file mode 100644 index 0000000000..77a2fd122d --- /dev/null +++ b/shared/nm-utils/nm-dedup-multi.h @@ -0,0 +1,379 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_DEDUP_MULTI_H__ +#define __NM_DEDUP_MULTI_H__ + +#include "nm-obj.h" +#include "c-list.h" + +/*****************************************************************************/ + +typedef struct _NMDedupMultiObj NMDedupMultiObj; +typedef struct _NMDedupMultiObjClass NMDedupMultiObjClass; +typedef struct _NMDedupMultiIdxType NMDedupMultiIdxType; +typedef struct _NMDedupMultiIdxTypeClass NMDedupMultiIdxTypeClass; +typedef struct _NMDedupMultiEntry NMDedupMultiEntry; +typedef struct _NMDedupMultiHeadEntry NMDedupMultiHeadEntry; +typedef struct _NMDedupMultiIndex NMDedupMultiIndex; + +typedef enum _NMDedupMultiIdxMode { + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + + /* append new objects to the end of the list. + * If the object is already in the cache, don't move it. */ + NM_DEDUP_MULTI_IDX_MODE_APPEND, + + /* like NM_DEDUP_MULTI_IDX_MODE_APPEND, but if the object + * is already in teh cache, move it to the end. */ + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, +} NMDedupMultiIdxMode; + +/*****************************************************************************/ + +struct _NMDedupMultiObj { + union { + NMObjBaseInst parent; + const NMDedupMultiObjClass *klass; + }; + NMDedupMultiIndex *_multi_idx; + guint _ref_count; +}; + +struct _NMDedupMultiObjClass { + NMObjBaseClass parent; + + const NMDedupMultiObj *(*obj_clone) (const NMDedupMultiObj *obj); + + gboolean (*obj_needs_clone) (const NMDedupMultiObj *obj); + + void (*obj_destroy) (NMDedupMultiObj *obj); + + gboolean obj_full_equality_allows_different_class; + + /* the NMDedupMultiObj can be deduplicated. For that the obj_full_hash() + * and obj_full_equal() compare *all* fields of the object, even minor ones. */ + guint (*obj_full_hash) (const NMDedupMultiObj *obj); + gboolean (*obj_full_equal) (const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b); +}; + +/*****************************************************************************/ + +const NMDedupMultiObj *nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj); +const NMDedupMultiObj *nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj); +const NMDedupMultiObj *nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj); +gboolean nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj); + +gconstpointer nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj); + +void nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj); + +/* const NMDedupMultiObj * */ gconstpointer nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj); + +/*****************************************************************************/ + +/* the NMDedupMultiIdxType is an access handle under which you can store and + * retrieve NMDedupMultiObj instances in NMDedupMultiIndex. + * + * The NMDedupMultiIdxTypeClass determines it's behavior, but you can have + * multiple instances (of the same class). + * + * For example, NMIP4Config can have idx-type to put there all IPv4 Routes. + * This idx-type instance is private to the NMIP4Config instance. Basically, + * the NMIP4Config instance uses the idx-type to maintain an ordered list + * of routes in NMDedupMultiIndex. + * + * However, a NMDedupMultiIdxType may also partition the set of objects + * in multiple distinct lists. NMIP4Config doesn't do that (because instead + * of creating one idx-type for IPv4 and IPv6 routes, it just cretaes + * to distinct idx-types, one for each address family. + * This partitioning is used by NMPlatform to maintain a lookup index for + * routes by ifindex. As the ifindex is dynamic, it does not create an + * idx-type instance for each ifindex. Instead, it has one idx-type for + * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj, + * the partitioning NMDedupMultiIdxType takes into accound the NMDedupMultiObj + * instance to associate it with the right list. + * + * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry + * instances, which each is the head for a list of NMDedupMultiEntry instances. + * In the platform example, the NMDedupMultiHeadEntry parition the indexed objects + * by their ifindex. */ +struct _NMDedupMultiIdxType { + union { + NMObjBaseInst parent; + const NMDedupMultiIdxTypeClass *klass; + }; + + CList lst_idx_head; + + guint len; +}; + +void nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, + const NMDedupMultiIdxTypeClass *klass); + +struct _NMDedupMultiIdxTypeClass { + NMObjBaseClass parent; + + guint (*idx_obj_id_hash) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj); + gboolean (*idx_obj_id_equal) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b); + + /* an NMDedupMultiIdxTypeClass which implements partitioning of the + * tracked objects, must implement the idx_obj_partition*() functions. + * + * idx_obj_partitionable() may return NULL if the object cannot be tracked. + * For example, a index for routes by ifindex, may not want to track any + * routes that don't have a valid ifindex. If the idx-type says that the + * object is not partitionable, it is never added to the NMDedupMultiIndex. */ + gboolean (*idx_obj_partitionable) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj); + guint (*idx_obj_partition_hash) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj); + gboolean (*idx_obj_partition_equal) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b); +}; + +static inline gboolean +nm_dedup_multi_idx_type_id_equal (const NMDedupMultiIdxType *idx_type, + /* const NMDedupMultiObj * */ gconstpointer obj_a, + /* const NMDedupMultiObj * */ gconstpointer obj_b) +{ + nm_assert (idx_type); + return idx_type->klass->idx_obj_id_equal (idx_type, + obj_a, + obj_b); +} + +static inline gboolean +nm_dedup_multi_idx_type_partition_equal (const NMDedupMultiIdxType *idx_type, + /* const NMDedupMultiObj * */ gconstpointer obj_a, + /* const NMDedupMultiObj * */ gconstpointer obj_b) +{ + nm_assert (idx_type); + if (idx_type->klass->idx_obj_partition_equal) { + nm_assert (obj_a); + nm_assert (obj_b); + return idx_type->klass->idx_obj_partition_equal (idx_type, + obj_a, + obj_b); + } + return TRUE; +} + +/*****************************************************************************/ + +struct _NMDedupMultiEntry { + + /* this is the list of all entries that share the same head entry. + * All entries compare equal according to idx_obj_partition_equal(). */ + CList lst_entries; + + /* const NMDedupMultiObj * */ gconstpointer obj; + + bool is_head; + bool dirty; + + const NMDedupMultiHeadEntry *head; +}; + +struct _NMDedupMultiHeadEntry { + + /* this is the list of all entries that share the same head entry. + * All entries compare equal according to idx_obj_partition_equal(). */ + CList lst_entries_head; + + const NMDedupMultiIdxType *idx_type; + + bool is_head; + + guint len; + + CList lst_idx; +}; + +static inline void +nm_dedup_multi_entry_set_dirty (const NMDedupMultiEntry *entry, + gboolean dirty) +{ + /* NMDedupMultiEntry is always exposed as a const object, because it is not + * supposed to be modified outside NMDedupMultiIndex API. Except the "dirty" + * flag. In C++ speak, it is a mutable field. + * + * Add this inline function, to case-away constness and set the dirty flag. */ + nm_assert (entry); + ((NMDedupMultiEntry *) entry)->dirty = dirty; +} + +/*****************************************************************************/ + +NMDedupMultiIndex *nm_dedup_multi_index_new (void); +NMDedupMultiIndex *nm_dedup_multi_index_ref (NMDedupMultiIndex *self); +NMDedupMultiIndex *nm_dedup_multi_index_unref (NMDedupMultiIndex *self); + +static inline void +_nm_auto_unref_dedup_multi_index (NMDedupMultiIndex **v) +{ + if (*v) + nm_dedup_multi_index_unref (*v); +} +#define nm_auto_unref_dedup_multi_index nm_auto(_nm_auto_unref_dedup_multi_index) + +#define NM_DEDUP_MULTI_ENTRY_MISSING ((const NMDedupMultiEntry *) GUINT_TO_POINTER (1)) +#define NM_DEDUP_MULTI_HEAD_ENTRY_MISSING ((const NMDedupMultiHeadEntry *) GUINT_TO_POINTER (1)) + +gboolean nm_dedup_multi_index_add_full (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry *entry_order, + const NMDedupMultiEntry *entry_existing, + const NMDedupMultiHeadEntry *head_existing, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old); + +gboolean nm_dedup_multi_index_add (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old); + +const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +guint nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, + gconstpointer entry); + +guint nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +guint nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +guint nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type); + +void nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +void nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type); + +guint nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + gboolean mark_survivors_dirty); + +/*****************************************************************************/ + +typedef struct _NMDedupMultiIter { + const NMDedupMultiHeadEntry *head; + const NMDedupMultiEntry *current; + const NMDedupMultiEntry *next; +} NMDedupMultiIter; + +static inline void +nm_dedup_multi_iter_init (NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *head) +{ + g_return_if_fail (iter); + + iter->head = head; + iter->current = NULL; + iter->next = head && !c_list_is_empty (&head->lst_entries_head) + ? c_list_entry (head->lst_entries_head.next, NMDedupMultiEntry, lst_entries) + : NULL; +} + +static inline gboolean +nm_dedup_multi_iter_next (NMDedupMultiIter *iter) +{ + g_return_val_if_fail (iter, FALSE); + + if (!iter->next) + return FALSE; + + /* we always look ahead for the @next. This way, the user + * may delete the current entry (but no other entries). */ + iter->current = iter->next; + if (iter->next->lst_entries.next == &iter->head->lst_entries_head) + iter->next = NULL; + else + iter->next = c_list_entry (iter->next->lst_entries.next, NMDedupMultiEntry, lst_entries); + return TRUE; +} + +static inline void +nm_dedup_multi_iter_rewind (NMDedupMultiIter *iter) +{ + /* rewind the iterator. + * + * In principle, you can always delete the current entry. + * However, if you delete *all* current entries, the list + * head becomes invalid too and rewinding will crash. + * + * So, either + * - don't modify the list + * - if you modify it: + * - only delete the current entry, don't delete other entries. + * - you may add more entries, however that may make iteration + * confusing. + * - you may rewind the iterator, but only if not all + * entires were deleted. + * + * Use with care. */ + g_return_if_fail (iter); + nm_dedup_multi_iter_init (iter, iter->head); +} + +#define nm_dedup_multi_iter_for_each(iter, head_entry) \ + for (nm_dedup_multi_iter_init ((iter), (head_entry)); \ + nm_dedup_multi_iter_next ((iter)); \ + ) + +/*****************************************************************************/ + +typedef gboolean (*NMDedupMultiFcnSelectPredicate) (/* const NMDedupMultiObj * */ gconstpointer obj, + gpointer user_data); + +GPtrArray *nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry, + NMDedupMultiFcnSelectPredicate predicate, + gpointer user_data); + +/*****************************************************************************/ + +#endif /* __NM_DEDUP_MULTI_H__ */ diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 011f45d0ee..66b4aa8d60 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -242,6 +242,35 @@ NM_G_ERROR_MSG (GError *error) /*****************************************************************************/ +#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__)) +#define _NM_CC_SUPPORT_GENERIC 1 +#else +#define _NM_CC_SUPPORT_GENERIC 0 +#endif + +#if _NM_CC_SUPPORT_GENERIC +#define _NM_CONSTCAST(type, obj) \ + (_Generic ((obj), \ + void * : ((type *) (obj)), \ + void *const : ((type *) (obj)), \ + const void * : ((const type *) (obj)), \ + const void *const: ((const type *) (obj)), \ + const type * : (obj), \ + const type *const: (obj), \ + type * : (obj), \ + type *const : (obj))) +#else +/* _NM_CONSTCAST() is there to preserve constness of a pointer. + * It uses C11's _Generic(). If that is not supported, we fall back + * to casting away constness. So, with _Generic, we get some additional + * static type checking by preserving constness, without, we cast it + * to a non-const pointer. */ +#define _NM_CONSTCAST(type, obj) \ + ((type *) (obj)) +#endif + +/*****************************************************************************/ + #define _NM_IN_SET_EVAL_1( op, _x, y) (_x == (y)) #define _NM_IN_SET_EVAL_2( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_1 (op, _x, __VA_ARGS__) #define _NM_IN_SET_EVAL_3( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_2 (op, _x, __VA_ARGS__) @@ -381,6 +410,21 @@ _NM_IN_STRSET_streq (const char *x, const char *s) /*****************************************************************************/ +static inline guint +NM_HASH_COMBINE (guint h, guint val) +{ + /* see g_str_hash() for reasons */ + return (h << 5) + h + val; +} + +static inline guint +NM_HASH_COMBINE_UINT64 (guint h, guint64 val) +{ + return NM_HASH_COMBINE (h, (((guint) val) & 0xFFFFFFFFu) + ((guint) (val >> 32))); +} + +/*****************************************************************************/ + /* NM_CACHED_QUARK() returns the GQuark for @string, but caches * it in a static variable to speed up future lookups. * @@ -522,7 +566,7 @@ _notify (obj_type *obj, _PropertyEnums prop) \ /* these are implemented as a macro, because they accept self * as both (type*) and (const type*), and return a const * private pointer accordingly. */ -#define __NM_GET_PRIVATE(self, type, is_check, result_cmd) \ +#define __NM_GET_PRIVATE(self, type, is_check, addrop) \ ({ \ /* preserve the const-ness of self. Unfortunately, that * way, @self cannot be a void pointer */ \ @@ -532,11 +576,11 @@ _notify (obj_type *obj, _PropertyEnums prop) \ _nm_unused const type *const _self2 = (_self); \ \ nm_assert (is_check (_self)); \ - ( result_cmd ); \ + ( addrop ( _NM_CONSTCAST (type, _self)->_priv) ); \ }) -#define _NM_GET_PRIVATE(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, &_self->_priv) -#define _NM_GET_PRIVATE_PTR(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, _self->_priv) +#define _NM_GET_PRIVATE(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, &) +#define _NM_GET_PRIVATE_PTR(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, ) #define __NM_GET_PRIVATE_VOID(self, type, is_check, result_cmd) \ ({ \ diff --git a/shared/nm-utils/nm-obj.h b/shared/nm-utils/nm-obj.h new file mode 100644 index 0000000000..1a9d48681a --- /dev/null +++ b/shared/nm-utils/nm-obj.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_OBJ_H__ +#define __NM_OBJ_H__ + +/*****************************************************************************/ + +#define NM_OBJ_REF_COUNT_STACKINIT (G_MAXINT) + +typedef struct _NMObjBaseInst NMObjBaseInst; +typedef struct _NMObjBaseClass NMObjBaseClass; + +struct _NMObjBaseInst { + /* The first field of NMObjBaseInst is compatible with GObject. + * Basically, NMObjBaseInst is an abstract base type of GTypeInstance. + * + * If you do it right, you may derive a type of NMObjBaseInst as a proper GTypeInstance. + * That involves allocating a GType for it, which can be inconvenient because + * a GType is dynamically created (and the class can no longer be immutable + * memory). + * + * Even if your implementation of NMObjBaseInst is not a full fledged GType(Instance), + * you still can use GTypeInstances in the same context as you can decide based on the + * NMObjBaseClass with what kind of object you are dealing with. + * + * Basically, the only thing NMObjBaseInst gives you is access to an + * NMObjBaseClass instance. + */ + union { + const NMObjBaseClass *klass; + GTypeInstance g_type_instance; + }; +}; + +struct _NMObjBaseClass { + /* NMObjBaseClass is the base class of all NMObjBaseInst implementations. + * Note that it is also an abstract super class of GTypeInstance, that means + * you may implement a NMObjBaseClass as a subtype of GTypeClass. + * + * For that to work, you must properly set the GTypeClass instance (and it's + * GType). + * + * Note that to implement a NMObjBaseClass that is *not* a GTypeClass, you wouldn't + * set the GType. Hence, this field is only useful for type implementations that actually + * extend GTypeClass. + * + * In a way it is wrong that NMObjBaseClass has the GType member, because it is + * a base class of GTypeClass and doesn't necessarily use the GType. However, + * it is here so that G_TYPE_CHECK_INSTANCE_TYPE() and friends work correctly + * on any NMObjectClass. That means, while not necessary, it is convenient that + * a NMObjBaseClass has all members of GTypeClass. + * Also note that usually you have only one instance of a certain type, so this + * wastes just a few bytes for the unneeded GType. + */ + union { + GType g_type; + GTypeClass g_type_class; + }; +}; + +/*****************************************************************************/ + +#endif /* __NM_OBJ_H__ */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 3abf46cb10..4bf34cd5b2 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -35,11 +35,14 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-common-macros.h" #include "nm-device-private.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" #include "dhcp/nm-dhcp-manager.h" @@ -647,12 +650,32 @@ nm_device_get_netns (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->netns; } +NMDedupMultiIndex * +nm_device_get_multi_index (NMDevice *self) +{ + return nm_netns_get_multi_idx (nm_device_get_netns (self)); +} + NMPlatform * nm_device_get_platform (NMDevice *self) { return nm_netns_get_platform (nm_device_get_netns (self)); } +static NMIP4Config * +_ip4_config_new (NMDevice *self) +{ + return nm_ip4_config_new (nm_device_get_multi_index (self), + nm_device_get_ip_ifindex (self)); +} + +static NMIP6Config * +_ip6_config_new (NMDevice *self) +{ + return nm_ip6_config_new (nm_device_get_multi_index (self), + nm_device_get_ip_ifindex (self)); +} + /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_sys_iface_state_to_str, NMDeviceSysIfaceState, @@ -5058,7 +5081,7 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes NMIP4Config *empty; if (success) { - empty = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + empty = _ip4_config_new (self); nm_device_activate_schedule_ip4_config_result (self, empty); g_object_unref (empty); } else { @@ -5215,7 +5238,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) NMPlatformIP4Address address; NMPlatformIP4Route route; - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); g_assert (config); memset (&address, 0, sizeof (address)); @@ -5383,45 +5406,47 @@ ipv4ll_start (NMDevice *self) static gboolean _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route) { - gboolean success = FALSE; int ifindex = nm_device_get_ip_ifindex (self); - GArray *routes; + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; + const NMPlatformIPRoute *route = NULL; + guint32 route_metric = G_MAXUINT32; - if (addr_family == AF_INET) - routes = nm_platform_ip4_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - else - routes = nm_platform_ip6_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); + pl_head_entry = nm_platform_lookup_route_visible (nm_device_get_platform (self), + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + 0, + TRUE); + nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { + guint32 m; + const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj); - if (routes) { - guint route_metric = G_MAXUINT32, m; - const NMPlatformIPRoute *route = NULL, *r; - guint i; + if (r->ifindex != ifindex) + continue; + if (r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + continue; /* if there are several default routes, find the one with the best metric */ - for (i = 0; i < routes->len; i++) { - if (addr_family == AF_INET) { - r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i); - m = r->metric; - } else { - r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i); - m = nm_utils_ip6_route_metric_normalize (r->metric); - } - if (!route || m < route_metric) { - route = r; - route_metric = m; - } - } + m = r->metric; + if (addr_family != AF_INET) + m = nm_utils_ip6_route_metric_normalize (r->metric); - if (route) { - if (addr_family == AF_INET) - *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); - else - *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); - success = TRUE; + if (!route || m < route_metric) { + route = NMP_OBJECT_CAST_IP_ROUTE (plobj); + route_metric = m; } - g_array_free (routes, TRUE); } - return success; + + if (route) { + if (addr_family == AF_INET) + *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); + else + *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); + return TRUE; + } + return FALSE; } /*****************************************************************************/ @@ -5430,7 +5455,6 @@ static void ensure_con_ip4_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip4_config) @@ -5440,7 +5464,7 @@ ensure_con_ip4_config (NMDevice *self) if (!connection) return; - priv->con_ip4_config = nm_ip4_config_new (ip_ifindex); + priv->con_ip4_config = _ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip4_config, nm_connection_get_setting_ip4_config (connection), nm_device_get_ip4_route_metric (self)); @@ -5456,7 +5480,6 @@ static void ensure_con_ip6_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip6_config) @@ -5466,7 +5489,7 @@ ensure_con_ip6_config (NMDevice *self) if (!connection) return; - priv->con_ip6_config = nm_ip6_config_new (ip_ifindex); + priv->con_ip6_config = _ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip6_config, nm_connection_get_setting_ip6_config (connection), nm_device_get_ip6_route_metric (self)); @@ -5548,14 +5571,15 @@ ip4_config_merge_and_apply (NMDevice *self, } } - composite = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + composite = _ip4_config_new (self); init_ip4_config_dns_priority (self, composite); if (commit) { ensure_con_ip4_config (self); if (priv->queued_ip4_config_id) { g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self), + priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), nm_device_get_ip_ifindex (self), FALSE); } @@ -5824,7 +5848,7 @@ dhcp4_state_changed (NMDhcpClient *client, connection = nm_device_get_applied_connection (self); g_assert (connection); - manual = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + manual = _ip4_config_new (self); nm_ip4_config_merge_setting (manual, nm_connection_get_setting_ip4_config (connection), nm_device_get_ip4_route_metric (self)); @@ -5905,6 +5929,7 @@ dhcp4_start (NMDevice *self, /* Begin DHCP on the interface */ g_warn_if_fail (priv->dhcp4.client == NULL); priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), + nm_netns_get_multi_idx (nm_device_get_netns (self)), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, @@ -6012,7 +6037,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection) is_generated = TRUE; } - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); nm_ip4_config_add_address (config, &address); if (is_generated) { /* Remove the address lock when the object gets disposed */ @@ -6173,7 +6198,7 @@ act_stage3_ip4_config_start (NMDevice *self, } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) { NMIP4Config **configs, *config; - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); nm_ip4_config_merge_setting (config, nm_connection_get_setting_ip4_config (connection), nm_device_get_ip4_route_metric (self)); @@ -6273,7 +6298,7 @@ ip6_config_merge_and_apply (NMDevice *self, } } - composite = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + composite = _ip6_config_new (self); nm_ip6_config_set_privacy (composite, priv->ndisc ? priv->ndisc_use_tempaddr : @@ -6285,7 +6310,8 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->queued_ip6_config_id) { g_clear_object (&priv->ext_ip6_config); g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), nm_device_get_ip_ifindex (self), FALSE, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); @@ -6702,6 +6728,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) } priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), + nm_device_get_multi_index (self), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, @@ -6830,7 +6857,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) NMPlatformIP6Address address = *subnet; if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); /* Assign a ::1 address in the subnet for us. */ address.address.s6_addr32[3] |= htonl (1); @@ -6860,7 +6887,7 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device) nm_ip6_config_reset_nameservers (priv->ac_ip6_config); nm_ip6_config_reset_searches (priv->ac_ip6_config); } else - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); if (from_device) from_config = nm_device_get_ip6_config (from_device); @@ -7280,7 +7307,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in g_return_if_fail (priv->act_request); if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); if (changed & NM_NDISC_CONFIG_GATEWAYS) { /* Use the first gateway as ordered in neighbor discovery cache. */ @@ -7802,7 +7829,8 @@ act_stage3_ip6_config_start (NMDevice *self, */ nm_platform_process_events (nm_device_get_platform (self)); g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), nm_device_get_ip_ifindex (self), FALSE, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); @@ -7876,7 +7904,7 @@ nm_device_activate_stage3_ip4_start (NMDevice *self) ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip4_config) - ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + ip4_config = _ip4_config_new (self); nm_device_activate_schedule_ip4_config_result (self, ip4_config); g_object_unref (ip4_config); } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { @@ -7923,7 +7951,7 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip6_config) - ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + ip6_config = _ip6_config_new (self); /* Here we get a static IPv6 config, like for Shared where it's * autogenerated or from modems where it comes from ModemManager. */ @@ -8510,7 +8538,7 @@ dad6_get_pending_addresses (NMDevice *self) nm_platform_ip6_address_to_string (pl_addr, NULL, 0)); if (!dad6_config) - dad6_config = nm_ip6_config_new (ifindex); + dad6_config = _ip6_config_new (self); nm_ip6_config_add_address (dad6_config, pl_addr); } @@ -8925,7 +8953,7 @@ nm_device_reactivate_ip4_config (NMDevice *self, if (priv->ip4_state != IP_NONE) { g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->ext_ip4_config); - priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + priv->con_ip4_config = _ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip4_config, s_ip4_new, nm_device_get_ip4_route_metric (self)); @@ -8967,7 +8995,7 @@ nm_device_reactivate_ip6_config (NMDevice *self, if (priv->ip6_state != IP_NONE) { g_clear_object (&priv->con_ip6_config); g_clear_object (&priv->ext_ip6_config); - priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->con_ip6_config = _ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip6_config, s_ip6_new, nm_device_get_ip6_route_metric (self)); @@ -10562,6 +10590,7 @@ find_ip4_lease_config (NMDevice *self, g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), + nm_device_get_multi_index (self), ip_iface, ip_ifindex, nm_connection_get_uuid (connection), @@ -10680,7 +10709,8 @@ update_ip4_config (NMDevice *self, gboolean initial) /* IPv4 */ g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self), + priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), ifindex, capture_resolv_conf); if (priv->ext_ip4_config) { @@ -10755,7 +10785,11 @@ update_ip6_config (NMDevice *self, gboolean initial) /* IPv6 */ g_clear_object (&priv->ext_ip6_config); g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), + ifindex, + capture_resolv_conf, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); if (priv->ext_ip6_config_captured) { priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index fb87de896a..358b59af20 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -416,6 +416,7 @@ typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, GType nm_device_get_type (void); +struct _NMDedupMultiIndex *nm_device_get_multi_index (NMDevice *self); NMNetns *nm_device_get_netns (NMDevice *self); NMPlatform *nm_device_get_platform (NMDevice *self); diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index bfd631f0fd..8f12fd8595 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -274,12 +274,12 @@ static guint lldp_neighbor_id_hash (gconstpointer ptr) { const LldpNeighbor *neigh = ptr; - guint hash; + guint hash = 23423423u; - hash = 23423423u + ((guint) (neigh->chassis_id ? g_str_hash (neigh->chassis_id) : 12321u)); - hash = (hash * 33u) + ((guint) (neigh->port_id ? g_str_hash (neigh->port_id) : 34342343u)); - hash = (hash * 33u) + ((guint) neigh->chassis_id_type); - hash = (hash * 33u) + ((guint) neigh->port_id_type); + hash = NM_HASH_COMBINE (hash, neigh->chassis_id ? g_str_hash (neigh->chassis_id) : 12321u); + hash = NM_HASH_COMBINE (hash, neigh->port_id ? g_str_hash (neigh->port_id) : 34342343u); + hash = NM_HASH_COMBINE (hash, neigh->chassis_id_type); + hash = NM_HASH_COMBINE (hash, neigh->port_id_type); return hash; } diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 4b16fb1440..819ff2a171 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -910,7 +910,8 @@ static_stage3_ip4_done (NMModemBroadband *self) data_port = mm_bearer_get_interface (self->_priv.bearer); g_assert (data_port); - config = nm_ip4_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); + config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); memset (&address, 0, sizeof (address)); address.address = address_network; @@ -1004,7 +1005,8 @@ stage3_ip6_done (NMModemBroadband *self) data_port = mm_bearer_get_interface (self->_priv.bearer); g_assert (data_port); - config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); address.plen = mm_bearer_ip_config_get_prefix (self->_priv.ipv6_config); if (address.plen <= 128) diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index d1ad016a91..e0b45e07ca 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -908,7 +908,8 @@ context_property_changed (GDBusProxy *proxy, * * This needs discussion with upstream. */ - priv->ip4_config = nm_ip4_config_new (0); + priv->ip4_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + 0); /* TODO: simply if/else error logic! */ diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 0906f5beba..b08e05051d 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -30,6 +30,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-dhcp-utils.h" @@ -48,6 +50,7 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE_BASE ( + PROP_MULTI_IDX, PROP_IFACE, PROP_IFINDEX, PROP_HWADDR, @@ -58,6 +61,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( ); typedef struct _NMDhcpClientPrivate { + NMDedupMultiIndex *multi_idx; char * iface; int ifindex; GByteArray * hwaddr; @@ -91,6 +95,14 @@ nm_dhcp_client_get_pid (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid; } +NMDedupMultiIndex * +nm_dhcp_client_get_multi_idx (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->multi_idx; +} + const char * nm_dhcp_client_get_iface (NMDhcpClient *self) { @@ -765,13 +777,15 @@ nm_dhcp_client_handle_event (gpointer unused, if (g_hash_table_size (str_options)) { if (priv->ipv6) { prefix = nm_dhcp_utils_ip6_prefix_from_options (str_options); - ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (priv->ifindex, + ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (nm_dhcp_client_get_multi_idx (self), + priv->ifindex, priv->iface, str_options, priv->priority, priv->info_only); } else { - ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (priv->ifindex, + ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (nm_dhcp_client_get_multi_idx (self), + priv->ifindex, priv->iface, str_options, priv->priority); @@ -847,6 +861,13 @@ set_property (GObject *object, guint prop_id, NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE ((NMDhcpClient *) object); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFACE: /* construct-only */ priv->iface = g_value_dup_string (value); @@ -924,6 +945,8 @@ dispose (GObject *object) } G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); + + priv->multi_idx = nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -940,6 +963,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) client_class->stop = stop; client_class->get_duid = get_duid; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_DHCP_CLIENT_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_IFACE] = g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, "", "", NULL, diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index e41a59a265..890c5ff595 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -38,6 +38,7 @@ #define NM_DHCP_CLIENT_UUID "uuid" #define NM_DHCP_CLIENT_PRIORITY "priority" #define NM_DHCP_CLIENT_TIMEOUT "timeout" +#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed" #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated" @@ -101,6 +102,8 @@ typedef struct { GType nm_dhcp_client_get_type (void); +struct _NMDedupMultiIndex *nm_dhcp_client_get_multi_idx (NMDhcpClient *self); + pid_t nm_dhcp_client_get_pid (NMDhcpClient *self); const char *nm_dhcp_client_get_iface (NMDhcpClient *self); @@ -173,7 +176,8 @@ typedef struct { GType (*get_type)(void); const char *name; const char *(*get_path) (void); - GSList *(*get_lease_ip_configs) (const char *iface, + GSList *(*get_lease_ip_configs) (struct _NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, const char *uuid, gboolean ipv6, diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 11f868e260..6b4ac1f436 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -25,6 +25,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-dhcp-utils.h" #include "nm-ip4-config.h" #include "nm-utils.h" @@ -659,6 +661,7 @@ lease_validity_span (const char *str_expire, GDateTime *now) /** * nm_dhcp_dhclient_read_lease_ip_configs: + * @multi_idx: the multi index instance for the ip config object * @iface: the interface name to match leases with * @ifindex: interface index of @iface * @contents: the contents of a dhclient leasefile @@ -673,7 +676,8 @@ lease_validity_span (const char *str_expire, GDateTime *now) * #NMIP6Config objects (if @ipv6 is %TRUE) containing the lease data. */ GSList * -nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, +nm_dhcp_dhclient_read_lease_ip_configs (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, const char *contents, gboolean ipv6, @@ -783,7 +787,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, address.lifetime = address.preferred = expiry; address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - ip4 = nm_ip4_config_new (ifindex); + ip4 = nm_ip4_config_new (multi_idx, ifindex); nm_ip4_config_add_address (ip4, &address); nm_ip4_config_set_gateway (ip4, gw); diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index 994b1b9f02..dd276bea45 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -42,7 +42,8 @@ gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, const char *escaped_duid, GError **error); -GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, +GSList *nm_dhcp_dhclient_read_lease_ip_configs (struct _NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, const char *contents, gboolean ipv6, diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index 8a3f642a72..f9b6f879cd 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -38,6 +38,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-utils.h" #include "nm-dhcp-dhclient-utils.h" #include "nm-dhcp-manager.h" @@ -148,7 +150,8 @@ get_dhclient_leasefile (const char *iface, } static GSList * -nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, +nm_dhcp_dhclient_get_lease_ip_configs (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, const char *uuid, gboolean ipv6, @@ -166,7 +169,7 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, && g_file_get_contents (leasefile, &contents, NULL, NULL) && contents && contents[0]) - leases = nm_dhcp_dhclient_read_lease_ip_configs (iface, ifindex, contents, ipv6, NULL); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, iface, ifindex, contents, ipv6, NULL); g_free (leasefile); g_free (contents); diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index fff9f9ec30..42ec390860 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -34,6 +34,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-config.h" #include "NetworkManagerUtils.h" @@ -152,6 +154,7 @@ client_state_changed (NMDhcpClient *client, static NMDhcpClient * client_start (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, @@ -195,6 +198,7 @@ client_start (NMDhcpManager *self, /* And make a new one */ client = g_object_new (priv->client_factory->get_type (), + NM_DHCP_CLIENT_MULTI_IDX, multi_idx, NM_DHCP_CLIENT_INTERFACE, iface, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, @@ -222,6 +226,7 @@ client_start (NMDhcpManager *self, /* Caller owns a reference to the NMDhcpClient on return */ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, @@ -267,7 +272,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, } } - return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL, + return client_start (self, multi_idx, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL, dhcp_client_id, timeout, dhcp_anycast_addr, hostname, use_fqdn, FALSE, 0, last_ip_address, 0); } @@ -275,6 +280,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, /* Caller owns a reference to the NMDhcpClient on return */ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, @@ -299,7 +305,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, /* Always prefer the explicit dhcp-hostname if given */ hostname = dhcp_hostname ? dhcp_hostname : priv->default_hostname; } - return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE, + return client_start (self, multi_idx, iface, ifindex, hwaddr, uuid, priority, TRUE, ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, TRUE, info_only, privacy, NULL, needed_prefixes); } @@ -320,6 +326,7 @@ nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostna GSList * nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, + NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const char *uuid, @@ -336,7 +343,7 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, priv = NM_DHCP_MANAGER_GET_PRIVATE (self); if ( priv->client_factory && priv->client_factory->get_lease_ip_configs) - return priv->client_factory->get_lease_ip_configs (iface, ifindex, uuid, ipv6, default_route_metric); + return priv->client_factory->get_lease_ip_configs (multi_idx, iface, ifindex, uuid, ipv6, default_route_metric); return NULL; } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index 66fdd145db..2376ea8921 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -46,6 +46,7 @@ void nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostname); NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, + struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, @@ -60,6 +61,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, const char *last_ip_address); NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, + struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const GByteArray *hwaddr, @@ -75,6 +77,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, guint needed_prefixes); GSList * nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, + struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, const char *uuid, diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index b909475552..2dca9c83d8 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -28,6 +28,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-utils.h" #include "nm-dhcp-utils.h" #include "NetworkManagerUtils.h" @@ -216,7 +218,8 @@ G_STMT_START { \ } G_STMT_END static NMIP4Config * -lease_to_ip4_config (const char *iface, +lease_to_ip4_config (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, sd_dhcp_lease *lease, GHashTable *options, @@ -244,7 +247,7 @@ lease_to_ip4_config (const char *iface, g_return_val_if_fail (lease != NULL, NULL); - ip4_config = nm_ip4_config_new (ifindex); + ip4_config = nm_ip4_config_new (multi_idx, ifindex); /* Address */ sd_dhcp_lease_get_address (lease, &tmp_addr); @@ -433,7 +436,8 @@ get_leasefile_path (const char *iface, const char *uuid, gboolean ipv6) } static GSList * -nm_dhcp_systemd_get_lease_ip_configs (const char *iface, +nm_dhcp_systemd_get_lease_ip_configs (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, const char *uuid, gboolean ipv6, @@ -451,7 +455,7 @@ nm_dhcp_systemd_get_lease_ip_configs (const char *iface, path = get_leasefile_path (iface, uuid, FALSE); r = dhcp_lease_load (&lease, path); if (r == 0 && lease) { - ip4_config = lease_to_ip4_config (iface, ifindex, lease, NULL, default_route_metric, FALSE, NULL); + ip4_config = lease_to_ip4_config (multi_idx, iface, ifindex, lease, NULL, default_route_metric, FALSE, NULL); if (ip4_config) leases = g_slist_append (leases, ip4_config); sd_dhcp_lease_unref (lease); @@ -505,7 +509,8 @@ bound4_handle (NMDhcpSystemd *self) _LOGD ("lease available"); options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - ip4_config = lease_to_ip4_config (iface, + ip4_config = lease_to_ip4_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), + iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, options, @@ -725,7 +730,8 @@ error: } static NMIP6Config * -lease_to_ip6_config (const char *iface, +lease_to_ip6_config (NMDedupMultiIndex *multi_idx, + const char *iface, int ifindex, sd_dhcp6_lease *lease, GHashTable *options, @@ -743,7 +749,7 @@ lease_to_ip6_config (const char *iface, gint32 ts; g_return_val_if_fail (lease, NULL); - ip6_config = nm_ip6_config_new (ifindex); + ip6_config = nm_ip6_config_new (multi_idx, ifindex); ts = nm_utils_get_monotonic_timestamp_s (); /* Addresses */ @@ -830,7 +836,8 @@ bound6_handle (NMDhcpSystemd *self) _LOGD ("lease available"); options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - ip6_config = lease_to_ip6_config (iface, + ip6_config = lease_to_ip6_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), + iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, options, diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index e55a21b49a..3f17110410 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -24,6 +24,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-dhcp-utils.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" @@ -383,7 +385,8 @@ ip4_add_domain_search (gpointer data, gpointer user_data) } NMIP4Config * -nm_dhcp_utils_ip4_config_from_options (int ifindex, +nm_dhcp_utils_ip4_config_from_options (NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, guint32 priority) @@ -398,7 +401,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, g_return_val_if_fail (options != NULL, NULL); - ip4_config = nm_ip4_config_new (ifindex); + ip4_config = nm_ip4_config_new (multi_idx, ifindex); memset (&address, 0, sizeof (address)); address.timestamp = nm_utils_get_monotonic_timestamp_s (); @@ -616,7 +619,8 @@ nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options) } NMIP6Config * -nm_dhcp_utils_ip6_config_from_options (int ifindex, +nm_dhcp_utils_ip6_config_from_options (NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, guint32 priority, @@ -633,7 +637,7 @@ nm_dhcp_utils_ip6_config_from_options (int ifindex, address.plen = 128; address.timestamp = nm_utils_get_monotonic_timestamp_s (); - ip6_config = nm_ip6_config_new (ifindex); + ip6_config = nm_ip6_config_new (multi_idx, ifindex); str = g_hash_table_lookup (options, "max_life"); if (str) { diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h index 05982b166d..3cd0dbc405 100644 --- a/src/dhcp/nm-dhcp-utils.h +++ b/src/dhcp/nm-dhcp-utils.h @@ -24,12 +24,14 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" -NMIP4Config *nm_dhcp_utils_ip4_config_from_options (int ifindex, +NMIP4Config *nm_dhcp_utils_ip4_config_from_options (struct _NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, guint priority); -NMIP6Config *nm_dhcp_utils_ip6_config_from_options (int ifindex, +NMIP6Config *nm_dhcp_utils_ip6_config_from_options (struct _NMDedupMultiIndex *multi_idx, + int ifindex, const char *iface, GHashTable *options, guint priority, diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index 2308cf75fa..e90a7976d2 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -24,6 +24,8 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "NetworkManagerUtils.h" #include "dhcp/nm-dhcp-dhclient-utils.h" #include "dhcp/nm-dhcp-utils.h" @@ -838,6 +840,7 @@ test_interface2 (void) static void test_read_lease_ip4_config_basic (void) { + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); GError *error = NULL; char *contents = NULL; gboolean success; @@ -854,7 +857,7 @@ test_read_lease_ip4_config_basic (void) /* Date from before the least expiration */ now = g_date_time_new_utc (2013, 11, 1, 19, 55, 32); - leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", -1, contents, FALSE, now); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, "wlan0", -1, contents, FALSE, now); g_assert_cmpint (g_slist_length (leases), ==, 2); /* IP4Config #1 */ @@ -915,6 +918,7 @@ test_read_lease_ip4_config_basic (void) static void test_read_lease_ip4_config_expired (void) { + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); GError *error = NULL; char *contents = NULL; gboolean success; @@ -928,7 +932,7 @@ test_read_lease_ip4_config_expired (void) /* Date from *after* the lease expiration */ now = g_date_time_new_utc (2013, 12, 1, 19, 55, 32); - leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", -1, contents, FALSE, now); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, "wlan0", -1, contents, FALSE, now); g_assert (leases == NULL); g_date_time_unref (now); @@ -938,6 +942,7 @@ test_read_lease_ip4_config_expired (void) static void test_read_lease_ip4_config_expect_failure (gconstpointer user_data) { + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); GError *error = NULL; char *contents = NULL; gboolean success; @@ -950,7 +955,7 @@ test_read_lease_ip4_config_expect_failure (gconstpointer user_data) /* Date from before the least expiration */ now = g_date_time_new_utc (2013, 11, 1, 1, 1, 1); - leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", -1, contents, FALSE, now); + leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, "wlan0", -1, contents, FALSE, now); g_assert (leases == NULL); g_date_time_unref (now); diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c index ffd6349361..f3fa963e52 100644 --- a/src/dhcp/tests/test-dhcp-utils.c +++ b/src/dhcp/tests/test-dhcp-utils.c @@ -23,6 +23,7 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" #include "nm-utils.h" #include "dhcp/nm-dhcp-utils.h" @@ -30,6 +31,20 @@ #include "nm-test-utils-core.h" +static NMIP4Config * +_ip4_config_from_options (int ifindex, + const char *iface, + GHashTable *options, + guint32 priority) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + NMIP4Config *config; + + config = nm_dhcp_utils_ip4_config_from_options (multi_idx, ifindex, iface, options, priority); + g_assert (config); + return config; +} + typedef struct { const char *name; const char *value; @@ -86,8 +101,7 @@ test_generic_options (void) const char *expected_route2_gw = "10.1.1.1"; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 address */ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); @@ -121,7 +135,7 @@ test_generic_options (void) g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); /* Route #1 */ - route = nm_ip4_config_get_route (ip4_config, 0); + route = _nmtst_nm_ip4_config_get_route (ip4_config, 0); g_assert (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0); g_assert (route->network == tmp); g_assert (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0); @@ -130,7 +144,7 @@ test_generic_options (void) g_assert_cmpint (route->metric, ==, 0); /* Route #2 */ - route = nm_ip4_config_get_route (ip4_config, 1); + route = _nmtst_nm_ip4_config_get_route (ip4_config, 1); g_assert (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0); g_assert (route->network == tmp); g_assert (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0); @@ -157,8 +171,7 @@ test_wins_options (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 address */ g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); @@ -184,16 +197,14 @@ test_vendor_option_metered (void) }; options = fill_table (generic_options, NULL); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert (nm_ip4_config_get_metered (ip4_config) == FALSE); g_hash_table_destroy (options); g_clear_object (&ip4_config); options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert (nm_ip4_config_get_metered (ip4_config) == TRUE); g_hash_table_destroy (options); } @@ -210,7 +221,7 @@ ip4_test_route (NMIP4Config *ip4_config, g_assert (expected_prefix <= 32); - route = nm_ip4_config_get_route (ip4_config, route_num); + route = _nmtst_nm_ip4_config_get_route (ip4_config, route_num); g_assert (inet_pton (AF_INET, expected_dest, &tmp) > 0); g_assert (route->network == tmp); g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0); @@ -246,8 +257,7 @@ test_classless_static_routes_1 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); @@ -274,8 +284,7 @@ test_classless_static_routes_2 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); @@ -303,8 +312,7 @@ test_fedora_dhclient_classless_static_routes (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2); @@ -335,8 +343,7 @@ test_dhclient_invalid_classless_routes_1 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* IP4 routes */ @@ -366,8 +373,7 @@ test_dhcpcd_invalid_classless_routes_1 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* Test falling back to old-style static routes if the classless static @@ -399,8 +405,7 @@ test_dhclient_invalid_classless_routes_2 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* Test falling back to old-style static routes if the classless static @@ -432,8 +437,7 @@ test_dhcpcd_invalid_classless_routes_2 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* Test falling back to old-style static routes if the classless static @@ -465,8 +469,7 @@ test_dhclient_invalid_classless_routes_3 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid classless static routes*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* IP4 routes */ @@ -493,8 +496,7 @@ test_dhcpcd_invalid_classless_routes_3 (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*DHCP provided invalid classless static route*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* IP4 routes */ @@ -519,8 +521,7 @@ test_dhclient_gw_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); @@ -547,8 +548,7 @@ test_dhcpcd_gw_in_classless_routes (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* IP4 routes */ g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1); @@ -575,8 +575,7 @@ test_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); /* domain searches */ g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 3); @@ -602,8 +601,7 @@ test_invalid_escaped_domain_searches (void) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*invalid domain search*"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); /* domain searches */ @@ -623,8 +621,7 @@ test_ip4_missing_prefix (const char *ip, guint32 expected_prefix) g_hash_table_insert (options, "ip_address", (gpointer) ip); g_hash_table_remove (options, "subnet_mask"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); address = nm_ip4_config_get_address (ip4_config, 0); @@ -668,8 +665,7 @@ test_ip4_prefix_classless (void) g_hash_table_insert (options, "ip_address", "172.16.54.22"); g_hash_table_insert (options, "subnet_mask", "255.255.252.0"); - ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); - g_assert (ip4_config); + ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1); address = nm_ip4_config_get_address (ip4_config, 0); diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index a6204ae4a0..d5b51af25e 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -80,7 +80,9 @@ get_ip4_rdns_domains (NMIP4Config *ip4) { char **strv; GPtrArray *domains = NULL; - int i; + guint i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; g_return_val_if_fail (ip4 != NULL, NULL); @@ -92,11 +94,8 @@ get_ip4_rdns_domains (NMIP4Config *ip4) nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); } - for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (ip4, i); - + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); - } /* Terminating NULL so we can use g_strfreev() to free it */ g_ptr_array_add (domains, NULL); @@ -112,7 +111,9 @@ get_ip6_rdns_domains (NMIP6Config *ip6) { char **strv; GPtrArray *domains = NULL; - int i; + guint i; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; g_return_val_if_fail (ip6 != NULL, NULL); @@ -124,11 +125,8 @@ get_ip6_rdns_domains (NMIP6Config *ip6) nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); } - for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (ip6, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); - } /* Terminating NULL so we can use g_strfreev() to free it */ g_ptr_array_add (domains, NULL); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 8b330a5a0d..81a58ad3ab 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -190,6 +190,19 @@ nm_utils_exp10 (gint16 ex) /*****************************************************************************/ +guint +nm_utils_in6_addr_hash (const struct in6_addr *addr) +{ + guint hash = (guint) 0x897da53981a13ULL; + int i; + + for (i = 0; i < sizeof (*addr); i++) + hash = NM_HASH_COMBINE (hash, ((const guint8 *) addr)[i]); + return hash; +} + +/*****************************************************************************/ + /* * nm_ethernet_address_is_valid: * @addr: pointer to a binary or ASCII Ethernet address diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 51a0c9cff7..20df8d7e96 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -109,6 +109,14 @@ extern const NMIPAddr nm_ip_addr_zero; /*****************************************************************************/ +guint nm_utils_in6_addr_hash (const struct in6_addr *addr); + +static inline guint +NM_HASH_COMBINE_IN6_ADDR (guint h, const struct in6_addr *addr) +{ + return NM_HASH_COMBINE (h, addr ? nm_utils_in6_addr_hash (addr) : 0); +} + gboolean nm_ethernet_address_is_valid (gconstpointer addr, gssize len); gconstpointer nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen); diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 609c1658ef..f6362bccf6 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -28,6 +28,7 @@ #include "devices/nm-device.h" #include "vpn/nm-vpn-connection.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "nm-manager.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" @@ -195,26 +196,20 @@ typedef struct { static const VTableIP vtable_ip4, vtable_ip6; -static NMPlatformIPRoute * -_vt_route_index (const VTableIP *vtable, GArray *routes, guint index) -{ - if (vtable->vt->is_ip4) - return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, index); - else - return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, index); -} - static gboolean -_vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry) +_vt_routes_has_entry (const VTableIP *vtable, const GPtrArray *routes, const Entry *entry) { guint i; NMPlatformIPXRoute route = entry->route; + if (!routes) + return FALSE; + route.rx.metric = entry->effective_metric; if (vtable->vt->is_ip4) { for (i = 0; i < routes->len; i++) { - NMPlatformIP4Route *r = &g_array_index (routes, NMPlatformIP4Route, i); + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (routes->pdata[i]); route.rx.rt_source = r->rt_source; if (nm_platform_ip4_route_cmp (r, &route.r4) == 0) @@ -222,7 +217,7 @@ _vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry } } else { for (i = 0; i < routes->len; i++) { - NMPlatformIP6Route *r = &g_array_index (routes, NMPlatformIP6Route, i); + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (routes->pdata[i]); route.rx.rt_source = r->rt_source; if (nm_platform_ip6_route_cmp (r, &route.r6) == 0) @@ -331,19 +326,27 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, { NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); GPtrArray *entries = vtable->get_entries (priv); - GArray *routes; + gs_unref_ptrarray GPtrArray *routes = NULL; guint i, j; gboolean changed = FALSE; /* prune all other default routes from this device. */ - routes = vtable->vt->route_get_all (priv->platform, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); + + routes = nm_platform_lookup_route_visible_clone (priv->platform, + vtable->vt->obj_type, + 0, + TRUE, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL); + if (!routes) + return FALSE; for (i = 0; i < routes->len; i++) { const NMPlatformIPRoute *route; gboolean has_ifindex_synced = FALSE; Entry *entry = NULL; - route = _vt_route_index (vtable, routes, i); + route = NMP_OBJECT_CAST_IP_ROUTE (routes->pdata[i]); /* look at all entries and see if the route for this ifindex pair is * a known entry. */ @@ -372,7 +375,6 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, changed = TRUE; } } - g_array_free (routes, TRUE); return changed; } @@ -419,7 +421,7 @@ _sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data) } static GHashTable * -_get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *self, GArray *routes) +_get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *self, const GPtrArray *routes) { NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); GPtrArray *entries; @@ -435,24 +437,26 @@ _get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *s result = g_hash_table_new (NULL, NULL); - for (i = 0; i < routes->len; i++) { - gboolean ifindex_has_synced_entry = FALSE; - const NMPlatformIPRoute *route; + if (routes) { + for (i = 0; i < routes->len; i++) { + gboolean ifindex_has_synced_entry = FALSE; + const NMPlatformIPRoute *route; - route = _vt_route_index (vtable, routes, i); + route = NMP_OBJECT_CAST_IP_ROUTE (routes->pdata[i]); - for (j = 0; j < entries->len; j++) { - Entry *e = g_ptr_array_index (entries, j); + for (j = 0; j < entries->len; j++) { + Entry *e = g_ptr_array_index (entries, j); - if ( e->synced - && e->route.rx.ifindex == route->ifindex) { - ifindex_has_synced_entry = TRUE; - break; + if ( e->synced + && e->route.rx.ifindex == route->ifindex) { + ifindex_has_synced_entry = TRUE; + break; + } } - } - if (!ifindex_has_synced_entry) - g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric))); + if (!ifindex_has_synced_entry) + g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric))); + } } /* also add all non-synced metrics from our entries list. We might have there some metrics that @@ -493,7 +497,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c GPtrArray *entries; GArray *changed_metrics = g_array_new (FALSE, FALSE, sizeof (guint32)); GHashTable *assumed_metrics; - GArray *routes; + gs_unref_ptrarray GPtrArray *routes = NULL; gboolean changed = FALSE; int ifindex_to_flush = 0; @@ -511,7 +515,12 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c entries = vtable->get_entries (priv); - routes = vtable->vt->route_get_all (priv->platform, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); + routes = nm_platform_lookup_route_visible_clone (priv->platform, + vtable->vt->obj_type, + 0, + TRUE, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL); assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes); @@ -560,13 +569,15 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c * If there are any, we have to pick another effective_metric. */ /* However, if there is a matching route (ifindex+metric) for our current entry, we are done. */ - for (j = 0; j < routes->len; j++) { - const NMPlatformIPRoute *r = _vt_route_index (vtable, routes, i); + if (routes) { + for (j = 0; j < routes->len; j++) { + const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (routes->pdata[i]); - if ( r->metric == expected_metric - && r->ifindex == entry->route.rx.ifindex) { - has_metric_for_ifindex = TRUE; - break; + if ( r->metric == expected_metric + && r->ifindex == entry->route.rx.ifindex) { + has_metric_for_ifindex = TRUE; + break; + } } } if (has_metric_for_ifindex) @@ -611,8 +622,6 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c last_metric = expected_metric; } - g_array_free (routes, TRUE); - g_array_sort_with_data (changed_metrics, nm_cmp_uint32_p_with_data, NULL); last_metric = -1; for (j = 0; j < changed_metrics->len; j++) { diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 0d482e0cad..2393e8035f 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -113,6 +113,7 @@ static void dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder) { GVariantBuilder int_builder; + NMDedupMultiIter ipconf_iter; guint n, i; const NMPlatformIP4Address *addr; const NMPlatformIP4Route *route; @@ -163,9 +164,7 @@ dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder) /* Static routes */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aau")); - n = nm_ip4_config_get_num_routes (ip4); - for (i = 0; i < n; i++) { - route = nm_ip4_config_get_route (ip4, i); + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { array[0] = route->network; array[1] = route->plen; array[2] = route->gateway; @@ -183,6 +182,7 @@ static void dump_ip6_to_props (NMIP6Config *ip6, GVariantBuilder *builder) { GVariantBuilder int_builder; + NMDedupMultiIter ipconf_iter; guint n, i; const NMPlatformIP6Address *addr; const struct in6_addr *gw_bytes; @@ -231,9 +231,7 @@ dump_ip6_to_props (NMIP6Config *ip6, GVariantBuilder *builder) /* Static routes */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("a(ayuayu)")); - n = nm_ip6_config_get_num_routes (ip6); - for (i = 0; i < n; i++) { - route = nm_ip6_config_get_route (ip6, i); + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &route->network, sizeof (struct in6_addr), 1); diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index f8df2b9a7f..3b60e0d64f 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -122,7 +122,8 @@ dhcp4_state_changed (NMDhcpClient *client, switch (state) { case NM_DHCP_STATE_BOUND: g_assert (ip4_config); - existing = nm_ip4_config_capture (NM_PLATFORM_GET, gl.ifindex, FALSE); + existing = nm_ip4_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), + NM_PLATFORM_GET, gl.ifindex, FALSE); if (last_config) nm_ip4_config_subtract (existing, last_config); @@ -132,7 +133,8 @@ dhcp4_state_changed (NMDhcpClient *client, if (last_config) g_object_unref (last_config); - last_config = nm_ip4_config_new (nm_dhcp_client_get_ifindex (client)); + last_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_dhcp_client_get_ifindex (client)); nm_ip4_config_replace (last_config, ip4_config, NULL); break; case NM_DHCP_STATE_TIMEOUT: @@ -177,11 +179,14 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in ifa_flags |= IFA_F_MANAGETEMPADDR; } - existing = nm_ip6_config_capture (NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr); + existing = nm_ip6_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), + NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr); if (ndisc_config) nm_ip6_config_subtract (existing, ndisc_config); - else - ndisc_config = nm_ip6_config_new (gl.ifindex); + else { + ndisc_config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + gl.ifindex); + } if (changed & NM_NDISC_CONFIG_GATEWAYS) { /* Use the first gateway as ordered in neighbor discovery cache. */ @@ -469,6 +474,7 @@ main (int argc, char *argv[]) nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries")), "1"); dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), + nm_platform_get_multi_idx (NM_PLATFORM_GET), global_opt.ifname, gl.ifindex, hwaddr, diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index ae6af9c350..1ec3115d4e 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -26,7 +26,10 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-utils.h" +#include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "NetworkManagerUtils.h" @@ -43,7 +46,91 @@ G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF); /*****************************************************************************/ +gboolean +nm_ip_config_obj_id_equal_ip4_route (const NMPlatformIP4Route *r_a, + const NMPlatformIP4Route *r_b) +{ + return r_a->network == r_b->network + && r_a->plen == r_b->plen; +} + +gboolean +nm_ip_config_obj_id_equal_ip6_route (const NMPlatformIP6Route *r_a, + const NMPlatformIP6Route *r_b) +{ + return r_a->plen == r_b->plen + && IN6_ARE_ADDR_EQUAL (&r_a->network, &r_b->network); +} + +static guint +_idx_obj_id_hash (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + const NMPObject *o = (NMPObject *) obj; + guint h; + + switch (NMP_OBJECT_GET_TYPE (o)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + g_return_val_if_reached (0); + case NMP_OBJECT_TYPE_IP4_ROUTE: + h = 40303327; + h = NM_HASH_COMBINE (h, o->ip4_route.network); + h = NM_HASH_COMBINE (h, o->ip_route.plen); + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + h = 577629323; + h = NM_HASH_COMBINE_IN6_ADDR (h, &o->ip6_route.network); + h = NM_HASH_COMBINE (h, o->ip_route.plen); + break; + default: + g_return_val_if_reached (0); + }; + + return h; +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const NMPObject *o_a = (NMPObject *) obj_a; + const NMPObject *o_b = (NMPObject *) obj_b; + + nm_assert (NMP_OBJECT_GET_TYPE (o_a) == NMP_OBJECT_GET_TYPE (o_b)); + + switch (NMP_OBJECT_GET_TYPE (o_a)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + g_return_val_if_reached (FALSE); + case NMP_OBJECT_TYPE_IP4_ROUTE: + return nm_ip_config_obj_id_equal_ip4_route (&o_a->ip4_route, &o_b->ip4_route); + case NMP_OBJECT_TYPE_IP6_ROUTE: + return nm_ip_config_obj_id_equal_ip6_route (&o_a->ip6_route, &o_b->ip6_route); + default: + g_return_val_if_reached (FALSE); + }; +} + +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = { + .idx_obj_id_hash = _idx_obj_id_hash, + .idx_obj_id_equal = _idx_obj_id_equal, +}; + +void +nm_ip_config_dedup_multi_idx_type_init (NMIPConfigDedupMultiIdxType *idx_type, + NMPObjectType obj_type) +{ + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, + &_dedup_multi_idx_type_class); + idx_type->obj_type = obj_type; +} + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, + PROP_MULTI_IDX, PROP_IFINDEX, PROP_ADDRESS_DATA, PROP_ADDRESSES, @@ -70,7 +157,6 @@ typedef struct { gint dns_priority; gint64 route_metric; GArray *addresses; - GArray *routes; GArray *nameservers; GPtrArray *domains; GPtrArray *searches; @@ -80,6 +166,8 @@ typedef struct { GArray *wins; GVariant *address_data_variant; GVariant *addresses_variant; + NMDedupMultiIndex *multi_idx; + NMDedupMultiIdxType idx_ip4_routes; } NMIP4ConfigPrivate; struct _NMIP4Config { @@ -97,12 +185,22 @@ G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) /*****************************************************************************/ +static void _add_route (NMIP4Config *config, const NMPObject *o_new, const NMPlatformIP4Route *new); + +/*****************************************************************************/ + int nm_ip4_config_get_ifindex (const NMIP4Config *config) { return NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex; } +NMDedupMultiIndex * +nm_ip4_config_get_multi_idx (const NMIP4Config *config) +{ + return NM_IP4_CONFIG_GET_PRIVATE (config)->multi_idx; +} + /*****************************************************************************/ static gboolean @@ -114,6 +212,49 @@ _ipv4_is_zeronet (in_addr_t network) /*****************************************************************************/ +static const NMDedupMultiHeadEntry * +_idx_ip4_routes (const NMIP4Config *self) +{ + const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip4_routes, + NULL); +} + +static const NMPlatformIP4Route * +_entry_iter_get_ip4_route (const CList *iter) +{ + const NMDedupMultiEntry *e = c_list_entry (iter, NMDedupMultiEntry, lst_entries); + const NMPObject *o = e->obj; + + nm_assert (o); + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP4_ROUTE); + return &o->ip4_route; +} + +void +nm_ip4_config_iter_ip4_route_init (NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) +{ + g_return_if_fail (NM_IS_IP4_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, _idx_ip4_routes (self)); +} + +gboolean +nm_ip4_config_iter_ip4_route_next (NMDedupMultiIter *ipconf_iter, const NMPlatformIP4Route **out_route) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (ipconf_iter); + if (has_next) { + nm_assert (NMP_OBJECT_GET_TYPE (ipconf_iter->current->obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + NM_SET_OUT (out_route, &(((const NMPObject *) ipconf_iter->current->obj)->ip4_route)); + } + return has_next; +} + +/*****************************************************************************/ + /** * nm_ip4_config_capture_resolv_conf(): * @nameservers: array of guint32 @@ -186,13 +327,6 @@ addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Addre && ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0; } -static gboolean -routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_gateway_and_metric) -{ - return a->network == b->network && a->plen == b->plen && - (!consider_gateway_and_metric || (a->gateway == b->gateway && a->metric == b->metric)); -} - /*****************************************************************************/ static gint @@ -259,67 +393,76 @@ sort_captured_addresses (gconstpointer a, gconstpointer b) } NMIP4Config * -nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) +nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) { NMIP4Config *config; NMIP4ConfigPrivate *priv; - guint i; - guint32 lowest_metric = G_MAXUINT32; + guint32 lowest_metric; guint32 old_gateway = 0; gboolean old_has_gateway = FALSE; + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; /* Slaves have no IP configuration */ if (nm_platform_link_get_master (platform, ifindex) > 0) return NULL; - config = nm_ip4_config_new (ifindex); + config = nm_ip4_config_new (multi_idx, ifindex); priv = NM_IP4_CONFIG_GET_PRIVATE (config); g_array_unref (priv->addresses); - g_array_unref (priv->routes); priv->addresses = nm_platform_ip4_address_get_all (platform, ifindex); g_array_sort (priv->addresses, sort_captured_addresses); - priv->routes = nm_platform_ip4_route_get_all (platform, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + pl_head_entry = nm_platform_lookup_route_visible (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex, + FALSE); /* Extract gateway from default route */ old_gateway = priv->gateway; old_has_gateway = priv->has_gateway; - for (i = 0; i < priv->routes->len; ) { - const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { + lowest_metric = G_MAXUINT32; + priv->has_gateway = FALSE; + nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { + const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE (plobj); + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route) + && route->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) { if (route->metric < lowest_metric) { priv->gateway = route->gateway; lowest_metric = route->metric; } priv->has_gateway = TRUE; - /* Remove the default route from the list */ - g_array_remove_index_fast (priv->routes, i); - continue; } - i++; } /* we detect the route metric based on the default route. All non-default * routes have their route metrics explicitly set. */ priv->route_metric = priv->has_gateway ? (gint64) lowest_metric : (gint64) -1; - /* If there is a host route to the gateway, ignore that route. It is - * automatically added by NetworkManager when needed. - */ - if (priv->has_gateway) { - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); + nm_dedup_multi_iter_rewind (&iter); + nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { + const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE (plobj); - if ( (route->plen == 32) - && (route->network == priv->gateway) - && (route->gateway == 0)) { - g_array_remove_index (priv->routes, i); - i--; - } + if (route->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + continue; + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; + + if ( priv->has_gateway + && route->plen == 32 + && route->network == priv->gateway + && route->gateway == 0) { + /* If there is a host route to the gateway, ignore that route. It is + * automatically added by NetworkManager when needed. + */ + continue; } + _add_route (config, plobj, NULL); } /* If the interface has the default route, and has IPv4 addresses, capture @@ -357,11 +500,15 @@ nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteMa /* Routes */ { + const NMDedupMultiHeadEntry *head_entry; guint i; - guint count = nm_ip4_config_get_num_routes (config); - GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count); - gboolean success; + gs_unref_array GArray *routes = NULL; gs_unref_array GArray *device_route_purge_list = NULL; + const CList *iter; + + head_entry = _idx_ip4_routes (config); + + routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), head_entry ? head_entry->len : 0); if ( default_route_metric >= 0 && added_addresses) { @@ -404,20 +551,15 @@ nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteMa } } - for (i = 0; i < count; i++) { - const NMPlatformIP4Route *route; - - route = nm_ip4_config_get_route (config, i); - /* duplicates in @routes are no problem as route-manager handles them - * gracefully (by ignoring them). */ - g_array_append_vals (routes, route, 1); + if (head_entry) { + c_list_for_each (iter, &head_entry->lst_entries_head) + g_array_append_vals (routes, _entry_iter_get_ip4_route (iter), 1); } nm_route_manager_ip4_route_register_device_route_purge_list (route_manager, device_route_purge_list); - success = nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, default_route_metric < 0, routes_full_sync); - g_array_unref (routes); - if (!success) + if (!nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, + default_route_metric < 0, routes_full_sync)) return FALSE; } @@ -537,7 +679,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu route.rt_source = NM_IP_CONFIG_SOURCE_USER; merge_route_attributes (s_route, &route); - nm_ip4_config_add_route (config, &route); + _add_route (config, NULL, &route); } /* DNS */ @@ -573,10 +715,12 @@ nm_ip4_config_create_setting (const NMIP4Config *config) { NMSettingIPConfig *s_ip4; guint32 gateway; - guint naddresses, nroutes, nnameservers, nsearches, noptions; + guint naddresses, nnameservers, nsearches, noptions; const char *method = NULL; int i; gint64 route_metric; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); @@ -589,7 +733,6 @@ nm_ip4_config_create_setting (const NMIP4Config *config) gateway = nm_ip4_config_get_gateway (config); naddresses = nm_ip4_config_get_num_addresses (config); - nroutes = nm_ip4_config_get_num_routes (config); nnameservers = nm_ip4_config_get_num_nameservers (config); nsearches = nm_ip4_config_get_num_searches (config); noptions = nm_ip4_config_get_num_dns_options (config); @@ -636,8 +779,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config) NULL); /* Routes */ - for (i = 0; i < nroutes; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, config, &route) { NMIPRoute *s_route; /* Ignore default route. */ @@ -690,6 +832,7 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; guint32 i; + NMDedupMultiIter ipconf_iter; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -715,8 +858,10 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) - nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i)); + const NMPlatformIP4Route *route; + + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, src, &route) + _add_route (dst, NMP_OBJECT_UP_CAST (route), NULL); } if (dst_priv->route_metric == -1) @@ -811,22 +956,6 @@ _nameservers_get_index (const NMIP4Config *self, guint32 ns) return -1; } -static int -_routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - guint i; - - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP4Route *r = &g_array_index (priv->routes, NMPlatformIP4Route, i); - - if ( route->network == r->network - && route->plen == r->plen) - return (int) i; - } - return -1; -} - static int _domains_get_index (const NMIP4Config *self, const char *domain) { @@ -914,12 +1043,17 @@ _wins_get_index (const NMIP4Config *self, guint32 wins_server) void nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) { - guint32 i; + NMIP4ConfigPrivate *priv_dst; + guint i; gint idx; + const NMPlatformIP4Route *r; + NMDedupMultiIter ipconf_iter; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); + priv_dst = NM_IP4_CONFIG_GET_PRIVATE (dst); + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ @@ -947,10 +1081,10 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) /* ignore route_metric */ /* routes */ - for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) { - idx = _routes_get_index (dst, nm_ip4_config_get_route (src, i)); - if (idx >= 0) - nm_ip4_config_del_route (dst, idx); + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) { + nm_dedup_multi_index_remove_obj (priv_dst->multi_idx, + &priv_dst->idx_ip4_routes, + NMP_OBJECT_UP_CAST (r)); } /* domains */ @@ -1010,14 +1144,21 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) void nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) { - guint32 i; + NMIP4ConfigPrivate *priv_dst; + const NMIP4ConfigPrivate *priv_src; + guint i; gint idx; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *r; - g_return_if_fail (src != NULL); - g_return_if_fail (dst != NULL); + g_return_if_fail (src); + g_return_if_fail (dst); g_object_freeze_notify (G_OBJECT (dst)); + priv_dst = NM_IP4_CONFIG_GET_PRIVATE (dst); + priv_src = NM_IP4_CONFIG_GET_PRIVATE (src); + /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (dst); ) { idx = _addresses_get_index (src, nm_ip4_config_get_address (dst, i)); @@ -1038,12 +1179,15 @@ nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) } /* routes */ - for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) { - idx = _routes_get_index (src, nm_ip4_config_get_route (dst, i)); - if (idx < 0) - nm_ip4_config_del_route (dst, i); - else - i++; + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) { + if (nm_dedup_multi_index_lookup_obj (priv_src->multi_idx, + &priv_src->idx_ip4_routes, + NMP_OBJECT_UP_CAST (r))) + continue; + + if (nm_dedup_multi_index_remove_entry (priv_dst->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); } /* ignore domains */ @@ -1081,7 +1225,7 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; const NMPlatformIP4Address *dst_addr, *src_addr; - const NMPlatformIP4Route *dst_route, *src_route; + NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; g_return_val_if_fail (src != NULL, FALSE); g_return_val_if_fail (dst != NULL, FALSE); @@ -1148,26 +1292,45 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev } /* routes */ - num = nm_ip4_config_get_num_routes (src); - are_equal = num == nm_ip4_config_get_num_routes (dst); - if (are_equal) { - for (i = 0; i < num; i++ ) { - if (nm_platform_ip4_route_cmp (src_route = nm_ip4_config_get_route (src, i), - dst_route = nm_ip4_config_get_route (dst, i))) { - are_equal = FALSE; - if (!routes_are_duplicate (src_route, dst_route, TRUE)) { - has_relevant_changes = TRUE; - break; - } + nm_ip4_config_iter_ip4_route_init (&ipconf_iter_src, src); + nm_ip4_config_iter_ip4_route_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP4Route *r_src, *r_dst; + + has = nm_ip4_config_iter_ip4_route_next (&ipconf_iter_src, &r_src); + if (has != nm_ip4_config_iter_ip4_route_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip4_route_cmp (r_src, r_dst) != 0) { + are_equal = FALSE; + if (!nm_ip_config_obj_id_equal_ip4_route (r_src, r_dst)) { + has_relevant_changes = TRUE; + break; } } - } else - has_relevant_changes = TRUE; + } if (!are_equal) { - nm_ip4_config_reset_routes (dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i)); + const NMPlatformIP4Route *r_src; + has_minor_changes = TRUE; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes); + nm_dedup_multi_iter_rewind (&ipconf_iter_src); + while (nm_ip4_config_iter_ip4_route_next (&ipconf_iter_src, &r_src)) { + nm_dedup_multi_index_add (dst_priv->multi_idx, + &dst_priv->idx_ip4_routes, + NMP_OBJECT_UP_CAST (r_src), + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, + NULL, + NULL); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes, FALSE); } /* nameservers */ @@ -1329,8 +1492,11 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev void nm_ip4_config_dump (const NMIP4Config *config, const char *detail) { - guint32 i, tmp; + guint32 tmp; + guint i; const char *str; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; g_message ("--------- NMIP4Config %p (%s)", config, detail); @@ -1360,8 +1526,8 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail) } /* routes */ - for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) - g_message (" rt: %s", nm_platform_ip4_route_to_string (nm_ip4_config_get_route (config, i), NULL, 0)); + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, config, &route) + g_message (" rt: %s", nm_platform_ip4_route_to_string (route, NULL, 0)); /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) @@ -1611,13 +1777,84 @@ nm_ip4_config_reset_routes (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - if (priv->routes->len != 0) { - g_array_set_size (priv->routes, 0); + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip4_routes) > 0) { _notify (config, PROP_ROUTE_DATA); _notify (config, PROP_ROUTES); } } +static void +_add_route (NMIP4Config *config, const NMPObject *o_new, const NMPlatformIP4Route *new) +{ + NMIP4ConfigPrivate *priv; + NMPObject o_new_storage; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + nm_assert (NM_IS_IP4_CONFIG (config)); + + priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + nm_assert (priv->ifindex > 0); + + /* we go through extra lengths to accept a full o_new object. That one, + * can be reused by increasing the ref-count. */ + if (!o_new) { + nm_assert (new); + nm_assert (new->plen > 0 && new->plen <= 32); + nmp_object_stackinit (&o_new_storage, NMP_OBJECT_TYPE_IP4_ROUTE, + (const NMPlatformObject *) new); + o_new_storage.ip4_route.ifindex = priv->ifindex; + o_new = &o_new_storage; + } else { + nm_assert (!new); + nm_assert (NMP_OBJECT_GET_TYPE (o_new) == NMP_OBJECT_TYPE_IP4_ROUTE); + nm_assert (o_new->ip4_route.plen > 0 && o_new->ip4_route.plen <= 32); + if (o_new->ip4_route.ifindex != priv->ifindex) { + nmp_object_stackinit (&o_new_storage, NMP_OBJECT_TYPE_IP4_ROUTE, &o_new->object); + o_new_storage.ip4_route.ifindex = priv->ifindex; + o_new = &o_new_storage; + } + } + + if (!nm_dedup_multi_index_add (priv->multi_idx, + &priv->idx_ip4_routes, + o_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + &obj_old)) + return; + + if (obj_old) { + NMIPConfigSource old_source; + + old_source = obj_old->ip_route.rt_source; + /* we want to keep the maximum rt_source. But since we expect + * that usually we already add the maxiumum right away, we first try to + * add the new route (replacing the old one). Only if we later + * find out that rt_source is now lower, we fix it. + */ + if (o_new->ip_route.rt_source < old_source) { + if (o_new != &o_new_storage) { + nmp_object_stackinit (&o_new_storage, NMP_OBJECT_TYPE_IP4_ROUTE, + &o_new->object); + o_new = &o_new_storage; + } + o_new_storage.ip_route.rt_source = old_source; + if (!nm_dedup_multi_index_add (priv->multi_idx, + &priv->idx_ip4_routes, + o_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + NULL)) + nm_assert_not_reached (); + } + } + + _notify (config, PROP_ROUTE_DATA); + _notify (config, PROP_ROUTES); +} + /** * nm_ip4_config_add_route: * @config: the #NMIP4Config @@ -1631,76 +1868,70 @@ nm_ip4_config_reset_routes (NMIP4Config *config) void nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - NMIPConfigSource old_source; - int i; - - g_return_if_fail (new != NULL); + g_return_if_fail (config); + g_return_if_fail (new); g_return_if_fail (new->plen > 0 && new->plen <= 32); - g_return_if_fail (priv->ifindex > 0); + g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex > 0); - for (i = 0; i < priv->routes->len; i++ ) { - NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); - - if (routes_are_duplicate (item, new, FALSE)) { - if (nm_platform_ip4_route_cmp (item, new) == 0) - return; - old_source = item->rt_source; - memcpy (item, new, sizeof (*item)); - /* Restore highest priority source */ - item->rt_source = MAX (old_source, new->rt_source); - item->ifindex = priv->ifindex; - goto NOTIFY; - } - } - - g_array_append_val (priv->routes, *new); - g_array_index (priv->routes, NMPlatformIP4Route, priv->routes->len - 1).ifindex = priv->ifindex; -NOTIFY: - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + _add_route (config, NULL, new); } void -nm_ip4_config_del_route (NMIP4Config *config, guint i) +_nmtst_nm_ip4_config_del_route (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + const NMPlatformIP4Route *r; - g_return_if_fail (i < priv->routes->len); + r = _nmtst_nm_ip4_config_get_route (self, i); + g_return_if_fail (r); - g_array_remove_index (priv->routes, i); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + if (nm_dedup_multi_index_remove_obj (priv->multi_idx, + &priv->idx_ip4_routes, + NMP_OBJECT_UP_CAST (r)) != 1) + g_return_if_reached (); + _notify (self, PROP_ROUTE_DATA); + _notify (self, PROP_ROUTES); } guint -nm_ip4_config_get_num_routes (const NMIP4Config *config) +nm_ip4_config_get_num_routes (const NMIP4Config *self) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->routes->len; + head_entry = _idx_ip4_routes (self); + nm_assert ((head_entry ? head_entry->len : 0) == c_list_length (&head_entry->lst_entries_head)); + return head_entry ? head_entry->len : 0; } const NMPlatformIP4Route * -nm_ip4_config_get_route (const NMIP4Config *config, guint i) +_nmtst_nm_ip4_config_get_route (const NMIP4Config *self, guint i) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; + CList *iter; + guint j; - return &g_array_index (priv->routes, NMPlatformIP4Route, i); + head_entry = _idx_ip4_routes (self); + if (head_entry) { + j = 0; + c_list_for_each (iter, &head_entry->lst_entries_head) { + if (i == j) + return _entry_iter_get_ip4_route (iter); + j++; + } + } + g_return_val_if_reached (NULL); } const NMPlatformIP4Route * -nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host) +nm_ip4_config_get_direct_route_for_host (const NMIP4Config *self, guint32 host) { - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP4Route *best_route = NULL; + const NMPlatformIP4Route *best_route = NULL; + const NMPlatformIP4Route *item; + NMDedupMultiIter ipconf_iter; g_return_val_if_fail (host, NULL); - for (i = 0; i < priv->routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); - + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, self, &item) { if (item->gateway != 0) continue; @@ -1715,7 +1946,6 @@ nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host best_route = item; } - return best_route; } @@ -2183,6 +2413,8 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only { guint i; const char *s; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; g_return_if_fail (config); g_return_if_fail (sum); @@ -2198,9 +2430,8 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (address->plen)); } - for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, config, &route) { hash_u32 (sum, route->network); hash_u32 (sum, route->plen); hash_u32 (sum, route->gateway); @@ -2285,6 +2516,9 @@ get_property (GObject *object, guint prop_id, { NMIP4Config *config = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; + GVariantBuilder array_builder, addr_builder, route_builder; switch (prop_id) { case PROP_IFINDEX: @@ -2293,7 +2527,6 @@ get_property (GObject *object, guint prop_id, case PROP_ADDRESS_DATA: case PROP_ADDRESSES: { - GVariantBuilder array_builder, addr_builder; gs_unref_array GArray *new = NULL; guint naddr, i; @@ -2360,14 +2593,8 @@ return_cached: break; case PROP_ROUTE_DATA: { - GVariantBuilder array_builder, route_builder; - guint nroutes = nm_ip4_config_get_num_routes (config); - guint i; - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); - + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, config, &route) { g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&route_builder, "{sv}", "dest", @@ -2392,13 +2619,8 @@ return_cached: break; case PROP_ROUTES: { - GVariantBuilder array_builder; - guint nroutes = nm_ip4_config_get_num_routes (config); - guint i; - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aau")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (config, i); + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, config, &route) { guint32 dbus_route[4]; /* legacy versions of nm_ip4_route_set_prefix() in libnm-util assert that the @@ -2467,6 +2689,13 @@ set_property (GObject *object, NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); @@ -2484,8 +2713,10 @@ nm_ip4_config_init (NMIP4Config *config) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_routes, + NMP_OBJECT_TYPE_IP4_ROUTE); + priv->addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address)); - priv->routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); priv->nameservers = g_array_new (FALSE, FALSE, sizeof (guint32)); priv->domains = g_ptr_array_new_with_free_func (g_free); priv->searches = g_ptr_array_new_with_free_func (g_free); @@ -2496,10 +2727,11 @@ nm_ip4_config_init (NMIP4Config *config) } NMIP4Config * -nm_ip4_config_new (int ifindex) +nm_ip4_config_new (NMDedupMultiIndex *multi_idx, int ifindex) { g_return_val_if_fail (ifindex >= -1, NULL); return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG, + NM_IP4_CONFIG_MULTI_IDX, multi_idx, NM_IP4_CONFIG_IFINDEX, ifindex, NULL); } @@ -2510,10 +2742,11 @@ finalize (GObject *object) NMIP4Config *self = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_routes); + nm_clear_g_variant (&priv->address_data_variant); nm_clear_g_variant (&priv->addresses_variant); g_array_unref (priv->addresses); - g_array_unref (priv->routes); g_array_unref (priv->nameservers); g_ptr_array_unref (priv->domains); g_ptr_array_unref (priv->searches); @@ -2523,6 +2756,8 @@ finalize (GObject *object) g_array_unref (priv->wins); G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object); + + nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -2537,6 +2772,11 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class) object_class->set_property = set_property; object_class->finalize = finalize; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_IP4_CONFIG_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); obj_properties[PROP_IFINDEX] = g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "", -1, G_MAXINT, -1, diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index ceb52ac547..7049f9e5f3 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -24,6 +24,32 @@ #include "nm-exported-object.h" #include "nm-setting-ip4-config.h" +#include "nm-utils/nm-dedup-multi.h" + +/*****************************************************************************/ + +typedef struct { + NMDedupMultiIdxType parent; + NMPObjectType obj_type; +} NMIPConfigDedupMultiIdxType; + +void nm_ip_config_dedup_multi_idx_type_init (NMIPConfigDedupMultiIdxType *idx_type, NMPObjectType obj_type); + +void nm_ip4_config_iter_ip4_route_init (NMDedupMultiIter *iter, const NMIP4Config *self); +gboolean nm_ip4_config_iter_ip4_route_next (NMDedupMultiIter *iter, const NMPlatformIP4Route **out_route); + +#define nm_ip4_config_iter_ip4_route_for_each(iter, self, route) \ + for (nm_ip4_config_iter_ip4_route_init ((iter), (self)); \ + nm_ip4_config_iter_ip4_route_next ((iter), (route)); \ + ) + +gboolean nm_ip_config_obj_id_equal_ip4_route (const NMPlatformIP4Route *r_a, + const NMPlatformIP4Route *r_b); +gboolean nm_ip_config_obj_id_equal_ip6_route (const NMPlatformIP6Route *r_a, + const NMPlatformIP6Route *r_b); + +/*****************************************************************************/ + #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ()) #define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) #define NM_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) @@ -34,6 +60,7 @@ typedef struct _NMIP4ConfigClass NMIP4ConfigClass; /* internal */ +#define NM_IP4_CONFIG_MULTI_IDX "multi-idx" #define NM_IP4_CONFIG_IFINDEX "ifindex" /* public*/ @@ -54,12 +81,14 @@ typedef struct _NMIP4ConfigClass NMIP4ConfigClass; GType nm_ip4_config_get_type (void); -NMIP4Config * nm_ip4_config_new (int ifindex); +NMIP4Config * nm_ip4_config_new (NMDedupMultiIndex *multi_idx, + int ifindex); int nm_ip4_config_get_ifindex (const NMIP4Config *config); +NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self); -NMIP4Config *nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf); +NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf); gboolean nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric); void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config); @@ -90,9 +119,9 @@ gboolean nm_ip4_config_address_exists (const NMIP4Config *config, const NMPlatfo void nm_ip4_config_reset_routes (NMIP4Config *config); void nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *route); -void nm_ip4_config_del_route (NMIP4Config *config, guint i); +void _nmtst_nm_ip4_config_del_route (NMIP4Config *config, guint i); guint nm_ip4_config_get_num_routes (const NMIP4Config *config); -const NMPlatformIP4Route *nm_ip4_config_get_route (const NMIP4Config *config, guint i); +const NMPlatformIP4Route *_nmtst_nm_ip4_config_get_route (const NMIP4Config *config, guint i); const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index af88b21c74..dde62e9501 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -26,12 +26,16 @@ #include #include +#include "nm-utils/nm-dedup-multi.h" + #include "nm-utils.h" +#include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "nm-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" +#include "nm-ip4-config.h" #include "introspection/org.freedesktop.NetworkManager.IP6Config.h" @@ -46,13 +50,14 @@ typedef struct { gint64 route_metric; struct in6_addr gateway; GArray *addresses; - GArray *routes; GArray *nameservers; GPtrArray *domains; GPtrArray *searches; GPtrArray *dns_options; GVariant *address_data_variant; GVariant *addresses_variant; + NMDedupMultiIndex *multi_idx; + NMDedupMultiIdxType idx_ip6_routes; } NMIP6ConfigPrivate; struct _NMIP6Config { @@ -69,6 +74,7 @@ G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT) #define NM_IP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP6Config, NM_IS_IP6_CONFIG) NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config, + PROP_MULTI_IDX, PROP_IFINDEX, PROP_ADDRESS_DATA, PROP_ADDRESSES, @@ -84,12 +90,22 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config, /*****************************************************************************/ +static void _add_route (NMIP6Config *config, const NMPObject *o_new, const NMPlatformIP6Route *new); + +/*****************************************************************************/ + int nm_ip6_config_get_ifindex (const NMIP6Config *config) { return NM_IP6_CONFIG_GET_PRIVATE (config)->ifindex; } +NMDedupMultiIndex * +nm_ip6_config_get_multi_idx (const NMIP6Config *config) +{ + return NM_IP6_CONFIG_GET_PRIVATE (config)->multi_idx; +} + void nm_ip6_config_set_privacy (NMIP6Config *config, NMSettingIP6ConfigPrivacy privacy) { @@ -100,6 +116,49 @@ nm_ip6_config_set_privacy (NMIP6Config *config, NMSettingIP6ConfigPrivacy privac /*****************************************************************************/ +static const NMDedupMultiHeadEntry * +_idx_ip6_routes (const NMIP6Config *self) +{ + const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + return nm_dedup_multi_index_lookup_head (priv->multi_idx, + &priv->idx_ip6_routes, + NULL); +} + +static const NMPlatformIP6Route * +_entry_iter_get_ip6_route (const CList *iter) +{ + const NMDedupMultiEntry *e = c_list_entry (iter, NMDedupMultiEntry, lst_entries); + const NMPObject *o = e->obj; + + nm_assert (o); + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP6_ROUTE); + return &o->ip6_route; +} + +void +nm_ip6_config_iter_ip6_route_init (NMDedupMultiIter *ipconf_iter, const NMIP6Config *self) +{ + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + nm_dedup_multi_iter_init (ipconf_iter, _idx_ip6_routes (self)); +} + +gboolean +nm_ip6_config_iter_ip6_route_next (NMDedupMultiIter *ipconf_iter, const NMPlatformIP6Route **out_route) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (ipconf_iter); + if (has_next) { + nm_assert (NMP_OBJECT_GET_TYPE (ipconf_iter->current->obj) == NMP_OBJECT_TYPE_IP6_ROUTE); + NM_SET_OUT (out_route, &(((const NMPObject *) ipconf_iter->current->obj)->ip6_route)); + } + return has_next; +} + +/*****************************************************************************/ + static void notify_addresses (NMIP6Config *self) { @@ -183,15 +242,6 @@ addresses_are_duplicate (const NMPlatformIP6Address *a, const NMPlatformIP6Addre return IN6_ARE_ADDR_EQUAL (&a->address, &b->address); } -static gboolean -routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_gateway_and_metric) -{ - return IN6_ARE_ADDR_EQUAL (&a->network, &b->network) && a->plen == b->plen && - ( !consider_gateway_and_metric - || ( IN6_ARE_ADDR_EQUAL (&a->gateway, &b->gateway) - && nm_utils_ip6_route_metric_normalize (a->metric) == nm_utils_ip6_route_metric_normalize (b->metric))); -} - static gint _addresses_sort_cmp_get_prio (const struct in6_addr *addr) { @@ -301,65 +351,75 @@ nm_ip6_config_addresses_sort (NMIP6Config *self) } NMIP6Config * -nm_ip6_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary) +nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary) { NMIP6Config *config; NMIP6ConfigPrivate *priv; - guint i; guint32 lowest_metric = G_MAXUINT32; struct in6_addr old_gateway = IN6ADDR_ANY_INIT; - gboolean has_gateway = FALSE; + gboolean has_gateway; + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; gboolean notify_nameservers = FALSE; /* Slaves have no IP configuration */ if (nm_platform_link_get_master (platform, ifindex) > 0) return NULL; - config = nm_ip6_config_new (ifindex); + config = nm_ip6_config_new (multi_idx, ifindex); priv = NM_IP6_CONFIG_GET_PRIVATE (config); g_array_unref (priv->addresses); - g_array_unref (priv->routes); priv->addresses = nm_platform_ip6_address_get_all (platform, ifindex); - priv->routes = nm_platform_ip6_route_get_all (platform, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + + pl_head_entry = nm_platform_lookup_route_visible (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex, + FALSE); /* Extract gateway from default route */ old_gateway = priv->gateway; - for (i = 0; i < priv->routes->len; ) { - const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i); - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { + lowest_metric = G_MAXUINT32; + has_gateway = FALSE; + nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { + const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE (plobj); + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route) + && route->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) { if (route->metric < lowest_metric) { priv->gateway = route->gateway; lowest_metric = route->metric; } has_gateway = TRUE; - /* Remove the default route from the list */ - g_array_remove_index_fast (priv->routes, i); - continue; } - i++; } /* we detect the route metric based on the default route. All non-default * routes have their route metrics explicitly set. */ priv->route_metric = has_gateway ? (gint64) lowest_metric : (gint64) -1; - /* If there is a host route to the gateway, ignore that route. It is - * automatically added by NetworkManager when needed. - */ - if (has_gateway) { - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i); + nm_dedup_multi_iter_rewind (&iter); + nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { + const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE (plobj); - if ( route->plen == 128 - && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway) - && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { - g_array_remove_index (priv->routes, i); - i--; - } + if (route->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + continue; + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; + + if ( has_gateway + && route->plen == 128 + && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway) + && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { + /* If there is a host route to the gateway, ignore that route. It is + * automatically added by NetworkManager when needed. + */ + continue; } + _add_route (config, plobj, NULL); } /* If the interface has the default route, and has IPv6 addresses, capture @@ -393,7 +453,6 @@ nm_ip6_config_commit (const NMIP6Config *config, gboolean routes_full_sync) { const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - gboolean success; g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (config != NULL, FALSE); @@ -403,21 +462,24 @@ nm_ip6_config_commit (const NMIP6Config *config, /* Routes */ { - guint i; - guint count = nm_ip6_config_get_num_routes (config); - GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), count); - const NMPlatformIP6Route *route; + const NMDedupMultiHeadEntry *head_entry; + gs_unref_array GArray *routes = NULL; + const CList *iter; - for (i = 0; i < count; i++) { - route = nm_ip6_config_get_route (config, i); - g_array_append_vals (routes, route, 1); + head_entry = _idx_ip6_routes (config); + + routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), head_entry ? head_entry->len : 0); + + if (head_entry) { + c_list_for_each (iter, &head_entry->lst_entries_head) + g_array_append_vals (routes, _entry_iter_get_ip6_route (iter), 1); } - success = nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync); - g_array_unref (routes); + if (!nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync)) + return FALSE; } - return success; + return TRUE; } static void @@ -548,7 +610,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu route.rt_source = NM_IP_CONFIG_SOURCE_USER; merge_route_attributes (s_route, &route); - nm_ip6_config_add_route (config, &route); + _add_route (config, NULL, &route); } /* DNS */ @@ -584,10 +646,12 @@ nm_ip6_config_create_setting (const NMIP6Config *config) { NMSettingIPConfig *s_ip6; const struct in6_addr *gateway; - guint naddresses, nroutes, nnameservers, nsearches, noptions; + guint naddresses, nnameservers, nsearches, noptions; const char *method = NULL; int i; gint64 route_metric; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); @@ -600,7 +664,6 @@ nm_ip6_config_create_setting (const NMIP6Config *config) gateway = nm_ip6_config_get_gateway (config); naddresses = nm_ip6_config_get_num_addresses (config); - nroutes = nm_ip6_config_get_num_routes (config); nnameservers = nm_ip6_config_get_num_nameservers (config); nsearches = nm_ip6_config_get_num_searches (config); noptions = nm_ip6_config_get_num_dns_options (config); @@ -651,8 +714,7 @@ nm_ip6_config_create_setting (const NMIP6Config *config) NULL); /* Routes */ - for (i = 0; i < nroutes; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, config, &route) { NMIPRoute *s_route; /* Ignore link-local route. */ @@ -708,6 +770,7 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl NMIP6ConfigPrivate *dst_priv; const NMIP6ConfigPrivate *src_priv; guint32 i; + NMDedupMultiIter ipconf_iter; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -733,8 +796,10 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) - nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i)); + const NMPlatformIP6Route *route; + + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, src, &route) + _add_route (dst, NMP_OBJECT_UP_CAST (route), NULL); } if (dst_priv->route_metric == -1) @@ -823,21 +888,6 @@ _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns) return -1; } -static int -_routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - guint i; - - for (i = 0; i < priv->routes->len; i++) { - const NMPlatformIP6Route *r = &g_array_index (priv->routes, NMPlatformIP6Route, i); - - if (routes_are_duplicate (route, r, FALSE)) - return (int) i; - } - return -1; -} - static int _domains_get_index (const NMIP6Config *self, const char *domain) { @@ -895,13 +945,18 @@ _dns_options_get_index (const NMIP6Config *self, const char *option) void nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) { + NMIP6ConfigPrivate *priv_dst; guint i; gint idx; + const NMPlatformIP6Route *r; + NMDedupMultiIter ipconf_iter; const struct in6_addr *dst_tmp, *src_tmp; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); + priv_dst = NM_IP6_CONFIG_GET_PRIVATE (dst); + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ @@ -930,10 +985,10 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) /* ignore route_metric */ /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) { - idx = _routes_get_index (dst, nm_ip6_config_get_route (src, i)); - if (idx >= 0) - nm_ip6_config_del_route (dst, idx); + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, src, &r) { + nm_dedup_multi_index_remove_obj (priv_dst->multi_idx, + &priv_dst->idx_ip6_routes, + NMP_OBJECT_UP_CAST (r)); } /* domains */ @@ -970,12 +1025,19 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) void nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src) { + NMIP6ConfigPrivate *priv_dst; + const NMIP6ConfigPrivate *priv_src; guint i; gint idx; const struct in6_addr *dst_tmp, *src_tmp; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *r; - g_return_if_fail (src != NULL); - g_return_if_fail (dst != NULL); + g_return_if_fail (src); + g_return_if_fail (dst); + + priv_dst = NM_IP6_CONFIG_GET_PRIVATE (dst); + priv_src = NM_IP6_CONFIG_GET_PRIVATE (src); g_object_freeze_notify (G_OBJECT (dst)); @@ -1002,12 +1064,15 @@ nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src) } /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (dst); ) { - idx = _routes_get_index (src, nm_ip6_config_get_route (dst, i)); - if (idx < 0) - nm_ip6_config_del_route (dst, i); - else - i++; + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, dst, &r) { + if (nm_dedup_multi_index_lookup_obj (priv_src->multi_idx, + &priv_src->idx_ip6_routes, + NMP_OBJECT_UP_CAST (r))) + continue; + + if (nm_dedup_multi_index_remove_entry (priv_dst->multi_idx, + ipconf_iter.current) != 1) + nm_assert_not_reached (); } /* ignore domains */ @@ -1042,7 +1107,7 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev NMIP6ConfigPrivate *dst_priv; const NMIP6ConfigPrivate *src_priv; const NMPlatformIP6Address *dst_addr, *src_addr; - const NMPlatformIP6Route *dst_route, *src_route; + NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; g_return_val_if_fail (NM_IS_IP6_CONFIG (src), FALSE); g_return_val_if_fail (NM_IS_IP6_CONFIG (dst), FALSE); @@ -1107,26 +1172,45 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev } /* routes */ - num = nm_ip6_config_get_num_routes (src); - are_equal = num == nm_ip6_config_get_num_routes (dst); - if (are_equal) { - for (i = 0; i < num; i++ ) { - if (nm_platform_ip6_route_cmp (src_route = nm_ip6_config_get_route (src, i), - dst_route = nm_ip6_config_get_route (dst, i))) { - are_equal = FALSE; - if (!routes_are_duplicate (src_route, dst_route, TRUE)) { - has_relevant_changes = TRUE; - break; - } + nm_ip6_config_iter_ip6_route_init (&ipconf_iter_src, src); + nm_ip6_config_iter_ip6_route_init (&ipconf_iter_dst, dst); + are_equal = TRUE; + while (TRUE) { + gboolean has; + const NMPlatformIP6Route *r_src, *r_dst; + + has = nm_ip6_config_iter_ip6_route_next (&ipconf_iter_src, &r_src); + if (has != nm_ip6_config_iter_ip6_route_next (&ipconf_iter_dst, &r_dst)) { + are_equal = FALSE; + has_relevant_changes = TRUE; + break; + } + if (!has) + break; + + if (nm_platform_ip6_route_cmp (r_src, r_dst) != 0) { + are_equal = FALSE; + if (!nm_ip_config_obj_id_equal_ip6_route (r_src, r_dst)) { + has_relevant_changes = TRUE; + break; } } - } else - has_relevant_changes = TRUE; + } if (!are_equal) { - nm_ip6_config_reset_routes (dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i)); + const NMPlatformIP6Route *r_src; + has_minor_changes = TRUE; + nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes); + nm_dedup_multi_iter_rewind (&ipconf_iter_src); + while (nm_ip6_config_iter_ip6_route_next (&ipconf_iter_src, &r_src)) { + nm_dedup_multi_index_add (dst_priv->multi_idx, + &dst_priv->idx_ip6_routes, + NMP_OBJECT_UP_CAST (r_src), + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, + NULL, + NULL); + } + nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes, FALSE); } /* nameservers */ @@ -1242,6 +1326,8 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail) const struct in6_addr *tmp; guint32 i; const char *str; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; g_return_if_fail (config != NULL); @@ -1267,8 +1353,8 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail) } /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) - g_message (" rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i), NULL, 0)); + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, config, &route) + g_message (" rt: %s", nm_platform_ip6_route_to_string (route, NULL, 0)); /* domains */ for (i = 0; i < nm_ip6_config_get_num_domains (config); i++) @@ -1514,13 +1600,84 @@ nm_ip6_config_reset_routes (NMIP6Config *config) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - if (priv->routes->len != 0) { - g_array_set_size (priv->routes, 0); + if (nm_dedup_multi_index_remove_idx (priv->multi_idx, + &priv->idx_ip6_routes) > 0) { _notify (config, PROP_ROUTE_DATA); _notify (config, PROP_ROUTES); } } +static void +_add_route (NMIP6Config *config, const NMPObject *o_new, const NMPlatformIP6Route *new) +{ + NMIP6ConfigPrivate *priv; + NMPObject o_new_storage; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + nm_assert (NM_IS_IP6_CONFIG (config)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (config); + + nm_assert (priv->ifindex > 0); + + /* we go through extra lengths to accept a full o_new object. That one, + * can be reused by increasing the ref-count. */ + if (!o_new) { + nm_assert (new); + nm_assert (new->plen > 0 && new->plen <= 128); + nmp_object_stackinit (&o_new_storage, NMP_OBJECT_TYPE_IP6_ROUTE, + (const NMPlatformObject *) new); + o_new_storage.ip6_route.ifindex = priv->ifindex; + o_new = &o_new_storage; + } else { + nm_assert (!new); + nm_assert (NMP_OBJECT_GET_TYPE (o_new) == NMP_OBJECT_TYPE_IP6_ROUTE); + nm_assert (o_new->ip6_route.plen > 0 && o_new->ip6_route.plen <= 128); + if (o_new->ip6_route.ifindex != priv->ifindex) { + nmp_object_stackinit (&o_new_storage, NMP_OBJECT_TYPE_IP6_ROUTE, &o_new->object); + o_new_storage.ip6_route.ifindex = priv->ifindex; + o_new = &o_new_storage; + } + } + + if (!nm_dedup_multi_index_add (priv->multi_idx, + &priv->idx_ip6_routes, + o_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + &obj_old)) + return; + + if (obj_old) { + NMIPConfigSource old_source; + + old_source = obj_old->ip_route.rt_source; + /* we want to keep the maximum rt_source. But since we expect + * that usually we already add the maxiumum right away, we first try to + * add the new route (replacing the old one). Only if we later + * find out that rt_source is now lower, we fix it. + */ + if (o_new->ip_route.rt_source < old_source) { + if (o_new != &o_new_storage) { + nmp_object_stackinit (&o_new_storage, NMP_OBJECT_TYPE_IP6_ROUTE, + &o_new->object); + o_new = &o_new_storage; + } + o_new_storage.ip_route.rt_source = old_source; + if (!nm_dedup_multi_index_add (priv->multi_idx, + &priv->idx_ip6_routes, + o_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + NULL)) + nm_assert_not_reached (); + } + } + + _notify (config, PROP_ROUTE_DATA); + _notify (config, PROP_ROUTES); +} + /** * nm_ip6_config_add_route: * @config: the #NMIP6Config @@ -1534,76 +1691,70 @@ nm_ip6_config_reset_routes (NMIP6Config *config) void nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - NMIPConfigSource old_source; - int i; - - g_return_if_fail (new != NULL); + g_return_if_fail (config); + g_return_if_fail (new); g_return_if_fail (new->plen > 0 && new->plen <= 128); - g_return_if_fail (priv->ifindex > 0); + g_return_if_fail (NM_IP6_CONFIG_GET_PRIVATE (config)->ifindex > 0); - for (i = 0; i < priv->routes->len; i++ ) { - NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i); - - if (routes_are_duplicate (item, new, FALSE)) { - if (nm_platform_ip6_route_cmp (item, new) == 0) - return; - old_source = item->rt_source; - *item = *new; - /* Restore highest priority source */ - item->rt_source = MAX (old_source, new->rt_source); - item->ifindex = priv->ifindex; - goto NOTIFY; - } - } - - g_array_append_val (priv->routes, *new); - g_array_index (priv->routes, NMPlatformIP6Route, priv->routes->len - 1).ifindex = priv->ifindex; -NOTIFY: - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + _add_route (config, NULL, new); } void -nm_ip6_config_del_route (NMIP6Config *config, guint i) +_nmtst_ip6_config_del_route (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + const NMPlatformIP6Route *r; - g_return_if_fail (i < priv->routes->len); + r = _nmtst_ip6_config_get_route (self, i); + g_return_if_fail (r); - g_array_remove_index (priv->routes, i); - _notify (config, PROP_ROUTE_DATA); - _notify (config, PROP_ROUTES); + if (nm_dedup_multi_index_remove_obj (priv->multi_idx, + &priv->idx_ip6_routes, + NMP_OBJECT_UP_CAST (r)) != 1) + g_return_if_reached (); + _notify (self, PROP_ROUTE_DATA); + _notify (self, PROP_ROUTES); } guint -nm_ip6_config_get_num_routes (const NMIP6Config *config) +nm_ip6_config_get_num_routes (const NMIP6Config *self) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; - return priv->routes->len; + head_entry = _idx_ip6_routes (self); + nm_assert ((head_entry ? head_entry->len : 0) == c_list_length (&head_entry->lst_entries_head)); + return head_entry ? head_entry->len : 0; } const NMPlatformIP6Route * -nm_ip6_config_get_route (const NMIP6Config *config, guint i) +_nmtst_ip6_config_get_route (const NMIP6Config *self, guint i) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + const NMDedupMultiHeadEntry *head_entry; + CList *iter; + guint j; - return &g_array_index (priv->routes, NMPlatformIP6Route, i); + head_entry = _idx_ip6_routes (self); + if (head_entry) { + j = 0; + c_list_for_each (iter, &head_entry->lst_entries_head) { + if (i == j) + return _entry_iter_get_ip6_route (iter); + j++; + } + } + g_return_val_if_reached (NULL); } const NMPlatformIP6Route * -nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host) +nm_ip6_config_get_direct_route_for_host (const NMIP6Config *self, const struct in6_addr *host) { - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP6Route *best_route = NULL; + const NMPlatformIP6Route *best_route = NULL; + const NMPlatformIP6Route *item; + NMDedupMultiIter ipconf_iter; g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL); - for (i = 0; i < priv->routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, self, &item) { if (!IN6_IS_ADDR_UNSPECIFIED (&item->gateway)) continue; @@ -1619,7 +1770,6 @@ nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct best_route = item; } - return best_route; } @@ -1953,6 +2103,8 @@ nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only { guint32 i; const char *s; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; g_return_if_fail (config); g_return_if_fail (sum); @@ -1967,9 +2119,7 @@ nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only hash_u32 (sum, address->plen); } - for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, config, &route) { hash_in6addr (sum, &route->network); hash_u32 (sum, route->plen); hash_in6addr (sum, &route->gateway); @@ -2064,6 +2214,9 @@ get_property (GObject *object, guint prop_id, { NMIP6Config *config = NM_IP6_CONFIG (object); NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; + GVariantBuilder array_builder, addr_builder, route_builder; switch (prop_id) { case PROP_IFINDEX: @@ -2072,7 +2225,6 @@ get_property (GObject *object, guint prop_id, case PROP_ADDRESS_DATA: case PROP_ADDRESSES: { - GVariantBuilder array_builder, addr_builder; gs_unref_array GArray *new = NULL; const struct in6_addr *gateway; guint naddr, i; @@ -2134,14 +2286,8 @@ return_cached: break; case PROP_ROUTE_DATA: { - GVariantBuilder array_builder, route_builder; - guint nroutes = nm_ip6_config_get_num_routes (config); - int i; - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, config, &route) { g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&route_builder, "{sv}", "dest", @@ -2167,14 +2313,8 @@ return_cached: break; case PROP_ROUTES: { - GVariantBuilder array_builder; - int nroutes = nm_ip6_config_get_num_routes (config); - int i; - g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuayu)")); - for (i = 0; i < nroutes; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, config, &route) { /* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the * plen is positive. Skip the default routes not to break older clients. */ if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) @@ -2229,6 +2369,13 @@ set_property (GObject *object, NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + priv->multi_idx = g_value_get_pointer (value); + if (!priv->multi_idx) + g_return_if_reached (); + nm_dedup_multi_index_ref (priv->multi_idx); + break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); @@ -2246,8 +2393,10 @@ nm_ip6_config_init (NMIP6Config *config) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip6_routes, + NMP_OBJECT_TYPE_IP6_ROUTE); + priv->addresses = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Address)); - priv->routes = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Route)); priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr)); priv->domains = g_ptr_array_new_with_free_func (g_free); priv->searches = g_ptr_array_new_with_free_func (g_free); @@ -2256,10 +2405,11 @@ nm_ip6_config_init (NMIP6Config *config) } NMIP6Config * -nm_ip6_config_new (int ifindex) +nm_ip6_config_new (NMDedupMultiIndex *multi_idx, int ifindex) { g_return_val_if_fail (ifindex >= -1, NULL); return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, + NM_IP6_CONFIG_MULTI_IDX, multi_idx, NM_IP6_CONFIG_IFINDEX, ifindex, NULL); } @@ -2271,7 +2421,8 @@ nm_ip6_config_new_cloned (const NMIP6Config *src) g_return_val_if_fail (NM_IS_IP6_CONFIG (src), NULL); - new = nm_ip6_config_new (nm_ip6_config_get_ifindex (src)); + new = nm_ip6_config_new (nm_ip6_config_get_multi_idx (src), + nm_ip6_config_get_ifindex (src)); nm_ip6_config_replace (new, src, NULL); return new; } @@ -2282,8 +2433,9 @@ finalize (GObject *object) NMIP6Config *self = NM_IP6_CONFIG (object); NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip6_routes); + g_array_unref (priv->addresses); - g_array_unref (priv->routes); g_array_unref (priv->nameservers); g_ptr_array_unref (priv->domains); g_ptr_array_unref (priv->searches); @@ -2292,6 +2444,8 @@ finalize (GObject *object) nm_clear_g_variant (&priv->addresses_variant); G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object); + + nm_dedup_multi_index_unref (priv->multi_idx); } static void @@ -2306,6 +2460,11 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class) object_class->set_property = set_property; object_class->finalize = finalize; + obj_properties[PROP_MULTI_IDX] = + g_param_spec_pointer (NM_IP6_CONFIG_MULTI_IDX, "", "", + G_PARAM_WRITABLE + | G_PARAM_CONSTRUCT_ONLY + | G_PARAM_STATIC_STRINGS); obj_properties[PROP_IFINDEX] = g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "", -1, G_MAXINT, -1, diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 557041c958..a390ff1c23 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -26,6 +26,20 @@ #include "nm-exported-object.h" #include "nm-setting-ip6-config.h" +#include "nm-utils/nm-dedup-multi.h" + +/*****************************************************************************/ + +void nm_ip6_config_iter_ip6_route_init (NMDedupMultiIter *iter, const NMIP6Config *self); +gboolean nm_ip6_config_iter_ip6_route_next (NMDedupMultiIter *iter, const NMPlatformIP6Route **out_route); + +#define nm_ip6_config_iter_ip6_route_for_each(iter, self, route) \ + for (nm_ip6_config_iter_ip6_route_init ((iter), (self)); \ + nm_ip6_config_iter_ip6_route_next ((iter), (route)); \ + ) + +/*****************************************************************************/ + #define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type ()) #define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) #define NM_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) @@ -36,6 +50,7 @@ typedef struct _NMIP6ConfigClass NMIP6ConfigClass; /* internal */ +#define NM_IP6_CONFIG_MULTI_IDX "multi-idx" #define NM_IP6_CONFIG_IFINDEX "ifindex" /* public */ @@ -55,13 +70,15 @@ typedef struct _NMIP6ConfigClass NMIP6ConfigClass; GType nm_ip6_config_get_type (void); -NMIP6Config * nm_ip6_config_new (int ifindex); +NMIP6Config * nm_ip6_config_new (struct _NMDedupMultiIndex *multi_idx, int ifindex); NMIP6Config * nm_ip6_config_new_cloned (const NMIP6Config *src); int nm_ip6_config_get_ifindex (const NMIP6Config *config); +struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx (const NMIP6Config *self); -NMIP6Config *nm_ip6_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); +NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, + gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); gboolean nm_ip6_config_commit (const NMIP6Config *config, NMPlatform *platform, NMRouteManager *route_manager, @@ -98,9 +115,9 @@ gboolean nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, void nm_ip6_config_reset_routes (NMIP6Config *config); void nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *route); -void nm_ip6_config_del_route (NMIP6Config *config, guint i); +void _nmtst_ip6_config_del_route (NMIP6Config *config, guint i); guint nm_ip6_config_get_num_routes (const NMIP6Config *config); -const NMPlatformIP6Route *nm_ip6_config_get_route (const NMIP6Config *config, guint i); +const NMPlatformIP6Route *_nmtst_ip6_config_get_route (const NMIP6Config *config, guint i); const NMPlatformIP6Route *nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host); const NMPlatformIP6Address *nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_addr *host); diff --git a/src/nm-manager.c b/src/nm-manager.c index 90ad66403e..3a8ada4360 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -35,6 +35,7 @@ #include "devices/nm-device.h" #include "devices/nm-device-generic.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "nm-hostname-manager.h" #include "nm-rfkill-manager.h" #include "dhcp/nm-dhcp-manager.h" @@ -2478,8 +2479,7 @@ platform_link_cb (NMPlatform *platform, static void platform_query_devices (NMManager *self) { - GArray *links_array; - NMPlatformLink *links; + gs_unref_ptrarray GPtrArray *links = NULL; int i; gboolean guess_assume; const char *order; @@ -2489,21 +2489,21 @@ platform_query_devices (NMManager *self) NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, NM_CONFIG_GET_VALUE_STRIP); - links_array = nm_platform_link_get_all (NM_PLATFORM_GET, !nm_streq0 (order, "index")); - links = (NMPlatformLink *) links_array->data; - for (i = 0; i < links_array->len; i++) { + links = nm_platform_link_get_all (NM_PLATFORM_GET, !nm_streq0 (order, "index")); + if (!links) + return; + for (i = 0; i < links->len; i++) { + const NMPlatformLink *link = NMP_OBJECT_CAST_LINK (links->pdata[i]); gs_free NMConfigDeviceStateData *dev_state = NULL; - dev_state = nm_config_device_state_load (links[i].ifindex); + dev_state = nm_config_device_state_load (link->ifindex); platform_link_added (self, - links[i].ifindex, - &links[i], + link->ifindex, + link, guess_assume && (!dev_state || !dev_state->connection_uuid), dev_state); } - - g_array_unref (links_array); } static void diff --git a/src/nm-multi-index.c b/src/nm-multi-index.c deleted file mode 100644 index 6ae4c21fab..0000000000 --- a/src/nm-multi-index.c +++ /dev/null @@ -1,473 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-multi-index.h" - -#include - -struct NMMultiIndex { - NMMultiIndexFuncEqual equal_fcn; - NMMultiIndexFuncClone clone_fcn; - GHashTable *hash; -}; - -typedef struct { - /* when storing the first item for a multi-index id, we don't yet create - * the hashtable @index. Instead we store it inplace to @value0. Note that - * &values_data->value0 is a NULL terminated array with one item that is - * suitable to be returned directly from nm_multi_index_lookup(). */ - union { - gpointer value0; - gpointer *values; - }; - GHashTable *index; -} ValuesData; - -/*****************************************************************************/ - -static void -_values_data_destroy (ValuesData *values_data) -{ - if (values_data->index) { - g_free (values_data->values); - g_hash_table_unref (values_data->index); - } - g_slice_free (ValuesData, values_data); -} - -static gboolean -_values_data_contains (ValuesData *values_data, gconstpointer value) -{ - return values_data->index - ? g_hash_table_contains (values_data->index, value) - : value == values_data->value0; -} - -static void -_values_data_get_data (ValuesData *values_data, - void *const**out_data, - guint *out_len) -{ - guint i, len; - gpointer *values; - GHashTableIter iter; - - nm_assert (values_data); - - if (!values_data->index) { - NM_SET_OUT (out_data, &values_data->value0); - NM_SET_OUT (out_len, 1); - return; - } - - nm_assert (values_data->index && g_hash_table_size (values_data->index) > 0); - - if (!values_data->values) { - len = g_hash_table_size (values_data->index); - values = g_new (gpointer, len + 1); - - g_hash_table_iter_init (&iter, values_data->index); - for (i = 0; g_hash_table_iter_next (&iter, &values[i], NULL); i++) - nm_assert (i < len); - nm_assert (i == len); - values[i] = NULL; - - values_data->values = values; - NM_SET_OUT (out_len, len); - } else if (out_len) - NM_SET_OUT (out_len, g_hash_table_size (values_data->index)); - - NM_SET_OUT (out_data, values_data->values); -} - -/*****************************************************************************/ - -/** - * nm_multi_index_lookup(): - * @index: - * @id: - * @out_len: (allow-none): output the number of values - * that are returned. - * - * Returns: (transfer none): %NULL if there are no values - * or a %NULL terminated array of pointers. - */ -void *const* -nm_multi_index_lookup (const NMMultiIndex *index, - const NMMultiIndexId *id, - guint *out_len) -{ - ValuesData *values_data; - void *const*values; - - g_return_val_if_fail (index, NULL); - g_return_val_if_fail (id, NULL); - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) { - if (out_len) - *out_len = 0; - return NULL; - } - _values_data_get_data (values_data, &values, out_len); - return values; -} - -gboolean -nm_multi_index_contains (const NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - ValuesData *values_data; - - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (id, FALSE); - g_return_val_if_fail (value, FALSE); - - values_data = g_hash_table_lookup (index->hash, id); - return values_data && _values_data_contains (values_data, value); -} - -const NMMultiIndexId * -nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, - gconstpointer value) -{ - GHashTableIter iter; - const NMMultiIndexId *id; - ValuesData *values_data; - - g_return_val_if_fail (index, NULL); - g_return_val_if_fail (value, NULL); - - /* reverse-lookup needs to iterate over all hash tables. It should - * still be fairly quick, if the number of hash tables is small. - * There is no O(1) reverse lookup implemented, because this access - * pattern is not what NMMultiIndex is here for. - * You are supposed to use NMMultiIndex by always knowing which @id - * a @value has. - */ - - g_hash_table_iter_init (&iter, index->hash); - while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { - if (_values_data_contains (values_data, value)) - return id; - } - return NULL; -} - -void -nm_multi_index_foreach (const NMMultiIndex *index, - gconstpointer value, - NMMultiIndexFuncForeach foreach_func, - gpointer user_data) -{ - GHashTableIter iter; - const NMMultiIndexId *id; - ValuesData *values_data; - guint len; - void *const*values; - - g_return_if_fail (index); - g_return_if_fail (foreach_func); - - g_hash_table_iter_init (&iter, index->hash); - while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { - if (value && !_values_data_contains (values_data, value)) - continue; - - _values_data_get_data (values_data, &values, &len); - if (!foreach_func (id, values, len, user_data)) - return; - } -} - -void -nm_multi_index_iter_init (NMMultiIndexIter *iter, - const NMMultiIndex *index, - gconstpointer value) -{ - g_return_if_fail (index); - g_return_if_fail (iter); - - g_hash_table_iter_init (&iter->_iter, index->hash); - iter->_index = index; - iter->_value = value; -} - -gboolean -nm_multi_index_iter_next (NMMultiIndexIter *iter, - const NMMultiIndexId **out_id, - void *const**out_values, - guint *out_len) -{ - const NMMultiIndexId *id; - ValuesData *values_data; - - g_return_val_if_fail (iter, FALSE); - - while (g_hash_table_iter_next (&iter->_iter, (gpointer *) &id, (gpointer *) &values_data)) { - if ( !iter->_value - || _values_data_contains (values_data, iter->_value)) { - if (out_values || out_len) - _values_data_get_data (values_data, out_values, out_len); - if (out_id) - *out_id = id; - return TRUE; - } - } - return FALSE; -} - -/*****************************************************************************/ - -void -nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, - const NMMultiIndex *index, - const NMMultiIndexId *id) -{ - ValuesData *values_data; - - g_return_if_fail (index); - g_return_if_fail (iter); - g_return_if_fail (id); - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) - iter->_state = 2; - else if (values_data->index) { - iter->_state = 1; - g_hash_table_iter_init (&iter->_iter, values_data->index); - } else { - iter->_state = 0; - iter->_value = values_data->value0; - } -} - -gboolean -nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, - void **out_value) -{ - g_return_val_if_fail (iter, FALSE); - - switch (iter->_state) { - case 0: - iter->_state = 2; - NM_SET_OUT (out_value, iter->_value); - return TRUE; - case 1: - return g_hash_table_iter_next (&iter->_iter, out_value, NULL); - case 2: - iter->_state = 3; - return FALSE; - default: - g_return_val_if_reached (FALSE); - } -} - -/*****************************************************************************/ - -static gboolean -_do_add (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - ValuesData *values_data; - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) { - NMMultiIndexId *id_new; - - /* Contrary to GHashTable, we don't take ownership of the @id that was - * provided to nm_multi_index_add(). Instead we clone it via @clone_fcn - * when needed. - * - * The reason is, that we expect in most cases that there exists - * already a @id so that we don't need ownership of it (or clone it). - * By doing this, the caller can pass a stack allocated @id or - * reuse the @id for other insertions. - */ - id_new = index->clone_fcn (id); - if (!id_new) - g_return_val_if_reached (FALSE); - - values_data = g_slice_new0 (ValuesData); - values_data->value0 = (gpointer) value; - - g_hash_table_insert (index->hash, id_new, values_data); - } else { - if (!values_data->index) { - if (values_data->value0 == value) - return FALSE; - values_data->index = g_hash_table_new (NULL, NULL); - g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value); - g_hash_table_replace (values_data->index, values_data->value0, values_data->value0); - values_data->values = NULL; - } else { - if (!nm_g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value)) - return FALSE; - g_clear_pointer (&values_data->values, g_free); - } - } - return TRUE; -} - -static gboolean -_do_remove (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - ValuesData *values_data; - - values_data = g_hash_table_lookup (index->hash, id); - if (!values_data) - return FALSE; - - if (values_data->index) { - if (!g_hash_table_remove (values_data->index, value)) - return FALSE; - if (g_hash_table_size (values_data->index) == 0) - g_hash_table_remove (index->hash, id); - else - g_clear_pointer (&values_data->values, g_free); - } else { - if (values_data->value0 != value) - return FALSE; - g_hash_table_remove (index->hash, id); - } - - return TRUE; -} - -gboolean -nm_multi_index_add (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (id, FALSE); - g_return_val_if_fail (value, FALSE); - - return _do_add (index, id, value); -} - -gboolean -nm_multi_index_remove (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value) -{ - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (value, FALSE); - - if (!id) - g_return_val_if_reached (FALSE); - return _do_remove (index, id, value); -} - -/** - * nm_multi_index_move: - * @index: - * @id_old: (allow-none): remove @value at @id_old - * @id_new: (allow-none): add @value under @id_new - * @value: the value to add - * - * Similar to a remove(), followed by an add(). The difference - * is, that we allow %NULL for both @id_old and @id_new. - * And the return value indicates whether @value was successfully - * removed *and* added. - * - * Returns: %TRUE, if the value was removed from @id_old and added - * as %id_new. %FALSE could mean, that @value was not added to @id_old - * before, or that that @value was already part of @id_new. */ -gboolean -nm_multi_index_move (NMMultiIndex *index, - const NMMultiIndexId *id_old, - const NMMultiIndexId *id_new, - gconstpointer value) -{ - g_return_val_if_fail (index, FALSE); - g_return_val_if_fail (value, FALSE); - - if (!id_old && !id_new) { - /* nothing to do, @value was and is not in @index. */ - return TRUE; - } if (!id_old) { - /* add @value to @index with @id_new */ - return _do_add (index, id_new, value); - } else if (!id_new) { - /* remove @value from @index with @id_old */ - return _do_remove (index, id_old, value); - } else if (index->equal_fcn (id_old, id_new)) { - if (_do_add (index, id_new, value)) { - /* we would expect, that @value is already in @index, - * Return %FALSE, if it wasn't. */ - return FALSE; - } - return TRUE; - } else { - gboolean did_remove; - - did_remove = _do_remove (index, id_old, value); - return _do_add (index, id_new, value) && did_remove; - } -} - -/*****************************************************************************/ - -guint -nm_multi_index_get_num_groups (const NMMultiIndex *index) -{ - g_return_val_if_fail (index, 0); - return g_hash_table_size (index->hash); -} - -NMMultiIndex * -nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, - NMMultiIndexFuncEqual equal_fcn, - NMMultiIndexFuncClone clone_fcn, - NMMultiIndexFuncDestroy destroy_fcn) -{ - NMMultiIndex *index; - - g_return_val_if_fail (hash_fcn, NULL); - g_return_val_if_fail (equal_fcn, NULL); - g_return_val_if_fail (clone_fcn, NULL); - g_return_val_if_fail (destroy_fcn, NULL); - - index = g_new (NMMultiIndex, 1); - index->equal_fcn = equal_fcn; - index->clone_fcn = clone_fcn; - - index->hash = g_hash_table_new_full ((GHashFunc) hash_fcn, - (GEqualFunc) equal_fcn, - (GDestroyNotify) destroy_fcn, - (GDestroyNotify) _values_data_destroy); - return index; -} - -void -nm_multi_index_free (NMMultiIndex *index) -{ - g_return_if_fail (index); - g_hash_table_unref (index->hash); - g_free (index); -} - diff --git a/src/nm-multi-index.h b/src/nm-multi-index.h deleted file mode 100644 index fb102574a1..0000000000 --- a/src/nm-multi-index.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_MULTI_INDEX__ -#define __NM_MULTI_INDEX__ - -typedef struct { - char _dummy; -} NMMultiIndexId; - -typedef struct NMMultiIndex NMMultiIndex; - -typedef struct { - GHashTableIter _iter; - const NMMultiIndex *_index; - gconstpointer _value; -} NMMultiIndexIter; - -typedef struct { - union { - GHashTableIter _iter; - gpointer _value; - }; - guint _state; -} NMMultiIndexIdIter; - -typedef gboolean (*NMMultiIndexFuncEqual) (const NMMultiIndexId *id_a, const NMMultiIndexId *id_b); -typedef guint (*NMMultiIndexFuncHash) (const NMMultiIndexId *id); -typedef NMMultiIndexId *(*NMMultiIndexFuncClone) (const NMMultiIndexId *id); -typedef void (*NMMultiIndexFuncDestroy) (NMMultiIndexId *id); - -typedef gboolean (*NMMultiIndexFuncForeach) (const NMMultiIndexId *id, void *const* values, guint len, gpointer user_data); - - -NMMultiIndex *nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, - NMMultiIndexFuncEqual equal_fcn, - NMMultiIndexFuncClone clone_fcn, - NMMultiIndexFuncDestroy destroy_fcn); - -void nm_multi_index_free (NMMultiIndex *index); - -gboolean nm_multi_index_add (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value); - -gboolean nm_multi_index_remove (NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value); - -gboolean nm_multi_index_move (NMMultiIndex *index, - const NMMultiIndexId *id_old, - const NMMultiIndexId *id_new, - gconstpointer value); - -guint nm_multi_index_get_num_groups (const NMMultiIndex *index); - -void *const*nm_multi_index_lookup (const NMMultiIndex *index, - const NMMultiIndexId *id, - guint *out_len); - -gboolean nm_multi_index_contains (const NMMultiIndex *index, - const NMMultiIndexId *id, - gconstpointer value); - -const NMMultiIndexId *nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, - gconstpointer value); - -void nm_multi_index_foreach (const NMMultiIndex *index, - gconstpointer value, - NMMultiIndexFuncForeach foreach_func, - gpointer user_data); - -void nm_multi_index_iter_init (NMMultiIndexIter *iter, - const NMMultiIndex *index, - gconstpointer value); -gboolean nm_multi_index_iter_next (NMMultiIndexIter *iter, - const NMMultiIndexId **out_id, - void *const**out_values, - guint *out_len); - -void nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, - const NMMultiIndex *index, - const NMMultiIndexId *id); -gboolean nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, - void **out_value); - -#endif /* __NM_MULTI_INDEX__ */ - diff --git a/src/nm-netns.c b/src/nm-netns.c index a81aa696ee..f5e6b0014d 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -22,6 +22,8 @@ #include "nm-netns.h" +#include "nm-utils/nm-dedup-multi.h" + #include "platform/nm-platform.h" #include "platform/nmp-netns.h" #include "nm-route-manager.h" @@ -74,6 +76,12 @@ nm_netns_get_platform (NMNetns *self) return NM_NETNS_GET_PRIVATE (self)->platform; } +NMDedupMultiIndex * +nm_netns_get_multi_idx (NMNetns *self) +{ + return nm_platform_get_multi_idx (NM_NETNS_GET_PRIVATE (self)->platform); +} + NMDefaultRouteManager * nm_netns_get_default_route_manager (NMNetns *self) { diff --git a/src/nm-netns.h b/src/nm-netns.h index fd5daf47ff..ebe9d1f2a8 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -42,6 +42,8 @@ NMPNetns *nm_netns_get_platform_netns (NMNetns *self); NMRouteManager *nm_netns_get_route_manager (NMNetns *self); NMDefaultRouteManager *nm_netns_get_default_route_manager (NMNetns *self); +struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); + #define NM_NETNS_GET (nm_netns_get ()) #endif /* __NM_NETNS_H__ */ diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index 0a1cd823f4..d9095cce5b 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -168,8 +168,10 @@ add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config static void get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) { + NMDedupMultiIter ipconf_iter; char *cidr; - int i; + const NMPlatformIP4Route *routes; + guint i; /* Extract searches */ for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++) @@ -189,9 +191,7 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) g_ptr_array_add (domains, cidr); } - for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) { - const NMPlatformIP4Route *routes = nm_ip4_config_get_route (ip4, i); - + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &routes) { cidr = g_strdup_printf ("%s/%u", nm_utils_inet4_ntop (routes->network, NULL), routes->plen); @@ -202,8 +202,10 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) static void get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) { + NMDedupMultiIter ipconf_iter; char *cidr; - int i; + const NMPlatformIP6Route *routes; + guint i; /* Extract searches */ for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++) @@ -223,9 +225,7 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) g_ptr_array_add (domains, cidr); } - for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) { - const NMPlatformIP6Route *routes = nm_ip6_config_get_route (ip6, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &routes) { cidr = g_strdup_printf ("%s/%u", nm_utils_inet6_ntop (&routes->network, NULL), routes->plen); diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c index b58cdeb069..e980cfe0a6 100644 --- a/src/nm-route-manager.c +++ b/src/nm-route-manager.c @@ -306,6 +306,55 @@ _route_index_create (const VTableIP *vtable, const GArray *routes) return index; } +static RouteIndex * +_route_index_create_from_platform (const VTableIP *vtable, + NMPlatform *platform, + int ifindex, + gboolean ignore_kernel_routes, + GPtrArray **out_storage) +{ + RouteIndex *index; + guint i, j, len; + GPtrArray *storage; + + nm_assert (out_storage && !*out_storage); + + storage = nm_platform_lookup_route_visible_clone (platform, + vtable->vt->obj_type, + ifindex, + FALSE, + NULL, + NULL); + if (!storage) + return _route_index_create (vtable, NULL); + + len = storage->len; + index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); + + j = 0; + for (i = 0; i < len; i++) { + const NMPlatformIPXRoute *ipx_route = NMP_OBJECT_CAST_IPX_ROUTE (storage->pdata[i]); + + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (ipx_route)) + continue; + + /* we cast away the const-ness of the NMPObjects. The caller must + * ensure not to modify the object via index->entries. */ + index->entries[j++] = (NMPlatformIPXRoute *) ipx_route; + } + index->entries[j] = NULL; + index->len = j; + + /* this is a stable sort, which is very important at this point. */ + g_qsort_with_data (index->entries, + index->len, + sizeof (NMPlatformIPXRoute *), + (GCompareDataFunc) _route_index_create_sort, + (gpointer) vtable); + *out_storage = storage; + return index; +} + static int _vx_route_id_cmp_full (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, const VTableIP *vtable) { @@ -458,7 +507,7 @@ static gboolean _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) { NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - GArray *plat_routes; + gs_unref_ptrarray GPtrArray *plat_routes = NULL; RouteEntries *ipx_routes; RouteIndex *plat_routes_idx, *known_routes_idx; gboolean success = TRUE; @@ -475,16 +524,15 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const nm_platform_process_events (priv->platform); ipx_routes = vtable->vt->is_ip4 ? &priv->ip4_routes : &priv->ip6_routes; - plat_routes = vtable->vt->route_get_all (priv->platform, ifindex, - ignore_kernel_routes - ? NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT - : NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); - plat_routes_idx = _route_index_create (vtable, plat_routes); + + /* the objects referenced by play_routes_idx are shared from the platform cache. They + * must not be modified. */ + plat_routes_idx = _route_index_create_from_platform (vtable, priv->platform, ifindex, ignore_kernel_routes, &plat_routes); + known_routes_idx = _route_index_create (vtable, known_routes); effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - ASSERT_route_index_valid (vtable, plat_routes, plat_routes_idx, TRUE); ASSERT_route_index_valid (vtable, known_routes, known_routes_idx, FALSE); _LOGD (vtable->vt->addr_family, "%3d: sync %u IPv%c routes", ifindex, known_routes_idx->len, vtable->vt->is_ip4 ? '4' : '6'); @@ -918,7 +966,6 @@ next: g_free (known_routes_idx); g_free (plat_routes_idx); - g_array_unref (plat_routes); return success; } @@ -1173,7 +1220,7 @@ nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *sel ? "update" : "new", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); g_hash_table_replace (priv->ip4_device_routes.entries, - nmp_object_ref (entry->obj), + (NMPObject *) nmp_object_ref (entry->obj), entry); } if (priv->ip4_device_routes.gc_id == 0) { diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 4e8e2f98e0..879e9190af 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -226,6 +226,25 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP } } +#ifdef __NMP_OBJECT_H__ + +static inline void +nmtst_platform_ip4_routes_equal_aptr (const NMPObject *const*a, const NMPlatformIP4Route *b, gsize len, gboolean ignore_order) +{ + gsize i; + gs_free NMPlatformIP4Route *c_a = NULL; + + g_assert (len > 0); + g_assert (a); + + c_a = g_new (NMPlatformIP4Route, len); + for (i = 0; i < len; i++) + c_a[i] = *NMP_OBJECT_CAST_IP4_ROUTE (a[i]); + nmtst_platform_ip4_routes_equal (c_a, b, len, ignore_order); +} + +#endif + static inline int _nmtst_platform_ip6_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data) { @@ -260,18 +279,48 @@ nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP } } +#ifdef __NMP_OBJECT_H__ + +static inline void +nmtst_platform_ip6_routes_equal_aptr (const NMPObject *const*a, const NMPlatformIP6Route *b, gsize len, gboolean ignore_order) +{ + gsize i; + gs_free NMPlatformIP6Route *c_a = NULL; + + g_assert (len > 0); + g_assert (a); + + c_a = g_new (NMPlatformIP6Route, len); + for (i = 0; i < len; i++) + c_a[i] = *NMP_OBJECT_CAST_IP6_ROUTE (a[i]); + nmtst_platform_ip6_routes_equal (c_a, b, len, ignore_order); +} + +#endif + #endif #ifdef __NETWORKMANAGER_IP4_CONFIG_H__ +#include "nm-utils/nm-dedup-multi.h" + +static inline NMIP4Config * +nmtst_ip4_config_new (int ifindex) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + + return nm_ip4_config_new (multi_idx, ifindex); +} + static inline NMIP4Config * nmtst_ip4_config_clone (NMIP4Config *config) { - NMIP4Config *copy = nm_ip4_config_new (-1); + NMIP4Config *copy; - g_assert (copy); g_assert (config); + copy = nm_ip4_config_new (nm_ip4_config_get_multi_idx (config), -1); + g_assert (copy); nm_ip4_config_replace (copy, config, NULL); return copy; } @@ -281,13 +330,24 @@ nmtst_ip4_config_clone (NMIP4Config *config) #ifdef __NETWORKMANAGER_IP6_CONFIG_H__ +#include "nm-utils/nm-dedup-multi.h" + +static inline NMIP6Config * +nmtst_ip6_config_new (int ifindex) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); + + return nm_ip6_config_new (multi_idx, ifindex); +} + static inline NMIP6Config * nmtst_ip6_config_clone (NMIP6Config *config) { - NMIP6Config *copy = nm_ip6_config_new (-1); + NMIP6Config *copy; - g_assert (copy); g_assert (config); + copy = nm_ip6_config_new (nm_ip6_config_get_multi_idx (config), -1); + g_assert (copy); nm_ip6_config_replace (copy, config, NULL); return copy; } diff --git a/src/nm-types.h b/src/nm-types.h index 44b4fecbb3..36a624de8b 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -56,6 +56,8 @@ typedef struct _NMSleepMonitor NMSleepMonitor; typedef struct _NMLldpListener NMLldpListener; typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData; +struct _NMDedupMultiIndex; + /*****************************************************************************/ typedef enum { diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 27fde50d61..718ee974ae 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -32,6 +32,7 @@ #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "nmp-object.h" #include "nm-test-utils-core.h" @@ -39,20 +40,14 @@ /*****************************************************************************/ typedef struct { - NMPlatformLink link; - + const NMPObject *obj; char *udi; - NMPObject *lnk; struct in6_addr ip6_lladdr; } NMFakePlatformLink; typedef struct { GHashTable *options; GArray *links; - GArray *ip4_addresses; - GArray *ip6_addresses; - GArray *ip4_routes; - GArray *ip6_routes; } NMFakePlatformPrivate; struct _NMFakePlatform { @@ -96,7 +91,24 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) /*****************************************************************************/ -static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal); +static void link_changed (NMPlatform *platform, + NMFakePlatformLink *device, + NMPCacheOpsType cache_op, + const NMPObject *obj_old); + +static gboolean ipx_address_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer addr, + const guint8 *plen, + gconstpointer peer_addr); + +static gboolean ipx_route_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer network, + const guint8 *plen, + const guint32 *metric); static gboolean ip6_address_add (NMPlatform *platform, int ifindex, @@ -110,14 +122,6 @@ static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in /*****************************************************************************/ -static gboolean -_ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, guint8 plen) -{ - return ((peer1 ^ peer2) & nm_utils_ip4_prefix_to_netmask (plen)) == 0; -} - -/*****************************************************************************/ - #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ G_STMT_START { \ const char *const _pathid = (pathid); \ @@ -158,165 +162,126 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat return g_strdup (g_hash_table_lookup (priv->options, path)); } -static const char * -type_to_type_name (NMLinkType type) -{ - switch (type) { - case NM_LINK_TYPE_UNKNOWN: - return "unknown"; - case NM_LINK_TYPE_LOOPBACK: - return "loopback"; - case NM_LINK_TYPE_ETHERNET: - return "ethernet"; - case NM_LINK_TYPE_DUMMY: - return "dummy"; - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bond"; - case NM_LINK_TYPE_TEAM: - return "team"; - case NM_LINK_TYPE_VLAN: - return "vlan"; - case NM_LINK_TYPE_NONE: - default: - return NULL; - } -} - -static void -link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) -{ - gs_free char *ip6_lladdr = NULL; - - g_assert (!name || strlen (name) < sizeof(device->link.name)); - - memset (device, 0, sizeof (*device)); - - ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - - device->link.ifindex = name ? ifindex : 0; - device->link.type = type; - device->link.kind = type_to_type_name (type); - device->link.driver = type_to_type_name (type); - device->udi = g_strdup_printf ("fake:%d", ifindex); - device->link.initialized = TRUE; - device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); - if (name) - strcpy (device->link.name, name); - switch (device->link.type) { - case NM_LINK_TYPE_DUMMY: - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_NOARP); - break; - default: - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_NOARP); - break; - } -} - static NMFakePlatformLink * link_get (NMPlatform *platform, int ifindex) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMFakePlatformLink *device; + int idx; - if (ifindex >= priv->links->len) + if (ifindex <= 0) + g_return_val_if_reached (NULL); + + idx = ifindex - 1; + if (idx >= priv->links->len) goto not_found; - device = &g_array_index (priv->links, NMFakePlatformLink, ifindex); - if (!device->link.ifindex) + + device = &g_array_index (priv->links, NMFakePlatformLink, idx); + if (!device->obj) goto not_found; + g_assert (ifindex == NMP_OBJECT_CAST_LINK (device->obj)->ifindex); + g_assert (device->obj == nm_platform_link_get_obj (platform, ifindex, FALSE)); + return device; not_found: _LOGD ("link not found: %d", ifindex); return NULL; } -static GArray * -link_get_all (NMPlatform *platform) +static void +link_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + NMPObject *obj_tmp) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *links = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), priv->links->len); - int i; + gboolean connected; - for (i = 0; i < priv->links->len; i++) - if (g_array_index (priv->links, NMFakePlatformLink, i).link.ifindex) - g_array_append_val (links, g_array_index (priv->links, NMFakePlatformLink, i).link); + /* we must clear the driver, because platform cache want's to set it */ + g_assert (obj_tmp->link.driver == g_intern_string (obj_tmp->link.driver)); + obj_tmp->link.driver = NULL; - return links; -} + if (NM_IN_SET (obj_tmp->link.type, NM_LINK_TYPE_BRIDGE, + NM_LINK_TYPE_BOND)) { + connected = FALSE; + if (NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP)) { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject *slave_candidate = NULL; -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? &device->link : NULL; -} - -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, const char *ifname) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - guint i; - - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - - if (!strcmp (device->link.name, ifname)) - return &device->link; - } - return NULL; -} - -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - guint i; - - if ( length == 0 - || length > NM_UTILS_HWADDR_LEN_MAX - || !address) - g_return_val_if_reached (NULL); - - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - - if ( device->link.addr.len == length - && memcmp (device->link.addr.data, address, length) == 0) { - return &device->link; + nmp_cache_iter_for_each (&iter, + nmp_cache_lookup (nm_platform_get_cache (platform), + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_LINK)), + &slave_candidate) { + if (nmp_cache_link_connected_for_slave (obj_tmp->link.ifindex, slave_candidate)) { + connected = TRUE; + break; + } + } } - } - return NULL; + } else + connected = NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP); + + obj_tmp->link.n_ifi_flags = NM_FLAGS_ASSIGN (obj_tmp->link.n_ifi_flags, IFF_LOWER_UP, connected); + obj_tmp->link.connected = connected; } -static const NMPObject * -link_get_lnk (NMPlatform *platform, - int ifindex, - NMLinkType link_type, - const NMPlatformLink **out_link) +static NMFakePlatformLink * +link_add_pre (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len) { - NMFakePlatformLink *device = link_get (platform, ifindex); + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); + NMFakePlatformLink *device; + int ifindex; + NMPObject *o; + NMPlatformLink *link; + gs_free char *ip6_lladdr = NULL; - if (!device) - return NULL; + g_assert (!name || strlen (name) < IFNAMSIZ); - NM_SET_OUT (out_link, &device->link); + g_array_set_size (priv->links, priv->links->len + 1); + device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); + ifindex = priv->links->len; - if (!device->lnk) - return NULL; + memset (device, 0, sizeof (*device)); - if (link_type == NM_LINK_TYPE_NONE) - return device->lnk; + o = nmp_object_new_link (ifindex); + link = NMP_OBJECT_CAST_LINK (o); - if ( link_type != device->link.type - || link_type != NMP_OBJECT_GET_CLASS (device->lnk)->lnk_link_type) - return NULL; + ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - return device->lnk; + link->ifindex = name ? ifindex : 0; + link->type = type; + link->kind = g_intern_string (nm_link_type_to_string (type)); + link->initialized = TRUE; + if (name) + strcpy (link->name, name); + switch (link->type) { + case NM_LINK_TYPE_DUMMY: + link->n_ifi_flags = NM_FLAGS_SET (link->n_ifi_flags, IFF_NOARP); + break; + default: + link->n_ifi_flags = NM_FLAGS_UNSET (link->n_ifi_flags, IFF_NOARP); + break; + } + + o->_link.netlink.is_in_netlink = TRUE; + + if (address) { + g_assert (address_len > 0 && address_len <= sizeof (link->addr.data)); + memcpy (link->addr.data, address, address_len); + link->addr.len = address_len; + } else + g_assert (address_len == 0); + + device->obj = o; + device->udi = g_strdup_printf ("fake:%d", ifindex); + device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); + + return device; } static gboolean @@ -328,135 +293,201 @@ link_add (NMPlatform *platform, size_t address_len, const NMPlatformLink **out_link) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMFakePlatformLink device; - NMFakePlatformLink device_veth = { }; - NMFakePlatformLink *new_device; + NMFakePlatformLink *device; + NMFakePlatformLink *device_veth = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old_veth = NULL; + nm_auto_nmpobj const NMPObject *obj_new_veth = NULL; + NMPCacheOpsType cache_op; + NMPCacheOpsType cache_op_veth = NMP_CACHE_OPS_UNCHANGED; - link_init (&device, priv->links->len, type, name); - - if (address) { - g_return_val_if_fail (address_len > 0 && address_len <= sizeof (device.link.addr.data), FALSE); - memcpy (device.link.addr.data, address, address_len); - device.link.addr.len = address_len; - } - - g_array_append_val (priv->links, device); - new_device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); + device = link_add_pre (platform, name, type, address, address_len); if (veth_peer) { - link_init (&device_veth, priv->links->len, type, veth_peer); - g_array_append_val (priv->links, device_veth); - new_device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 2); + g_assert (type == NM_LINK_TYPE_VETH); + device_veth = link_add_pre (platform, veth_peer, type, NULL, 0); } else g_assert (type != NM_LINK_TYPE_VETH); + link_add_prepare (platform, device, (NMPObject *) device->obj); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device->obj, + &obj_old, &obj_new); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + if (veth_peer) { + link_add_prepare (platform, device_veth, (NMPObject *) device_veth->obj); + cache_op_veth = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device_veth->obj, + &obj_old_veth, &obj_new_veth); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + } if (out_link) - *out_link = &new_device->link; + *out_link = NMP_OBJECT_CAST_LINK (device->obj); - if (device.link.ifindex) { - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, (int) NM_PLATFORM_SIGNAL_ADDED); - - link_changed (platform, new_device, FALSE); - } - - if (veth_peer) { - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device_veth.link.ifindex, &device_veth, (int) NM_PLATFORM_SIGNAL_ADDED); - - link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE); - } + link_changed (platform, device, cache_op, NULL); + if (veth_peer) + link_changed (platform, device_veth, cache_op_veth, NULL); return TRUE; } +static NMFakePlatformLink * +link_add_one (NMPlatform *platform, + const char *name, + NMLinkType link_type, + void (*prepare_fcn) (NMPlatform *platform, NMFakePlatformLink *device, gconstpointer user_data), + gconstpointer user_data, + const NMPlatformLink **out_link) +{ + NMFakePlatformLink *device; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCacheOpsType cache_op; + int ifindex; + + device = link_add_pre (platform, name, NM_LINK_TYPE_VLAN, NULL, 0); + + ifindex = NMP_OBJECT_CAST_LINK (device->obj)->ifindex; + + if (prepare_fcn) + prepare_fcn (platform, device, user_data); + + link_add_prepare (platform, device, (NMPObject *) device->obj); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + (NMPObject *) device->obj, + &obj_old, &obj_new); + g_assert (cache_op == NMP_CACHE_OPS_ADDED); + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + + link_changed (platform, device, cache_op, obj_old); + + device = link_get (platform, ifindex); + if (!device) + g_assert_not_reached (); + + NM_SET_OUT (out_link, NMP_OBJECT_CAST_LINK (device->obj)); + return device; +} + static gboolean link_delete (NMPlatform *platform, int ifindex) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMFakePlatformLink *device = link_get (platform, ifindex); - NMPlatformLink deleted_device; - int i; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_old2 = NULL; + NMPCacheOpsType cache_op; - if (!device || !device->link.ifindex) + if (!device) return FALSE; - memcpy (&deleted_device, &device->link, sizeof (deleted_device)); - memset (&device->link, 0, sizeof (device->link)); - g_clear_pointer (&device->lnk, nmp_object_unref); + obj_old = g_steal_pointer (&device->obj); g_clear_pointer (&device->udi, g_free); + cache_op = nmp_cache_remove (nm_platform_get_cache (platform), + obj_old, + FALSE, + &obj_old2); + g_assert (cache_op == NMP_CACHE_OPS_REMOVED); + g_assert (obj_old2); + g_assert (obj_old == obj_old2); + /* Remove addresses and routes which belong to the deleted interface */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if (address->ifindex == ifindex) - memset (address, 0, sizeof (*address)); - } - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if (address->ifindex == ifindex) - memset (address, 0, sizeof (*address)); - } - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (route->ifindex == ifindex) - memset (route, 0, sizeof (*route)); - } - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (route->ifindex == ifindex) - memset (route, 0, sizeof (*route)); - } - - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, (int) NM_PLATFORM_SIGNAL_REMOVED); + ipx_address_delete (platform, AF_INET, ifindex, NULL, NULL, NULL); + ipx_address_delete (platform, AF_INET6, ifindex, NULL, NULL, NULL); + ipx_route_delete (platform, AF_INET, ifindex, NULL, NULL, NULL); + ipx_route_delete (platform, AF_INET6, ifindex, NULL, NULL, NULL); + nm_platform_cache_update_emit_signal (platform, + cache_op, + obj_old2, + NULL); return TRUE; } -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) +static void +link_set_obj (NMPlatform *platform, + NMFakePlatformLink *device, + NMPObject *obj_tmp) { - return type_to_type_name (nm_platform_link_get_type (platform, ifindex)); + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj NMPObject *obj_tmp_tmp = NULL; + NMPCacheOpsType cache_op; + + g_assert (device); + g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); + + if (!obj_tmp) { + obj_tmp_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp = obj_tmp_tmp; + } + + g_assert (NMP_OBJECT_GET_TYPE (obj_tmp) == NMP_OBJECT_TYPE_LINK); + + link_add_prepare (platform, device, obj_tmp); + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + obj_tmp, &obj_old, &obj_new); + g_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_UNCHANGED, + NMP_CACHE_OPS_UPDATED)); + g_assert (obj_old == device->obj); + g_assert (obj_new); + + nmp_object_unref (device->obj); + device->obj = nmp_object_ref (obj_new); + + link_changed (platform, device, cache_op, obj_old); } static void -link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal) +link_set_flags (NMPlatform *platform, + NMFakePlatformLink *device, + guint n_ifi_flags) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (raise_signal) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, (int) NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, (int) NM_PLATFORM_SIGNAL_CHANGED); + g_assert (device); + g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); - if (device->link.ifindex && !IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { - if (device->link.connected) - ip6_address_add (platform, device->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.n_ifi_flags = n_ifi_flags; + link_set_obj (platform, device, obj_tmp); +} + +static void +link_changed (NMPlatform *platform, + NMFakePlatformLink *device, + NMPCacheOpsType cache_op, + const NMPObject *obj_old) +{ + g_assert (device->obj); + + g_assert (!nmp_cache_link_connected_needs_toggle (nm_platform_get_cache (platform), + device->obj, NULL, NULL)); + + nm_platform_cache_update_emit_signal (platform, + cache_op, + obj_old, + device->obj); + + if (!IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { + if (device->obj->link.connected) + ip6_address_add (platform, device->obj->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); else - ip6_address_delete (platform, device->link.ifindex, device->ip6_lladdr, 64); + ip6_address_delete (platform, device->obj->link.ifindex, device->ip6_lladdr, 64); } - if (device->link.master) { - gboolean connected = FALSE; + if (device->obj->link.master) { + NMFakePlatformLink *master; - NMFakePlatformLink *master = link_get (platform, device->link.master); - - g_return_if_fail (master && master != device); - - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *slave = &g_array_index (priv->links, NMFakePlatformLink, i); - - if (slave && slave->link.master == master->link.ifindex && slave->link.connected) - connected = TRUE; - } - - if (master->link.connected != connected) { - master->link.connected = connected; - link_changed (platform, master, TRUE); - } + master = link_get (platform, device->obj->link.master); + link_set_obj (platform, master, NULL); } } @@ -464,7 +495,6 @@ static gboolean link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { NMFakePlatformLink *device = link_get (platform, ifindex); - gboolean up, connected; if (out_no_firmware) *out_no_firmware = FALSE; @@ -474,29 +504,9 @@ link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) return FALSE; } - up = TRUE; - connected = TRUE; - switch (device->link.type) { - case NM_LINK_TYPE_DUMMY: - case NM_LINK_TYPE_VLAN: - break; - case NM_LINK_TYPE_BRIDGE: - case NM_LINK_TYPE_BOND: - case NM_LINK_TYPE_TEAM: - connected = FALSE; - break; - default: - connected = FALSE; - g_error ("Unexpected device type: %d", device->link.type); - } - - if ( NM_FLAGS_HAS (device->link.n_ifi_flags, IFF_UP) != !!up - || device->link.connected != connected) { - device->link.n_ifi_flags = NM_FLAGS_ASSIGN (device->link.n_ifi_flags, IFF_UP, up); - device->link.connected = connected; - link_changed (platform, device, TRUE); - } - + link_set_flags (platform, + device, + NM_FLAGS_ASSIGN (device->obj->link.n_ifi_flags, IFF_UP, TRUE)); return TRUE; } @@ -510,13 +520,9 @@ link_set_down (NMPlatform *platform, int ifindex) return FALSE; } - if (NM_FLAGS_HAS (device->link.n_ifi_flags, IFF_UP) || device->link.connected) { - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_UP); - device->link.connected = FALSE; - - link_changed (platform, device, TRUE); - } - + link_set_flags (platform, + device, + NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_UP)); return TRUE; } @@ -530,10 +536,9 @@ link_set_arp (NMPlatform *platform, int ifindex) return FALSE; } - device->link.n_ifi_flags = NM_FLAGS_UNSET (device->link.n_ifi_flags, IFF_NOARP); - - link_changed (platform, device, TRUE); - + link_set_flags (platform, + device, + NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } @@ -547,10 +552,9 @@ link_set_noarp (NMPlatform *platform, int ifindex) return FALSE; } - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_NOARP); - - link_changed (platform, device, TRUE); - + link_set_flags (platform, + device, + NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } @@ -558,21 +562,22 @@ static NMPlatformError link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if ( !device - || len == 0 + if ( len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - if ( device->link.addr.len != len - || ( len > 0 - && memcmp (device->link.addr.data, addr, len) != 0)) { - memcpy (device->link.addr.data, addr, len); - device->link.addr.len = len; - link_changed (platform, link_get (platform, ifindex), TRUE); - } + if (!device) + return NM_PLATFORM_ERROR_EXISTS; + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.addr.len = len; + memset (obj_tmp->link.addr.data, 0, sizeof (obj_tmp->link.addr.data)); + memcpy (obj_tmp->link.addr.data, addr, len); + + link_set_obj (platform, device, obj_tmp); return NM_PLATFORM_ERROR_SUCCESS; } @@ -580,14 +585,17 @@ static gboolean link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { NMFakePlatformLink *device = link_get (platform, ifindex); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (device) { - device->link.mtu = mtu; - link_changed (platform, device, TRUE); - } else + if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); + return FALSE; + } - return !!device; + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.mtu = mtu; + link_set_obj (platform, device, obj_tmp); + return TRUE; } static gboolean @@ -631,7 +639,7 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_DUMMY: return FALSE; default: @@ -647,7 +655,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: @@ -663,7 +671,7 @@ link_supports_sriov (NMPlatform *platform, int ifindex) if (!device) return FALSE; - switch (device->link.type) { + switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: @@ -680,15 +688,14 @@ link_enslave (NMPlatform *platform, int master, int slave) g_return_val_if_fail (device, FALSE); g_return_val_if_fail (master_device, FALSE); - if (device->link.master != master) { - device->link.master = master; + if (device->obj->link.master != master) { + nm_auto_nmpobj NMPObject *obj_tmp = NULL; - if (NM_IN_SET (master_device->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) { - device->link.n_ifi_flags = NM_FLAGS_SET (device->link.n_ifi_flags, IFF_UP); - device->link.connected = TRUE; - } - - link_changed (platform, device, TRUE); + obj_tmp = nmp_object_clone (device->obj, FALSE); + obj_tmp->link.master = master; + if (NM_IN_SET (master_device->obj->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) + obj_tmp->link.n_ifi_flags = NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_UP); + link_set_obj (platform, device, obj_tmp); } return TRUE; @@ -699,40 +706,56 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) { NMFakePlatformLink *master = link_get (platform, master_idx); NMFakePlatformLink *slave = link_get (platform, slave_idx); + nm_auto_nmpobj NMPObject *obj_tmp = NULL; g_return_val_if_fail (master, FALSE); g_return_val_if_fail (slave, FALSE); - if (slave->link.master != master->link.ifindex) + if (slave->obj->link.master != master->obj->link.ifindex) return FALSE; - slave->link.master = 0; - - link_changed (platform, slave, TRUE); - link_changed (platform, master, TRUE); - + obj_tmp = nmp_object_clone (slave->obj, FALSE); + obj_tmp->link.master = 0; + link_set_obj (platform, slave, obj_tmp); return TRUE; } +struct vlan_add_data { + guint32 vlan_flags; + int parent; + int vlan_id; +}; + +static void +_vlan_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const struct vlan_add_data *d = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; + + obj_tmp = (NMPObject *) device->obj; + + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + lnk->lnk_vlan.id = d->vlan_id; + lnk->lnk_vlan.flags = d->vlan_flags; + + obj_tmp->link.parent = d->parent; + obj_tmp->_link.netlink.lnk = lnk; +} + static gboolean vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, const NMPlatformLink **out_link) { - NMFakePlatformLink *device; + const struct vlan_add_data d = { + .parent = parent, + .vlan_id = vlan_id, + .vlan_flags = vlan_flags, + }; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, NULL, 0, out_link)) - return FALSE; - - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); - - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); - - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - device->lnk->lnk_vlan.id = vlan_id; - device->link.parent = parent; - - if (out_link) - *out_link = &device->link; + link_add_one (platform, name, NM_LINK_TYPE_VLAN, + _vlan_add_prepare, &d, out_link); return TRUE; } @@ -751,53 +774,76 @@ link_vlan_change (NMPlatform *platform, return FALSE; } +static void +_vxlan_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const NMPlatformLnkVxlan *props = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; + + obj_tmp = (NMPObject *) device->obj; + + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + lnk->lnk_vxlan = *props; + + obj_tmp->link.parent = props->parent_ifindex; + obj_tmp->_link.netlink.lnk = lnk; +} + static gboolean link_vxlan_add (NMPlatform *platform, const char *name, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link) { - NMFakePlatformLink *device; - - if (!link_add (platform, name, NM_LINK_TYPE_VXLAN, NULL, NULL, 0, out_link)) - return FALSE; - - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); - - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); - - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); - device->lnk->lnk_vxlan = *props; - device->link.parent = props->parent_ifindex; - - if (out_link) - *out_link = &device->link; + link_add_one (platform, name, NM_LINK_TYPE_VXLAN, + _vxlan_add_prepare, props, out_link); return TRUE; } +struct infiniband_add_data { + int parent; + int p_key; +}; + +static void +_infiniband_add_prepare (NMPlatform *platform, + NMFakePlatformLink *device, + gconstpointer user_data) +{ + const struct infiniband_add_data *d = user_data; + NMPObject *obj_tmp; + NMPObject *lnk; + + obj_tmp = (NMPObject *) device->obj; + + lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); + lnk->lnk_infiniband.p_key = d->p_key; + lnk->lnk_infiniband.mode = "datagram"; + + obj_tmp->link.parent = d->parent; + obj_tmp->_link.netlink.lnk = lnk; +} + static gboolean infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) { - NMFakePlatformLink *device, *parent_device; + NMFakePlatformLink *parent_device; char name[IFNAMSIZ]; + const struct infiniband_add_data d = { + .parent = parent, + .p_key = p_key, + }; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); - nm_utils_new_infiniband_name (name, parent_device->link.name, p_key); + nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); - if (!link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, NULL, 0, out_link)) - return FALSE; - - device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (!device->lnk, FALSE); - - device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - device->lnk->lnk_infiniband.p_key = p_key; - device->lnk->lnk_infiniband.mode = "datagram"; - device->link.parent = parent; + link_add_one (platform, name, NM_LINK_TYPE_INFINIBAND, + _infiniband_add_prepare, &d, out_link); return TRUE; } @@ -810,7 +856,7 @@ infiniband_partition_delete (NMPlatform *platform, int parent, int p_key) parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); - nm_utils_new_infiniband_name (name, parent_device->link.name, p_key); + nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); return link_delete (platform, nm_platform_link_get_ifindex (platform, name)); } @@ -821,7 +867,7 @@ wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabiliti g_return_val_if_fail (device, FALSE); - if (device->link.type != NM_LINK_TYPE_WIFI) + if (device->obj->link.type != NM_LINK_TYPE_WIFI) return FALSE; if (caps) { @@ -911,59 +957,25 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) /*****************************************************************************/ -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) +static gboolean +ipx_address_add (NMPlatform *platform, int addr_family, const NMPlatformObject *address) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *addresses; - NMPlatformIP4Address *address; - int count = 0, i; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPCacheOpsType cache_op; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCache *cache = nm_platform_get_cache (platform); - /* Count addresses */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address && address->ifindex == ifindex) - count++; - } + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count); + obj = nmp_object_new (addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + address); - /* Fill addresses */ - for (i = 0; i < priv->ip4_addresses->len; i++) { - address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address && address->ifindex == ifindex) - g_array_append_val (addresses, *address); - } - - return addresses; -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *addresses; - NMPlatformIP6Address *address; - int count = 0, i; - - /* Count addresses */ - for (i = 0; i < priv->ip6_addresses->len; i++) { - address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address && address->ifindex == ifindex) - count++; - } - - addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count); - - /* Fill addresses */ - count = 0; - for (i = 0; i < priv->ip6_addresses->len; i++) { - address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address && address->ifindex == ifindex) - g_array_append_val (addresses, *address); - } - - return addresses; + cache_op = nmp_cache_update_netlink (cache, obj, &obj_old, &obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + return TRUE; } static gboolean @@ -977,9 +989,7 @@ ip4_address_add (NMPlatform *platform, guint32 flags, const char *label) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMPlatformIP4Address address; - int i; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -994,28 +1004,7 @@ ip4_address_add (NMPlatform *platform, if (label) g_strlcpy (address.label, label, sizeof (address.label)); - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - gboolean changed; - - if ( item->ifindex != address.ifindex - || item->address != address.address - || item->plen != address.plen - || !_ip4_address_equal_peer_net (item->peer_address, address.peer_address, address.plen)) - continue; - - changed = !nm_platform_ip4_address_cmp (item, &address); - - memcpy (item, &address, sizeof (address)); - if (changed) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; - } - - g_array_append_val (priv->ip4_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; + return ipx_address_add (platform, AF_INET, (const NMPlatformObject *) &address); } static gboolean @@ -1028,9 +1017,7 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint32 flags) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); NMPlatformIP6Address address; - int i; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; @@ -1043,379 +1030,253 @@ ip6_address_add (NMPlatform *platform, address.preferred = preferred; address.n_ifa_flags = flags; - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - gboolean changed; + return ipx_address_add (platform, AF_INET6, (const NMPlatformObject *) &address); +} - if ( item->ifindex != address.ifindex - || !IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) - continue; +static gboolean +ipx_address_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer addr, + const guint8 *plen, + gconstpointer peer_addr) +{ + gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + NMDedupMultiIter iter; + const NMPObject *o = NULL; + guint i; + guint32 peer_addr_i; - changed = !nm_platform_ip6_address_cmp (item, &address); + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - memcpy (item, &address, sizeof (address)); - if (changed) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; + peer_addr_i = peer_addr ? *((guint32 *) peer_addr) : 0; + + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + 0), + &o) { + const NMPObject *obj_old = NULL; + + if (addr_family == AF_INET) { + const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (o); + + if ( address->ifindex != ifindex + || (addr && address->address != *((guint32 *) addr)) + || (plen && address->plen != *plen) + || ( peer_addr + && (((peer_addr_i ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) != 0))) + continue; + } else { + const NMPlatformIP6Address *address = NMP_OBJECT_CAST_IP6_ADDRESS (o); + + g_assert (!peer_addr); + if ( address->ifindex != ifindex + || (addr && !IN6_ARE_ADDR_EQUAL (&address->address, addr)) + || (plen && address->plen != *plen)) + continue; + } + + if (nmp_cache_remove (nm_platform_get_cache (platform), + o, + TRUE, + &obj_old) != NMP_CACHE_OPS_REMOVED) + g_assert_not_reached (); + g_assert (obj_old); + g_ptr_array_add (objs, (gpointer) obj_old); } - g_array_append_val (priv->ip6_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, (int) NM_PLATFORM_SIGNAL_ADDED); - + for (i = 0; i < objs->len; i++) { + nm_platform_cache_update_emit_signal (platform, + NMP_CACHE_OPS_REMOVED, + objs->pdata[i], + NULL); + } return TRUE; } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && address->address == addr - && ((peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (plen)) == 0) { - NMPlatformIP4Address deleted_address; - - memcpy (&deleted_address, address, sizeof (deleted_address)); - memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, (int) NM_PLATFORM_SIGNAL_REMOVED); - return TRUE; - } - } - - return TRUE; + return ipx_address_delete (platform, AF_INET, ifindex, &addr, &plen, &peer_address); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { - NMPlatformIP6Address deleted_address; - - memcpy (&deleted_address, address, sizeof (deleted_address)); - memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, (int) NM_PLATFORM_SIGNAL_REMOVED); - return TRUE; - } - } - - return TRUE; -} - -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_addresses->len; i++) { - NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - - if ( address->ifindex == ifindex - && address->plen == plen - && address->address == addr - && _ip4_address_equal_peer_net (address->peer_address, peer_address, plen)) - return address; - } - - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip6_addresses->len; i++) { - NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - - if ( address->ifindex == ifindex - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) - return address; - } - - return NULL; + return ipx_address_delete (platform, AF_INET6, ifindex, &addr, &plen, NULL); } /*****************************************************************************/ -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) +static gboolean +ipx_route_delete (NMPlatform *platform, + int addr_family, + int ifindex, + gconstpointer network, + const guint8 *plen, + const guint32 *metric) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *routes; - NMPlatformIP4Route *route; + gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + NMDedupMultiIter iter; + const NMPObject *o = NULL; guint i; - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + 0), + &o) { + const NMPObject *obj_old = NULL; - /* Fill routes */ - for (i = 0; i < priv->ip4_routes->len; i++) { - route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - if (route && (!ifindex || route->ifindex == ifindex)) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) - g_array_append_val (routes, *route); - } else { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - g_array_append_val (routes, *route); - } + if (addr_family == AF_INET) { + const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE (o); + + if ( route->ifindex != ifindex + || (network && route->network != *((guint32 *) network)) + || (plen && route->plen != *plen) + || (metric && route->metric != *metric)) + continue; + } else { + const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE (o); + + if ( route->ifindex != ifindex + || (network && !IN6_ARE_ADDR_EQUAL (&route->network, network)) + || (plen && route->plen != *plen) + || (metric && route->metric != *metric)) + continue; } + + if (nmp_cache_remove (nm_platform_get_cache (platform), + o, + TRUE, + &obj_old) != NMP_CACHE_OPS_REMOVED) + g_assert_not_reached (); + g_assert (obj_old); + g_ptr_array_add (objs, (gpointer) obj_old); } - return routes; -} - -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - GArray *routes; - NMPlatformIP6Route *route; - guint i; - - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - /* Fill routes */ - for (i = 0; i < priv->ip6_routes->len; i++) { - route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (route && (!ifindex || route->ifindex == ifindex)) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) - g_array_append_val (routes, *route); - } else { - if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - g_array_append_val (routes, *route); - } - } + for (i = 0; i < objs->len; i++) { + nm_platform_cache_update_emit_signal (platform, + NMP_CACHE_OPS_REMOVED, + objs->pdata[i], + NULL); } - - return routes; + return TRUE; } static gboolean ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - NMPlatformIP4Route deleted_route; - - if ( route->ifindex != ifindex - || route->network != network - || route->plen != plen - || route->metric != metric) - continue; - - memcpy (&deleted_route, route, sizeof (deleted_route)); - g_array_remove_index (priv->ip4_routes, i); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, (int) NM_PLATFORM_SIGNAL_REMOVED); - } - - return TRUE; + return ipx_route_delete (platform, AF_INET, ifindex, &network, &plen, &metric); } static gboolean ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - metric = nm_utils_ip6_route_metric_normalize (metric); + return ipx_route_delete (platform, AF_INET6, ifindex, &network, &plen, &metric); +} - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - NMPlatformIP6Route deleted_route; +static gboolean +ipx_route_add (NMPlatform *platform, int addr_family, const NMPlatformObject *route) +{ + NMDedupMultiIter iter; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPlatformIPRoute *rt; + NMPCacheOpsType cache_op; + const NMPObject *o = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCache *cache = nm_platform_get_cache (platform); + gboolean has_gateway = FALSE; + NMPlatformIP4Route *rt4 = NULL; + NMPlatformIP6Route *rt6 = NULL; - if ( route->ifindex != ifindex - || !IN6_ARE_ADDR_EQUAL (&route->network, &network) - || route->plen != plen - || route->metric != metric) - continue; + g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - memcpy (&deleted_route, route, sizeof (deleted_route)); - g_array_remove_index (priv->ip6_routes, i); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, (int) NM_PLATFORM_SIGNAL_REMOVED); + obj = nmp_object_new (addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + route); + rt = &obj->ip_route; + rt->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt->rt_source); + + if (addr_family == AF_INET) { + rt4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); + rt4->scope_inv = nm_platform_route_scope_inv (rt4->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK); + rt4->network = nm_utils_ip4_address_clear_host_address (rt4->network, rt4->plen); + if (rt4->gateway) + has_gateway = TRUE; + } else { + rt6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); + rt->metric = nm_utils_ip6_route_metric_normalize (rt->metric); + nm_utils_ip6_address_clear_host_address (&rt6->network, &rt6->network, rt->plen); + if (!IN6_IS_ADDR_UNSPECIFIED (&rt6->gateway)) + has_gateway = TRUE; } + if (has_gateway) { + gboolean has_route_to_gw = FALSE; + + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_addrroute (platform, + NMP_OBJECT_GET_TYPE (obj), + 0), + &o) { + if (addr_family == AF_INET) { + const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE (o); + guint32 n = nm_utils_ip4_address_clear_host_address (item->network, item->plen); + guint32 g = nm_utils_ip4_address_clear_host_address (rt4->gateway, item->plen); + + if ( rt->ifindex == item->ifindex + && n == g) { + has_route_to_gw = TRUE; + break; + } + } else { + const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE (o); + + if ( rt->ifindex == item->ifindex + && nm_utils_ip6_address_same_prefix (&rt6->gateway, &item->network, item->plen)) { + has_route_to_gw = TRUE; + break; + } + } + } + if (!has_route_to_gw) { + if (addr_family == AF_INET) { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", + rt->ifindex, nm_utils_inet4_ntop (rt4->network, NULL), rt->plen, rt->metric); + } else { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", + rt->ifindex, nm_utils_inet6_ntop (&rt6->network, NULL), rt->plen, rt->metric); + } + return FALSE; + } + } + + cache_op = nmp_cache_update_netlink (cache, obj, &obj_old, &obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); return TRUE; } static gboolean ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP4Route rt = *route; - guint i; - - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - rt.network = nm_utils_ip4_address_clear_host_address (rt.network, rt.plen); - rt.scope_inv = nm_platform_route_scope_inv (rt.gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK); - - if (rt.gateway) { - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, - NMPlatformIP4Route, i); - guint32 gate = ntohl (item->network) >> (32 - item->plen); - guint32 host = ntohl (rt.gateway) >> (32 - item->plen); - - if (rt.ifindex == item->ifindex && gate == host) - break; - } - if (i == priv->ip4_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - rt.ifindex, nm_utils_inet4_ntop (rt.network, NULL), rt.plen, rt.metric); - return FALSE; - } - } - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (item->network != rt.network) - continue; - if (item->plen != rt.plen) - continue; - if (item->metric != rt.metric) - continue; - - if (item->ifindex != rt.ifindex) { - ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); - i--; - continue; - } - - memcpy (item, &rt, sizeof (rt)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; - } - - g_array_append_val (priv->ip4_routes, rt); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; + return ipx_route_add (platform, AF_INET, (const NMPlatformObject *) route); } static gboolean ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) { - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP6Route rt = *route; - guint i; - - rt.metric = nm_utils_ip6_route_metric_normalize (rt.metric); - rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); - nm_utils_ip6_address_clear_host_address (&rt.network, &rt.network, rt.plen); - - if (!IN6_IS_ADDR_UNSPECIFIED (&rt.gateway)) { - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, - NMPlatformIP6Route, i); - guint8 gate_bits = rt.gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - - if ( rt.ifindex == item->ifindex - && memcmp (&rt.gateway, &item->network, item->plen / 8) == 0 - && gate_bits == host_bits) - break; - } - if (i == priv->ip6_routes->len) { - nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - rt.ifindex, nm_utils_inet6_ntop (&rt.network, NULL), rt.plen, rt.metric); - return FALSE; - } - } - - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (!IN6_ARE_ADDR_EQUAL (&item->network, &rt.network)) - continue; - if (item->plen != rt.plen) - continue; - if (item->metric != rt.metric) - continue; - - if (item->ifindex != rt.ifindex) { - ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); - i--; - continue; - } - - memcpy (item, &rt, sizeof (rt)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); - return TRUE; - } - - g_array_append_val (priv->ip6_routes, rt); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, - rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); - - return TRUE; -} - -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - for (i = 0; i < priv->ip4_routes->len; i++) { - NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - - if (route->ifindex == ifindex - && route->network == network - && route->plen == plen - && route->metric == metric) - return route; - } - - return NULL; -} - -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - int i; - - metric = nm_utils_ip6_route_metric_normalize (metric); - - for (i = 0; i < priv->ip6_routes->len; i++) { - NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - - if (route->ifindex == ifindex - && IN6_ARE_ADDR_EQUAL (&route->network, &network) - && route->plen == plen - && route->metric == metric) - return route; - } - - return NULL; + return ipx_route_add (platform, AF_INET6, (const NMPlatformObject *) route); } /*****************************************************************************/ @@ -1427,10 +1288,6 @@ nm_fake_platform_init (NMFakePlatform *fake_platform) priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); priv->links = g_array_new (TRUE, TRUE, sizeof (NMFakePlatformLink)); - priv->ip4_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Address)); - priv->ip6_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Address)); - priv->ip4_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); - priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); } void @@ -1444,9 +1301,6 @@ nm_fake_platform_setup (void) nm_platform_setup (platform); - /* skip zero element */ - link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, NULL, 0, NULL); - /* add loopback interface */ link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, NULL, 0, NULL); @@ -1467,13 +1321,9 @@ finalize (GObject *object) NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_free (device->udi); - g_clear_pointer (&device->lnk, nmp_object_unref); + g_clear_pointer (&device->obj, nmp_object_unref); } g_array_unref (priv->links); - g_array_unref (priv->ip4_addresses); - g_array_unref (priv->ip6_addresses); - g_array_unref (priv->ip4_routes); - g_array_unref (priv->ip6_routes); G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object); } @@ -1489,15 +1339,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_get_udi = link_get_udi; @@ -1541,19 +1384,11 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; platform_class->ip6_route_add = ip6_route_add; platform_class->ip4_route_delete = ip4_route_delete; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 8d4df98616..6a4210de90 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -48,6 +48,7 @@ #include "nmp-object.h" #include "nmp-netns.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" #include "nm-utils/unaligned.h" @@ -255,10 +256,12 @@ static void delayed_action_schedule (NMPlatform *platform, DelayedActionType act static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name); static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type); -static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); -static void cache_prune_candidates_prune (NMPlatform *platform); +static void cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); +static void cache_prune_all (NMPlatform *platform); static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); -static void ASSERT_NETNS_CURRENT (NMPlatform *platform); /*****************************************************************************/ @@ -628,7 +631,7 @@ _linktype_get_type (NMPlatform *platform, { guint i; - ASSERT_NETNS_CURRENT (platform); + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (platform); nm_assert (ifname); if (completed_from_cache) { @@ -1680,7 +1683,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr * Also, sometimes the info-data is missing for updates. In this case * we want to keep the previously received lnk_data. */ nmp_object_unref (lnk_data); - lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); + lnk_data = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk); } if (address_complete_from_cache) obj->link.addr = link_cached->link.addr; @@ -2570,11 +2573,12 @@ typedef struct { guint32 nlh_seq_last_handled; #endif guint32 nlh_seq_last_seen; - NMPCache *cache; GIOChannel *event_channel; guint event_id; - gboolean sysctl_get_warned; + bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + + bool sysctl_get_warned; GHashTable *sysctl_get_prev_values; NMUdevClient *udev_client; @@ -2595,8 +2599,6 @@ typedef struct { gint is_handling; } delayed_action; - GHashTable *prune_candidates; - GHashTable *wifi_data; } NMLinuxPlatformPrivate; @@ -2616,8 +2618,15 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) NMPlatform * nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) { + gboolean use_udev = FALSE; + + if ( nmp_netns_is_initial () + && access ("/sys", W_OK) == 0) + use_udev = TRUE; + return g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, + NM_PLATFORM_USE_UDEV, use_udev, NM_PLATFORM_NETNS_SUPPORT, netns_support, NULL); } @@ -2628,13 +2637,6 @@ nm_linux_platform_setup (void) nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); } -static void -ASSERT_NETNS_CURRENT (NMPlatform *platform) -{ - nm_assert (NM_IS_LINUX_PLATFORM (platform)); - nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ())); -} - /*****************************************************************************/ #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ @@ -2913,86 +2915,6 @@ process_events (NMPlatform *platform) /*****************************************************************************/ -#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ - ({ \ - NMPCacheId _cache_id; \ - \ - ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ - nmp_cache_id_init_object_type (&_cache_id, (obj_type), (visible_only)), \ - NULL)); \ - }) - -/*****************************************************************************/ - -static void -do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible) -{ - gboolean is_visible; - NMPObject obj_clone; - const NMPClass *klass; - - nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); - - nm_assert (obj || cache_op == NMP_CACHE_OPS_UNCHANGED); - nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - - ASSERT_NETNS_CURRENT (platform); - - switch (cache_op) { - case NMP_CACHE_OPS_ADDED: - if (!nmp_object_is_visible (obj)) - return; - break; - case NMP_CACHE_OPS_UPDATED: - is_visible = nmp_object_is_visible (obj); - if (!was_visible && is_visible) - cache_op = NMP_CACHE_OPS_ADDED; - else if (was_visible && !is_visible) { - /* This is a bit ugly. The object was visible and changed in a way that it became invisible. - * We raise a removed signal, but contrary to a real 'remove', @obj is already changed to be - * different from what it was when the user saw it the last time. - * - * The more correct solution would be to have cache_pre_hook() create a clone of the original - * value before it was changed to become invisible. - * - * But, don't bother. Probably nobody depends on the original values and only cares about the - * id properties (which are still correct). - */ - cache_op = NMP_CACHE_OPS_REMOVED; - } else if (!is_visible) - return; - break; - case NMP_CACHE_OPS_REMOVED: - if (!was_visible) - return; - break; - default: - g_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); - return; - } - - klass = NMP_OBJECT_GET_CLASS (obj); - - _LOGt ("emit signal %s %s: %s", - klass->signal_type, - nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - /* don't expose @obj directly, but clone the public fields. A signal handler might - * call back into NMPlatform which could invalidate (or modify) @obj. */ - memcpy (&obj_clone.object, &obj->object, klass->sizeof_public); - g_signal_emit (platform, - _nm_platform_signal_id_get (klass->signal_type_id), - 0, - (int) klass->obj_type, - obj_clone.object.ifindex, - &obj_clone.object, - (int) cache_op); -} - -/*****************************************************************************/ - _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObjectType, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_LINK, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS), @@ -3163,13 +3085,15 @@ delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, static void delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + cache_op = nmp_cache_update_link_master_connected (nm_platform_get_cache (platform), master_ifindex, &obj_old, &obj_new); + if (cache_op == NMP_CACHE_OPS_UNCHANGED) + return; + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } static void @@ -3290,7 +3214,7 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) any = TRUE; priv->delayed_action.is_handling--; - cache_prune_candidates_prune (platform); + cache_prune_all (platform); return any; } @@ -3353,146 +3277,113 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, /*****************************************************************************/ static void -cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type) +cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, - nmp_cache_id_init_object_type (&cache_id, obj_type, FALSE), - priv->prune_candidates); - _LOGt ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name, - priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0); -} - -static void -cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj) -{ - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - if (!priv->prune_candidates) - priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - if (_LOGt_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj)); -} - -static void -cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj) -{ - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - if (priv->prune_candidates) { - if (_LOGt_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_remove (priv->prune_candidates, obj); - } -} - -static void -cache_prune_candidates_prune (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GHashTable *prune_candidates; - GHashTableIter iter; + NMDedupMultiIter iter; const NMPObject *obj; - gboolean was_visible; NMPCacheOpsType cache_op; + NMPLookup lookup; + NMPCache *cache = nm_platform_get_cache (platform); - if (!priv->prune_candidates) - return; + nmp_lookup_init_obj_type (&lookup, + obj_type); + nm_dedup_multi_iter_init (&iter, + nmp_cache_lookup (cache, + &lookup)); + while (nm_dedup_multi_iter_next (&iter)) { + if (iter.current->dirty) { + nm_auto_nmpobj const NMPObject *obj_old = NULL; - prune_candidates = priv->prune_candidates; - priv->prune_candidates = NULL; - - g_hash_table_iter_init (&iter, prune_candidates); - while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) { - nm_auto_nmpobj NMPObject *obj_cache = NULL; - - _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + obj = iter.current->obj; + _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + cache_op = nmp_cache_remove (cache, obj, TRUE, &obj_old); + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_old, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, NULL); + } } - - g_hash_table_unref (prune_candidates); } static void -cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +cache_prune_all (NMPlatform *platform) { - NMPlatform *platform = user_data; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionType iflags, action_type; + + action_type = DELAYED_ACTION_TYPE_REFRESH_ALL; + FOR_EACH_DELAYED_ACTION (iflags, action_type) { + bool *p = &priv->pruning[delayed_action_refresh_all_to_idx (iflags)]; + + if (*p) { + *p = FALSE; + cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags)); + } + } +} + +static void +cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) +{ const NMPClass *klass; char str_buf[sizeof (_nm_utils_to_string_buffer)]; char str_buf2[sizeof (_nm_utils_to_string_buffer)]; + NMPCache *cache = nm_platform_get_cache (platform); - nm_assert (old || new); - nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); - nm_assert (ops_type != NMP_CACHE_OPS_ADDED || (old == NULL && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old))); - nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old)); - nm_assert (!old || !new || NMP_OBJECT_GET_CLASS (old) == NMP_OBJECT_GET_CLASS (new)); + ASSERT_nmp_cache_ops (cache, cache_op, obj_old, obj_new); - klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new); + if (cache_op == NMP_CACHE_OPS_UNCHANGED) + return; - nm_assert (klass == (new ? NMP_OBJECT_GET_CLASS (new) : NMP_OBJECT_GET_CLASS (old))); + klass = obj_old ? NMP_OBJECT_GET_CLASS (obj_old) : NMP_OBJECT_GET_CLASS (obj_new); _LOGt ("update-cache-%s: %s: %s%s%s", klass->obj_type_name, - (ops_type == NMP_CACHE_OPS_UPDATED + (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE" - : (ops_type == NMP_CACHE_OPS_REMOVED + : (cache_op == NMP_CACHE_OPS_REMOVED ? "REMOVE" - : (ops_type == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), - (ops_type != NMP_CACHE_OPS_ADDED - ? nmp_object_to_string (old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) - : nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), - (ops_type == NMP_CACHE_OPS_UPDATED) ? " -> " : "", - (ops_type == NMP_CACHE_OPS_UPDATED - ? nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) + : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), + (cache_op != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string (obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) + : nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), + (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (cache_op == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) : "")); switch (klass->obj_type) { case NMP_OBJECT_TYPE_LINK: { /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ - if ( old - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, old->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (old->link.master)); - if ( new - && (!old || old->link.master != new->link.master) - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, new->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.master)); + if ( obj_old + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_old->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_old->link.master)); + if ( obj_new + && (!obj_old || obj_old->link.master != obj_new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_new->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.master)); } { /* check whether we are about to change a master link that needs toggling connected state. */ - if ( new /* <-- nonsensical, make coverity happy */ - && nmp_cache_link_connected_needs_toggle (cache, new, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.ifindex)); + if ( obj_new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle (cache, obj_new, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.ifindex)); } { int ifindex = 0; /* if we remove a link (from netlink), we must refresh the addresses and routes */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && !new->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && !obj_new->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink != obj_old->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { delayed_action_schedule (platform, @@ -3511,40 +3402,40 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP * Currently, kernel misses to sent us a notification in this case * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && !new->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && !obj_new->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { - const NMPlatformLink *const *links; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *l; - links = cache_lookup_all_objects (NMPlatformLink, platform, NMP_OBJECT_TYPE_LINK, FALSE); - if (links) { - for (; *links; links++) { - const NMPlatformLink *l = (*links); - - if (l->parent == ifindex) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); - } + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, &lookup), + &l) { + if (l->parent == ifindex) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); } } } { /* if a link goes down, we must refresh routes */ - if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink - && ( ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_UP)) - || ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP)))) { + if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink + && ( ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_UP)) + || ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I * think kernel does send RTM_DELROUTE events for IPv6 routes, so * we might not need to refresh IPv6 routes. */ @@ -3554,17 +3445,17 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP NULL); } } - if ( NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) - && (new && new->_link.netlink.is_in_netlink) - && (!old || !old->_link.netlink.is_in_netlink)) + if ( NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) + && (obj_new && obj_new->_link.netlink.is_in_netlink) + && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { - if (!new->_link.netlink.lnk) { + if (!obj_new->_link.netlink.lnk) { /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that * kernel didn't send this notification, thus when we first learn about a link * that lacks an lnk_data we re-request it again. * * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - switch (new->link.type) { + switch (obj_new->link.type) { case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_IP6TNL: case NM_LINK_TYPE_INFINIBAND: @@ -3575,23 +3466,23 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP case NM_LINK_TYPE_VXLAN: delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); break; default: break; } } - if ( new->link.type == NM_LINK_TYPE_VETH - && new->link.parent == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_VETH + && obj_new->link.parent == 0) { /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). * Request it again. */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } - if ( new->link.type == NM_LINK_TYPE_ETHERNET - && new->link.addr.len == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET + && obj_new->link.addr.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -3601,7 +3492,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } } { @@ -3609,14 +3500,14 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP int ifindex1 = 0, ifindex2 = 0; gboolean changed_master, changed_connected; - changed_master = (new && new->_link.netlink.is_in_netlink && new->link.master > 0 ? new->link.master : 0) - != (old && old->_link.netlink.is_in_netlink && old->link.master > 0 ? old->link.master : 0); - changed_connected = (new && new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP) : 2) - != (old && old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) : 2); + changed_master = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 ? obj_new->link.master : 0) + != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 ? obj_old->link.master : 0); + changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP) : 2) + != (obj_old && obj_old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) : 2); if (changed_master || changed_connected) { - ifindex1 = (old && old->_link.netlink.is_in_netlink && old->link.master > 0) ? old->link.master : 0; - ifindex2 = (new && new->_link.netlink.is_in_netlink && new->link.master > 0) ? new->link.master : 0; + ifindex1 = (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) ? obj_old->link.master : 0; + ifindex2 = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) ? obj_new->link.master : 0; if (ifindex1 > 0) delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); @@ -3625,19 +3516,19 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP } } { - if ( ( (ops_type == NMP_CACHE_OPS_REMOVED) - || ( (ops_type == NMP_CACHE_OPS_UPDATED) - && new - && !new->_link.netlink.is_in_netlink)) - && old - && old->_link.netlink.is_in_netlink - && old->link.master) { + if ( ( (cache_op == NMP_CACHE_OPS_REMOVED) + || ( (cache_op == NMP_CACHE_OPS_UPDATED) + && obj_new + && !obj_new->_link.netlink.is_in_netlink)) + && obj_old + && obj_old->_link.netlink.is_in_netlink + && obj_old->link.master) { /* sometimes we receive a wrong RTM_DELLINK message when unslaving * a device. Refetch the link again to check whether the device * is really gone. * * https://bugzilla.redhat.com/show_bug.cgi?id=1285719#c2 */ - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (old->link.ifindex)); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (obj_old->link.ifindex)); } } break; @@ -3646,7 +3537,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP { /* Address deletion is sometimes accompanied by route deletion. We need to * check all routes belonging to the same interface. */ - if (ops_type == NMP_CACHE_OPS_REMOVED) { + if (cache_op == NMP_CACHE_OPS_REMOVED) { delayed_action_schedule (platform, (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES @@ -3664,14 +3555,10 @@ static void cache_post (NMPlatform *platform, struct nlmsghdr *msghdr, NMPCacheOpsType cache_op, - NMPObject *obj, - NMPObject *obj_cache) + const NMPObject *obj, + const NMPObject *obj_old, + const NMPObject *obj_new) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!obj_cache || nmp_object_id_equal (obj, obj_cache)); - if (msghdr->nlmsg_type == RTM_NEWROUTE) { DelayedActionType action_type; @@ -3679,7 +3566,7 @@ cache_post (NMPlatform *platform, ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; if ( !delayed_action_refresh_all_in_progress (platform, action_type) - && nmp_cache_find_other_route_for_same_destination (priv->cache, obj)) { + && nmp_cache_find_other_route_for_same_destination (nm_platform_get_cache (platform), obj)) { /* via `iproute route change` the user can update an existing route which effectively * means that a new object (with a different ID) comes into existance, replacing the * old on. In other words, as the ID of the object changes, we really see a new @@ -3739,8 +3626,13 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha _LOGD ("do-request-link: %d %s", ifindex, name ? name : ""); if (ifindex > 0) { - cache_prune_candidates_record_one (platform, - (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); + const NMDedupMultiEntry *entry; + + entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); + if (entry) { + priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE; + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } } event_handler_read_netlink (platform, FALSE); @@ -3772,7 +3664,9 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; FOR_EACH_DELAYED_ACTION (iflags, action_type) { - cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags)); + priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE; + nmp_cache_dirty_set_all (nm_platform_get_cache (platform), + delayed_action_refresh_to_object_type (iflags)); } FOR_EACH_DELAYED_ACTION (iflags, action_type) { @@ -3896,14 +3790,12 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu static void event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_nmpobj NMPObject *obj = NULL; - nm_auto_nmpobj NMPObject *obj_cache = NULL; NMPCacheOpsType cache_op; struct nlmsghdr *msghdr; char buf_nlmsg_type[16]; gboolean id_only = FALSE; - gboolean was_visible; + NMPCache *cache = nm_platform_get_cache (platform); msghdr = nlmsg_hdr (msg); @@ -3919,7 +3811,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event id_only = TRUE; } - obj = nmp_object_new_from_nl (platform, priv->cache, msg, id_only); + obj = nmp_object_new_from_nl (platform, cache, msg, id_only); if (!obj) { _LOGT ("event-notification: %s, seq %u: ignore", _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), @@ -3932,143 +3824,36 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event msghdr->nlmsg_seq, nmp_object_to_string (obj, id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - switch (msghdr->nlmsg_type) { + { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_GETLINK: - cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); + switch (msghdr->nlmsg_type) { - cache_post (platform, msghdr, cache_op, obj, obj_cache); + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + case RTM_GETLINK: + cache_op = nmp_cache_update_netlink (cache, obj, &obj_old, &obj_new); + cache_on_change (platform, cache_op, obj_old, obj_new); + cache_post (platform, msghdr, cache_op, obj, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + break; - do_emit_signal (platform, obj_cache, cache_op, was_visible); - break; + case RTM_DELLINK: + case RTM_DELADDR: + case RTM_DELROUTE: + cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); - break; - - default: - break; + default: + break; + } } - - cache_prune_candidates_drop (platform, obj_cache); -} - -/*****************************************************************************/ - -static const NMPObject * -cache_lookup_link (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if (!nmp_object_is_visible (obj_cache)) - return NULL; - - return obj_cache; -} - -const NMPlatformObject *const* -nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL); - g_return_val_if_fail (cache_id, NULL); - - return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - cache_id, out_len); -} - -static GArray * -link_get_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - return nmp_cache_lookup_multi_to_array (priv->cache, - NMP_OBJECT_TYPE_LINK, - nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, TRUE)); -} - -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj; - - obj = cache_lookup_link (platform, ifindex); - return obj ? &obj->link : NULL; -} - -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, - const char *ifname) -{ - const NMPObject *obj = NULL; - - if (ifname && *ifname) { - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); - } - return obj ? &obj->link : NULL; -} - -struct _nm_platform_link_get_by_address_data { - gconstpointer address; - guint8 length; -}; - -static gboolean -_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) -{ - return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); -} - -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - const NMPObject *obj; - struct _nm_platform_link_get_by_address_data d = { - .address = address, - .length = length, - }; - - if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) - return NULL; - if (!address) - return NULL; - - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, NULL, TRUE, NM_LINK_TYPE_NONE, - (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); - return obj ? &obj->link : NULL; -} - -/*****************************************************************************/ - -static const NMPObject * -link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - NM_SET_OUT (out_link, &obj->link); - - if (!obj->_link.netlink.lnk) - return NULL; - if ( link_type != NM_LINK_TYPE_NONE - && ( link_type != obj->link.type - || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) - return NULL; - - return obj->_link.netlink.lnk; } /*****************************************************************************/ @@ -4080,19 +3865,19 @@ do_add_link_with_lookup (NMPlatform *platform, struct nl_msg *nlmsg, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj = NULL; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; + NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); - if (nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { + if (nmp_cache_lookup_link_full (cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { /* hm, a link with such a name already exists. Try reloading first. */ do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); + obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); if (obj) { _LOGE ("do-add-link[%s/%s]: link already exists: %s", name, @@ -4124,13 +3909,13 @@ do_add_link_with_lookup (NMPlatform *platform, wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); + obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); if (!obj) { /* either kernel signaled failure, or it signaled success and the link object * is not (yet) in the cache. Try to reload it... */ do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); + obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); } if (out_link) @@ -4141,11 +3926,11 @@ do_add_link_with_lookup (NMPlatform *platform, static gboolean do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; const NMPObject *obj; + NMPCache *cache = nm_platform_get_cache (platform); nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, @@ -4183,10 +3968,10 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * * FIXME: if the object already existed previously, we might not notice a * missing update. It's not clear how to fix that reliably without refechting * all the time. */ - obj = nmp_cache_lookup_obj (priv->cache, obj_id); + obj = nmp_cache_lookup_obj (cache, obj_id); if (!obj) { do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - obj = nmp_cache_lookup_obj (priv->cache, obj_id); + obj = nmp_cache_lookup_obj (cache, obj_id); } /* Adding is only successful, if kernel reported success *and* we have the @@ -4197,12 +3982,12 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * static gboolean do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; gboolean success = TRUE; const char *log_detail = ""; + NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); @@ -4241,13 +4026,13 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * log_detail); out: - if (!nmp_cache_lookup_obj (priv->cache, obj_id)) + if (!nmp_cache_lookup_obj (cache, obj_id)) return TRUE; /* such an object still exists in the cache. To be sure, refetch it (and * hope it's gone) */ do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - return !!nmp_cache_lookup_obj (priv->cache, obj_id); + return !!nmp_cache_lookup_obj (cache, obj_id); } static WaitForNlResponseResult @@ -4382,11 +4167,10 @@ static gboolean link_delete (NMPlatform *platform, int ifindex) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); NMPObject obj_id; const NMPObject *obj; - obj = nmp_cache_lookup_link (priv->cache, ifindex); + obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; @@ -4401,56 +4185,11 @@ link_delete (NMPlatform *platform, int ifindex) return do_delete_object (platform, &obj_id, nlmsg); } -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { - /* We could detect the @link_type. In this case the function returns - * our internel module names, which differs from rtnl_link_get_type(): - * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") - * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). - * Note that this functions is only used by NMDeviceGeneric to - * set type_description. */ - return nm_link_type_to_string (obj->link.type); - } - /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ - return obj->link.kind ?: "unknown"; -} - -static gboolean -link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPObject *link; - struct udev_device *udevice = NULL; - const char *uproperty; - - link = nmp_cache_lookup_link (priv->cache, ifindex); - if (!link) - return FALSE; - - udevice = link->_link.udev.device; - if (!udevice) - return FALSE; - - uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); - if (!uproperty) - return FALSE; - - *unmanaged = nm_udev_utils_property_as_boolean (uproperty); - return TRUE; -} - static gboolean link_refresh (NMPlatform *platform, int ifindex) { do_request_link (platform, ifindex, NULL); - return !!cache_lookup_link (platform, ifindex); + return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } static gboolean @@ -4537,7 +4276,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) static const char * link_get_udi (NMPlatform *platform, int ifindex) { - const NMPObject *obj = cache_lookup_link (platform, ifindex); + const NMPObject *obj = nm_platform_link_get_obj (platform, ifindex, TRUE); if ( !obj || !obj->_link.netlink.is_in_netlink @@ -4546,20 +4285,6 @@ link_get_udi (NMPlatform *platform, int ifindex) return udev_device_get_syspath (obj->_link.udev.device); } -static struct udev_device * -link_get_udev_device (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - /* we don't use cache_lookup_link() because this would return NULL - * if the link is not visible in libnl. For link_get_udev_device() - * we want to return whatever we have, even if the link itself - * appears invisible via other platform functions. */ - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - return obj_cache ? obj_cache->_link.udev.device : NULL; -} - static NMPlatformError link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { @@ -4624,7 +4349,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) nm_auto_pop_netns NMPNetns *netns = NULL; const NMPObject *obj; - obj = cache_lookup_link (platform, ifindex); + obj = nm_platform_link_get_obj (platform, ifindex, TRUE); /* Only ARPHRD_ETHER links can possibly support VLANs. */ if (!obj || obj->link.arptype != ARPHRD_ETHER) @@ -4694,7 +4419,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size /* workaround ENFILE which may be wrongly returned (bgo #770456). * If the MAC address is as expected, assume success? */ - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if ( obj_cache && obj_cache->link.addr.len == length && memcmp (obj_cache->link.addr.data, address, length) == 0) { @@ -5377,7 +5102,6 @@ link_vlan_change (NMPlatform *platform, const NMVlanQosMapping *egress_map, gsize n_egress_map) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj_cache; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const NMPObjectLnkVlan *lnk; @@ -5389,7 +5113,7 @@ link_vlan_change (NMPlatform *platform, char s_ingress[256]; char s_egress[256]; - obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if ( !obj_cache || !obj_cache->_link.netlink.is_in_netlink) { _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); @@ -5508,7 +5232,7 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, return FALSE; } do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, NULL, NULL); @@ -5558,7 +5282,6 @@ _infiniband_partition_action (NMPlatform *platform, int p_key, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_close int dirfd = -1; char ifname_parent[IFNAMSIZ]; const NMPObject *obj; @@ -5594,7 +5317,7 @@ _infiniband_partition_action (NMPlatform *platform, if (action == INFINIBAND_ACTION_DELETE_CHILD) return TRUE; - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL); if (out_link) *out_link = obj ? &obj->link : NULL; @@ -5732,16 +5455,15 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru static gboolean link_can_assume (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformObject *const *objs; - guint i, len; - const NMPObject *link; + NMPLookup lookup; + const NMPObject *link, *o; + NMDedupMultiIter iter; + NMPCache *cache = nm_platform_get_cache (platform); if (ifindex <= 0) return FALSE; - link = cache_lookup_link (platform, ifindex); + link = nm_platform_link_get_obj (platform, ifindex, TRUE); if (!link) return FALSE; @@ -5751,21 +5473,21 @@ link_can_assume (NMPlatform *platform, int ifindex) if (link->link.master > 0) return TRUE; - if (nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex), - NULL)) + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex); + if (nmp_cache_lookup (cache, &lookup)) return TRUE; - objs = nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), - &len); - if (objs) { - for (i = 0; i < len; i++) { - const NMPlatformIP6Address *a = (NMPlatformIP6Address *) objs[i]; - - if (!IN6_IS_ADDR_LINKLOCAL (&a->address)) - return TRUE; - } + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex); + nmp_cache_iter_for_each (&iter, + nmp_cache_lookup (cache, &lookup), + &o) { + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP6_ADDRESS); + if (!IN6_IS_ADDR_LINKLOCAL (&o->ip6_address.address)) + return TRUE; } return FALSE; } @@ -5840,33 +5562,6 @@ link_get_driver_info (NMPlatform *platform, /*****************************************************************************/ -static GArray * -ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - - return nmp_cache_lookup_multi_to_array (priv->cache, - obj_type, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, - obj_type, - ifindex)); -} - -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); -} - static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -5978,85 +5673,8 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui return do_delete_object (platform, &obj_id, nlmsg); } -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_address; - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_address; - return NULL; -} - /*****************************************************************************/ -static GArray * -ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformIPRoute *const* routes; - GArray *array; - const NMPClass *klass; - gboolean with_rtprot_kernel; - guint i, len; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - klass = nmp_class_from_type (obj_type); - - nmp_cache_id_init_routes_visible (&cache_id, - obj_type, - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT), - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT), - ifindex); - - routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - - with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass); - - if ( with_rtprot_kernel - || routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - g_array_append_vals (array, routes[i], 1); - } - return array; -} - -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ROUTE, flags); -} - -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); -} - static guint32 ip_route_get_lock_flag (NMPlatformIPRoute *route) { @@ -6142,7 +5760,6 @@ ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) static gboolean ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; NMPObject obj_id; @@ -6151,6 +5768,8 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 p nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); if (metric == 0) { + NMPCache *cache = nm_platform_get_cache (platform); + /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. * If no route with metric 0 exists, it might delete another route to the same destination. * For nm_platform_ip4_route_delete() we don't want this semantic. @@ -6159,7 +5778,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 p * delayed actions (including re-reading data from netlink). */ delayed_action_handle_all (platform, TRUE); - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) { + if (!nmp_cache_lookup_obj (cache, &obj_id)) { /* hmm... we are about to delete an IP4 route with metric 0. We must only * send the delete request if such a route really exists. Above we refreshed * the platform cache, still no such route exists. @@ -6174,7 +5793,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 p * additional expensive cache-resync. */ do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE); - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) + if (!nmp_cache_lookup_obj (cache, &obj_id)) return TRUE; } } @@ -6245,34 +5864,6 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, gu return do_delete_object (platform, &obj_id, nlmsg); } -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_route; - return NULL; -} - -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; - - metric = nm_utils_ip6_route_metric_normalize (metric); - - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_route; - return NULL; -} - /*****************************************************************************/ #define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) @@ -6633,19 +6224,19 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, struct udev_device *udevice) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_cache, &was_visible, cache_pre_hook, platform); + cache_op = nmp_cache_update_link_udev (nm_platform_get_cache (platform), ifindex, udevice, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { nm_auto_pop_netns NMPNetns *netns = NULL; + cache_on_change (platform, cache_op, obj_old, obj_new); if (!nm_platform_netns_push (platform, &netns)) return; - do_emit_signal (platform, obj_cache, cache_op, was_visible); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } } @@ -6701,7 +6292,7 @@ udev_device_removed (NMPlatform *platform, if (ifindex <= 0) { const NMPObject *obj; - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udevice); if (obj) ifindex = obj->link.ifindex; @@ -6753,22 +6344,12 @@ static void nm_linux_platform_init (NMLinuxPlatform *self) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self); - gboolean use_udev; - - use_udev = nmp_netns_is_initial () - && access ("/sys", W_OK) == 0; priv->nlh_seq_next = 1; - priv->cache = nmp_cache_new (use_udev); priv->delayed_action.list_master_connected = g_ptr_array_new (); priv->delayed_action.list_refresh_link = g_ptr_array_new (); priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); - - if (use_udev) { - priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, - handle_udev_event, self); - } } static void @@ -6782,6 +6363,11 @@ constructed (GObject *_object) nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ()); + if (nm_platform_get_use_udev (platform)) { + priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, + handle_udev_event, platform); + } + _LOGD ("create (%s netns, %s, %s udev)", !platform->_netns ? "ignore" : "use", !platform->_netns && nmp_netns_is_initial () @@ -6791,7 +6377,7 @@ constructed (GObject *_object) : nm_sprintf_bufa (100, "in netns[%p]%s", nmp_netns_get_current (), nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")), - nmp_cache_use_udev_get (priv->cache) ? "use" : "no"); + nm_platform_get_use_udev (platform) ? "use" : "no"); priv->nlh = nl_socket_alloc (); g_assert (priv->nlh); @@ -6892,8 +6478,6 @@ dispose (GObject *object) g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - g_clear_pointer (&priv->prune_candidates, g_hash_table_unref); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } @@ -6902,8 +6486,6 @@ finalize (GObject *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); - nmp_cache_free (priv->cache); - g_ptr_array_unref (priv->delayed_action.list_master_connected); g_ptr_array_unref (priv->delayed_action.list_refresh_link); g_array_unref (priv->delayed_action.list_wait_for_nl_response); @@ -6937,16 +6519,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - platform_class->link_get_unmanaged = link_get_unmanaged; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_refresh = link_refresh; @@ -6958,7 +6532,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_noarp = link_set_noarp; platform_class->link_get_udi = link_get_udi; - platform_class->link_get_udev_device = link_get_udev_device; platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; platform_class->link_set_token = link_set_token; @@ -7013,19 +6586,11 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_ipip_add = link_ipip_add; platform_class->link_sit_add = link_sit_add; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; platform_class->ip6_route_add = ip6_route_add; platform_class->ip4_route_delete = ip4_route_delete; diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index 6b66ea699c..bff6c00cc7 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -39,10 +39,4 @@ NMPlatform *nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support void nm_linux_platform_setup (void); -struct _NMPCacheId; - -const NMPlatformObject *const *nm_linux_platform_lookup (NMPlatform *platform, - const struct _NMPCacheId *cache_id, - guint *out_len); - #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h new file mode 100644 index 0000000000..b6c94baaf7 --- /dev/null +++ b/src/platform/nm-platform-private.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#ifndef __NM_PLATFORM_PRIVATE_H__ +#define __NM_PLATFORM_PRIVATE_H__ + +#include "nm-platform.h" +#include "nmp-object.h" + +NMPCache *nm_platform_get_cache (NMPlatform *self); + +#define NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform) \ + G_STMT_START { \ + NMPlatform *_platform = (platform); \ + \ + nm_assert (NM_IS_PLATFORM (_platform)); \ + nm_assert (NM_IN_SET (nm_platform_netns_get (_platform), NULL, nmp_netns_get_current ())); \ + } G_STMT_END + +void nm_platform_cache_update_emit_signal (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); + +#endif /* __NM_PLATFORM_PRIVATE_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 7f12fb743b..176f377bdb 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -32,12 +32,16 @@ #include #include #include +#include #include "nm-utils.h" #include "nm-core-internal.h" +#include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/nm-udev-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "nmp-object.h" #include "nmp-netns.h" @@ -79,12 +83,16 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 }; enum { PROP_0, PROP_NETNS_SUPPORT, + PROP_USE_UDEV, PROP_LOG_WITH_PTR, LAST_PROP, }; typedef struct _NMPlatformPrivate { + bool use_udev:1; bool log_with_ptr:1; + NMDedupMultiIndex *multi_idx; + NMPCache *cache; } NMPlatformPrivate; G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) @@ -93,6 +101,12 @@ G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) /*****************************************************************************/ +gboolean +nm_platform_get_use_udev (NMPlatform *self) +{ + return NM_PLATFORM_GET_PRIVATE (self)->use_udev; +} + gboolean nm_platform_get_log_with_ptr (NMPlatform *self) { @@ -193,6 +207,16 @@ nm_platform_get () /*****************************************************************************/ +NMDedupMultiIndex * +nm_platform_get_multi_idx (NMPlatform *self) +{ + g_return_val_if_fail (NM_IS_PLATFORM (self), NULL); + + return NM_PLATFORM_GET_PRIVATE (self)->multi_idx; +} + +/*****************************************************************************/ + /** * _nm_platform_error_to_string: * @error_code: the error code to stringify. @@ -440,8 +464,8 @@ _link_get_all_presort (gconstpointer p_a, gconstpointer p_b, gpointer sort_by_name) { - const NMPlatformLink *a = p_a; - const NMPlatformLink *b = p_b; + const NMPlatformLink *a = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_a)); + const NMPlatformLink *b = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_b)); /* Loopback always first */ if (a->ifindex == 1) @@ -463,43 +487,56 @@ _link_get_all_presort (gconstpointer p_a, /** * nm_platform_link_get_all: - * self: platform instance + * @self: platform instance + * @sort_by_name: whether to sort by name or ifindex. * * Retrieve a snapshot of configuration for all links at once. The result is - * owned by the caller and should be freed with g_array_unref(). + * owned by the caller and should be freed with g_ptr_array_unref(). */ -GArray * +GPtrArray * nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) { - GArray *links, *result; - guint i, j, nresult; - GHashTable *unseen; - NMPlatformLink *item; + gs_unref_ptrarray GPtrArray *links = NULL; + GPtrArray *result; + guint i, nresult; + gs_unref_hashtable GHashTable *unseen = NULL; + const NMPlatformLink *item; + NMPLookup lookup; _CHECK_SELF (self, klass, NULL); - links = klass->link_get_all (self); + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + links = nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, &lookup), + NULL, NULL); + if (!links) + return NULL; - if (!links || links->len == 0) - return links; + for (i = 0; i < links->len; ) { + if (!nmp_object_is_visible (links->pdata[i])) + g_ptr_array_remove_index_fast (links, i); + else + i++; + } + + if (links->len == 0) + return NULL; /* first sort the links by their ifindex or name. Below we will sort * further by moving children/slaves to the end. */ - g_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); + g_ptr_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); unseen = g_hash_table_new (g_direct_hash, g_direct_equal); for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); - + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); nm_assert (item->ifindex > 0); if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL)) nm_assert_not_reached (); } -#ifndef G_DISABLE_ASSERT +#if NM_MORE_ASSERTS /* Ensure that link_get_all returns a consistent and valid result. */ for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); if (!item->ifindex) continue; @@ -519,54 +556,75 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) #endif /* Re-order the links list such that children/slaves come after all ancestors */ - nresult = g_hash_table_size (unseen); - result = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nresult); - g_array_set_size (result, nresult); + nm_assert (g_hash_table_size (unseen) == links->len); + nresult = links->len; + result = g_ptr_array_new_full (nresult, (GDestroyNotify) nmp_object_unref); - j = 0; - do { + while (TRUE) { gboolean found_something = FALSE; guint first_idx = G_MAXUINT; for (i = 0; i < links->len; i++) { - item = &g_array_index (links, NMPlatformLink, i); + item = NMP_OBJECT_CAST_LINK (links->pdata[i]); - if (!item->ifindex) + if (!item) continue; - if (first_idx == G_MAXUINT) - first_idx = i; - g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master))) - continue; + goto skip; if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) - continue; + goto skip; g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); - g_array_index (result, NMPlatformLink, j++) = *item; - item->ifindex = 0; + g_ptr_array_add (result, links->pdata[i]); + links->pdata[i] = NULL; found_something = TRUE; + continue; +skip: + if (first_idx == G_MAXUINT) + first_idx = i; } - if (!found_something) { + if (found_something) { + if (first_idx == G_MAXUINT) + break; + } else { + nm_assert (first_idx != G_MAXUINT); /* There is a loop, pop the first (remaining) element from the list. * This can happen for veth pairs where each peer is parent of the other end. */ - item = &g_array_index (links, NMPlatformLink, first_idx); - + item = NMP_OBJECT_CAST_LINK (links->pdata[first_idx]); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); - g_array_index (result, NMPlatformLink, j++) = *item; - item->ifindex = 0; + g_ptr_array_add (result, links->pdata[first_idx]); + links->pdata[first_idx] = NULL; } - } while (j < nresult); - - g_hash_table_destroy (unseen); - g_array_free (links, TRUE); + nm_assert (result->len < nresult); + } + nm_assert (result->len == nresult); return result; } +/*****************************************************************************/ + +const NMPObject * +nm_platform_link_get_obj (NMPlatform *self, + int ifindex, + gboolean visible_only) +{ + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); + if ( !obj_cache + || ( visible_only + && !nmp_object_is_visible (obj_cache))) + return NULL; + return obj_cache; +} + +/*****************************************************************************/ + /** * nm_platform_link_get: * @self: platform instance @@ -582,11 +640,15 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) const NMPlatformLink * nm_platform_link_get (NMPlatform *self, int ifindex) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - if (ifindex > 0) - return klass->link_get (self, ifindex); - return NULL; + if (ifindex <= 0) + return NULL; + + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + return NMP_OBJECT_CAST_LINK (obj); } /** @@ -599,11 +661,27 @@ nm_platform_link_get (NMPlatform *self, int ifindex) const NMPlatformLink * nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - if (ifname && *ifname) - return klass->link_get_by_ifname (self, ifname); - return NULL; + if (!ifname || !*ifname) + return NULL; + + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + return NMP_OBJECT_CAST_LINK (obj); +} + +struct _nm_platform_link_get_by_address_data { + gconstpointer address; + guint8 length; +}; + +static gboolean +_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) +{ + return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); } /** @@ -620,15 +698,26 @@ nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length) { + const NMPObject *obj; + struct _nm_platform_link_get_by_address_data d = { + .address = address, + .length = length, + }; + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (length == 0 || address, NULL); - if (length > 0) { - if (length > NM_UTILS_HWADDR_LEN_MAX) - g_return_val_if_reached (NULL); - return klass->link_get_by_address (self, address, length); - } - return NULL; + if (length == 0) + return NULL; + + if (length > NM_UTILS_HWADDR_LEN_MAX) + g_return_val_if_reached (NULL); + if (!address) + g_return_val_if_reached (NULL); + + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + 0, NULL, TRUE, NM_LINK_TYPE_NONE, + (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); + return NMP_OBJECT_CAST_LINK (obj); } static NMPlatformError @@ -858,9 +947,26 @@ nm_platform_link_get_type (NMPlatform *self, int ifindex) const char * nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - return klass->link_get_type_name (self, ifindex); + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + + if (!obj) + return NULL; + + if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { + /* We could detect the @link_type. In this case the function returns + * our internel module names, which differs from rtnl_link_get_type(): + * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") + * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). + * Note that this functions is only used by NMDeviceGeneric to + * set type_description. */ + return nm_link_type_to_string (obj->link.type); + } + /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ + return obj->link.kind ?: "unknown"; } /** @@ -875,11 +981,26 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged) { + const NMPObject *link; + struct udev_device *udevice = NULL; + const char *uproperty; + _CHECK_SELF (self, klass, FALSE); - if (klass->link_get_unmanaged) - return klass->link_get_unmanaged (self, ifindex, unmanaged); - return FALSE; + link = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); + if (!link) + return FALSE; + + udevice = link->_link.udev.device; + if (!udevice) + return FALSE; + + uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); + if (!uproperty) + return FALSE; + + *unmanaged = nm_udev_utils_property_as_boolean (uproperty); + return TRUE; } /** @@ -1025,13 +1146,14 @@ nm_platform_link_get_udi (NMPlatform *self, int ifindex) struct udev_device * nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) { + const NMPObject *obj_cache; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, NULL); - if (klass->link_get_udev_device) - return klass->link_get_udev_device (self, ifindex); - return NULL; + obj_cache = nm_platform_link_get_obj (self, ifindex, FALSE); + return obj_cache ? obj_cache->_link.udev.device : NULL; } /** @@ -1531,13 +1653,28 @@ nm_platform_link_can_assume (NMPlatform *self, int ifindex) const NMPObject * nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) { + const NMPObject *obj; + _CHECK_SELF (self, klass, FALSE); NM_SET_OUT (out_link, NULL); g_return_val_if_fail (ifindex > 0, NULL); - return klass->link_get_lnk (self, ifindex, link_type, out_link); + obj = nm_platform_link_get_obj (self, ifindex, TRUE); + if (!obj) + return NULL; + + NM_SET_OUT (out_link, &obj->link); + + if (!obj->_link.netlink.lnk) + return NULL; + if ( link_type != NM_LINK_TYPE_NONE + && ( link_type != obj->link.type + || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) + return NULL; + + return obj->_link.netlink.lnk; } static gconstpointer @@ -2629,6 +2766,52 @@ nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean * /*****************************************************************************/ +const NMDedupMultiHeadEntry * +nm_platform_lookup (NMPlatform *self, + const NMPLookup *lookup) +{ + return nmp_cache_lookup (nm_platform_get_cache (self), + lookup); +} + +gboolean +nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data) +{ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + return obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; +} + +/** + * nm_platform_lookup_clone: + * @self: + * @lookup: + * @predicate: if given, only objects for which @predicate returns %TRUE are included + * in the result. + * @user_data: user data for @predicate + * + * Returns the result of lookup in a GPtrArray. The result array contains + * references objects from the cache, it's destroy function will unref them. + * + * The user must unref the GPtrArray, which will also unref the NMPObject + * elements. + * + * The elements in the array *must* not be modified. + * + * Returns: the result of the lookup. + */ +GPtrArray * +nm_platform_lookup_clone (NMPlatform *self, + const NMPLookup *lookup, + gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + gpointer user_data) +{ + return nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, lookup), + (NMDedupMultiFcnSelectPredicate) predicate, + user_data); +} + void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) { @@ -2648,6 +2831,20 @@ nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr) return &addr->peer_address; } +static GArray * +ipx_address_get_all (NMPlatform *self, int ifindex, NMPObjectType obj_type) +{ + NMPLookup lookup; + + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); + nmp_lookup_init_addrroute (&lookup, + obj_type, + ifindex); + return nmp_cache_lookup_to_array (nmp_cache_lookup (nm_platform_get_cache (self), &lookup), + obj_type, + FALSE /*addresses are always visible. */); +} + GArray * nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { @@ -2655,7 +2852,7 @@ nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) g_return_val_if_fail (ifindex > 0, NULL); - return klass->ip4_address_get_all (self, ifindex); + return ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); } GArray * @@ -2665,7 +2862,7 @@ nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) g_return_val_if_fail (ifindex > 0, NULL); - return klass->ip6_address_get_all (self, ifindex); + return ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); } gboolean @@ -2780,19 +2977,31 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a const NMPlatformIP4Address * nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, guint32 peer_address) { + NMPObject obj_id; + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (plen <= 32, NULL); - return klass->ip4_address_get (self, ifindex, address, plen, peer_address); + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, address, plen, peer_address); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + nm_assert (!obj || nmp_object_is_visible (obj)); + return NMP_OBJECT_CAST_IP4_ADDRESS (obj); } const NMPlatformIP6Address * nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address) { + NMPObject obj_id; + const NMPObject *obj; + _CHECK_SELF (self, klass, NULL); - return klass->ip6_address_get (self, ifindex, address); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &address); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + nm_assert (!obj || nmp_object_is_visible (obj)); + return NMP_OBJECT_CAST_IP6_ADDRESS (obj); } static const NMPlatformIP4Address * @@ -3130,26 +3339,6 @@ nm_platform_address_flush (NMPlatform *self, int ifindex) /*****************************************************************************/ -GArray * -nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex >= 0, NULL); - - return klass->ip4_route_get_all (self, ifindex, flags); -} - -GArray * -nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) -{ - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex >= 0, NULL); - - return klass->ip6_route_get_all (self, ifindex, flags); -} - /** * nm_platform_ip4_route_add: * @self: @@ -3228,17 +3417,33 @@ nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr net const NMPlatformIP4Route * nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric) { + NMPObject obj_id; + const NMPObject *obj; + _CHECK_SELF (self, klass, FALSE); - return klass->ip4_route_get (self ,ifindex, network, plen, metric); + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + if (nmp_object_is_visible (obj)) + return &obj->ip4_route; + return NULL; } const NMPlatformIP6Route * nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) { + NMPObject obj_id; + const NMPObject *obj; + _CHECK_SELF (self, klass, FALSE); - return klass->ip6_route_get (self, ifindex, network, plen, metric); + metric = nm_utils_ip6_route_metric_normalize (metric); + + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); + if (nmp_object_is_visible (obj)) + return &obj->ip6_route; + return NULL; } /*****************************************************************************/ @@ -4130,6 +4335,39 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi return c < 0 ? -1 : 1; \ } G_STMT_END +guint +nm_platform_link_hash (const NMPlatformLink *obj) +{ + guint h = 99413953; + guint8 i8; + + h = NM_HASH_COMBINE (h, obj->ifindex); + h = NM_HASH_COMBINE (h, obj->type); + h = NM_HASH_COMBINE (h, g_str_hash (obj->name)); + h = NM_HASH_COMBINE (h, obj->master); + h = NM_HASH_COMBINE (h, obj->parent); + h = NM_HASH_COMBINE (h, obj->n_ifi_flags); + h = NM_HASH_COMBINE (h, obj->connected); + h = NM_HASH_COMBINE (h, obj->mtu); + h = NM_HASH_COMBINE (h, !!obj->initialized); + h = NM_HASH_COMBINE (h, obj->arptype); + h = NM_HASH_COMBINE (h, obj->addr.len); + h = NM_HASH_COMBINE (h, obj->inet6_addr_gen_mode_inv); + if (obj->kind) + h = NM_HASH_COMBINE (h, g_str_hash (obj->kind)); + if (obj->driver) + h = NM_HASH_COMBINE (h, g_str_hash (obj->driver)); + for (i8 = 0; i8 < obj->addr.len; i8++) + h = NM_HASH_COMBINE (h, obj->addr.data[i8]); + for (i8 = 0; i8 < sizeof (obj->inet6_token); i8++) + h = NM_HASH_COMBINE (h, obj->inet6_token.id_u8[i8]); + h = NM_HASH_COMBINE (h, obj->rx_packets); + h = NM_HASH_COMBINE (h, obj->rx_bytes); + h = NM_HASH_COMBINE (h, obj->tx_packets); + h = NM_HASH_COMBINE (h, obj->tx_bytes); + return h; +} + int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) { @@ -4158,6 +4396,24 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) return 0; } +guint +nm_platform_lnk_gre_hash (const NMPlatformLnkGre *obj) +{ + guint h = 1887023311; + + h = NM_HASH_COMBINE (h, obj->parent_ifindex); + h = NM_HASH_COMBINE (h, obj->input_flags); + h = NM_HASH_COMBINE (h, obj->output_flags); + h = NM_HASH_COMBINE (h, obj->input_key); + h = NM_HASH_COMBINE (h, obj->output_key); + h = NM_HASH_COMBINE (h, obj->local); + h = NM_HASH_COMBINE (h, obj->remote); + h = NM_HASH_COMBINE (h, obj->ttl); + h = NM_HASH_COMBINE (h, obj->tos); + h = NM_HASH_COMBINE (h, !obj->path_mtu_discovery); + return h; +} + int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) { @@ -4175,6 +4431,17 @@ nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) return 0; } +guint +nm_platform_lnk_infiniband_hash (const NMPlatformLnkInfiniband *obj) +{ + guint h = 1748638583; + + h = NM_HASH_COMBINE (h, obj->p_key); + if (obj->mode) + h = NM_HASH_COMBINE (h, g_str_hash (obj->mode)); + return h; +} + int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b) { @@ -4184,6 +4451,22 @@ nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatfo return 0; } +guint +nm_platform_lnk_ip6tnl_hash (const NMPlatformLnkIp6Tnl *obj) +{ + guint h = 1651660009; + + h = NM_HASH_COMBINE (h, obj->parent_ifindex); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->local)); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->remote)); + h = NM_HASH_COMBINE (h, obj->ttl); + h = NM_HASH_COMBINE (h, obj->tclass); + h = NM_HASH_COMBINE (h, obj->encap_limit); + h = NM_HASH_COMBINE (h, obj->flow_label); + h = NM_HASH_COMBINE (h, obj->proto); + return h; +} + int nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b) { @@ -4199,6 +4482,20 @@ nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6 return 0; } +guint +nm_platform_lnk_ipip_hash (const NMPlatformLnkIpIp *obj) +{ + guint h = 861934429; + + h = NM_HASH_COMBINE (h, obj->parent_ifindex); + h = NM_HASH_COMBINE (h, obj->local); + h = NM_HASH_COMBINE (h, obj->remote); + h = NM_HASH_COMBINE (h, obj->ttl); + h = NM_HASH_COMBINE (h, obj->tos); + h = NM_HASH_COMBINE (h, obj->path_mtu_discovery); + return h; +} + int nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b) { @@ -4212,6 +4509,26 @@ nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b return 0; } +guint +nm_platform_lnk_macsec_hash (const NMPlatformLnkMacsec *obj) +{ + guint h = 226984267; + + h = NM_HASH_COMBINE (h, obj->sci); + h = NM_HASH_COMBINE_UINT64 (h, obj->icv_length); + h = NM_HASH_COMBINE_UINT64 (h, obj->cipher_suite); + h = NM_HASH_COMBINE (h, obj->window); + h = NM_HASH_COMBINE (h, obj->encoding_sa); + h = NM_HASH_COMBINE (h, obj->validation); + h = NM_HASH_COMBINE (h, obj->encrypt); + h = NM_HASH_COMBINE (h, obj->protect); + h = NM_HASH_COMBINE (h, obj->include_sci); + h = NM_HASH_COMBINE (h, obj->es); + h = NM_HASH_COMBINE (h, obj->scb); + h = NM_HASH_COMBINE (h, obj->replay_protect); + return h; +} + int nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b) { @@ -4231,15 +4548,43 @@ nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMac return 0; } +guint +nm_platform_lnk_macvlan_hash (const NMPlatformLnkMacvlan *obj) +{ + guint h = 771014989; + + h = NM_HASH_COMBINE (h, obj->mode); + h = NM_HASH_COMBINE (h, obj->no_promisc); + h = NM_HASH_COMBINE (h, obj->tap); + return h; +} + int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b) { _CMP_SELF (a, b); _CMP_FIELD (a, b, mode); - _CMP_FIELD_BOOL (a, b, no_promisc); + _CMP_FIELD (a, b, no_promisc); + _CMP_FIELD (a, b, tap); return 0; } +guint +nm_platform_lnk_sit_hash (const NMPlatformLnkSit *obj) +{ + guint h = 1690154969; + + h = NM_HASH_COMBINE (h, obj->parent_ifindex); + h = NM_HASH_COMBINE (h, obj->local); + h = NM_HASH_COMBINE (h, obj->remote); + h = NM_HASH_COMBINE (h, obj->ttl); + h = NM_HASH_COMBINE (h, obj->tos); + h = NM_HASH_COMBINE (h, obj->path_mtu_discovery); + h = NM_HASH_COMBINE (h, obj->flags); + h = NM_HASH_COMBINE (h, obj->proto); + return h; +} + int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) { @@ -4255,6 +4600,16 @@ nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) return 0; } +guint +nm_platform_lnk_vlan_hash (const NMPlatformLnkVlan *obj) +{ + guint h = 58751383; + + h = NM_HASH_COMBINE (h, obj->id); + h = NM_HASH_COMBINE (h, obj->flags); + return h; +} + int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) { @@ -4264,6 +4619,32 @@ nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b return 0; } +guint +nm_platform_lnk_vxlan_hash (const NMPlatformLnkVxlan *obj) +{ + guint h = 461041297; + + h = NM_HASH_COMBINE (h, obj->parent_ifindex); + h = NM_HASH_COMBINE (h, obj->id); + h = NM_HASH_COMBINE (h, obj->group); + h = NM_HASH_COMBINE (h, obj->local); + h = NM_HASH_COMBINE_IN6_ADDR (h, &obj->group6); + h = NM_HASH_COMBINE_IN6_ADDR (h, &obj->local6); + h = NM_HASH_COMBINE (h, obj->tos); + h = NM_HASH_COMBINE (h, obj->ttl); + h = NM_HASH_COMBINE (h, obj->learning); + h = NM_HASH_COMBINE (h, obj->ageing); + h = NM_HASH_COMBINE (h, obj->limit); + h = NM_HASH_COMBINE (h, obj->dst_port); + h = NM_HASH_COMBINE (h, obj->src_port_min); + h = NM_HASH_COMBINE (h, obj->src_port_max); + h = NM_HASH_COMBINE (h, obj->proxy); + h = NM_HASH_COMBINE (h, obj->rsc); + h = NM_HASH_COMBINE (h, obj->l2miss); + h = NM_HASH_COMBINE (h, obj->l3miss); + return h; +} + int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b) { @@ -4289,6 +4670,26 @@ nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan return 0; } +guint +nm_platform_ip4_address_hash (const NMPlatformIP4Address *obj) +{ + guint h = 469681301; + + if (obj) { + h = NM_HASH_COMBINE (h, obj->ifindex); + h = NM_HASH_COMBINE (h, obj->address); + h = NM_HASH_COMBINE (h, obj->plen); + h = NM_HASH_COMBINE (h, obj->peer_address); + h = NM_HASH_COMBINE (h, obj->addr_source); + h = NM_HASH_COMBINE (h, obj->timestamp); + h = NM_HASH_COMBINE (h, obj->lifetime); + h = NM_HASH_COMBINE (h, obj->preferred); + h = NM_HASH_COMBINE (h, obj->n_ifa_flags); + h = NM_HASH_COMBINE (h, g_str_hash (obj->label)); + } + return h; +} + int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { @@ -4306,6 +4707,25 @@ nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4A return 0; } +guint +nm_platform_ip6_address_hash (const NMPlatformIP6Address *obj) +{ + guint h = 605908909; + + if (obj) { + h = NM_HASH_COMBINE (h, obj->ifindex); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->address)); + h = NM_HASH_COMBINE (h, obj->plen); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->peer_address)); + h = NM_HASH_COMBINE (h, obj->addr_source); + h = NM_HASH_COMBINE (h, obj->timestamp); + h = NM_HASH_COMBINE (h, obj->lifetime); + h = NM_HASH_COMBINE (h, obj->preferred); + h = NM_HASH_COMBINE (h, obj->n_ifa_flags); + } + return h; +} + int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) { @@ -4326,6 +4746,37 @@ nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6A return 0; } +guint +nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj) +{ + guint h = 1228913327; + + if (obj) { + h = NM_HASH_COMBINE (h, obj->ifindex); + h = NM_HASH_COMBINE (h, obj->network); + h = NM_HASH_COMBINE (h, obj->plen); + h = NM_HASH_COMBINE (h, obj->metric); + h = NM_HASH_COMBINE (h, obj->gateway); + h = NM_HASH_COMBINE (h, obj->rt_source); + h = NM_HASH_COMBINE (h, obj->mss); + h = NM_HASH_COMBINE (h, obj->scope_inv); + h = NM_HASH_COMBINE (h, obj->pref_src); + h = NM_HASH_COMBINE (h, obj->rt_cloned); + h = NM_HASH_COMBINE (h, obj->tos); + h = NM_HASH_COMBINE (h, obj->lock_window); + h = NM_HASH_COMBINE (h, obj->lock_cwnd); + h = NM_HASH_COMBINE (h, obj->lock_initcwnd); + h = NM_HASH_COMBINE (h, obj->lock_initrwnd); + h = NM_HASH_COMBINE (h, obj->lock_mtu); + h = NM_HASH_COMBINE (h, obj->window); + h = NM_HASH_COMBINE (h, obj->cwnd); + h = NM_HASH_COMBINE (h, obj->initcwnd); + h = NM_HASH_COMBINE (h, obj->initrwnd); + h = NM_HASH_COMBINE (h, obj->mtu); + } + return h; +} + int nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part) { @@ -4359,6 +4810,38 @@ nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4 return 0; } +guint +nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj) +{ + guint h = 1053326051; + + if (obj) { + h = NM_HASH_COMBINE (h, obj->ifindex); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->network)); + h = NM_HASH_COMBINE (h, obj->plen); + h = NM_HASH_COMBINE (h, obj->metric); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->gateway)); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->pref_src)); + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj->src)); + h = NM_HASH_COMBINE (h, obj->src_plen); + h = NM_HASH_COMBINE (h, obj->rt_source); + h = NM_HASH_COMBINE (h, obj->mss); + h = NM_HASH_COMBINE (h, obj->rt_cloned); + h = NM_HASH_COMBINE (h, obj->tos); + h = NM_HASH_COMBINE (h, obj->lock_window); + h = NM_HASH_COMBINE (h, obj->lock_cwnd); + h = NM_HASH_COMBINE (h, obj->lock_initcwnd); + h = NM_HASH_COMBINE (h, obj->lock_initrwnd); + h = NM_HASH_COMBINE (h, obj->lock_mtu); + h = NM_HASH_COMBINE (h, obj->window); + h = NM_HASH_COMBINE (h, obj->cwnd); + h = NM_HASH_COMBINE (h, obj->initcwnd); + h = NM_HASH_COMBINE (h, obj->initrwnd); + h = NM_HASH_COMBINE (h, obj->mtu); + } + return h; +} + int nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part) { @@ -4495,6 +4978,80 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform /*****************************************************************************/ +void +nm_platform_cache_update_emit_signal (NMPlatform *self, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) +{ + gboolean visible_new; + gboolean visible_old; + const NMPObject *o; + const NMPClass *klass; + + nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); + + ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new); + + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self); + + switch (cache_op) { + case NMP_CACHE_OPS_ADDED: + if (!nmp_object_is_visible (obj_new)) + return; + o = obj_new; + break; + case NMP_CACHE_OPS_UPDATED: + visible_old = nmp_object_is_visible (obj_old); + visible_new = nmp_object_is_visible (obj_new); + if (!visible_old && visible_new) { + o = obj_new; + cache_op = NMP_CACHE_OPS_ADDED; + } else if (visible_old && !visible_new) { + o = obj_old; + cache_op = NMP_CACHE_OPS_REMOVED; + } else if (!visible_new) { + /* it was invisible and stayed invisible. Nothing to do. */ + return; + } else + o = obj_new; + break; + case NMP_CACHE_OPS_REMOVED: + if (!nmp_object_is_visible (obj_old)) + return; + o = obj_old; + break; + default: + nm_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); + return; + } + + klass = NMP_OBJECT_GET_CLASS (o); + + _LOGt ("emit signal %s %s: %s", + klass->signal_type, + nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + nmp_object_ref (o); + g_signal_emit (self, + _nm_platform_signal_id_get (klass->signal_type_id), + 0, + (int) klass->obj_type, + o->object.ifindex, + &o->object, + (int) cache_op); + nmp_object_unref (o); +} + +/*****************************************************************************/ + +NMPCache * +nm_platform_get_cache (NMPlatform *self) +{ + return NM_PLATFORM_GET_PRIVATE (self)->cache; +} + NMPNetns * nm_platform_netns_get (NMPlatform *self) { @@ -4504,17 +5061,17 @@ nm_platform_netns_get (NMPlatform *self) } gboolean -nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns) +nm_platform_netns_push (NMPlatform *self, NMPNetns **netns) { - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + g_return_val_if_fail (NM_IS_PLATFORM (self), FALSE); - if ( platform->_netns - && !nmp_netns_push (platform->_netns)) { + if ( self->_netns + && !nmp_netns_push (self->_netns)) { NM_SET_OUT (netns, NULL); return FALSE; } - NM_SET_OUT (netns, platform->_netns); + NM_SET_OUT (netns, self->_netns); return TRUE; } @@ -4588,11 +5145,11 @@ _vtr_v6_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, .addr_family = AF_INET, .sizeof_route = sizeof (NMPlatformIP4Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip4_route_cmp_full, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .route_get_all = nm_platform_ip4_route_get_all, .route_add = _vtr_v4_route_add, .route_delete = _vtr_v4_route_delete, .route_delete_default = _vtr_v4_route_delete_default, @@ -4601,11 +5158,11 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, .addr_family = AF_INET6, .sizeof_route = sizeof (NMPlatformIP6Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip6_route_cmp_full, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .route_get_all = nm_platform_ip6_route_get_all, .route_add = _vtr_v6_route_add, .route_delete = _vtr_v6_route_delete, .route_delete_default = _vtr_v6_route_delete_default, @@ -4632,6 +5189,10 @@ set_property (GObject *object, guint prop_id, self->_netns = g_object_ref (netns); } break; + case PROP_USE_UDEV: + /* construct-only */ + priv->use_udev = g_value_get_boolean (value); + break; case PROP_LOG_WITH_PTR: /* construct-only */ priv->log_with_ptr = g_value_get_boolean (value); @@ -4648,12 +5209,37 @@ nm_platform_init (NMPlatform *self) self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_PLATFORM, NMPlatformPrivate); } +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMPlatform *self; + NMPlatformPrivate *priv; + + object = G_OBJECT_CLASS (nm_platform_parent_class)->constructor (type, + n_construct_params, + construct_params); + self = NM_PLATFORM (object); + priv = NM_PLATFORM_GET_PRIVATE (self); + + priv->multi_idx = nm_dedup_multi_index_new (); + + priv->cache = nmp_cache_new (nm_platform_get_multi_idx (self), + priv->use_udev); + return object; +} + static void finalize (GObject *object) { NMPlatform *self = NM_PLATFORM (object); + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); g_clear_object (&self->_netns); + nm_dedup_multi_index_unref (priv->multi_idx); + nmp_cache_free (priv->cache); } static void @@ -4663,6 +5249,7 @@ nm_platform_class_init (NMPlatformClass *platform_class) g_type_class_add_private (object_class, sizeof (NMPlatformPrivate)); + object_class->constructor = constructor; object_class->set_property = set_property; object_class->finalize = finalize; @@ -4676,6 +5263,14 @@ nm_platform_class_init (NMPlatformClass *platform_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_USE_UDEV, + g_param_spec_boolean (NM_PLATFORM_USE_UDEV, "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_LOG_WITH_PTR, g_param_spec_boolean (NM_PLATFORM_LOG_WITH_PTR, "", "", diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 97c8f6ccf2..6e7a8b0658 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -45,6 +45,7 @@ /*****************************************************************************/ #define NM_PLATFORM_NETNS_SUPPORT "netns-support" +#define NM_PLATFORM_USE_UDEV "use-udev" #define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" /*****************************************************************************/ @@ -172,18 +173,6 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -typedef enum { /*< skip >*/ - NM_PLATFORM_GET_ROUTE_FLAGS_NONE = 0, - - /* Whether to include default-routes/non-default-routes. Omitting - * both WITH_DEFAULT and WITH_NON_DEFAULT, is equal to specifying - * both of them. */ - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT = (1LL << 0), - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT = (1LL << 1), - - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL = (1LL << 2), -} NMPlatformGetRouteFlags; - typedef struct { __NMPlatformObject_COMMON; } NMPlatformObject; @@ -372,11 +361,11 @@ typedef union { typedef struct { gboolean is_ip4; + NMPObjectType obj_type; int addr_family; gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part); const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); - GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); gboolean (*route_delete_default) (NMPlatform *self, int ifindex, guint32 metric); @@ -426,7 +415,7 @@ typedef struct { typedef struct { int parent_ifindex; - guint64 sci; /* host byte order */ + guint64 sci; /* host byte order */ guint64 cipher_suite; guint32 window; guint8 icv_length; @@ -517,13 +506,6 @@ 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); - const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); - const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); - const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); - - const NMPObject *(*link_get_lnk) (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); - - GArray *(*link_get_all) (NMPlatform *); gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, @@ -532,8 +514,6 @@ typedef struct { size_t address_len, const NMPlatformLink **out_link); gboolean (*link_delete) (NMPlatform *, int ifindex); - const char *(*link_get_type_name) (NMPlatform *, int ifindex); - gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged); gboolean (*link_refresh) (NMPlatform *, int ifindex); @@ -642,8 +622,6 @@ typedef struct { gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); gboolean (*mesh_set_ssid) (NMPlatform *, int ifindex, const guint8 *ssid, gsize len); - GArray * (*ip4_address_get_all) (NMPlatform *, int ifindex); - GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex); gboolean (*ip4_address_add) (NMPlatform *, int ifindex, in_addr_t address, @@ -663,17 +641,11 @@ typedef struct { guint32 flags); gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); - const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address); - GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); - GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*ip4_route_add) (NMPlatform *, const NMPlatformIP4Route *route); gboolean (*ip6_route_add) (NMPlatform *, const NMPlatformIP6Route *route); gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); - const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); - const NMPlatformIP6Route *(*ip6_route_get) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); gboolean (*check_support_user_ipv6ll) (NMPlatform *); @@ -726,6 +698,7 @@ _nm_platform_uint8_inv (guint8 scope) return (guint8) ~scope; } +gboolean nm_platform_get_use_udev (NMPlatform *self); gboolean nm_platform_get_log_with_ptr (NMPlatform *self); NMPNetns *nm_platform_netns_get (NMPlatform *self); @@ -760,11 +733,14 @@ 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); +const NMPObject *nm_platform_link_get_obj (NMPlatform *self, + int ifindex, + gboolean visible_only); const NMPlatformLink *nm_platform_link_get (NMPlatform *self, int ifindex); const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname); const NMPlatformLink *nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length); -GArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); +GPtrArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); NMPlatformError nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, const NMPlatformLink **out_link); NMPlatformError nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); @@ -775,6 +751,19 @@ gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd); +struct _NMDedupMultiHeadEntry; +struct _NMPLookup; +const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *platform, + const struct _NMPLookup *lookup); + +gboolean nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data); + +GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, + const struct _NMPLookup *lookup, + gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + gpointer user_data); + /* convienience methods to lookup the link and access fields of NMPlatformLink. */ int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); @@ -969,8 +958,6 @@ gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); -GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); -GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); gboolean nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route); gboolean nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route); gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); @@ -1024,6 +1011,21 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route return nm_platform_ip6_route_cmp_full (a, b, TRUE); } +guint nm_platform_link_hash (const NMPlatformLink *obj); +guint nm_platform_ip4_address_hash (const NMPlatformIP4Address *obj); +guint nm_platform_ip6_address_hash (const NMPlatformIP6Address *obj); +guint nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj); +guint nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj); +guint nm_platform_lnk_gre_hash (const NMPlatformLnkGre *obj); +guint nm_platform_lnk_infiniband_hash (const NMPlatformLnkInfiniband *obj); +guint nm_platform_lnk_ip6tnl_hash (const NMPlatformLnkIp6Tnl *obj); +guint nm_platform_lnk_ipip_hash (const NMPlatformLnkIpIp *obj); +guint nm_platform_lnk_macsec_hash (const NMPlatformLnkMacsec *obj); +guint nm_platform_lnk_macvlan_hash (const NMPlatformLnkMacvlan *obj); +guint nm_platform_lnk_sit_hash (const NMPlatformLnkSit *obj); +guint nm_platform_lnk_vlan_hash (const NMPlatformLnkVlan *obj); +guint nm_platform_lnk_vxlan_hash (const NMPlatformLnkVxlan *obj); + gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self); gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self); @@ -1038,4 +1040,6 @@ gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMS gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +struct _NMDedupMultiIndex *nm_platform_get_multi_idx (NMPlatform *self); + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 3ea3fbba4f..b6a51e58bc 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -51,6 +51,11 @@ /*****************************************************************************/ +typedef struct { + NMDedupMultiIdxType parent; + NMPCacheIdType cache_id_type; +} DedupMultiIdxType; + struct _NMPCache { /* the cache contains only one hash table for all object types, and similarly * it contains only one NMMultiIndex. @@ -66,23 +71,248 @@ struct _NMPCache { * This effectively merges the udev-device cache into the NMPCache. */ - GHashTable *idx_main; - NMMultiIndex *idx_multi; + NMDedupMultiIndex *multi_idx; + + /* an idx_type entry for each NMP_CACHE_ID_TYPE. Note that NONE (zero) + * is skipped, so the index is shifted by one: idx_type[cache_id_type - 1]. + * + * Don't bother, use _idx_type_get() instead! */ + DedupMultiIdxType idx_types[NMP_CACHE_ID_TYPE_MAX]; gboolean use_udev; }; /*****************************************************************************/ -static inline guint -_id_hash_ip6_addr (const struct in6_addr *addr) -{ - guint hash = (guint) 0x897da53981a13ULL; - int i; +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class; - for (i = 0; i < sizeof (*addr); i++) - hash = (hash * 33) + ((const guint8 *) addr)[i]; - return hash; +static guint +_idx_obj_id_hash (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + const NMPObject *o = (NMPObject *) obj; + + nm_assert (idx_type && idx_type->klass == &_dedup_multi_idx_type_class); + nm_assert (NMP_OBJECT_GET_TYPE (o) != NMP_OBJECT_TYPE_UNKNOWN); + + return nmp_object_id_hash (o); +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + const NMPObject *o_a = (NMPObject *) obj_a; + const NMPObject *o_b = (NMPObject *) obj_b; + + nm_assert (idx_type && idx_type->klass == &_dedup_multi_idx_type_class); + nm_assert (NMP_OBJECT_GET_TYPE (o_a) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (NMP_OBJECT_GET_TYPE (o_b) != NMP_OBJECT_TYPE_UNKNOWN); + + return nmp_object_id_equal (o_a, o_b); +} + +/* the return value of _idx_obj_part() encodes 3 things: + * 1) for idx_obj_partitionable(), it returns 0 or non-zero. + * 2) for idx_obj_partition_hash(), it returns the hash value (which + * must never be zero not to clash with idx_obj_partitionable(). + * 3) for idx_obj_partition_equal(), returns 0 or 1 depending + * on whether the objects are equal. + * + * _HASH_NON_ZERO() is used to for case 2), to avoid that the a zero hash value + * is returned. */ +#define _HASH_NON_ZERO(h) \ + ((h) ?: (1998098407 + __LINE__)) \ + +static guint +_idx_obj_part (const DedupMultiIdxType *idx_type, + gboolean request_hash, + const NMPObject *obj_a, + const NMPObject *obj_b) +{ + guint h; + NMPObjectType obj_type; + + /* the hash/equals functions are strongly related. So, keep them + * side-by-side and do it all in _idx_obj_part(). */ + + nm_assert (idx_type); + nm_assert (idx_type->parent.klass == &_dedup_multi_idx_type_class); + nm_assert (obj_a); + nm_assert (NMP_OBJECT_GET_TYPE (obj_a) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_b || (NMP_OBJECT_GET_TYPE (obj_b) != NMP_OBJECT_TYPE_UNKNOWN)); + nm_assert (!request_hash || !obj_b); + + switch (idx_type->cache_id_type) { + + case NMP_CACHE_ID_TYPE_OBJECT_TYPE: + if (obj_b) + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b); + if (request_hash) { + h = (guint) idx_type->cache_id_type; + h = NM_HASH_COMBINE (h, NMP_OBJECT_GET_TYPE (obj_a)); + return _HASH_NON_ZERO (h); + } + return 1; + + case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: + if (NMP_OBJECT_GET_TYPE (obj_a) != NMP_OBJECT_TYPE_LINK) { + /* first check, whether obj_a is suitable for this idx_type. + * If not, return 0 (which is correct for partitionable(), hash() and equal() + * functions. */ + return 0; + } + if (obj_b) { + /* we are in equal() mode. Compare obj_b with obj_a. */ + return NMP_OBJECT_GET_TYPE (obj_b) == NMP_OBJECT_TYPE_LINK + && nm_streq (obj_a->link.name, obj_b->link.name); + } + if (request_hash) { + /* we request a hash from obj_a. Hash the relevant parts. */ + h = (guint) idx_type->cache_id_type; + h = NM_HASH_COMBINE (h, g_str_hash (obj_a->link.name)); + return _HASH_NON_ZERO (h); + } + /* just return 1, to indicate that obj_a is partitionable by this idx_type. */ + return 1; + + case NMP_CACHE_ID_TYPE_DEFAULT_ROUTES: + if ( !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj_a->ip_route) + || !nmp_object_is_visible (obj_a)) + return 0; + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj_b->ip_route) + && nmp_object_is_visible (obj_b); + } + if (request_hash) { + h = (guint) idx_type->cache_id_type; + h = NM_HASH_COMBINE (h, NMP_OBJECT_GET_TYPE (obj_a)); + return _HASH_NON_ZERO (h); + } + return 1; + + case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX: + if ( !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || !nmp_object_is_visible (obj_a)) + return 0; + nm_assert (obj_a->object.ifindex > 0); + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_a) == NMP_OBJECT_GET_TYPE (obj_b) + && obj_a->object.ifindex == obj_b->object.ifindex + && nmp_object_is_visible (obj_b); + } + if (request_hash) { + h = (guint) idx_type->cache_id_type; + h = NM_HASH_COMBINE (h, NMP_OBJECT_GET_TYPE (obj_a)); + h = NM_HASH_COMBINE (h, obj_a->object.ifindex); + return _HASH_NON_ZERO (h); + } + return 1; + + case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION: + obj_type = NMP_OBJECT_GET_TYPE (obj_a); + if ( !NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE) + || obj_a->object.ifindex <= 0) + return 0; + if (obj_b) { + return obj_type == NMP_OBJECT_GET_TYPE (obj_b) + && obj_b->object.ifindex > 0 + && obj_a->ip_route.plen == obj_b->ip_route.plen + && obj_a->ip_route.metric == obj_b->ip_route.metric + && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + ? obj_a->ip4_route.network == obj_b->ip4_route.network + : IN6_ARE_ADDR_EQUAL (&obj_a->ip6_route.network, &obj_b->ip6_route.network)); + } + if (request_hash) { + h = (guint) idx_type->cache_id_type; + h = NM_HASH_COMBINE (h, obj_a->ip_route.plen); + h = NM_HASH_COMBINE (h, obj_a->ip_route.metric); + h = NM_HASH_COMBINE (h, obj_type); + if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) + h = NM_HASH_COMBINE (h, obj_a->ip4_route.network); + else + h = NM_HASH_COMBINE (h, nm_utils_in6_addr_hash (&obj_a->ip6_route.network)); + return _HASH_NON_ZERO (h); + } + return 1; + + case NMP_CACHE_ID_TYPE_NONE: + case __NMP_CACHE_ID_TYPE_MAX: + break; + } + nm_assert_not_reached (); + return 0; +} + +static gboolean +_idx_obj_partitionable (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + FALSE, + (NMPObject *) obj, + NULL) != 0; +} + +static guint +_idx_obj_partition_hash (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + TRUE, + (NMPObject *) obj, + NULL); +} + +static gboolean +_idx_obj_partition_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return _idx_obj_part ((DedupMultiIdxType *) idx_type, + FALSE, + (NMPObject *) obj_a, + (NMPObject *) obj_b); +} + +static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = { + .idx_obj_id_hash = _idx_obj_id_hash, + .idx_obj_id_equal = _idx_obj_id_equal, + .idx_obj_partitionable = _idx_obj_partitionable, + .idx_obj_partition_hash = _idx_obj_partition_hash, + .idx_obj_partition_equal = _idx_obj_partition_equal, +}; + +static void +_dedup_multi_idx_type_init (DedupMultiIdxType *idx_type, NMPCacheIdType cache_id_type) +{ + nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, + &_dedup_multi_idx_type_class); + idx_type->cache_id_type = cache_id_type; +} + +/*****************************************************************************/ + +static guint +_vlan_xgress_qos_mappings_hash (guint n_map, + const NMVlanQosMapping *map) +{ + guint h = 1453577309; + guint i; + + for (i = 0; i < n_map; i++) { + h = NM_HASH_COMBINE (h, map[i].from); + h = NM_HASH_COMBINE (h, map[i].to); + } + return h; } static int @@ -150,12 +380,18 @@ _link_get_driver (struct udev_device *udevice, const char *kind, int ifindex) } void -_nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) +_nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev) { const char *driver = NULL; gboolean initialized = FALSE; + NMPObject *obj; - nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + nm_assert (obj_orig || *obj_new); + nm_assert (obj_new); + nm_assert (!obj_orig || NMP_OBJECT_GET_TYPE (obj_orig) == NMP_OBJECT_TYPE_LINK); + nm_assert (!*obj_new || NMP_OBJECT_GET_TYPE (*obj_new) == NMP_OBJECT_TYPE_LINK); + + obj = *obj_new ?: obj_orig; /* The link contains internal fields that are combined by * properties from netlink and udev. Update those properties */ @@ -179,17 +415,34 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) } } + if ( nm_streq0 (obj->link.driver, driver) + && obj->link.initialized == initialized) + return; + + if (!*obj_new) + obj = *obj_new = nmp_object_clone (obj, FALSE); + obj->link.driver = driver; obj->link.initialized = initialized; } static void -_nmp_object_fixup_link_master_connected (NMPObject *obj, const NMPCache *cache) +_nmp_object_fixup_link_master_connected (NMPObject **obj_new, NMPObject *obj_orig, const NMPCache *cache) { - nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + NMPObject *obj; - if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) + nm_assert (obj_orig || *obj_new); + nm_assert (obj_new); + nm_assert (!obj_orig || NMP_OBJECT_GET_TYPE (obj_orig) == NMP_OBJECT_TYPE_LINK); + nm_assert (!*obj_new || NMP_OBJECT_GET_TYPE (*obj_new) == NMP_OBJECT_TYPE_LINK); + + obj = *obj_new ?: obj_orig; + + if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) { + if (!*obj_new) + obj = *obj_new = nmp_object_clone (obj, FALSE); obj->link.connected = !obj->link.connected; + } } /*****************************************************************************/ @@ -204,33 +457,6 @@ nmp_class_from_type (NMPObjectType obj_type) /*****************************************************************************/ -NMPObject * -nmp_object_ref (NMPObject *obj) -{ - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); - g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL); - obj->_ref_count++; - - return obj; -} - -void -nmp_object_unref (NMPObject *obj) -{ - if (obj) { - g_return_if_fail (obj->_ref_count > 0); - g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT); - if (--obj->_ref_count <= 0) { - const NMPClass *klass = obj->_class; - - nm_assert (!obj->is_cached); - if (klass->cmd_obj_dispose) - klass->cmd_obj_dispose (obj); - g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), obj); - } - } -} - static void _vt_cmd_obj_dispose_link (NMPObject *obj) { @@ -259,7 +485,7 @@ _nmp_object_new_from_class (const NMPClass *klass) obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object)); obj->_class = klass; - obj->_ref_count = 1; + obj->parent._ref_count = 1; return obj; } @@ -287,14 +513,29 @@ nmp_object_new_link (int ifindex) /*****************************************************************************/ -static const NMPObject * +static void _nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) { + nm_assert (obj); nm_assert (klass); memset (obj, 0, sizeof (NMPObject)); obj->_class = klass; - obj->_ref_count = NMP_REF_COUNT_STACKINIT; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; +} + +static NMPObject * +_nmp_object_stackinit_from_type (NMPObject *obj, NMPObjectType obj_type) +{ + const NMPClass *klass; + + nm_assert (obj); + klass = nmp_class_from_type (obj_type); + nm_assert (klass); + + memset (obj, 0, sizeof (NMPObject)); + obj->_class = klass; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; } @@ -319,7 +560,7 @@ nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) klass = NMP_OBJECT_GET_CLASS (src); if (!klass->cmd_obj_stackinit_id) - nmp_object_stackinit (obj, klass->obj_type, NULL); + _nmp_object_stackinit_from_class (obj, klass); else klass->cmd_obj_stackinit_id (obj, src); return obj; @@ -328,7 +569,7 @@ nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) const NMPObject * nmp_object_stackinit_id_link (NMPObject *obj, int ifindex) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_LINK, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_LINK); obj->link.ifindex = ifindex; return obj; } @@ -342,7 +583,7 @@ _vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src) const NMPObject * nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP4_ADDRESS); obj->ip4_address.ifindex = ifindex; obj->ip4_address.address = address; obj->ip4_address.plen = plen; @@ -359,7 +600,7 @@ _vt_cmd_obj_stackinit_id_ip4_address (NMPObject *obj, const NMPObject *src) const NMPObject * nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP6_ADDRESS); obj->ip4_address.ifindex = ifindex; if (address) obj->ip6_address.address = *address; @@ -375,7 +616,7 @@ _vt_cmd_obj_stackinit_id_ip6_address (NMPObject *obj, const NMPObject *src) const NMPObject * nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP4_ROUTE); obj->ip4_route.ifindex = ifindex; obj->ip4_route.network = network; obj->ip4_route.plen = plen; @@ -392,7 +633,7 @@ _vt_cmd_obj_stackinit_id_ip4_route (NMPObject *obj, const NMPObject *src) const NMPObject * nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric) { - nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP6_ROUTE); obj->ip6_route.ifindex = ifindex; if (network) obj->ip6_route.network = *network; @@ -434,9 +675,8 @@ nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2))); @@ -461,9 +701,8 @@ _vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_strin return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', obj->_link.netlink.is_in_netlink ? '+' : '-', @@ -502,9 +741,8 @@ _vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_s case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", - klass->obj_type_name, obj, obj->_ref_count, - obj->is_cached ? '+' : '-', + "[%s,%p,%u,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->parent._ref_count, nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); @@ -566,6 +804,58 @@ _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", _vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); _vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); +guint +nmp_object_hash (const NMPObject *obj) +{ + const NMPClass *klass; + guint h; + + if (!obj) + return 0; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0); + + klass = NMP_OBJECT_GET_CLASS (obj); + + if (klass->cmd_obj_hash) + h = klass->cmd_obj_hash (obj); + else if (klass->cmd_plobj_hash) + h = klass->cmd_plobj_hash (&obj->object); + else + return GPOINTER_TO_UINT (obj); + return NM_HASH_COMBINE (h, klass->obj_type); +} + +static guint +_vt_cmd_obj_hash_link (const NMPObject *obj) +{ + guint h = 1228913327; + + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + + h = NM_HASH_COMBINE (h, nm_platform_link_hash (&obj->link)); + h = NM_HASH_COMBINE (h, obj->_link.netlink.is_in_netlink); + if (obj->_link.netlink.lnk) + h = NM_HASH_COMBINE (h, nmp_object_hash (obj->_link.netlink.lnk)); + h = NM_HASH_COMBINE (h, GPOINTER_TO_UINT (obj->_link.udev.device)); + return h; +} + +static guint +_vt_cmd_obj_hash_lnk_vlan (const NMPObject *obj) +{ + guint h = 914932607; + + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LNK_VLAN); + + h = NM_HASH_COMBINE (h, nm_platform_lnk_vlan_hash (&obj->lnk_vlan)); + h = NM_HASH_COMBINE (h, _vlan_xgress_qos_mappings_hash (obj->_lnk_vlan.n_ingress_qos_map, + obj->_lnk_vlan.ingress_qos_map)); + h = NM_HASH_COMBINE (h, _vlan_xgress_qos_mappings_hash (obj->_lnk_vlan.n_egress_qos_map, + obj->_lnk_vlan.egress_qos_map)); + return h; +} + int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) { @@ -846,39 +1136,39 @@ _vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ } _vt_cmd_plobj_id_hash (link, NMPlatformLink, { hash = (guint) 3982791431u; - hash = hash + ((guint) obj->ifindex); + hash = hash + ((guint) obj->ifindex); }) _vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { hash = (guint) 3591309853u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->address); - + hash = hash + ((guint) obj->ifindex); + hash = NM_HASH_COMBINE (hash, obj->plen); + hash = NM_HASH_COMBINE (hash, obj->address); /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen))); + hash = NM_HASH_COMBINE (hash, (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen))); }) _vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { hash = (guint) 2907861637u; - hash = hash + ((guint) obj->ifindex); + hash = hash + ((guint) obj->ifindex); /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - hash = hash * 33 + _id_hash_ip6_addr (&obj->address); + hash = NM_HASH_COMBINE (hash, nm_utils_in6_addr_hash (&obj->address)); }) _vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { hash = (guint) 2569857221u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->metric); - hash = hash * 33 + ((guint) nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); + hash = hash + ((guint) obj->ifindex); + hash = NM_HASH_COMBINE (hash, obj->plen); + hash = NM_HASH_COMBINE (hash, obj->metric); + hash = NM_HASH_COMBINE (hash, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); }) _vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { hash = (guint) 3999787007u; - hash = hash + ((guint) obj->ifindex); - hash = hash * 33 + ((guint) obj->plen); - hash = hash * 33 + ((guint) obj->metric); - hash = hash * 33 + ({ - struct in6_addr n1; - _id_hash_ip6_addr (nm_utils_ip6_address_clear_host_address (&n1, &obj->network, obj->plen)); - }); + hash = hash + ((guint) obj->ifindex); + hash = NM_HASH_COMBINE (hash, obj->plen); + hash = NM_HASH_COMBINE (hash, obj->metric); + hash = NM_HASH_COMBINE (hash, + ({ + struct in6_addr n1; + nm_utils_in6_addr_hash (nm_utils_ip6_address_clear_host_address (&n1, &obj->network, obj->plen)); + })); }) gboolean @@ -957,355 +1247,85 @@ _vt_cmd_obj_is_visible_link (const NMPObject *obj) /*****************************************************************************/ -_NM_UTILS_LOOKUP_DEFINE (static, _nmp_cache_id_size_by_type, NMPCacheIdType, guint, - NM_UTILS_LOOKUP_DEFAULT (({ nm_assert_not_reached (); (guint) 0; })), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, nm_offsetofend (NMPCacheId, object_type)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, nm_offsetofend (NMPCacheId, object_type_by_ifindex)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, nm_offsetofend (NMPCacheId, link_by_ifname)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, nm_offsetofend (NMPCacheId, routes_by_destination_ip4)), - NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, nm_offsetofend (NMPCacheId, routes_by_destination_ip6)), - NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_CACHE_ID_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM_IGNORE (__NMP_CACHE_ID_TYPE_MAX), -); - -gboolean -nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b) -{ - if (a->_id_type != b->_id_type) - return FALSE; - return memcmp (a, b, _nmp_cache_id_size_by_type (a->_id_type)) == 0; -} - -guint -nmp_cache_id_hash (const NMPCacheId *id) -{ - guint hash = 5381; - guint i, n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - for (i = 0; i < n; i++) - hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */ - return hash; -} - -NMPCacheId * -nmp_cache_id_clone (const NMPCacheId *id) -{ - NMPCacheId *id2; - guint n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - id2 = g_slice_alloc (n); - memcpy (id2, id, n); - return id2; -} - -void -nmp_cache_id_destroy (NMPCacheId *id) -{ - guint n; - - n = _nmp_cache_id_size_by_type (id->_id_type); - g_slice_free1 (n, id); -} - -/*****************************************************************************/ - -static void -_nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type) -{ - /* there is no need to set the entire @id to zero when - * initializing the ID. - * - * First, depending on the @id_type only part of the - * @id is actually used (_nmp_cache_id_size_by_type). - * - * Second, the nmp_cache_id_init_*() *MUST* anyway make sure - * that all relevant fields are set. Since it happens that - * all structs have the packed attribute, there are no holes - * due to alignment, and it becomes simple for nmp_cache_id_init_*() - * to ensure that all fields are set. */ - -#if NM_MORE_ASSERTS - nm_assert (id); - { - guint i; - - /* initialized with some bogus canary to hopefully detect when we miss - * to initialize a field of the cache-id. */ - for (i = 0; i < sizeof (*id); i++) { - ((char *) id)[i] = GPOINTER_TO_UINT (id) ^ i; - } - } -#endif - - id->_id_type = id_type; -} - -NMPCacheId * -nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only) -{ - _nmp_cache_id_init (id, visible_only - ? NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY - : NMP_CACHE_ID_TYPE_OBJECT_TYPE); - id->object_type.obj_type = obj_type; - return id; -} - -NMPCacheId * -nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, - NMPObjectType obj_type, - int ifindex) -{ - g_return_val_if_fail (NM_IN_SET (obj_type, - NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); - - if (ifindex <= 0) - return nmp_cache_id_init_object_type (id, obj_type, TRUE); - - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX); - id->object_type_by_ifindex.obj_type = obj_type; - memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_routes_visible (NMPCacheId *id, - NMPObjectType obj_type, - gboolean with_default, - gboolean with_non_default, - int ifindex) -{ - g_return_val_if_fail (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); - - if (with_default) { - if (with_non_default) { - if (ifindex <= 0) - return nmp_cache_id_init_object_type (id, obj_type, TRUE); - return nmp_cache_id_init_addrroute_visible_by_ifindex (id, obj_type, ifindex); - } - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT); - } else if (with_non_default) - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT); - else - g_return_val_if_reached (NULL); - - id->object_type_by_ifindex.obj_type = obj_type; - memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_link_by_ifname (NMPCacheId *id, - const char *ifname) -{ - gsize l; - - if ( !ifname - || (l = strlen (ifname)) > sizeof (id->link_by_ifname.ifname_short)) - g_return_val_if_reached (id); - - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME); - - memset (id->link_by_ifname.ifname_short, 0, sizeof (id->link_by_ifname.ifname_short)); - /* the trailing NUL is dropped!! */ - memcpy (id->link_by_ifname.ifname_short, ifname, l); - - return id; -} - -NMPCacheId * -nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id, - guint32 network, - guint8 plen, - guint32 metric) -{ - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4); - id->routes_by_destination_ip4.plen = plen; - memcpy (&id->routes_by_destination_ip4._misaligned_metric, &metric, sizeof (guint32)); - memcpy (&id->routes_by_destination_ip4._misaligned_network, &network, sizeof (guint32)); - return id; -} - -NMPCacheId * -nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id, - const struct in6_addr *network, - guint8 plen, - guint32 metric) -{ - _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6); - id->routes_by_destination_ip4.plen = plen; - memcpy (&id->routes_by_destination_ip6._misaligned_metric, &metric, sizeof (guint32)); - memcpy (&id->routes_by_destination_ip6._misaligned_network, network ?: &nm_ip_addr_zero.addr6, sizeof (struct in6_addr)); - return id; -} - -/*****************************************************************************/ - -static gboolean -_nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); - - switch (id_type) { - case NMP_CACHE_ID_TYPE_OBJECT_TYPE: - *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, FALSE); - return TRUE; - case NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY: - if (nmp_object_is_visible (obj)) - *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, TRUE); - else - *out_id = NULL; - return TRUE; - default: - return klass->cmd_obj_init_cache_id - && klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); - } -} - static const guint8 _supported_cache_ids_link[] = { NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, 0, }; -static gboolean -_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: - if (obj->link.name[0]) { - *out_id = nmp_cache_id_init_link_by_ifname (id, obj->link.name); - return TRUE; - } - break; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - static const guint8 _supported_cache_ids_ipx_address[] = { NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, 0, }; -static gboolean -_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (nmp_object_is_visible (obj)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); - return TRUE; - } - break; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - -static const guint8 _supported_cache_ids_ip4_route[] = { +static const guint8 _supported_cache_ids_ipx_route[] = { NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, + NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, + NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, + NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION, 0, }; -static const guint8 _supported_cache_ids_ip6_route[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, - NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, - 0, -}; - -static gboolean -_vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - switch (id_type) { - case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: - if (nmp_object_is_visible (obj)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: - if ( nmp_object_is_visible (obj) - && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: - if ( nmp_object_is_visible (obj) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT: - if ( nmp_object_is_visible (obj) - && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT: - if ( nmp_object_is_visible (obj) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { - nm_assert (obj->object.ifindex > 0); - *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex); - return TRUE; - } - break; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4: - if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { - *out_id = nmp_cache_id_init_routes_by_destination_ip4 (id, obj->ip4_route.network, obj->ip_route.plen, obj->ip_route.metric); - return TRUE; - } - return FALSE; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6: - if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) { - *out_id = nmp_cache_id_init_routes_by_destination_ip6 (id, &obj->ip6_route.network, obj->ip_route.plen, obj->ip_route.metric); - return TRUE; - } - return FALSE; - default: - return FALSE; - } - *out_id = NULL; - return TRUE; -} - /*****************************************************************************/ +static void +_vt_dedup_obj_destroy (NMDedupMultiObj *obj) +{ + NMPObject *o = (NMPObject *) obj; + const NMPClass *klass; + + nm_assert (o->parent._ref_count == 0); + nm_assert (!o->parent._multi_idx); + + klass = o->_class; + if (klass->cmd_obj_dispose) + klass->cmd_obj_dispose (o); + g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), o); +} + +static const NMDedupMultiObj * +_vt_dedup_obj_clone (const NMDedupMultiObj *obj) +{ + return (const NMDedupMultiObj *) nmp_object_clone ((const NMPObject *) obj, FALSE); +} + +static guint +_vt_dedup_obj_full_hash (const NMDedupMultiObj *obj) +{ + return nmp_object_hash ((NMPObject *) obj); +} + +static gboolean +_vt_dedup_obj_full_equal (const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return nmp_object_equal ((NMPObject *) obj_a, + (NMPObject *) obj_b); +} + +#define DEDUP_MULTI_OBJ_CLASS_INIT() \ + { \ + .obj_clone = _vt_dedup_obj_clone, \ + .obj_destroy = _vt_dedup_obj_destroy, \ + .obj_full_hash = _vt_dedup_obj_full_hash, \ + .obj_full_equal = _vt_dedup_obj_full_equal, \ + } + +/*****************************************************************************/ + +static NMDedupMultiIdxType * +_idx_type_get (const NMPCache *cache, NMPCacheIdType cache_id_type) +{ + nm_assert (cache); + nm_assert (cache_id_type > NMP_CACHE_ID_TYPE_NONE); + nm_assert (cache_id_type <= NMP_CACHE_ID_TYPE_MAX); + nm_assert ((int) cache_id_type - 1 >= 0); + nm_assert ((int) cache_id_type - 1 < G_N_ELEMENTS (cache->idx_types)); + + return (NMDedupMultiIdxType *) &cache->idx_types[cache_id_type - 1]; +} + gboolean nmp_cache_use_udev_get (const NMPCache *cache) { @@ -1316,6 +1336,17 @@ nmp_cache_use_udev_get (const NMPCache *cache) /*****************************************************************************/ +gboolean +nmp_cache_link_connected_for_slave (int ifindex_master, const NMPObject *slave) +{ + nm_assert (NMP_OBJECT_GET_TYPE (slave) == NMP_OBJECT_TYPE_LINK); + + return ifindex_master > 0 + && slave->link.master == ifindex_master + && slave->link.connected + && nmp_object_is_visible (slave); +} + /** * nmp_cache_link_connected_needs_toggle: * @cache: the platform cache @@ -1340,9 +1371,7 @@ nmp_cache_use_udev_get (const NMPCache *cache) gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave) { - const NMPlatformLink *const *links; gboolean is_lower_up = FALSE; - guint len, i; if ( !master || NMP_OBJECT_GET_TYPE (master) != NMP_OBJECT_TYPE_LINK @@ -1360,27 +1389,23 @@ nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *m potential_slave = NULL; if ( potential_slave - && nmp_object_is_visible (potential_slave) - && potential_slave->link.ifindex > 0 - && potential_slave->link.master == master->link.ifindex - && potential_slave->link.connected) { + && nmp_cache_link_connected_for_slave (master->link.ifindex, potential_slave)) is_lower_up = TRUE; - } else { - NMPCacheId cache_id; + else { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *link = NULL; - links = (const NMPlatformLink *const *) nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, FALSE), &len); - for (i = 0; i < len; i++) { - const NMPlatformLink *link = links[i]; + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_LINK)), + &link) { const NMPObject *obj = NMP_OBJECT_UP_CAST ((NMPlatformObject *) link); - nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST ((NMPlatformObject *) link)) == NMP_OBJECT_TYPE_LINK); - if ( (!potential_slave || potential_slave->link.ifindex != link->ifindex) && ignore_slave != obj - && link->ifindex > 0 - && link->master == master->link.ifindex - && nmp_object_is_visible (obj) - && link->connected) { + && nmp_cache_link_connected_for_slave (master->link.ifindex, obj)) { is_lower_up = TRUE; break; } @@ -1423,40 +1448,46 @@ nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int mas /*****************************************************************************/ -const NMPlatformObject *const * -nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len) +static const NMDedupMultiEntry * +_lookup_obj (const NMPCache *cache, const NMPObject *obj) { - return (const NMPlatformObject *const *) nm_multi_index_lookup (cache->idx_multi, - (const NMMultiIndexId *) cache_id, - out_len); -} + const NMDedupMultiEntry *entry; -GArray * -nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id) -{ - const NMPClass *klass = nmp_class_from_type (obj_type); - guint len, i; - const NMPlatformObject *const *objects; - GArray *array; + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj)); - g_return_val_if_fail (klass, NULL); - - objects = nmp_cache_lookup_multi (cache, cache_id, &len); - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[i])) == klass); - g_array_append_vals (array, objects[i], 1); - } - return array; + entry = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE), + obj); + nm_assert (!entry + || ( NMP_OBJECT_IS_VALID (entry->obj) + && NMP_OBJECT_GET_CLASS (entry->obj) == NMP_OBJECT_GET_CLASS (obj))); + return entry; } const NMPObject * nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) { + const NMDedupMultiEntry *entry; + + g_return_val_if_fail (cache, NULL); g_return_val_if_fail (obj, NULL); - return g_hash_table_lookup (cache->idx_main, obj); + entry = _lookup_obj (cache, obj); + return entry ? entry->obj : NULL; +} + +const NMDedupMultiEntry * +nmp_cache_lookup_entry_link (const NMPCache *cache, int ifindex) +{ + NMPObject obj_needle; + + nm_assert (cache); + + nmp_object_stackinit_id_link (&obj_needle, ifindex); + return nm_dedup_multi_index_lookup_obj (cache->multi_idx, + _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE), + &obj_needle); } const NMPObject * @@ -1467,6 +1498,197 @@ nmp_cache_lookup_link (const NMPCache *cache, int ifindex) return nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); } +/*****************************************************************************/ + +const NMDedupMultiHeadEntry * +nmp_cache_lookup_all (const NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *select_obj) +{ + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (select_obj)); + + return nm_dedup_multi_index_lookup_head (cache->multi_idx, + _idx_type_get (cache, cache_id_type), + select_obj); +} + +static const NMPLookup * +_L (const NMPLookup *lookup) +{ +#if NM_MORE_ASSERTS + DedupMultiIdxType idx_type; + + nm_assert (lookup); + _dedup_multi_idx_type_init (&idx_type, lookup->cache_id_type); + nm_assert (idx_type.parent.klass->idx_obj_partitionable ((NMDedupMultiIdxType *) &idx_type, (NMDedupMultiObj *) &lookup->selector_obj)); + nm_assert (idx_type.parent.klass->idx_obj_partition_hash ((NMDedupMultiIdxType *) &idx_type, (NMDedupMultiObj *) &lookup->selector_obj) > 0); +#endif + return lookup; +} + +const NMPLookup * +nmp_lookup_init_obj_type (NMPLookup *lookup, + NMPObjectType obj_type) +{ + NMPObject *o; + + nm_assert (lookup); + + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; + return _L (lookup); + default: + nm_assert_not_reached (); + return NULL; + } +} + +const NMPLookup * +nmp_lookup_init_link_by_ifname (NMPLookup *lookup, + const char *ifname) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (ifname); + nm_assert (strlen (ifname) < IFNAMSIZ); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_LINK); + if (g_strlcpy (o->link.name, ifname, sizeof (o->link.name)) >= sizeof (o->link.name)) + g_return_val_if_reached (NULL); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_LINK_BY_IFNAME; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_addrroute (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + if (ifindex <= 0) { + return nmp_lookup_init_obj_type (lookup, + obj_type); + } + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + o->object.ifindex = ifindex; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_route_visible (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex, + gboolean only_default) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + if (!only_default) { + return nmp_lookup_init_addrroute (lookup, + obj_type, + ifindex); + } + + if (ifindex > 0) { + /* there is no index to lookup a default-route by ifindex. + * You have to lookup all default-routes, and filter yourself */ + g_return_val_if_reached (NULL); + } + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + o->object.ifindex = 1; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_route_by_dest (NMPLookup *lookup, + int addr_family, + gconstpointer network, + guint plen, + guint32 metric) +{ + NMPObject *o; + + nm_assert (lookup); + + switch (addr_family) { + case AF_INET: + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip4_route.network = *((in_addr_t *) network); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION; + break; + case AF_INET6: + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip6_route.network = *((struct in6_addr *) network); + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION; + break; + default: + nm_assert_not_reached (); + return NULL; + } + return _L (lookup); +} + +/*****************************************************************************/ + +GArray * +nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, + NMPObjectType obj_type, + gboolean visible_only) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + NMDedupMultiIter iter; + const NMPObject *o; + GArray *array; + + g_return_val_if_fail (klass, NULL); + + array = g_array_sized_new (FALSE, FALSE, + klass->sizeof_public, + head_entry ? head_entry->len : 0); + nmp_cache_iter_for_each (&iter, + head_entry, + &o) { + nm_assert (NMP_OBJECT_GET_CLASS (o) == klass); + if ( visible_only + && !nmp_object_is_visible (o)) + continue; + g_array_append_vals (array, &o->object, 1); + } + return array; +} + +/*****************************************************************************/ + /** * nmp_cache_find_other_route_for_same_destination: * @cache: @@ -1482,32 +1704,36 @@ nmp_cache_lookup_link (const NMPCache *cache, int ifindex) const NMPObject * nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route) { - NMPCacheId cache_id; - const NMPlatformObject *const *list; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject *o = NULL; nm_assert (cache); switch (NMP_OBJECT_GET_TYPE (route)) { case NMP_OBJECT_TYPE_IP4_ROUTE: - nmp_cache_id_init_routes_by_destination_ip4 (&cache_id, route->ip4_route.network, route->ip_route.plen, route->ip_route.metric); + nmp_lookup_init_route_by_dest (&lookup, + AF_INET, + &route->ip4_route.network, + route->ip_route.plen, + route->ip_route.metric); break; case NMP_OBJECT_TYPE_IP6_ROUTE: - nmp_cache_id_init_routes_by_destination_ip6 (&cache_id, &route->ip6_route.network, route->ip_route.plen, route->ip_route.metric); + nmp_lookup_init_route_by_dest (&lookup, + AF_INET6, + &route->ip6_route.network, + route->ip_route.plen, + route->ip_route.metric); break; default: g_return_val_if_reached (NULL); } - list = nmp_cache_lookup_multi (cache, &cache_id, NULL); - if (list) { - for (; *list; list++) { - const NMPObject *candidate = NMP_OBJECT_UP_CAST (*list); + nmp_cache_iter_for_each (&iter, nmp_cache_lookup (cache, &lookup), &o) { + nm_assert (NMP_OBJECT_GET_CLASS (route) == NMP_OBJECT_GET_CLASS (o)); - nm_assert (NMP_OBJECT_GET_CLASS (route) == NMP_OBJECT_GET_CLASS (candidate)); - - if (!nmp_object_id_equal (route, candidate)) - return candidate; - } + if (!nmp_object_id_equal (route, o)) + return o; } return NULL; } @@ -1523,9 +1749,10 @@ nmp_cache_lookup_link_full (const NMPCache *cache, { NMPObject obj_needle; const NMPObject *obj; - const NMPlatformObject *const *list; - guint i, len; - NMPCacheId cache_id, *p_cache_id; + NMDedupMultiIter iter; + const NMDedupMultiHeadEntry *head_entry; + const NMPlatformLink *link = NULL; + NMPLookup lookup; if (ifindex > 0) { obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); @@ -1540,18 +1767,17 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } else if (!ifname && !match_fn) return NULL; else { - if ( ifname - && strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) { - p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname); + if (ifname) { + if (strlen (ifname) >= IFNAMSIZ) + return NULL; + nmp_lookup_init_link_by_ifname (&lookup, ifname); ifname = NULL; - } else { - p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only); - visible_only = FALSE; - } + } else + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); - list = nmp_cache_lookup_multi (cache, p_cache_id, &len); - for (i = 0; i < len; i++) { - obj = NMP_OBJECT_UP_CAST (list[i]); + head_entry = nmp_cache_lookup (cache, &lookup); + nmp_cache_iter_for_each_link (&iter, head_entry, &link) { + obj = NMP_OBJECT_UP_CAST (link); if (visible_only && !nmp_object_is_visible (obj)) continue; @@ -1568,190 +1794,255 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } } -GHashTable * -nmp_cache_lookup_all_to_hash (const NMPCache *cache, - NMPCacheId *cache_id, - GHashTable *hash) -{ - NMMultiIndexIdIter iter; - gpointer plobj; - - nm_multi_index_id_iter_init (&iter, cache->idx_multi, (const NMMultiIndexId *) cache_id); - - if (nm_multi_index_id_iter_next (&iter, &plobj)) { - if (!hash) - hash = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - do { - g_hash_table_add (hash, nmp_object_ref (NMP_OBJECT_UP_CAST (plobj))); - } while (nm_multi_index_id_iter_next (&iter, &plobj)); - } - - return hash; -} - /*****************************************************************************/ static void -_nmp_cache_update_cache (NMPCache *cache, NMPObject *obj, gboolean remove) +_idxcache_update_box_move (NMPCache *cache, + NMPCacheIdType cache_id_type, + const NMPObject *obj_old, + const NMPObject *obj_new) { - const guint8 *id_type; + const NMDedupMultiEntry *entry_new; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_order; + NMDedupMultiIdxType *idx_type; - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - NMPCacheId cache_id_storage; - const NMPCacheId *cache_id; + nm_assert (obj_new || obj_old); + nm_assert (!obj_new || NMP_OBJECT_GET_TYPE (obj_new) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_old || NMP_OBJECT_GET_TYPE (obj_old) != NMP_OBJECT_TYPE_UNKNOWN); + nm_assert (!obj_old || !obj_new || NMP_OBJECT_GET_CLASS (obj_new) == NMP_OBJECT_GET_CLASS (obj_old)); + nm_assert (!obj_old || !obj_new || !nmp_object_equal (obj_new, obj_old)); + nm_assert (!obj_new || obj_new == nm_dedup_multi_index_obj_find (cache->multi_idx, obj_new)); + nm_assert (!obj_old || obj_old == nm_dedup_multi_index_obj_find (cache->multi_idx, obj_old)); - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id)) - continue; - if (!cache_id) - continue; + idx_type = _idx_type_get (cache, cache_id_type); - /* We don't put @obj itself into the multi index, but &obj->object. As of now, all - * users expect a pointer to NMPlatformObject, not NMPObject. - * You can use NMP_OBJECT_UP_CAST() to retrieve the original @obj pointer. - * - * If need be, we could determine based on @id_type which pointer we want to store. */ - - if (remove) { - if (!nm_multi_index_remove (cache->idx_multi, &cache_id->base, &obj->object)) - g_assert_not_reached (); - } else { - if (!nm_multi_index_add (cache->idx_multi, &cache_id->base, &obj->object)) - g_assert_not_reached (); + if (obj_old) { + entry_old = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + idx_type, + obj_old); + if (!obj_new) { + if (entry_old) + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); + return; } - } + } else + entry_old = NULL; + + if (obj_new) { + if ( obj_old + && nm_dedup_multi_idx_type_id_equal (idx_type, obj_old, obj_new) + && nm_dedup_multi_idx_type_partition_equal (idx_type, obj_old, obj_new)) { + /* optimize. We just looked up the @obj_old entry and @obj_new compares equal + * according to idx_obj_id_equal(). entry_new is the same as entry_old. */ + entry_new = entry_old; + } else { + entry_new = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + idx_type, + obj_new); + } + + if (entry_new) + entry_order = entry_new; + else if ( entry_old + && nm_dedup_multi_idx_type_partition_equal (idx_type, entry_old->obj, obj_new)) + entry_order = entry_old; + else + entry_order = NULL; + nm_dedup_multi_index_add_full (cache->multi_idx, + idx_type, + obj_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + entry_order, + entry_new ?: NM_DEDUP_MULTI_ENTRY_MISSING, + entry_new ? entry_new->head : (entry_order ? entry_order->head : NULL), + &entry_new, + NULL); + +#if NM_MORE_ASSERTS + if (entry_new) { + nm_assert (idx_type->klass->idx_obj_partitionable); + nm_assert (idx_type->klass->idx_obj_partition_equal); + nm_assert (idx_type->klass->idx_obj_partitionable (idx_type, entry_new->obj)); + nm_assert (idx_type->klass->idx_obj_partition_equal (idx_type, (gpointer) obj_new, entry_new->obj)); + } +#endif + } else + entry_new = NULL; + + if ( entry_old + && entry_old != entry_new) + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); } static void -_nmp_cache_update_add (NMPCache *cache, NMPObject *obj) +_idxcache_update (NMPCache *cache, + const NMDedupMultiEntry *entry_old, + NMPObject *obj_new, + const NMDedupMultiEntry **out_entry_new) { - nm_assert (!obj->is_cached); - nmp_object_ref (obj); - nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); - if (!nm_g_hash_table_add (cache->idx_main, obj)) - g_assert_not_reached (); - obj->is_cached = TRUE; - _nmp_cache_update_cache (cache, obj, FALSE); -} + const NMPClass *klass; + const guint8 *i_idx_type; + NMDedupMultiIdxType *idx_type_o = _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE); + const NMDedupMultiEntry *entry_new = NULL; + nm_auto_nmpobj const NMPObject *obj_old = NULL; -static void -_nmp_cache_update_remove (NMPCache *cache, NMPObject *obj) -{ - nm_assert (obj->is_cached); - _nmp_cache_update_cache (cache, obj, TRUE); - obj->is_cached = FALSE; - if (!g_hash_table_remove (cache->idx_main, obj)) - g_assert_not_reached (); + /* we update an object in the cache. + * + * Note that @entry_old MUST be what is currently tracked in multi_idx, and it must + * have the same ID as @obj_new. */ - /* @obj is possibly a dangling pointer at this point. No problem, multi-index doesn't dereference. */ - nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); -} + nm_assert (cache); + nm_assert (entry_old || obj_new); + nm_assert (!obj_new || nmp_object_is_alive (obj_new)); + nm_assert (!entry_old || entry_old == nm_dedup_multi_index_lookup_obj (cache->multi_idx, idx_type_o, entry_old->obj)); + nm_assert (!obj_new || entry_old == nm_dedup_multi_index_lookup_obj (cache->multi_idx, idx_type_o, obj_new)); + nm_assert (!entry_old || entry_old->head->idx_type == idx_type_o); + nm_assert ( !entry_old + || !obj_new + || nm_dedup_multi_idx_type_partition_equal (idx_type_o, entry_old->obj, obj_new)); + nm_assert ( !entry_old + || !obj_new + || nm_dedup_multi_idx_type_id_equal (idx_type_o, entry_old->obj, obj_new)); + nm_assert ( !entry_old + || !obj_new + || ( obj_new->parent.klass == ((const NMPObject *) entry_old->obj)->parent.klass + && !obj_new->parent.klass->obj_full_equal ((NMDedupMultiObj *) obj_new, entry_old->obj))); -static void -_nmp_cache_update_update (NMPCache *cache, NMPObject *obj, const NMPObject *new) -{ - const guint8 *id_type; + /* keep a reference to the pre-existing entry */ + if (entry_old) + obj_old = nmp_object_ref (entry_old->obj); - nm_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (new)); - nm_assert (obj->is_cached); - nm_assert (!new->is_cached); + /* first update the main index NMP_CACHE_ID_TYPE_OBJECT_TYPE. + * We already know the pre-existing @entry old, so all that + * nm_dedup_multi_index_add_full() effectively does, is update the + * obj reference. + * + * We also get the new boxed object, which we need below. */ + if (obj_new) { + nm_auto_nmpobj NMPObject *obj_old2 = NULL; - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - NMPCacheId cache_id_storage_obj, cache_id_storage_new; - const NMPCacheId *cache_id_obj, *cache_id_new; + nm_dedup_multi_index_add_full (cache->multi_idx, + idx_type_o, + obj_new, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, + NULL, + &entry_new, + (const NMDedupMultiObj **) &obj_old2); + nm_assert (entry_new); + nm_assert (obj_old == obj_old2); + nm_assert (!entry_old || entry_old == entry_new); + } else + nm_dedup_multi_index_remove_entry (cache->multi_idx, entry_old); - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage_obj, &cache_id_obj)) + /* now update all other indexes. We know the previously boxed entry, and the + * newly boxed one. */ + klass = NMP_OBJECT_GET_CLASS (entry_new ? entry_new->obj : obj_old); + 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; - if (!_nmp_object_init_cache_id (new, *id_type, &cache_id_storage_new, &cache_id_new)) - g_assert_not_reached (); - if (!nm_multi_index_move (cache->idx_multi, (NMMultiIndexId *) cache_id_obj, (NMMultiIndexId *) cache_id_new, &obj->object)) - g_assert_not_reached (); + _idxcache_update_box_move (cache, id_type, + obj_old, + entry_new ? entry_new->obj : NULL); } - nmp_object_copy (obj, new, FALSE); + + NM_SET_OUT (out_entry_new, entry_new); } 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) +nmp_cache_remove (NMPCache *cache, + const NMPObject *obj_needle, + gboolean equals_by_ptr, + const NMPObject **out_obj_old) { - NMPObject *old; + const NMDedupMultiEntry *entry_old; + const NMPObject *obj_old; - nm_assert (NMP_OBJECT_IS_VALID (obj)); + entry_old = _lookup_obj (cache, obj_needle); - old = g_hash_table_lookup (cache->idx_main, obj); - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); return NMP_CACHE_OPS_UNCHANGED; } - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - if (equals_by_ptr && old != obj) { + obj_old = entry_old->obj; + + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + + if ( equals_by_ptr + && obj_old != obj_needle) { /* We found an identical object, but we only delete it if it's the same pointer as - * @obj. */ + * @obj_needle. */ return NMP_CACHE_OPS_UNCHANGED; } - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + _idxcache_update (cache, entry_old, NULL, NULL); return NMP_CACHE_OPS_REMOVED; } NMPCacheOpsType -nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_remove_netlink (NMPCache *cache, + const NMPObject *obj_needle, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new = NULL; + const NMPObject *obj_old; + NMPObject *obj_new; + entry_old = _lookup_obj (cache, obj_needle); + + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_UNCHANGED; + } + + obj_old = entry_old->obj; + + if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { /* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be * removed from netlink. Link objects are alive without being in netlink when they * have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink * flag. */ - old = (NMPObject *) nmp_cache_lookup_link (cache, obj_needle->link.ifindex); - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + + if (!obj_old->_link.netlink.is_in_netlink) { + nm_assert (obj_old->_link.udev.device); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; } - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - - if (!old->_link.netlink.is_in_netlink) { - nm_assert (old->_link.udev.device); - return NMP_CACHE_OPS_UNCHANGED; - } - - if (!old->_link.udev.device) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if (!obj_old->_link.udev.device) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_REMOVED; } - obj = nmp_object_clone (old, FALSE); - obj->_link.netlink.is_in_netlink = FALSE; + obj_new = nmp_object_clone (obj_old, FALSE); + obj_new->_link.netlink.is_in_netlink = FALSE; - _nmp_object_fixup_link_master_connected (obj, cache); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + _nmp_object_fixup_link_master_connected (&obj_new, NULL, cache); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + _idxcache_update (cache, + entry_old, + obj_new, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; - } else - return nmp_cache_remove (cache, obj_needle, FALSE, out_obj, out_was_visible, pre_hook, user_data); + } + + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, NULL); + _idxcache_update (cache, entry_old, NULL, NULL); + return NMP_CACHE_OPS_REMOVED; } /** @@ -1779,244 +2070,250 @@ nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObjec * Returns: how the cache changed. **/ NMPCacheOpsType -nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_update_netlink (NMPCache *cache, + NMPObject *obj_hand_over, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!NMP_OBJECT_IS_STACKINIT (obj)); - nm_assert (!obj->is_cached); + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + const NMPObject *obj_old; + gboolean is_alive; + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj_hand_over)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj_hand_over)); /* A link object from netlink must have the udev related fields unset. * We could implement to handle that, but there is no need to support such * a use-case */ - nm_assert (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_LINK || - ( !obj->_link.udev.device - && !obj->link.driver)); + nm_assert (NMP_OBJECT_GET_TYPE (obj_hand_over) != NMP_OBJECT_TYPE_LINK || + ( !obj_hand_over->_link.udev.device + && !obj_hand_over->link.driver)); + nm_assert (nm_dedup_multi_index_obj_find (cache->multi_idx, obj_hand_over) != obj_hand_over); - old = g_hash_table_lookup (cache->idx_main, obj); + entry_old = _lookup_obj (cache, obj_hand_over); - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + if (!entry_old) { - if (!old) { - if (!nmp_object_is_alive (obj)) + NM_SET_OUT (out_obj_old, NULL); + + if (!nmp_object_is_alive (obj_hand_over)) { + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_UNCHANGED; - - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - _nmp_object_fixup_link_master_connected (obj, cache); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); } - if (out_obj) - *out_obj = nmp_object_ref (obj); + if (NMP_OBJECT_GET_TYPE (obj_hand_over) == NMP_OBJECT_TYPE_LINK) { + _nmp_object_fixup_link_master_connected (&obj_hand_over, NULL, cache); + _nmp_object_fixup_link_udev_fields (&obj_hand_over, NULL, cache->use_udev); + } - if (pre_hook) - pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); - _nmp_cache_update_add (cache, obj); + _idxcache_update (cache, + entry_old, + obj_hand_over, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_ADDED; - } else if (old == obj) { - /* updating a cached object inplace is not supported because the object contributes to hash-key - * for NMMultiIndex. Modifying an object that is inside NMMultiIndex means that these - * keys change. - * The problem is, that for a given object NMMultiIndex does not support (efficient) - * reverse lookup to get all the NMPCacheIds to which it belongs. If that would be implemented, - * it would be possible to implement inplace-update. - * - * There is an un-optimized reverse lookup via nm_multi_index_iter_init(), but we don't want - * that because we might have a large number of indexes to search. - * - * We could add efficient reverse lookup by adding a reverse index to NMMultiIndex. But that - * also adds some cost to support an (uncommon?) usage pattern. - * - * Instead we just don't support it, instead we expect the user to - * create a new instance from netlink. - * - * TL;DR: a cached object must never be modified. - */ - g_assert_not_reached (); - } else { - gboolean is_alive = FALSE; + } - nm_assert (old->is_cached); + obj_old = entry_old->obj; - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - if (!obj->_link.netlink.is_in_netlink) { - if (!old->_link.netlink.is_in_netlink) { - nm_assert (old->_link.udev.device); - return NMP_CACHE_OPS_UNCHANGED; - } - if (old->_link.udev.device) { - /* @obj is not in netlink. - * - * This is similar to nmp_cache_remove_netlink(), but there we preserve the - * preexisting netlink properties. The use case of that is when kernel_get_object() - * cannot load an object (based on the id of a needle). - * - * Here we keep the data provided from @obj. The usecase is when receiving - * a valid @obj instance from netlink with RTM_DELROUTE. - */ - is_alive = TRUE; - } - } else - is_alive = TRUE; - - if (is_alive) { - _nmp_object_fixup_link_master_connected (obj, cache); - - /* Merge the netlink parts with what we have from udev. */ - udev_device_unref (obj->_link.udev.device); - obj->_link.udev.device = old->_link.udev.device ? udev_device_ref (old->_link.udev.device) : NULL; - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + if (NMP_OBJECT_GET_TYPE (obj_hand_over) == NMP_OBJECT_TYPE_LINK) { + if (!obj_hand_over->_link.netlink.is_in_netlink) { + if (!obj_old->_link.netlink.is_in_netlink) { + nm_assert (obj_old->_link.udev.device); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); + return NMP_CACHE_OPS_UNCHANGED; } + if (obj_old->_link.udev.device) { + /* @obj_hand_over is not in netlink. + * + * This is similar to nmp_cache_remove_netlink(), but there we preserve the + * preexisting netlink properties. The use case of that is when kernel_get_object() + * cannot load an object (based on the id of a needle). + * + * Here we keep the data provided from @obj_hand_over. The usecase is when receiving + * a valid @obj_hand_over instance from netlink with RTM_DELROUTE. + */ + is_alive = TRUE; + } else + is_alive = FALSE; } else - is_alive = nmp_object_is_alive (obj); + is_alive = TRUE; - if (!is_alive) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); - return NMP_CACHE_OPS_REMOVED; + if (is_alive) { + _nmp_object_fixup_link_master_connected (&obj_hand_over, NULL, cache); + + /* Merge the netlink parts with what we have from udev. */ + udev_device_unref (obj_hand_over->_link.udev.device); + obj_hand_over->_link.udev.device = obj_old->_link.udev.device ? udev_device_ref (obj_old->_link.udev.device) : NULL; + _nmp_object_fixup_link_udev_fields (&obj_hand_over, NULL, cache->use_udev); + + if (obj_hand_over->_link.netlink.lnk) { + nm_auto_nmpobj const NMPObject *lnk_old = obj_hand_over->_link.netlink.lnk; + + /* let's dedup/intern the lnk object. */ + obj_hand_over->_link.netlink.lnk = nm_dedup_multi_index_obj_intern (cache->multi_idx, lnk_old); + } } + } else + is_alive = nmp_object_is_alive (obj_hand_over); - if (nmp_object_equal (old, obj)) - return NMP_CACHE_OPS_UNCHANGED; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); - return NMP_CACHE_OPS_UPDATED; + if (!is_alive) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, NULL); + NM_SET_OUT (out_obj_new, NULL); + return NMP_CACHE_OPS_REMOVED; } + + if (nmp_object_equal (obj_old, obj_hand_over)) { + 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; + } + + _idxcache_update (cache, + entry_old, + obj_hand_over, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); + return NMP_CACHE_OPS_UPDATED; } 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) +nmp_cache_update_link_udev (NMPCache *cache, + int ifindex, + struct udev_device *udevice, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; - old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + entry_old = nmp_cache_lookup_entry_link (cache, ifindex); - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; - - if (!old) { - if (!udevice) + if (!entry_old) { + if (!udevice) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_UNCHANGED; + } - obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); - obj->link.ifindex = ifindex; - obj->_link.udev.device = udev_device_ref (udevice); + obj_new = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); + obj_new->link.ifindex = ifindex; + obj_new->_link.udev.device = udev_device_ref (udevice); - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - nm_assert (nmp_object_is_alive (obj)); - - if (out_obj) - *out_obj = nmp_object_ref (obj); - - if (pre_hook) - pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); - _nmp_cache_update_add (cache, obj); + _idxcache_update (cache, + NULL, + obj_new, + &entry_new); + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_ADDED; } else { - nm_assert (old->is_cached); + obj_old = entry_old->obj; + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - - if (old->_link.udev.device == udevice) + if (obj_old->_link.udev.device == udevice) { + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; + } - if (!udevice && !old->_link.netlink.is_in_netlink) { - /* the update would make @old invalid. Remove it. */ - if (pre_hook) - pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); - _nmp_cache_update_remove (cache, old); + if (!udevice && !obj_old->_link.netlink.is_in_netlink) { + /* the update would make @obj_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_REMOVED; } - obj = nmp_object_clone (old, FALSE); + obj_new = nmp_object_clone (obj_old, FALSE); - udev_device_unref (obj->_link.udev.device); - obj->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL; + udev_device_unref (obj_new->_link.udev.device); + obj_new->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL; - _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + _nmp_object_fixup_link_udev_fields (&obj_new, NULL, cache->use_udev); - nm_assert (nmp_object_is_alive (obj)); - - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + _idxcache_update (cache, + entry_old, + obj_new, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; } } NMPCacheOpsType -nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +nmp_cache_update_link_master_connected (NMPCache *cache, + int ifindex, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new) { - NMPObject *old; - nm_auto_nmpobj NMPObject *obj = NULL; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new = NULL; + const NMPObject *obj_old; + nm_auto_nmpobj NMPObject *obj_new = NULL; - old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); - - if (!old) { - if (out_obj) - *out_obj = NULL; - if (out_was_visible) - *out_was_visible = FALSE; + entry_old = nmp_cache_lookup_entry_link (cache, ifindex); + if (!entry_old) { + NM_SET_OUT (out_obj_old, NULL); + NM_SET_OUT (out_obj_new, NULL); return NMP_CACHE_OPS_UNCHANGED; } - nm_assert (old->is_cached); + obj_old = entry_old->obj; - if (out_obj) - *out_obj = nmp_object_ref (old); - if (out_was_visible) - *out_was_visible = nmp_object_is_visible (old); - - if (!nmp_cache_link_connected_needs_toggle (cache, old, NULL, NULL)) + if (!nmp_cache_link_connected_needs_toggle (cache, obj_old, NULL, NULL)) { + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; + } - obj = nmp_object_clone (old, FALSE); - obj->link.connected = !old->link.connected; + obj_new = nmp_object_clone (obj_old, FALSE); + obj_new->link.connected = !obj_old->link.connected; - nm_assert (nmp_object_is_alive (obj)); - - if (pre_hook) - pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); - _nmp_cache_update_update (cache, old, obj); + NM_SET_OUT (out_obj_old, nmp_object_ref (obj_old)); + _idxcache_update (cache, + entry_old, + obj_new, + &entry_new); + NM_SET_OUT (out_obj_new, nmp_object_ref (entry_new->obj)); return NMP_CACHE_OPS_UPDATED; } /*****************************************************************************/ -NMPCache * -nmp_cache_new (gboolean use_udev) +void +nmp_cache_dirty_set_all (NMPCache *cache, NMPObjectType obj_type) { - NMPCache *cache = g_new (NMPCache, 1); + NMPObject obj_needle; + + nm_assert (cache); + + nm_dedup_multi_index_dirty_set_head (cache->multi_idx, + _idx_type_get (cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE), + _nmp_object_stackinit_from_type (&obj_needle, obj_type)); +} + +/*****************************************************************************/ + +NMPCache * +nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev) +{ + NMPCache *cache = g_slice_new0 (NMPCache); + guint i; + + for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) + _dedup_multi_idx_type_init ((DedupMultiIdxType *) _idx_type_get (cache, i), i); + + cache->multi_idx = nm_dedup_multi_index_ref (multi_idx); - cache->idx_main = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - NULL); - cache->idx_multi = nm_multi_index_new ((NMMultiIndexFuncHash) nmp_cache_id_hash, - (NMMultiIndexFuncEqual) nmp_cache_id_equal, - (NMMultiIndexFuncClone) nmp_cache_id_clone, - (NMMultiIndexFuncDestroy) nmp_cache_id_destroy); cache->use_udev = !!use_udev; return cache; } @@ -2024,23 +2321,14 @@ nmp_cache_new (gboolean use_udev) void nmp_cache_free (NMPCache *cache) { - GHashTableIter iter; - NMPObject *obj; + guint i; - /* No need to cumbersomely remove the objects properly. They are not hooked up - * in a complicated way, we can just unref them together with cache->idx_main. - * - * But we must clear the @is_cached flag. */ - g_hash_table_iter_init (&iter, cache->idx_main); - while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { - nm_assert (obj->is_cached); - obj->is_cached = FALSE; - } + for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) + nm_dedup_multi_index_remove_idx (cache->multi_idx, _idx_type_get (cache, i)); - nm_multi_index_free (cache->idx_multi); - g_hash_table_unref (cache->idx_main); + nm_dedup_multi_index_unref (cache->multi_idx); - g_free (cache); + g_slice_free (NMPCache, cache); } /*****************************************************************************/ @@ -2048,63 +2336,13 @@ nmp_cache_free (NMPCache *cache) void ASSERT_nmp_cache_is_consistent (const NMPCache *cache) { -#if NM_MORE_ASSERTS - NMMultiIndexIter iter_multi; - GHashTableIter iter_hash; - guint i, len; - NMPCacheId cache_id_storage; - const NMPCacheId *cache_id, *cache_id2; - const NMPlatformObject *const *objects; - const NMPObject *obj; - - g_assert (cache); - - g_hash_table_iter_init (&iter_hash, cache->idx_main); - while (g_hash_table_iter_next (&iter_hash, (gpointer *) &obj, NULL)) { - const guint8 *id_type; - - g_assert (NMP_OBJECT_IS_VALID (obj)); - g_assert (nmp_object_is_alive (obj)); - - for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) { - if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id)) - continue; - if (!cache_id) - continue; - g_assert (nm_multi_index_contains (cache->idx_multi, &cache_id->base, &obj->object)); - } - } - - nm_multi_index_iter_init (&iter_multi, cache->idx_multi, NULL); - while (nm_multi_index_iter_next (&iter_multi, - (const NMMultiIndexId **) &cache_id, - (void *const**) &objects, - &len)) { - g_assert (len > 0 && objects && objects[len] == NULL); - - for (i = 0; i < len; i++) { - g_assert (objects[i]); - obj = NMP_OBJECT_UP_CAST (objects[i]); - g_assert (NMP_OBJECT_IS_VALID (obj)); - - /* for now, enforce that all objects for a certain index are of the same type. */ - g_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[0]))); - - if (!_nmp_object_init_cache_id (obj, cache_id->_id_type, &cache_id_storage, &cache_id2)) - g_assert_not_reached (); - g_assert (cache_id2); - g_assert (nmp_cache_id_equal (cache_id, cache_id2)); - g_assert_cmpint (nmp_cache_id_hash (cache_id), ==, nmp_cache_id_hash (cache_id2)); - - g_assert (obj == g_hash_table_lookup (cache->idx_main, obj)); - } - } -#endif } + /*****************************************************************************/ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { [NMP_OBJECT_TYPE_LINK - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LINK, .sizeof_data = sizeof (NMPObjectLink), .sizeof_public = sizeof (NMPlatformLink), @@ -2114,7 +2352,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, .supported_cache_ids = _supported_cache_ids_link, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link, + .cmd_obj_hash = _vt_cmd_obj_hash_link, .cmd_obj_cmp = _vt_cmd_obj_cmp_link, .cmd_obj_copy = _vt_cmd_obj_copy_link, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link, @@ -2127,9 +2365,11 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_link_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_link_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp, }, [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, .sizeof_data = sizeof (NMPObjectIP4Address), .sizeof_public = sizeof (NMPlatformIP4Address), @@ -2139,7 +2379,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address, @@ -2147,9 +2386,11 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_address_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_ip4_address_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp, }, [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, .sizeof_data = sizeof (NMPObjectIP6Address), .sizeof_public = sizeof (NMPlatformIP6Address), @@ -2159,7 +2400,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, @@ -2167,9 +2407,11 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_address_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_ip6_address_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, .sizeof_data = sizeof (NMPObjectIP4Route), .sizeof_public = sizeof (NMPlatformIP4Route), @@ -2178,8 +2420,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .rtm_gettype = RTM_GETROUTE, .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ip4_route, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, + .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, @@ -2187,9 +2428,11 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_ip4_route_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, .sizeof_data = sizeof (NMPObjectIP6Route), .sizeof_public = sizeof (NMPlatformIP6Route), @@ -2198,8 +2441,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .rtm_gettype = RTM_GETROUTE, .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ip6_route, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, + .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, @@ -2207,100 +2449,122 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_ip6_route_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, }, [NMP_OBJECT_TYPE_LNK_GRE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_GRE, .sizeof_data = sizeof (NMPObjectLnkGre), .sizeof_public = sizeof (NMPlatformLnkGre), .obj_type_name = "gre", .lnk_link_type = NM_LINK_TYPE_GRE, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_gre_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp, }, [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, .sizeof_data = sizeof (NMPObjectLnkInfiniband), .sizeof_public = sizeof (NMPlatformLnkInfiniband), .obj_type_name = "infiniband", .lnk_link_type = NM_LINK_TYPE_INFINIBAND, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_infiniband_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_infiniband_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_infiniband_cmp, }, [NMP_OBJECT_TYPE_LNK_IP6TNL - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IP6TNL, .sizeof_data = sizeof (NMPObjectLnkIp6Tnl), .sizeof_public = sizeof (NMPlatformLnkIp6Tnl), .obj_type_name = "ip6tnl", .lnk_link_type = NM_LINK_TYPE_IP6TNL, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_ip6tnl_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp, }, [NMP_OBJECT_TYPE_LNK_IPIP - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IPIP, .sizeof_data = sizeof (NMPObjectLnkIpIp), .sizeof_public = sizeof (NMPlatformLnkIpIp), .obj_type_name = "ipip", .lnk_link_type = NM_LINK_TYPE_IPIP, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ipip_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_ipip_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ipip_cmp, }, [NMP_OBJECT_TYPE_LNK_MACSEC - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACSEC, .sizeof_data = sizeof (NMPObjectLnkMacsec), .sizeof_public = sizeof (NMPlatformLnkMacsec), .obj_type_name = "macsec", .lnk_link_type = NM_LINK_TYPE_MACSEC, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macsec_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_macsec_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macsec_cmp, }, [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN, .sizeof_data = sizeof (NMPObjectLnkMacvlan), .sizeof_public = sizeof (NMPlatformLnkMacvlan), .obj_type_name = "macvlan", .lnk_link_type = NM_LINK_TYPE_MACVLAN, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_macvlan_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, }, [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP, .sizeof_data = sizeof (NMPObjectLnkMacvtap), .sizeof_public = sizeof (NMPlatformLnkMacvtap), .obj_type_name = "macvtap", .lnk_link_type = NM_LINK_TYPE_MACVTAP, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_macvlan_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, }, [NMP_OBJECT_TYPE_LNK_SIT - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_SIT, .sizeof_data = sizeof (NMPObjectLnkSit), .sizeof_public = sizeof (NMPlatformLnkSit), .obj_type_name = "sit", .lnk_link_type = NM_LINK_TYPE_SIT, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_sit_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_sit_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp, }, [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, .sizeof_data = sizeof (NMPObjectLnkVlan), .sizeof_public = sizeof (NMPlatformLnkVlan), .obj_type_name = "vlan", .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_hash = _vt_cmd_obj_hash_lnk_vlan, .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vlan_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_vlan_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, }, [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VXLAN, .sizeof_data = sizeof (NMPObjectLnkVxlan), .sizeof_public = sizeof (NMPlatformLnkVxlan), .obj_type_name = "vxlan", .lnk_link_type = NM_LINK_TYPE_VXLAN, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vxlan_to_string, + .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_lnk_vxlan_hash, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vxlan_cmp, }, }; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index fb7f713f31..9cdad2e808 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_DESTINATION, __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,10 +115,7 @@ 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); - + guint (*cmd_obj_hash) (const NMPObject *obj); 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); @@ -170,6 +130,7 @@ typedef struct { guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj); 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); + guint (*cmd_plobj_hash) (const NMPlatformObject *obj); int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; @@ -182,7 +143,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 +227,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 +288,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 +296,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 +305,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 +318,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,12 +337,84 @@ 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_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) +{ + /* 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; +} + NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); NMPObject *nmp_object_new_link (int ifindex); @@ -395,6 +427,7 @@ const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric); const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); +guint nmp_object_hash (const NMPObject *obj); 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); @@ -404,13 +437,13 @@ guint nmp_object_id_hash (const NMPObject *obj); 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)); } typedef struct _NMPCache NMPCache; @@ -418,23 +451,88 @@ 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_link (const NMPCache *cache, int ifindex); -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); - -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); +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_visible (NMPLookup *lookup, + NMPObjectType obj_type, + int ifindex, + gboolean only_default); +const NMPLookup *nmp_lookup_init_route_by_dest (NMPLookup *lookup, + int addr_family, + gconstpointer network, + guint plen, + guint32 metric); + +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); + if (has_next) { + nm_assert (NMP_OBJECT_IS_VALID (iter->current->obj)); + NM_SET_OUT (out_obj, iter->current->obj); + } + return has_next; +} + +static inline gboolean +nmp_cache_iter_next_link (NMDedupMultiIter *iter, const NMPlatformLink **out_obj) +{ + gboolean has_next; + + has_next = nm_dedup_multi_iter_next (iter); + if (has_next) { + nm_assert (NMP_OBJECT_GET_TYPE (iter->current->obj) == NMP_OBJECT_TYPE_LINK); + NM_SET_OUT (out_obj, &(((const NMPObject *) iter->current->obj)->link)); + } + return has_next; +} + +#define nmp_cache_iter_for_each(iter, head, obj) \ + for (nm_dedup_multi_iter_init ((iter), \ + (head)); \ + nmp_cache_iter_next ((iter), (obj)); \ + ) + +#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_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route); const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, @@ -444,10 +542,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 +551,141 @@ 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); +NMPCacheOpsType nmp_cache_remove (NMPCache *cache, + const NMPObject *obj_needle, + gboolean equals_by_ptr, + 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, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new); +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); -NMPCache *nmp_cache_new (gboolean use_udev); +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 +} + +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 const NMDedupMultiHeadEntry * +nm_platform_lookup_route_visible (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + gboolean only_default) +{ + NMPLookup lookup; + + nmp_lookup_init_route_visible (&lookup, obj_type, ifindex, only_default); + return nm_platform_lookup (platform, &lookup); +} + +static inline GPtrArray * +nm_platform_lookup_route_visible_clone (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + gboolean only_default, + gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + gpointer user_data) +{ + NMPLookup lookup; + + nmp_lookup_init_route_visible (&lookup, obj_type, ifindex, only_default); + return nm_platform_lookup_clone (platform, &lookup, predicate, user_data); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_route_by_dest (NMPlatform *platform, + int addr_family, + gconstpointer network, + guint plen, + guint32 metric) +{ + NMPLookup lookup; + + nmp_lookup_init_route_by_dest (&lookup, addr_family, network, plen, metric); + return nm_platform_lookup (platform, &lookup); +} + #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 26c8c2b4bd..690ba35a53 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -29,8 +29,8 @@ test_cleanup_internal (void) int ifindex; GArray *addresses4; GArray *addresses6; - GArray *routes4; - GArray *routes6; + GPtrArray *routes4; + GPtrArray *routes6; in_addr_t addr4; in_addr_t network4; int plen4 = 24; @@ -72,8 +72,8 @@ test_cleanup_internal (void) addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); - routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes4 = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); + routes6 = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); g_assert_cmpint (addresses4->len, ==, 1); g_assert_cmpint (addresses6->len, ==, 2); /* also has a IPv6 LL address. */ @@ -82,26 +82,24 @@ test_cleanup_internal (void) g_array_unref (addresses4); g_array_unref (addresses6); - g_array_unref (routes4); - g_array_unref (routes6); + g_ptr_array_unref (routes4); + g_ptr_array_unref (routes6); /* Delete interface with all addresses and routes */ g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); - routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes4 = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); + routes6 = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); g_assert_cmpint (addresses4->len, ==, 0); g_assert_cmpint (addresses6->len, ==, 0); - g_assert_cmpint (routes4->len, ==, 0); - g_assert_cmpint (routes6->len, ==, 0); + g_assert (!routes4); + g_assert (!routes6); g_array_unref (addresses4); g_array_unref (addresses6); - g_array_unref (routes4); - g_array_unref (routes6); } NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index eec92af1fd..62c2c83fd1 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -156,9 +156,9 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; - GArray *links; - NMPlatformLink *cached; - int i; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *cached; g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_LINK); g_assert (received); @@ -188,19 +188,21 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink /* Check the data */ g_assert (received->ifindex > 0); - links = nm_platform_link_get_all (NM_PLATFORM_GET, TRUE); - for (i = 0; i < links->len; i++) { - cached = &g_array_index (links, NMPlatformLink, i); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nm_platform_lookup (platform, &lookup), + &cached) { + if (!nmp_object_is_visible (NMP_OBJECT_UP_CAST (cached))) + continue; if (cached->ifindex == received->ifindex) { g_assert_cmpint (nm_platform_link_cmp (cached, received), ==, 0); g_assert (!memcmp (cached, received, sizeof (*cached))); if (data->change_type == NM_PLATFORM_SIGNAL_REMOVED) g_error ("Deleted link still found in the local cache."); - g_array_unref (links); return; } } - g_array_unref (links); if (data->change_type != NM_PLATFORM_SIGNAL_REMOVED) g_error ("Added/changed link not found in the local cache."); diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index b4ed0682ea..b9e879ff62 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -5,6 +5,7 @@ #include #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "platform/nm-fake-platform.h" #include "platform/nm-linux-platform.h" @@ -187,6 +188,30 @@ void nmtstp_ip6_route_add (NMPlatform *platform, guint32 metric, guint32 mss); +static inline GPtrArray * +nmtstp_ip4_route_get_all (NMPlatform *platform, + int ifindex) +{ + return nm_platform_lookup_route_visible_clone (platform, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex, + FALSE, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL); +} + +static inline GPtrArray * +nmtstp_ip6_route_get_all (NMPlatform *platform, + int ifindex) +{ + return nm_platform_lookup_route_visible_clone (platform, + NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex, + FALSE, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL); +} + /*****************************************************************************/ const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index e772662c37..342aa0d610 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -44,7 +44,7 @@ static void test_link_get_all (void) { gs_unref_object NMPlatform *platform = NULL; - gs_unref_array GArray *links = NULL; + gs_unref_ptrarray GPtrArray *links = NULL; platform = nm_linux_platform_new (TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index df204b4683..0e5fcbe453 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -264,7 +264,8 @@ test_slave (int master, int type, SignalData *master_changed) } g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - if (nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { + if ( nmtstp_is_root_test () + && nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { if (nm_platform_link_get_type (NM_PLATFORM_GET, master) == NM_LINK_TYPE_TEAM) { /* Older team versions (e.g. Fedora 17) have a bug that team master stays * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if @@ -285,7 +286,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 0, 2); /* Enslave again * @@ -327,7 +328,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); accept_signal (link_removed); } - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 0, 2); ensure_no_signal (master_changed); diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 42dfc572e6..11fc59e24b 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -33,6 +33,60 @@ struct { /*****************************************************************************/ +static void +test_obj_base (void) +{ + static const union { + GObject g; + NMPObject k; + } x = { }; + static const union { + GTypeClass k; + NMPClass c; + } l = { }; + static const GObject *g = &x.g; + static const GTypeClass *k = &l.k; + static const NMPObject *o = &x.k; + static const NMPClass *c = &l.c; + + NMObjBaseInst *obj; + gs_unref_object GCancellable *obj_cancellable = g_cancellable_new (); + nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10); + +#define STATIC_ASSERT(cond) \ + G_STMT_START { \ + G_STATIC_ASSERT (cond); \ + G_STATIC_ASSERT_EXPR (cond); \ + g_assert (cond); \ + } G_STMT_END + + STATIC_ASSERT (&g->g_type_instance == (void *) &o->_class); + STATIC_ASSERT (&g->g_type_instance.g_class == (void *) &o->_class); + + STATIC_ASSERT (sizeof (o->parent.parent) == sizeof (GTypeInstance)); + + STATIC_ASSERT (&c->parent == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type_class == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type == (void *) c); + STATIC_ASSERT (&c->parent.parent.g_type == &k->g_type); + + STATIC_ASSERT (sizeof (c->parent.parent) == sizeof (GTypeClass)); + + STATIC_ASSERT (&o->parent == (void *) o); + STATIC_ASSERT (&o->parent.klass == (void *) &o->_class); + + obj = (NMObjBaseInst *) obj_cancellable; + g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + + obj = (NMObjBaseInst *) obj_link; + g_assert (NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (!G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + +} + +/*****************************************************************************/ + static gboolean _nmp_object_id_equal (const NMPObject *a, const NMPObject *b) { @@ -56,150 +110,144 @@ _nmp_object_equal (const NMPObject *a, const NMPObject *b) /*****************************************************************************/ static void -_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains) +_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMDedupMultiHeadEntry *head_entry, const NMPObject *obj, gboolean visible_only, gboolean contains) { - const NMPlatformObject *const *objects; - guint i, len; + NMDedupMultiIter iter; gboolean found; + guint i, len; + const NMPObject *o; - g_assert (cache_id); g_assert (NMP_OBJECT_IS_VALID (obj)); g_assert (nmp_cache_lookup_obj (cache, obj) == obj); + g_assert (!head_entry || (head_entry->len > 0 && c_list_length (&head_entry->lst_entries_head) == head_entry->len)); - objects = nmp_cache_lookup_multi (cache, cache_id, &len); - - g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len])); + len = head_entry ? head_entry->len : 0; found = FALSE; - for (i = 0; i < len; i++) { - NMPObject *o; - - g_assert (objects[i]); - o = NMP_OBJECT_UP_CAST (objects[i]); + i = 0; + nmp_cache_iter_for_each (&iter, + head_entry, + &o) { g_assert (NMP_OBJECT_IS_VALID (o)); - if (obj == o) { - g_assert (!found); - found = TRUE; + if ( !visible_only + || nmp_object_is_visible (o)) { + g_assert (!found); + found = TRUE; + } } + i++; } + g_assert (len == i); g_assert (!!contains == found); } +static void +_assert_cache_multi_lookup_contains_link (const NMPCache *cache, + gboolean visible_only, + const NMPObject *obj, + gboolean contains) +{ + const NMDedupMultiHeadEntry *head_entry; + NMPLookup lookup; + + g_assert (cache); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + head_entry = nmp_cache_lookup (cache, &lookup); + _assert_cache_multi_lookup_contains (cache, head_entry, obj, visible_only, contains); +} + /*****************************************************************************/ -typedef struct { - NMPCache *cache; - NMPCacheOpsType expected_ops_type; - const NMPObject *obj_clone; - NMPObject *new_clone; - gboolean was_visible; - gboolean called; -} _NMPCacheUpdateData; - static void -_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +ops_post_check (NMPCache *cache, + NMPCacheOpsType ops_type, + const NMPObject *obj_old, + const NMPObject *obj_new, + const NMPObject *obj_new_expected, + NMPCacheOpsType expected_ops_type) { - _NMPCacheUpdateData *data = user_data; + g_assert (cache); - g_assert (data); - g_assert (!data->called); - g_assert (data->cache == cache); - - g_assert_cmpint (data->expected_ops_type, ==, ops_type); + g_assert_cmpint (expected_ops_type, ==, ops_type); switch (ops_type) { case NMP_CACHE_OPS_ADDED: - g_assert (!old); - g_assert (NMP_OBJECT_IS_VALID (new)); - g_assert (nmp_object_is_alive (new)); - g_assert (nmp_object_id_equal (data->obj_clone, new)); - g_assert (nmp_object_equal (data->obj_clone, new)); + g_assert (!obj_old); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (nmp_object_is_alive (obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + g_assert (nmp_object_equal (obj_new_expected, obj_new)); break; case NMP_CACHE_OPS_UPDATED: - g_assert (NMP_OBJECT_IS_VALID (old)); - g_assert (NMP_OBJECT_IS_VALID (new)); - g_assert (nmp_object_is_alive (old)); - g_assert (nmp_object_is_alive (new)); - g_assert (nmp_object_id_equal (data->obj_clone, new)); - g_assert (nmp_object_id_equal (data->obj_clone, old)); - g_assert (nmp_object_id_equal (old, new)); - g_assert (nmp_object_equal (data->obj_clone, new)); - g_assert (!nmp_object_equal (data->obj_clone, old)); - g_assert (!nmp_object_equal (old, new)); + g_assert (obj_old != obj_new); + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (nmp_object_is_alive (obj_old)); + g_assert (nmp_object_is_alive (obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_old)); + g_assert (nmp_object_id_equal (obj_old, obj_new)); + g_assert (nmp_object_equal (obj_new_expected, obj_new)); + g_assert (!nmp_object_equal (obj_new_expected, obj_old)); + g_assert (!nmp_object_equal (obj_old, obj_new)); break; case NMP_CACHE_OPS_REMOVED: - g_assert (!new); - g_assert (NMP_OBJECT_IS_VALID (old)); - g_assert (nmp_object_is_alive (old)); - g_assert (nmp_object_id_equal (data->obj_clone, old)); + g_assert (!obj_new); + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (nmp_object_is_alive (obj_old)); + if (obj_new_expected) + g_assert (nmp_object_id_equal (obj_new_expected, obj_old)); + break; + case NMP_CACHE_OPS_UNCHANGED: + g_assert (obj_old == obj_new); + if (obj_old) { + g_assert (NMP_OBJECT_IS_VALID (obj_old)); + g_assert (nmp_object_is_alive (obj_old)); + g_assert (nmp_object_equal (obj_old, obj_new)); + g_assert (nmp_object_id_equal (obj_new_expected, obj_new)); + } else + g_assert (!obj_new_expected); break; default: g_assert_not_reached (); } - - data->was_visible = old ? nmp_object_is_visible (old) : FALSE; - data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL; - data->called = TRUE; } static void -_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type) +_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **out_obj_old, const NMPObject **out_obj_new, NMPCacheOpsType expected_ops_type) { NMPCacheOpsType ops_type; - NMPObject *obj2; - gboolean was_visible; - nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); - nm_auto_nmpobj NMPObject *new_clone = NULL; + const NMPObject *obj_prev; const NMPObject *obj_old; - _NMPCacheUpdateData data = { - .cache = cache, - .expected_ops_type = expected_ops_type, - .obj_clone = obj_clone, - }; - - obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex); - if (obj_old && obj_old->_link.udev.device) - obj_clone->_link.udev.device = udev_device_ref (obj_old->_link.udev.device); - _nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache)); + const NMPObject *obj_new; + nm_auto_nmpobj NMPObject *obj_new_expected = NULL; g_assert (cache); g_assert (NMP_OBJECT_IS_VALID (obj)); - ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data); + obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex); + obj_new_expected = nmp_object_clone (obj, FALSE); + if (obj_prev && obj_prev->_link.udev.device) + obj_new_expected->_link.udev.device = udev_device_ref (obj_prev->_link.udev.device); + _nmp_object_fixup_link_udev_fields (&obj_new_expected, NULL, nmp_cache_use_udev_get (cache)); - new_clone = data.new_clone; + ops_type = nmp_cache_update_netlink (cache, obj, &obj_old, &obj_new); + ops_post_check (cache, ops_type, obj_old, obj_new, + nmp_object_is_alive (obj_new_expected) ? obj_new_expected : NULL, + expected_ops_type); - g_assert_cmpint (ops_type, ==, expected_ops_type); - - if (ops_type != NMP_CACHE_OPS_UNCHANGED) { - g_assert (NMP_OBJECT_IS_VALID (obj2)); - g_assert (data.called); - g_assert_cmpint (data.was_visible, ==, was_visible); - - if (ops_type == NMP_CACHE_OPS_REMOVED) - g_assert (!data.new_clone); - else { - g_assert (data.new_clone); - g_assert (nmp_object_equal (obj2, data.new_clone)); - } - } else { - g_assert (!data.called); - g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2)); - } - - g_assert (!obj2 || nmp_object_id_equal (obj, obj2)); - if (ops_type != NMP_CACHE_OPS_REMOVED && obj2) - g_assert (nmp_object_equal (obj, obj2)); - - if (out_obj) - *out_obj = obj2; + if (out_obj_new) + *out_obj_new = obj_new; else - nmp_object_unref (obj2); - if (out_was_visible) - *out_was_visible = was_visible; + nmp_object_unref (obj_new); + if (out_obj_old) + *out_obj_old = obj_old; + else + nmp_object_unref (obj_old); } static const NMPlatformLink pl_link_2 = { @@ -218,168 +266,189 @@ static void test_cache_link (void) { NMPCache *cache; - NMPObject *obj1, *obj2; + NMPObject *objm1; + const NMPObject *obj_old, *obj_new; NMPObject objs1; - gboolean was_visible; - NMPCacheId cache_id_storage; struct udev_device *udev_device_2 = g_list_nth_data (global.udev_devices, 0); struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0); NMPCacheOpsType ops_type; + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; - cache = nmp_cache_new (nmtst_get_rand_int () % 2); + multi_idx = nm_dedup_multi_index_new (); + + cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2); /* if we have a link, and don't set is_in_netlink, adding it has no effect. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1); - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (NMP_OBJECT_UP_CAST (&objm1->object) == objm1); + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (!obj2); - g_assert (!was_visible); - g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (!obj_old); + g_assert (!obj_new); + g_assert (!nmp_cache_lookup_obj (cache, objm1)); g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); - nmp_object_unref (obj1); + nmp_object_unref (objm1); /* Only when setting @is_in_netlink the link is added. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (!obj_old); + g_assert (obj_new); + g_assert (objm1 == obj_new); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_new); /* updating the same link with identical value, has no effect. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (obj_old); + g_assert (obj_new); + g_assert (obj_new != objm1); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + nmp_object_unref (objm1); + nmp_object_unref (obj_new); + nmp_object_unref (obj_new); /* remove the link from netlink */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_REMOVED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (was_visible); - g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (obj_old); + g_assert (!obj_new); + g_assert (!nmp_cache_lookup_obj (cache, objm1)); g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); if (udev_device_2) { /* now add the link only with aspect UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj_old, &obj_new); ASSERT_nmp_cache_is_consistent (cache); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (!nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj2); + g_assert (!obj_old); + g_assert (obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (!nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + nmp_object_unref (obj_new); } /* add it in netlink too. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + if (udev_device_2) { + g_assert (obj_old); + g_assert (!nmp_object_is_visible (obj_old)); + } else + g_assert (!obj_old); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* remove again from netlink. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - obj1->_link.netlink.is_in_netlink = FALSE; - g_assert (!nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + objm1->_link.netlink.is_in_netlink = FALSE; + g_assert (!nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (was_visible); + if (udev_device_2) + g_assert (obj_new == objm1); + else + g_assert (!obj_new); + g_assert (obj_old); + g_assert (nmp_object_is_alive (obj_old)); if (udev_device_2) { - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); - g_assert (!nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new); + g_assert (!nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); } else { - g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL); + g_assert (nmp_cache_lookup_obj (cache, objm1) == NULL); g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL); - g_assert (nmp_object_is_visible (obj2)); + g_assert (nmp_object_is_visible (obj_new)); } - nmp_object_unref (obj1); - nmp_object_unref (obj2); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* now another link only with aspect UDEV. */ if (udev_device_3) { /* now add the link only with aspect UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj_old, &obj_new); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (NMP_OBJECT_IS_VALID (obj2)); - g_assert (!was_visible); - g_assert (!nmp_object_is_visible (obj2)); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE); - g_assert_cmpint (obj2->link.initialized, ==, FALSE); - nmp_object_unref (obj2); + g_assert (NMP_OBJECT_IS_VALID (obj_new)); + g_assert (!obj_old); + g_assert (!nmp_object_is_visible (obj_new)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, FALSE); + g_assert_cmpint (obj_new->link.initialized, ==, FALSE); + nmp_object_unref (obj_new); /* add it in netlink too. */ - obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); - obj1->_link.netlink.is_in_netlink = TRUE; - g_assert (nmp_object_is_alive (obj1)); - _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED); + objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); + objm1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (objm1)); + _nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UPDATED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (obj2 != obj1); - g_assert (nmp_object_equal (obj1, obj2)); - g_assert (!was_visible); - g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint (obj2->link.initialized, ==, TRUE); - nmp_object_unref (obj1); - nmp_object_unref (obj2); + g_assert (obj_old); + g_assert (obj_new == objm1); + g_assert (nmp_object_equal (objm1, obj_new)); + g_assert (!obj_old || !nmp_object_is_visible (obj_old)); + g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj_new->link.initialized, ==, TRUE); + nmp_object_unref (objm1); + nmp_object_unref (obj_old); + nmp_object_unref (obj_new); /* remove UDEV. */ - ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL); + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj_old, &obj_new); g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED); ASSERT_nmp_cache_is_consistent (cache); - g_assert (was_visible); - g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); - g_assert (nmp_object_is_visible (obj2)); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); - _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); - g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache)); - nmp_object_unref (obj2); + g_assert (obj_old && nmp_object_is_visible (obj_old)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new); + g_assert (nmp_object_is_visible (obj_new)); + _assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE); + _assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE); + g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj_new->link.initialized, ==, !nmp_cache_use_udev_get (cache)); + nmp_object_unref (obj_new); + nmp_object_unref (obj_old); } nmp_cache_free (cache); @@ -429,6 +498,7 @@ main (int argc, char **argv) udev_enumerate_unref (enumerator); } + g_test_add_func ("/nmp-object/obj-base", test_obj_base); g_test_add_func ("/nmp-object/cache_link", test_cache_link); result = g_test_run (); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index c96af93e51..3fe0f3128a 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -182,7 +182,7 @@ test_ip4_route (void) SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); - GArray *routes; + GPtrArray *routes; NMPlatformIP4Route rts[3]; in_addr_t network; guint8 plen = 24; @@ -219,7 +219,7 @@ test_ip4_route (void) accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].network = gateway; @@ -246,8 +246,8 @@ test_ip4_route (void) rts[2].mss = mss; rts[2].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); g_assert_cmpint (routes->len, ==, 3); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); - g_array_unref (routes); + nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); @@ -277,7 +277,7 @@ test_ip6_route (void) SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_route_callback); - GArray *routes; + GPtrArray *routes; NMPlatformIP6Route rts[3]; struct in6_addr network; guint8 plen = 64; @@ -321,7 +321,7 @@ test_ip6_route (void) accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].network = gateway; @@ -348,8 +348,8 @@ test_ip6_route (void) rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); - g_array_unref (routes); + nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); @@ -400,7 +400,7 @@ test_ip4_route_options (void) int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); NMPlatformIP4Route route = { }; in_addr_t network; - GArray *routes; + GPtrArray *routes; NMPlatformIP4Route rts[1]; inet_pton (AF_INET, "172.16.1.0", &network); @@ -421,9 +421,7 @@ test_ip4_route_options (void) g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, &route)); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); memset (rts, 0, sizeof (rts)); rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK); @@ -438,14 +436,12 @@ test_ip4_route_options (void) rts[0].initrwnd = 50; rts[0].mtu = 1350; rts[0].lock_cwnd = TRUE; - g_assert_cmpint (routes->len, ==, 1); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); + nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE); + g_ptr_array_unref (routes); /* Remove route */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20)); - - g_array_unref (routes); } @@ -454,7 +450,7 @@ test_ip6_route_options (gconstpointer test_data) { const int TEST_IDX = GPOINTER_TO_INT (test_data); const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); - gs_unref_array GArray *routes = NULL; + GPtrArray *routes; #define RTS_MAX 1 NMPlatformIP6Route rts_add[RTS_MAX] = { }; NMPlatformIP6Route rts_cmp[RTS_MAX] = { }; @@ -523,9 +519,7 @@ test_ip6_route_options (gconstpointer test_data) for (i = 0; i < rts_n; i++) g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, &rts_add[i])); - routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX); switch (TEST_IDX) { case 1: case 2: @@ -537,9 +531,9 @@ test_ip6_route_options (gconstpointer test_data) default: g_assert_not_reached (); } - g_assert_cmpint (routes->len, ==, rts_n); - nmtst_platform_ip6_routes_equal ((const NMPlatformIP6Route *) routes->data, rts_cmp, rts_n, TRUE); + nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts_cmp, routes->len, TRUE); + g_ptr_array_unref (routes); for (i = 0; i < rts_n; i++) { g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, IFINDEX, diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index 6343df8bf4..104adf5475 100644 --- a/src/ppp/nm-ppp-manager.c +++ b/src/ppp/nm-ppp-manager.c @@ -409,7 +409,8 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, nm_clear_g_source (&priv->ppp_timeout_handler); - config = nm_ip4_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); + config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); memset (&address, 0, sizeof (address)); address.plen = 32; @@ -505,7 +506,8 @@ impl_ppp_manager_set_ip6_config (NMPPPManager *manager, nm_clear_g_source (&priv->ppp_timeout_handler); - config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), + nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface)); memset (&addr, 0, sizeof (addr)); addr.plen = 64; diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 492e1b1f60..c5d26163fe 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -29,7 +29,6 @@ #include #include "NetworkManagerUtils.h" -#include "nm-multi-index.h" #include "nm-test-utils-core.h" @@ -499,362 +498,6 @@ test_nm_ethernet_address_is_valid (void) /*****************************************************************************/ -typedef struct { - union { - NMMultiIndexId id_base; - guint bucket; - }; -} NMMultiIndexIdTest; - -typedef struct { - guint64 buckets; - gpointer ptr_value; -} NMMultiIndexTestValue; - -static gboolean -_mi_value_bucket_has (const NMMultiIndexTestValue *value, guint bucket) -{ - g_assert (value); - g_assert (bucket < 64); - - return (value->buckets & (((guint64) 0x01) << bucket)) != 0; -} - -static gboolean -_mi_value_bucket_set (NMMultiIndexTestValue *value, guint bucket) -{ - g_assert (value); - g_assert (bucket < 64); - - if (_mi_value_bucket_has (value, bucket)) - return FALSE; - - value->buckets |= (((guint64) 0x01) << bucket); - return TRUE; -} - -static gboolean -_mi_value_bucket_unset (NMMultiIndexTestValue *value, guint bucket) -{ - g_assert (value); - g_assert (bucket < 64); - - if (!_mi_value_bucket_has (value, bucket)) - return FALSE; - - value->buckets &= ~(((guint64) 0x01) << bucket); - return TRUE; -} - -static guint -_mi_idx_hash (const NMMultiIndexIdTest *id) -{ - g_assert (id && id->bucket < 64); - return id->bucket; -} - -static gboolean -_mi_idx_equal (const NMMultiIndexIdTest *a, const NMMultiIndexIdTest *b) -{ - g_assert (a && a->bucket < 64); - g_assert (b && b->bucket < 64); - - return a->bucket == b->bucket; -} - -static NMMultiIndexIdTest * -_mi_idx_clone (const NMMultiIndexIdTest *id) -{ - NMMultiIndexIdTest *n; - - g_assert (id && id->bucket < 64); - - n = g_new0 (NMMultiIndexIdTest, 1); - n->bucket = id->bucket; - return n; -} - -static void -_mi_idx_destroy (NMMultiIndexIdTest *id) -{ - g_assert (id && id->bucket < 64); - g_free (id); -} - -static NMMultiIndexTestValue * -_mi_create_array (guint num_values) -{ - NMMultiIndexTestValue *array = g_new0 (NMMultiIndexTestValue, num_values); - guint i; - - g_assert (num_values > 0); - - for (i = 0; i < num_values; i++) { - array[i].buckets = 0; - array[i].ptr_value = GUINT_TO_POINTER (i + 1); - } - return array; -} - -typedef struct { - guint num_values; - guint num_buckets; - NMMultiIndexTestValue *array; - int test_idx; -} NMMultiIndexAssertData; - -static gboolean -_mi_assert_index_equals_array_cb (const NMMultiIndexIdTest *id, void *const* values, guint len, NMMultiIndexAssertData *data) -{ - guint i; - gboolean has_test_idx = FALSE; - - g_assert (id && id->bucket < 64); - g_assert (data); - g_assert (values); - g_assert (len > 0); - g_assert (values[len] == NULL); - g_assert (data->test_idx >= -1 || data->test_idx < data->num_buckets); - - g_assert (id->bucket < data->num_buckets); - - for (i = 0; i < data->num_values; i++) - g_assert (!_mi_value_bucket_has (&data->array[i], id->bucket)); - - for (i = 0; i < len; i++) { - guint vi = GPOINTER_TO_UINT (values[i]); - - g_assert (vi >= 1); - g_assert (vi <= data->num_values); - vi--; - if (data->test_idx == vi) - has_test_idx = TRUE; - g_assert (data->array[vi].ptr_value == values[i]); - if (!_mi_value_bucket_set (&data->array[vi], id->bucket)) - g_assert_not_reached (); - } - g_assert ((data->test_idx == -1 && !has_test_idx) || has_test_idx); - return TRUE; -} - -static void -_mi_assert_index_equals_array (guint num_values, guint num_buckets, int test_idx, const NMMultiIndexTestValue *array, const NMMultiIndex *index) -{ - NMMultiIndexAssertData data = { - .num_values = num_values, - .num_buckets = num_buckets, - .test_idx = test_idx, - }; - NMMultiIndexIter iter; - const NMMultiIndexIdTest *id; - void *const* values; - guint len; - NMMultiIndexTestValue *v; - - data.array = _mi_create_array (num_values); - v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; - nm_multi_index_foreach (index, v, (NMMultiIndexFuncForeach) _mi_assert_index_equals_array_cb, &data); - if (test_idx >= 0) - g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); - else - g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); - g_free (data.array); - - - data.array = _mi_create_array (num_values); - v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; - nm_multi_index_iter_init (&iter, index, v); - while (nm_multi_index_iter_next (&iter, (gpointer) &id, &values, &len)) - _mi_assert_index_equals_array_cb (id, values, len, &data); - if (test_idx >= 0) - g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); - else - g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); - g_free (data.array); -} - -typedef enum { - MI_OP_ADD, - MI_OP_REMOVE, - MI_OP_MOVE, -} NMMultiIndexOperation; - -static void -_mi_rebucket (GRand *rand, guint num_values, guint num_buckets, NMMultiIndexOperation op, guint bucket, guint bucket_old, guint array_idx, NMMultiIndexTestValue *array, NMMultiIndex *index) -{ - NMMultiIndexTestValue *v; - NMMultiIndexIdTest id, id_old; - const NMMultiIndexIdTest *id_reverse; - guint64 buckets_old; - guint i; - gboolean had_bucket, had_bucket_old; - - g_assert (array_idx < num_values); - g_assert (bucket < (int) num_buckets); - - v = &array[array_idx]; - - buckets_old = v->buckets; - if (op == MI_OP_MOVE) - had_bucket_old = _mi_value_bucket_has (v, bucket_old); - else - had_bucket_old = FALSE; - had_bucket = _mi_value_bucket_has (v, bucket); - - switch (op) { - - case MI_OP_ADD: - _mi_value_bucket_set (v, bucket); - id.bucket = bucket; - if (nm_multi_index_add (index, &id.id_base, v->ptr_value)) - g_assert (!had_bucket); - else - g_assert (had_bucket); - break; - - case MI_OP_REMOVE: - _mi_value_bucket_unset (v, bucket); - id.bucket = bucket; - if (nm_multi_index_remove (index, &id.id_base, v->ptr_value)) - g_assert (had_bucket); - else - g_assert (!had_bucket); - break; - - case MI_OP_MOVE: - - _mi_value_bucket_unset (v, bucket_old); - _mi_value_bucket_set (v, bucket); - - id.bucket = bucket; - id_old.bucket = bucket_old; - - if (nm_multi_index_move (index, &id_old.id_base, &id.id_base, v->ptr_value)) { - if (bucket == bucket_old) - g_assert (had_bucket_old && had_bucket); - else - g_assert (had_bucket_old && !had_bucket); - } else { - if (bucket == bucket_old) - g_assert (!had_bucket_old && !had_bucket); - else - g_assert (!had_bucket_old || had_bucket); - } - break; - - default: - g_assert_not_reached (); - } - -#if 0 - g_print (">>> rebucket: idx=%3u, op=%3s, bucket=%3i%c -> %3i%c, buckets=%08llx -> %08llx %s\n", array_idx, - op == MI_OP_ADD ? "ADD" : (op == MI_OP_REMOVE ? "REM" : "MOV"), - bucket_old, had_bucket_old ? '*' : ' ', - bucket, had_bucket ? '*' : ' ', - (unsigned long long) buckets_old, (unsigned long long) v->buckets, - buckets_old != v->buckets ? "(changed)" : "(unchanged)"); -#endif - - id_reverse = (const NMMultiIndexIdTest *) nm_multi_index_lookup_first_by_value (index, v->ptr_value); - if (id_reverse) - g_assert (_mi_value_bucket_has (v, id_reverse->bucket)); - else - g_assert (v->buckets == 0); - - for (i = 0; i < 64; i++) { - id.bucket = i; - if (nm_multi_index_contains (index, &id.id_base, v->ptr_value)) - g_assert (_mi_value_bucket_has (v, i)); - else - g_assert (!_mi_value_bucket_has (v, i)); - } - - _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); - _mi_assert_index_equals_array (num_values, num_buckets, array_idx, array, index); - _mi_assert_index_equals_array (num_values, num_buckets, g_rand_int_range (rand, 0, num_values), array, index); -} - -static void -_mi_test_run (guint num_values, guint num_buckets) -{ - NMMultiIndex *index = nm_multi_index_new ((NMMultiIndexFuncHash) _mi_idx_hash, - (NMMultiIndexFuncEqual) _mi_idx_equal, - (NMMultiIndexFuncClone) _mi_idx_clone, - (NMMultiIndexFuncDestroy) _mi_idx_destroy); - gs_free NMMultiIndexTestValue *array = _mi_create_array (num_values); - GRand *rand = nmtst_get_rand (); - guint i, i_rd, i_idx, i_bucket; - guint num_buckets_all = num_values * num_buckets; - - g_assert (array[0].ptr_value == GUINT_TO_POINTER (1)); - - _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); - - _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); - _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 0, 0, 0, array, index); - - if (num_buckets >= 3) { - _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); - _mi_rebucket (rand, num_values, num_buckets, MI_OP_MOVE, 2, 0, 0, array, index); - _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 2, 0, 0, array, index); - } - - g_assert (nm_multi_index_get_num_groups (index) == 0); - - /* randomly change the bucket of entries. */ - for (i = 0; i < 5 * num_values; i++) { - guint array_idx = g_rand_int_range (rand, 0, num_values); - guint bucket = g_rand_int_range (rand, 0, num_buckets); - NMMultiIndexOperation op = g_rand_int_range (rand, 0, MI_OP_MOVE + 1); - guint bucket_old = 0; - - if (op == MI_OP_MOVE) { - if ((g_rand_int (rand) % 2) && array[array_idx].buckets != 0) { - guint64 b; - - /* choose the highest (existing) bucket. */ - bucket_old = 0; - for (b = array[array_idx].buckets; b; b >>= 1) - bucket_old++; - } else { - /* choose a random bucket (even if the item is currently not in that bucket). */ - bucket_old = g_rand_int_range (rand, 0, num_buckets); - } - } - - _mi_rebucket (rand, num_values, num_buckets, op, bucket, bucket_old, array_idx, array, index); - } - - /* remove all elements from all buckets */ - i_rd = g_rand_int (rand); - for (i = 0; i < num_buckets_all; i++) { - i_rd = (i_rd + 101) % num_buckets_all; - i_idx = i_rd / num_buckets; - i_bucket = i_rd % num_buckets; - - if (_mi_value_bucket_has (&array[i_idx], i_bucket)) - _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, i_bucket, 0, i_idx, array, index); - } - - g_assert (nm_multi_index_get_num_groups (index) == 0); - nm_multi_index_free (index); -} - -static void -test_nm_multi_index (void) -{ - guint i, j; - - for (i = 1; i < 7; i++) { - for (j = 1; j < 6; j++) - _mi_test_run (i, j); - } - _mi_test_run (50, 3); - _mi_test_run (50, 18); -} - -/*****************************************************************************/ - static void test_nm_utils_new_vlan_name (void) { @@ -909,7 +552,6 @@ main (int argc, char **argv) g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); g_test_add_func ("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid); - g_test_add_func ("/general/nm_multi_index", test_nm_multi_index); g_test_add_func ("/general/nm_utils_new_vlan_name", test_nm_utils_new_vlan_name); return g_test_run (); diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c index cd5b108903..e865f5f26a 100644 --- a/src/tests/test-ip4-config.c +++ b/src/tests/test-ip4-config.c @@ -36,7 +36,7 @@ build_test_config (void) NMPlatformIP4Route route; /* Build up the config to subtract */ - config = nm_ip4_config_new (1); + config = nmtst_ip4_config_new (1); addr = *nmtst_platform_ip4_address ("192.168.1.10", "1.2.3.4", 24); nm_ip4_config_add_address (config, &addr); @@ -121,7 +121,7 @@ test_subtract (void) g_assert_cmpuint (nm_ip4_config_get_gateway (dst), ==, 0); g_assert_cmpuint (nm_ip4_config_get_num_routes (dst), ==, 1); - test_route = nm_ip4_config_get_route (dst, 0); + test_route = _nmtst_nm_ip4_config_get_route (dst, 0); g_assert (test_route != NULL); g_assert_cmpuint (test_route->network, ==, nmtst_inet4_from_string (expected_route_dest)); g_assert_cmpuint (test_route->plen, ==, expected_route_plen); @@ -156,8 +156,8 @@ test_compare_with_source (void) NMPlatformIP4Address addr; NMPlatformIP4Route route; - a = nm_ip4_config_new (1); - b = nm_ip4_config_new (2); + a = nmtst_ip4_config_new (1); + b = nmtst_ip4_config_new (2); /* Address */ addr = *nmtst_platform_ip4_address ("1.2.3.4", NULL, 24); @@ -189,7 +189,7 @@ test_add_address_with_source (void) NMPlatformIP4Address addr; const NMPlatformIP4Address *test_addr; - a = nm_ip4_config_new (1); + a = nmtst_ip4_config_new (1); /* Test that a higher priority source is not overwritten */ addr = *nmtst_platform_ip4_address ("1.2.3.4", NULL, 24); @@ -229,34 +229,34 @@ test_add_route_with_source (void) NMPlatformIP4Route route; const NMPlatformIP4Route *test_route; - a = nm_ip4_config_new (1); + a = nmtst_ip4_config_new (1); /* Test that a higher priority source is not overwritten */ route = *nmtst_platform_ip4_route ("1.2.3.4", 24, "1.2.3.1"); route.rt_source = NM_IP_CONFIG_SOURCE_USER; nm_ip4_config_add_route (a, &route); - test_route = nm_ip4_config_get_route (a, 0); + test_route = _nmtst_nm_ip4_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); route.rt_source = NM_IP_CONFIG_SOURCE_VPN; nm_ip4_config_add_route (a, &route); - test_route = nm_ip4_config_get_route (a, 0); + test_route = _nmtst_nm_ip4_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); /* Test that a lower priority address source is overwritten */ - nm_ip4_config_del_route (a, 0); + _nmtst_nm_ip4_config_del_route (a, 0); route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; nm_ip4_config_add_route (a, &route); - test_route = nm_ip4_config_get_route (a, 0); + test_route = _nmtst_nm_ip4_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; nm_ip4_config_add_route (a, &route); - test_route = nm_ip4_config_get_route (a, 0); + test_route = _nmtst_nm_ip4_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); g_object_unref (a); @@ -306,7 +306,7 @@ test_strip_search_trailing_dot (void) { NMIP4Config *config; - config = nm_ip4_config_new (1); + config = nmtst_ip4_config_new (1); nm_ip4_config_add_search (config, "."); nm_ip4_config_add_search (config, "foo"); diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index 7e83625c5b..0887e9da98 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -34,7 +34,7 @@ build_test_config (void) NMIP6Config *config; /* Build up the config to subtract */ - config = nm_ip6_config_new (1); + config = nmtst_ip6_config_new (1); nm_ip6_config_add_address (config, nmtst_platform_ip6_address ("abcd:1234:4321::cdde", "1:2:3:4::5", 64)); nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL)); @@ -98,7 +98,7 @@ test_subtract (void) g_assert (nm_ip6_config_get_gateway (dst) == NULL); g_assert_cmpuint (nm_ip6_config_get_num_routes (dst), ==, 1); - test_route = nm_ip6_config_get_route (dst, 0); + test_route = _nmtst_ip6_config_get_route (dst, 0); g_assert (test_route != NULL); tmp = *nmtst_inet6_from_string (expected_route_dest); @@ -127,8 +127,8 @@ test_compare_with_source (void) NMPlatformIP6Address addr; NMPlatformIP6Route route; - a = nm_ip6_config_new (1); - b = nm_ip6_config_new (2); + a = nmtst_ip6_config_new (1); + b = nmtst_ip6_config_new (2); /* Address */ addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64); @@ -160,7 +160,7 @@ test_add_address_with_source (void) NMPlatformIP6Address addr; const NMPlatformIP6Address *test_addr; - a = nm_ip6_config_new (1); + a = nmtst_ip6_config_new (1); /* Test that a higher priority source is not overwritten */ addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64); @@ -200,34 +200,34 @@ test_add_route_with_source (void) NMPlatformIP6Route route; const NMPlatformIP6Route *test_route; - a = nm_ip6_config_new (1); + a = nmtst_ip6_config_new (1); /* Test that a higher priority source is not overwritten */ route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; nm_ip6_config_add_route (a, &route); - test_route = nm_ip6_config_get_route (a, 0); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); route.rt_source = NM_IP_CONFIG_SOURCE_VPN; nm_ip6_config_add_route (a, &route); - test_route = nm_ip6_config_get_route (a, 0); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); /* Test that a lower priority address source is overwritten */ - nm_ip6_config_del_route (a, 0); + _nmtst_ip6_config_del_route (a, 0); route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; nm_ip6_config_add_route (a, &route); - test_route = nm_ip6_config_get_route (a, 0); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; nm_ip6_config_add_route (a, &route); - test_route = nm_ip6_config_get_route (a, 0); + test_route = _nmtst_ip6_config_get_route (a, 0); g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); g_object_unref (a); @@ -327,7 +327,7 @@ test_strip_search_trailing_dot (void) { NMIP6Config *config; - config = nm_ip6_config_new (1); + config = nmtst_ip6_config_new (1); nm_ip6_config_add_search (config, "."); nm_ip6_config_add_search (config, "foo"); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index 6650d26c43..cf771f80ea 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -143,17 +143,45 @@ update_dev0_ip4 (int ifindex) static GArray * -ip4_routes (test_fixture *fixture) +ip_routes (test_fixture *fixture, NMPObjectType obj_type) { - GArray *routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, - fixture->ifindex0, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - GArray *routes1 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, - fixture->ifindex1, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + const NMPClass *klass; + GArray *routes; + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter iter; + const NMPObject *plobj = NULL; + guint i; - g_array_append_vals (routes, routes1->data, routes1->len); - g_array_free (routes1, TRUE); + g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + + klass = nmp_class_from_type (obj_type); + + routes = g_array_new (FALSE, FALSE, klass->sizeof_public); + + for (i = 0; i < 2; i++) { + int ifindex; + + if (i == 0) + ifindex = fixture->ifindex0; + else + ifindex = fixture->ifindex1; + + pl_head_entry = nm_platform_lookup_route_visible (NM_PLATFORM_GET, + obj_type, + ifindex, + FALSE); + nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { + const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj); + + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + continue; + if (r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + continue; + g_assert (r->ifindex == ifindex); + g_assert (nmp_object_is_visible (plobj)); + g_array_append_vals (routes, r, 1); + } + } return routes; } @@ -323,7 +351,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) * - 7.0.0.0/8 route, metric 21021 added * - 7.0.0.0/8 route, metric 22 added * - 8.0.0.0/8 could be added. */ - routes = ip4_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); @@ -334,7 +362,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) setup_dev0_ip4 (fixture->ifindex0, 0, 21); /* Ensure nothing changed. */ - routes = ip4_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); state1[0].mss = 0; state1[1].metric = 21; @@ -344,7 +372,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) update_dev0_ip4 (fixture->ifindex0); /* minor changes in the routes. Quite similar to state1. */ - routes = ip4_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE); g_array_free (routes, TRUE); @@ -356,7 +384,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) * 7.0.0.0/8 gone from dev0, still present on dev1 * 8.0.0.0/8 is present on dev1 * No dev0 routes left. */ - routes = ip4_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE); g_array_free (routes, TRUE); @@ -364,7 +392,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); /* No routes left. */ - routes = ip4_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); g_assert_cmpint (routes->len, ==, 0); g_array_free (routes, TRUE); } @@ -521,22 +549,6 @@ update_dev0_ip6 (int ifindex) g_array_free (routes, TRUE); } -static GArray * -ip6_routes (test_fixture *fixture) -{ - GArray *routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, - fixture->ifindex0, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - GArray *routes1 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, - fixture->ifindex1, - NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - - g_array_append_vals (routes, routes1->data, routes1->len); - g_array_free (routes1, TRUE); - - return routes; -} - static void test_ip6 (test_fixture *fixture, gconstpointer user_data) { @@ -739,7 +751,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) * 2001:db8:d34d::/64 on dev1 could not be added * 2001:db8:1337::/48 on dev0 won over 2001:db8:1337::/48 on dev1 and has metric 1024 * 2001:db8:abad:c0de::/64 routes did not clash */ - routes = ip6_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); @@ -750,7 +762,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) setup_dev0_ip6 (fixture->ifindex0); /* Ensure nothing changed. */ - routes = ip6_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); @@ -758,7 +770,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) update_dev0_ip6 (fixture->ifindex0); /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ - routes = ip6_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE); g_array_free (routes, TRUE); @@ -770,7 +782,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) * 2001:db8:1337::/48 is now on dev1, metric of 1024 still applies * 2001:db8:d34d::/64 is present now that 2001:db8:8086::/48 is on dev1 * No dev0 routes left. */ - routes = ip6_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE); g_array_free (routes, TRUE); @@ -778,7 +790,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); /* No routes left. */ - routes = ip6_routes (fixture); + routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); g_assert_cmpint (routes->len, ==, 0); g_array_free (routes, TRUE); } diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index ecc820685c..ab6103d497 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -942,6 +942,7 @@ print_vpn_config (NMVpnConnection *self) char *dns_domain = NULL; guint32 num, i; char buf[NM_UTILS_INET_ADDRSTRLEN]; + NMDedupMultiIter ipconf_iter; if (priv->ip4_external_gw) { _LOGI ("Data: VPN Gateway: %s", @@ -954,6 +955,8 @@ print_vpn_config (NMVpnConnection *self) _LOGI ("Data: Tunnel Device: %s%s%s", NM_PRINT_FMT_QUOTE_STRING (priv->ip_iface)); if (priv->ip4_config) { + const NMPlatformIP4Route *route; + _LOGI ("Data: IPv4 configuration:"); address4 = nm_ip4_config_get_address (priv->ip4_config, 0); @@ -965,10 +968,7 @@ print_vpn_config (NMVpnConnection *self) _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, NULL)); _LOGI ("Data: Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config)); - num = nm_ip4_config_get_num_routes (priv->ip4_config); - for (i = 0; i < num; i++) { - const NMPlatformIP4Route *route = nm_ip4_config_get_route (priv->ip4_config, i); - + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) { _LOGI ("Data: Static Route: %s/%d Next Hop: %s", nm_utils_inet4_ntop (route->network, NULL), route->plen, @@ -992,6 +992,8 @@ print_vpn_config (NMVpnConnection *self) _LOGI ("Data: No IPv4 configuration"); if (priv->ip6_config) { + const NMPlatformIP6Route *route; + _LOGI ("Data: IPv6 configuration:"); address6 = nm_ip6_config_get_address (priv->ip6_config, 0); @@ -1003,10 +1005,7 @@ print_vpn_config (NMVpnConnection *self) _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet6_ntop (&address6->peer_address, NULL)); _LOGI ("Data: Maximum Segment Size (MSS): %d", nm_ip6_config_get_mss (priv->ip6_config)); - num = nm_ip6_config_get_num_routes (priv->ip6_config); - for (i = 0; i < num; i++) { - const NMPlatformIP6Route *route = nm_ip6_config_get_route (priv->ip6_config, i); - + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) { _LOGI ("Data: Static Route: %s/%d Next Hop: %s", nm_utils_inet6_ntop (&route->network, NULL), route->plen, @@ -1046,10 +1045,14 @@ apply_parent_device_config (NMVpnConnection *self) NMIP6Config *vpn6_parent_config = NULL; if (priv->ip_ifindex > 0) { - if (priv->ip4_config) - vpn4_parent_config = nm_ip4_config_new (priv->ip_ifindex); - if (priv->ip6_config) - vpn6_parent_config = nm_ip6_config_new (priv->ip_ifindex); + if (priv->ip4_config) { + vpn4_parent_config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), + priv->ip_ifindex); + } + if (priv->ip6_config) { + vpn6_parent_config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), + priv->ip_ifindex); + } } else { int ifindex; @@ -1063,11 +1066,13 @@ apply_parent_device_config (NMVpnConnection *self) * default route. */ ifindex = nm_device_get_ip_ifindex (parent_dev); if (priv->ip4_config) { - vpn4_parent_config = nm_ip4_config_new (ifindex); + vpn4_parent_config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), + ifindex); nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_NO_DNS); } if (priv->ip6_config) { - vpn6_parent_config = nm_ip6_config_new (ifindex); + vpn6_parent_config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), + ifindex); nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_NO_DNS); nm_ip6_config_set_gateway (vpn6_parent_config, NULL); } @@ -1398,7 +1403,6 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) const char *str; GVariant *v; gboolean b; - guint i, n; int ip_ifindex; g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT)); @@ -1436,7 +1440,8 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) if (ip_ifindex <= 0) g_return_if_reached (); - config = nm_ip4_config_new (ip_ifindex); + config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), + ip_ifindex); nm_ip4_config_set_dns_priority (config, NM_DNS_PRIORITY_DEFAULT_VPN); memset (&address, 0, sizeof (address)); @@ -1498,9 +1503,11 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) if ( g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, "b", &b) && b) { if (priv->ip4_config) { - n = nm_ip4_config_get_num_routes (priv->ip4_config); - for (i = 0; i < n; i++) - nm_ip4_config_add_route (config, nm_ip4_config_get_route (priv->ip4_config, i)); + NMDedupMultiIter ipconf_iter; + const NMPlatformIP4Route *route; + + nm_ip4_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) + nm_ip4_config_add_route (config, route); } } else if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &iter)) { while (g_variant_iter_next (iter, "@au", &v)) { @@ -1570,7 +1577,6 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) const char *str; GVariant *v; gboolean b; - guint i, n; int ip_ifindex; g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT)); @@ -1595,7 +1601,8 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) if (ip_ifindex <= 0) g_return_if_reached (); - config = nm_ip6_config_new (ip_ifindex); + config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), + ip_ifindex); nm_ip6_config_set_dns_priority (config, NM_DNS_PRIORITY_DEFAULT_VPN); memset (&address, 0, sizeof (address)); @@ -1660,9 +1667,11 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict) if ( g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, "b", &b) && b) { if (priv->ip6_config) { - n = nm_ip6_config_get_num_routes (priv->ip6_config); - for (i = 0; i < n; i++) - nm_ip6_config_add_route (config, nm_ip6_config_get_route (priv->ip6_config, i)); + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Route *route; + + nm_ip6_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) + nm_ip6_config_add_route (config, route); } } else if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &iter)) { GVariant *dest, *next_hop;