From bfd6d608e2cc8cf0f96063ecd2262339ce3d8a6e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 5 Mar 2019 15:04:42 +0100 Subject: [PATCH 01/16] contrib: set shift for less in NM-log --- contrib/scripts/NM-log | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/scripts/NM-log b/contrib/scripts/NM-log index f81ef8441c..903d68ed86 100755 --- a/contrib/scripts/NM-log +++ b/contrib/scripts/NM-log @@ -77,7 +77,7 @@ NM-log() { fi ) | \ NM_LOG_GREP="$NM_LOG_GREP" NM-colorize | \ - LESS=FRSXM less -R + LESS=FRSXM less -R --shift=5 } if [[ "$NM_not_sourced" != "" ]]; then From 83fa4aaf3cb425a41795b1b9c1f091bf70587053 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Mar 2019 17:33:42 +0100 Subject: [PATCH 02/16] shared: add c_list_length_is() helper --- shared/nm-utils/c-list-util.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/shared/nm-utils/c-list-util.h b/shared/nm-utils/c-list-util.h index e87f1c1989..648bacc744 100644 --- a/shared/nm-utils/c-list-util.h +++ b/shared/nm-utils/c-list-util.h @@ -40,4 +40,27 @@ void c_list_sort (CList *head, CListSortCmp cmp, const void *user_data); +/* c_list_length_is: + * @list: the #CList list head + * @check_len: the length to compare + * + * Returns: basically the same as (c_list_length (@list) == @check_len), + * but does not require to iterate the entire list first. There is only + * one real use: to find out whether there is exactly one element in the + * list, by passing @check_len as 1. + */ +static inline int +c_list_length_is (const CList *list, unsigned long check_len) { + unsigned long n = 0; + const CList *iter; + + c_list_for_each (iter, list) { + ++n; + if (n > check_len) + return 0; + } + + return n == check_len; +} + #endif /* __C_LIST_UTIL_H__ */ From 237a17237d930ba2747b6bd8fca43085d92a0e87 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Mar 2019 10:32:41 +0100 Subject: [PATCH 03/16] shared: add nm_hash_obfuscate_ptr() helper --- shared/nm-utils/nm-hash-utils.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h index 1a1e44f50b..3f622f99fb 100644 --- a/shared/nm-utils/nm-hash-utils.h +++ b/shared/nm-utils/nm-hash-utils.h @@ -83,7 +83,11 @@ nm_hash_complete_u64 (NMHashState *state) * from nm_hash_complete() in two ways: * * - the type, guint64 vs. guint. - * - nm_hash_complete() never returns zero. */ + * - nm_hash_complete() never returns zero. + * + * In practice, nm_hash*() API is implemented via siphash24, so this returns + * the siphash24 value. But that is not guaranteed by the API, and if you need + * siphash24 directly, use c_siphash_*() and nm_hash_siphash42*() API. */ return c_siphash_finalize (&state->_state); } @@ -287,4 +291,25 @@ gboolean nm_pstr_equal (gconstpointer a, gconstpointer b); /*****************************************************************************/ +#define NM_HASH_OBFUSCATE_PTR_FMT "%016llx" + +/* sometimes we want to log a pointer directly, for providing context/information about + * the message that get logged. Logging pointer values directly defeats ASLR, so we should + * not do that. This returns a "unsigned long long" value that can be used + * instead. + * + * Note that there is a chance that two different pointer values hash to the same obfuscated + * value. So beware of that when reviewing logs. However, such a collision is very unlikely. */ +#define nm_hash_obfuscate_ptr(static_seed, val) \ + ({ \ + NMHashState _h; \ + const void *_val_obf_ptr = (val); \ + \ + nm_hash_init (&_h, (static_seed)); \ + nm_hash_update_val (&_h, _val_obf_ptr); \ + (unsigned long long) nm_hash_complete_u64 (&_h); \ + }) + +/*****************************************************************************/ + #endif /* __NM_HASH_UTILS_H__ */ From 7259195a91577fb953af169c78ae840b61e1cc65 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 4 Mar 2019 12:51:07 +0100 Subject: [PATCH 04/16] platform: unify IPv4 and IPv6 variables for NMPlatformVTableRoute --- src/platform/nm-platform.c | 42 +++++++++++++++++++------------------- src/platform/nm-platform.h | 11 ++++++++-- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index fe0cb662a2..1f7814f178 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4264,14 +4264,13 @@ nm_platform_ip_route_sync (NMPlatform *self, gboolean success = TRUE; char sbuf1[sizeof (_nm_utils_to_string_buffer)]; char sbuf2[sizeof (_nm_utils_to_string_buffer)]; + const gboolean IS_IPv4 = (addr_family == AF_INET); nm_assert (NM_IS_PLATFORM (self)); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); nm_assert (ifindex > 0); - vt = addr_family == AF_INET - ? &nm_platform_vtable_route_v4 - : &nm_platform_vtable_route_v6; + vt = &nm_platform_vtable_route.vx[IS_IPv4]; for (i_type = 0; routes && i_type < 2; i_type++) { for (i = 0; i < routes->len; i++) { @@ -7261,24 +7260,25 @@ _vtr_v4_metric_normalize (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, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .metric_normalize = _vtr_v4_metric_normalize, -}; - -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, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .metric_normalize = nm_utils_ip6_route_metric_normalize, +const _NMPlatformVTableRouteUnion 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, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, + .metric_normalize = _vtr_v4_metric_normalize, + }, + .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, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, + .metric_normalize = nm_utils_ip6_route_metric_normalize, + }, }; /*****************************************************************************/ diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 37aa58fdc5..665864a8e5 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -583,8 +583,15 @@ typedef struct { guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; -extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; -extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; +typedef union { + struct { + NMPlatformVTableRoute v6; + NMPlatformVTableRoute v4; + }; + NMPlatformVTableRoute vx[2]; +} _NMPlatformVTableRouteUnion; + +extern const _NMPlatformVTableRouteUnion nm_platform_vtable_route; typedef struct { guint16 id; From 8f3724a6bdefd8d31850c3c529528244a1a64d11 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 4 Mar 2019 13:43:12 +0100 Subject: [PATCH 05/16] platform: drop unnecessary casts from NMP_OBJECT_CAST_*() macros It's wrong to cast here. The caller must always provide an NMPObject pointer. --- src/platform/nmp-object.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 525e144001..d549af7741 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -458,7 +458,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_LINK); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_LINK); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ }) @@ -482,7 +482,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ }) @@ -490,7 +490,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ }) @@ -514,7 +514,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ }) @@ -522,7 +522,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ }) @@ -530,7 +530,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_QDISC); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_QDISC); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \ }) @@ -538,7 +538,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_TFILTER); \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_TFILTER); \ _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ }) From 667aa52f89616129ee953a1dd55235790ac9b54c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 4 Mar 2019 13:50:40 +0100 Subject: [PATCH 06/16] platform: move nmp_class_from_type() to header to allow inlining --- src/platform/nmp-object.c | 10 ---------- src/platform/nmp-object.h | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 6ec6fea138..97c2db3ec0 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -712,16 +712,6 @@ _nmp_object_fixup_link_master_connected (NMPObject **obj_new, NMPObject *obj_ori /*****************************************************************************/ -const NMPClass * -nmp_class_from_type (NMPObjectType obj_type) -{ - g_return_val_if_fail (obj_type > NMP_OBJECT_TYPE_UNKNOWN && obj_type <= NMP_OBJECT_TYPE_MAX, NULL); - - return &_nmp_classes[obj_type - 1]; -} - -/*****************************************************************************/ - static void _vt_cmd_obj_dispose_link (NMPObject *obj) { diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index d549af7741..3241573c27 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -397,6 +397,8 @@ struct _NMPObject { }; }; +/*****************************************************************************/ + static inline gboolean NMP_CLASS_IS_VALID (const NMPClass *klass) { @@ -405,6 +407,17 @@ NMP_CLASS_IS_VALID (const NMPClass *klass) && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0; } +static inline const NMPClass * +nmp_class_from_type (NMPObjectType obj_type) +{ + nm_assert (obj_type > 0); + nm_assert (obj_type <= G_N_ELEMENTS (_nmp_classes)); + nm_assert (_nmp_classes[obj_type - 1].obj_type == obj_type); + nm_assert (NMP_CLASS_IS_VALID (&_nmp_classes[obj_type - 1])); + + return &_nmp_classes[obj_type - 1]; +} + static inline NMPObject * NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { @@ -542,8 +555,6 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ }) -const NMPClass *nmp_class_from_type (NMPObjectType obj_type); - static inline const NMPObject * nmp_object_ref (const NMPObject *obj) { From ac4a1deba02708e7538bd6fbf1c88c7245342ae2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 4 Mar 2019 13:01:21 +0100 Subject: [PATCH 07/16] platform: add NMPlatformObjWithIfindex helper structure for handling NMPObject types Until now, all implemented NMPObject types have an ifindex field (from links, addresses, routes, qdisc to tfilter). The NMPObject structure contains a union of all available types, that makes it easier to down-case from an NMPObject pointer to the actual content. The "object" field of NMPObject of type NMPlatformObject is the lowest common denominator. We will add NMPlatformRoutingRules (for policy routing rules). That type won't have an ifindex field. Hence, drop the "ifindex" field from NMPlatformObject type. But also add a new type NMPlatformObjWithIfindex, that can represent all types that have an ifindex. --- src/nm-ip4-config.c | 6 +-- src/nm-types.h | 19 +++++----- src/platform/nm-fake-platform.c | 2 +- src/platform/nm-platform.c | 12 ++++-- src/platform/nm-platform.h | 28 ++++++++------ src/platform/nmp-object.c | 37 +++++++++--------- src/platform/nmp-object.h | 56 ++++++++++++++++++++++++++++ src/platform/tests/test-nmp-object.c | 2 +- 8 files changed, 115 insertions(+), 47 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 1c06a42ce9..2f9db03b66 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -115,13 +115,13 @@ _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, if (!obj_new) { nm_assert (pl_new); obj_new = nmp_object_stackinit (&obj_new_stackinit, idx_type->obj_type, pl_new); - obj_new_stackinit.object.ifindex = ifindex; + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex; } else { nm_assert (!pl_new); nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); - if (obj_new->object.ifindex != ifindex) { + if (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_new)->ifindex != ifindex) { obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); - obj_new_stackinit.object.ifindex = ifindex; + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex; } } nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); diff --git a/src/nm-types.h b/src/nm-types.h index b6b49028ca..f30cc19920 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -119,15 +119,16 @@ NM_IS_IP_CONFIG_SOURCE_RTPROT (NMIPConfigSource source) } /* platform */ -typedef struct _NMPlatform NMPlatform; -typedef struct _NMPlatformObject NMPlatformObject; -typedef struct _NMPlatformIP4Address NMPlatformIP4Address; -typedef struct _NMPlatformIP4Route NMPlatformIP4Route; -typedef struct _NMPlatformIP6Address NMPlatformIP6Address; -typedef struct _NMPlatformIP6Route NMPlatformIP6Route; -typedef struct _NMPlatformLink NMPlatformLink; -typedef struct _NMPNetns NMPNetns; -typedef struct _NMPObject NMPObject; +typedef struct _NMPlatform NMPlatform; +typedef struct _NMPlatformObject NMPlatformObject; +typedef struct _NMPlatformObjWithIfindex NMPlatformObjWithIfindex; +typedef struct _NMPlatformIP4Address NMPlatformIP4Address; +typedef struct _NMPlatformIP4Route NMPlatformIP4Route; +typedef struct _NMPlatformIP6Address NMPlatformIP6Address; +typedef struct _NMPlatformIP6Route NMPlatformIP6Route; +typedef struct _NMPlatformLink NMPlatformLink; +typedef struct _NMPNetns NMPNetns; +typedef struct _NMPObject NMPObject; typedef enum { /* Please don't interpret type numbers outside nm-platform and use functions diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 3046615954..9dad647cfb 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1119,7 +1119,7 @@ ipx_route_delete (NMPlatform *platform, g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); g_assert (ifindex == -1); - ifindex = obj->object.ifindex; + ifindex = NMP_OBJECT_CAST_IP_ROUTE (obj)->ifindex; obj_type = NMP_OBJECT_GET_TYPE (obj); } else { g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 1f7814f178..6aefcd5027 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4577,7 +4577,7 @@ _ip_route_add (NMPlatform *self, nm_assert (route); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - ifindex = ((NMPlatformObject *)route)->ifindex; + ifindex = ((const NMPlatformIPRoute *) route)->ifindex; _LOG3D ("route: %-10s IPv%c route: %s", _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), nm_utils_addr_family_to_char (addr_family), @@ -4629,7 +4629,8 @@ gboolean nm_platform_object_delete (NMPlatform *self, const NMPObject *obj) { - int ifindex = obj->object.ifindex; + int ifindex; + _CHECK_SELF (self, klass, FALSE); if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, @@ -4638,6 +4639,8 @@ nm_platform_object_delete (NMPlatform *self, NMP_OBJECT_TYPE_TFILTER)) g_return_val_if_reached (FALSE); + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj)->ifindex; + _LOG3D ("%s: delete %s", NMP_OBJECT_GET_CLASS (obj)->obj_type_name, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); @@ -7195,7 +7198,8 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, return; } - ifindex = o->object.ifindex; + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (o)->ifindex; + klass = NMP_OBJECT_GET_CLASS (o); if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE @@ -7213,7 +7217,7 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, _nm_platform_signal_id_get (klass->signal_type_id), 0, (int) klass->obj_type, - o->object.ifindex, + ifindex, &o->object, (int) cache_op); nmp_object_unref (o); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 665864a8e5..c9898603e2 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -181,12 +181,22 @@ typedef enum { #define NM_PLATFORM_LINK_OTHER_NETNS (-1) -#define __NMPlatformObject_COMMON \ +struct _NMPlatformObject { + /* the object type has no fields of its own, it is only used to having + * a special pointer type that can be used to indicate "any" type. */ + char _dummy_don_t_use_me; +}; + +#define __NMPlatformObjWithIfindex_COMMON \ int ifindex; \ ; +struct _NMPlatformObjWithIfindex { + __NMPlatformObjWithIfindex_COMMON; +}; + struct _NMPlatformLink { - __NMPlatformObject_COMMON; + __NMPlatformObjWithIfindex_COMMON; char name[NMP_IFNAMSIZ]; NMLinkType type; @@ -260,15 +270,11 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -struct _NMPlatformObject { - __NMPlatformObject_COMMON; -}; - #define NM_PLATFORM_IP_ADDRESS_CAST(address) \ NM_CONSTCAST (NMPlatformIPAddress, (address), NMPlatformIPXAddress, NMPlatformIP4Address, NMPlatformIP6Address) #define __NMPlatformIPAddress_COMMON \ - __NMPlatformObject_COMMON; \ + __NMPlatformObjWithIfindex_COMMON; \ NMIPConfigSource addr_source; \ \ /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). @@ -370,7 +376,7 @@ typedef union { #define NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE 0 #define __NMPlatformIPRoute_COMMON \ - __NMPlatformObject_COMMON; \ + __NMPlatformObjWithIfindex_COMMON; \ \ /* The NMIPConfigSource. For routes that we receive from cache this corresponds * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values). @@ -540,7 +546,7 @@ typedef union { #undef __NMPlatformIPRoute_COMMON typedef struct { - __NMPlatformObject_COMMON; + __NMPlatformObjWithIfindex_COMMON; const char *kind; int addr_family; guint32 handle; @@ -562,7 +568,7 @@ typedef struct { #define NM_PLATFORM_ACTION_KIND_SIMPLE "simple" typedef struct { - __NMPlatformObject_COMMON; + __NMPlatformObjWithIfindex_COMMON; const char *kind; int addr_family; guint32 handle; @@ -571,7 +577,7 @@ typedef struct { NMPlatformAction action; } NMPlatformTfilter; -#undef __NMPlatformObject_COMMON +#undef __NMPlatformObjWithIfindex_COMMON typedef struct { gboolean is_ip4; diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 97c2db3ec0..e3563d716f 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -389,16 +389,16 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, nm_hash_update_val (h, obj_a); return 0; } - nm_assert (obj_a->object.ifindex > 0); + nm_assert (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->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_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex == NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_b)->ifindex && nmp_object_is_visible (obj_b); } if (h) { nm_hash_update_vals (h, idx_type->cache_id_type, - obj_a->object.ifindex); + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex); } return 1; @@ -406,14 +406,14 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, 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) { + || NMP_OBJECT_CAST_IP_ROUTE (obj_a)->ifindex <= 0) { if (h) nm_hash_update_val (h, obj_a); return 0; } if (obj_b) { return obj_type == NMP_OBJECT_GET_TYPE (obj_b) - && obj_b->object.ifindex > 0 + && NMP_OBJECT_CAST_IP_ROUTE (obj_b)->ifindex > 0 && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE ? (nm_platform_ip4_route_cmp (&obj_a->ip4_route, &obj_b->ip4_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0) : (nm_platform_ip6_route_cmp (&obj_a->ip6_route, &obj_b->ip6_route, NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) == 0)); @@ -1564,13 +1564,14 @@ nmp_object_is_alive (const NMPObject *obj) static gboolean _vt_cmd_obj_is_alive_link (const NMPObject *obj) { - return obj->object.ifindex > 0 && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); + return NMP_OBJECT_CAST_LINK (obj)->ifindex > 0 + && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); } static gboolean _vt_cmd_obj_is_alive_ipx_address (const NMPObject *obj) { - return obj->object.ifindex > 0; + return NMP_OBJECT_CAST_IP_ADDRESS (obj)->ifindex > 0; } static gboolean @@ -1591,20 +1592,20 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) * Instead we create a dead object, and nmp_cache_update_netlink() * will remove the old version of the update. **/ - return obj->object.ifindex > 0 + return NMP_OBJECT_CAST_IP_ROUTE (obj)->ifindex > 0 && !NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED); } static gboolean _vt_cmd_obj_is_alive_qdisc (const NMPObject *obj) { - return obj->object.ifindex > 0; + return NMP_OBJECT_CAST_QDISC (obj)->ifindex > 0; } static gboolean _vt_cmd_obj_is_alive_tfilter (const NMPObject *obj) { - return obj->object.ifindex > 0; + return NMP_OBJECT_CAST_TFILTER (obj)->ifindex > 0; } gboolean @@ -1988,7 +1989,7 @@ nmp_lookup_init_object (NMPLookup *lookup, } o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); - o->object.ifindex = ifindex; + o->obj_with_ifindex.ifindex = ifindex; lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX; return _L (lookup); } @@ -2004,7 +2005,7 @@ nmp_lookup_init_route_default (NMPLookup *lookup, NMP_OBJECT_TYPE_IP6_ROUTE)); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); - o->object.ifindex = 1; + o->ip_route.ifindex = 1; lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES; return _L (lookup); } @@ -2052,9 +2053,9 @@ nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, nm_assert (lookup); 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; + o->ip4_route.ifindex = 1; + o->ip4_route.plen = plen; + o->ip4_route.metric = metric; if (network) o->ip4_route.network = network; o->ip4_route.tos = tos; @@ -2075,9 +2076,9 @@ nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, nm_assert (lookup); 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; + o->ip6_route.ifindex = 1; + o->ip6_route.plen = plen; + o->ip6_route.metric = metric; if (network) o->ip6_route.network = *network; if (src) diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 3241573c27..7d64b094ce 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -340,6 +340,8 @@ struct _NMPObject { union { NMPlatformObject object; + NMPlatformObjWithIfindex obj_with_ifindex; + NMPlatformLink link; NMPObjectLink _link; @@ -467,6 +469,60 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; } +static inline gboolean +_NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) +{ + 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: + + case NMP_OBJECT_TYPE_QDISC: + + case NMP_OBJECT_TYPE_TFILTER: + + case NMP_OBJECT_TYPE_LNK_GRE: + case NMP_OBJECT_TYPE_LNK_GRETAP: + case NMP_OBJECT_TYPE_LNK_INFINIBAND: + case NMP_OBJECT_TYPE_LNK_IP6TNL: + case NMP_OBJECT_TYPE_LNK_IP6GRE: + case NMP_OBJECT_TYPE_LNK_IP6GRETAP: + case NMP_OBJECT_TYPE_LNK_IPIP: + case NMP_OBJECT_TYPE_LNK_MACSEC: + case NMP_OBJECT_TYPE_LNK_MACVLAN: + case NMP_OBJECT_TYPE_LNK_MACVTAP: + case NMP_OBJECT_TYPE_LNK_SIT: + case NMP_OBJECT_TYPE_LNK_TUN: + case NMP_OBJECT_TYPE_LNK_VLAN: + case NMP_OBJECT_TYPE_LNK_VXLAN: + case NMP_OBJECT_TYPE_LNK_WIREGUARD: + return TRUE; + default: + nm_assert (nmp_class_from_type (obj_type)); + return FALSE; + } +} + +#define NMP_OBJECT_CAST_OBJECT(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert ( !_obj \ + || nmp_class_from_type (NMP_OBJECT_GET_TYPE (_obj)))); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->object : NULL; \ + }) + +#define NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert ( !_obj \ + || _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMP_OBJECT_GET_TYPE (_obj))); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->obj_with_ifindex : NULL; \ + }) + #define NMP_OBJECT_CAST_LINK(obj) \ ({ \ typeof (obj) _obj = (obj); \ diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 12acf4a544..280ed5208f 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -223,7 +223,7 @@ _nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **ou g_assert (cache); g_assert (NMP_OBJECT_IS_VALID (obj)); - obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex); + obj_prev = nmp_cache_lookup_link (cache, NMP_OBJECT_CAST_LINK (obj)->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); From 2c37a3fb1ed4faccf60f06b81b1ca2a7f4fa6f5e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 27 Feb 2019 16:54:43 +0100 Subject: [PATCH 08/16] platform: add NULL check in inline nmp_object_unref() function This allows the compiler to see that this function does nothing for %NULL. That is not so unusual, as we use nm_auto_nmpobj to free objects. Often the compiler can see that these pointers are %NULL. --- src/platform/nmp-object.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 7d64b094ce..5c5b510ad0 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -632,9 +632,11 @@ nmp_object_ref (const NMPObject *obj) static inline void nmp_object_unref (const NMPObject *obj) { - nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + if (obj) { + nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); + nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); + } } #define nm_clear_nmp_object(ptr) \ From bbfb8a9b333643effe84829f78289939bc3bb025 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 21 Feb 2019 16:39:51 +0100 Subject: [PATCH 09/16] platform: suppress unnecessary logging in do_request_all_no_delayed_actions() When we refresh all links, we clear all flags to refresh a specific link. However, only log a message if there was anything to clear. --- src/platform/nm-linux-platform.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 2f5c75b055..396ae742a3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -5281,10 +5281,15 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio /* clear any delayed action that request a refresh of this object type. */ priv->delayed_action.flags &= ~iflags; _LOGt_delayed_action (iflags, NULL, "handle (do-request-all)"); + if (obj_type == NMP_OBJECT_TYPE_LINK) { - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; - g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)"); + nm_assert ( (priv->delayed_action.list_refresh_link->len > 0) + == NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { + _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)"); + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); + } } event_handler_read_netlink (platform, FALSE); From 7c5ad2d910c29df678a8c56d249755f113075df7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 21 Feb 2019 16:55:51 +0100 Subject: [PATCH 10/16] platform: drop unused nm_platform_refresh_all() The function is unused. It would require redesign to work with future changes, and since it's unused, just drop it. The long reasoning is: Currently, a refresh-all is tied to an NMPObjectType. However, with NMPObjectRoutingRule (for policy-routing-rules) that will no longer be the case. That is because NMPObjectRoutingRule will be one object type for AF_INET and AF_INET6. Contrary to IPv4 addresses and routes, where there are two sets of NMPObject types. The reason is, that it's preferable to treat IPv4 and IPv6 objects similarly, that is: as the same type with an address family property. That also follows netlink, which uses RTM_GET* messages for both address families, and the address family is expressed inside the message. But then an API like nm_platform_refresh_all() makes little sense, it would require at least an addr_family argument. But since the API is unused, just drop it. --- src/platform/nm-linux-platform.c | 7 ------- src/platform/nm-platform.c | 15 --------------- src/platform/nm-platform.h | 4 ---- 3 files changed, 26 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 396ae742a3..d94d5a968c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -5896,12 +5896,6 @@ link_refresh (NMPlatform *platform, int ifindex) return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } -static void -refresh_all (NMPlatform *platform, NMPObjectType obj_type) -{ - do_request_one_type (platform, obj_type); -} - static gboolean link_set_netns (NMPlatform *platform, int ifindex, @@ -8462,7 +8456,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->refresh_all = refresh_all; platform_class->link_refresh = link_refresh; platform_class->link_set_netns = link_set_netns; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 6aefcd5027..f0fb2a528d 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1188,21 +1188,6 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) return (nm_platform_link_get_type (self, ifindex) & 0x20000); } -/** - * nm_platform_refresh_all: - * @self: platform instance - * @obj_type: The object type to request. - * - * Resync and re-request all objects from kernel of a certain @obj_type. - */ -void -nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type) -{ - _CHECK_SELF_VOID (self, klass); - - klass->refresh_all (self, obj_type); -} - /** * nm_platform_link_refresh: * @self: platform instance diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index c9898603e2..9b626fb4a0 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -806,8 +806,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); - void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); - int (*link_add) (NMPlatform *, const char *name, NMLinkType type, @@ -1180,8 +1178,6 @@ gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); -void nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type); - const NMPObject *nm_platform_link_get_obj (NMPlatform *self, int ifindex, gboolean visible_only); From 0a2a861782c7e0234d2ffe693b4ac6eb5a17ea14 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 21 Feb 2019 13:04:57 +0100 Subject: [PATCH 11/16] platform/trivial: rename enum DELAYED_ACTION_IDX_REFRESH_ALL_* to REFRESH_ALL_TYPE_* While these numbers are strongly related to DELAYED_ACTION_TYPE_REFRESH_ALL_*, they differ in their meaning. These are the refresh-all-types that we support. While some of the delayed-actions are indeed for refresh-all, they are not the same thing. Rename the enum. --- src/platform/nm-linux-platform.c | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d94d5a968c..777c866472 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -284,25 +284,25 @@ typedef struct { } ChangeLinkData; enum { - DELAYED_ACTION_IDX_REFRESH_ALL_LINKS, - DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES, - DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES, - DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES, - DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES, - DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS, - DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS, - _DELAYED_ACTION_IDX_REFRESH_ALL_NUM, + REFRESH_ALL_TYPE_LINKS, + REFRESH_ALL_TYPE_IP4_ADDRESSES, + REFRESH_ALL_TYPE_IP6_ADDRESSES, + REFRESH_ALL_TYPE_IP4_ROUTES, + REFRESH_ALL_TYPE_IP6_ROUTES, + REFRESH_ALL_TYPE_QDISCS, + REFRESH_ALL_TYPE_TFILTERS, + _REFRESH_ALL_TYPE_NUM, }; typedef enum { DELAYED_ACTION_TYPE_NONE = 0, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = (1LL << /* 0 */ DELAYED_ACTION_IDX_REFRESH_ALL_LINKS), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = (1LL << /* 1 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = (1LL << /* 2 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << /* 3 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << /* 4 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = (1LL << /* 5 */ DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = (1LL << /* 6 */ DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS), + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = (1LL << /* 0 */ REFRESH_ALL_TYPE_LINKS), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = (1LL << /* 1 */ REFRESH_ALL_TYPE_IP4_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = (1LL << /* 2 */ REFRESH_ALL_TYPE_IP6_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << /* 3 */ REFRESH_ALL_TYPE_IP4_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << /* 4 */ REFRESH_ALL_TYPE_IP6_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = (1LL << /* 5 */ REFRESH_ALL_TYPE_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = (1LL << /* 6 */ REFRESH_ALL_TYPE_TFILTERS), DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 7), DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 11), DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 12), @@ -387,7 +387,7 @@ typedef struct { GIOChannel *event_channel; guint event_id; - bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + bool pruning[_REFRESH_ALL_TYPE_NUM]; GHashTable *sysctl_get_prev_values; CList sysctl_list; @@ -401,7 +401,7 @@ typedef struct { /* counter that a refresh all action is in progress, separated * by type. */ - int refresh_all_in_progress[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + int refresh_all_in_progress[_REFRESH_ALL_TYPE_NUM]; GPtrArray *list_master_connected; GPtrArray *list_refresh_link; @@ -4373,13 +4373,13 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_to_object_type, DelayedA _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_all_to_idx, DelayedActionType, guint, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (0), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, DELAYED_ACTION_IDX_REFRESH_ALL_LINKS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, REFRESH_ALL_TYPE_IP4_ADDRESSES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), ); @@ -5166,7 +5166,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); if (entry) { - priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE; + priv->pruning[REFRESH_ALL_TYPE_LINKS] = TRUE; nm_dedup_multi_entry_set_dirty (entry, TRUE); } } From b9ee40b86b03fcc6c5dd25caa9db403a4f71d546 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 21 Feb 2019 09:17:36 +0100 Subject: [PATCH 12/16] platform: separate the refresh-type from the object type Currently, there is a directy one to one relation between - DELAYED_ACTION_TYPE_REFRESH_ALL_* - REFRESH_ALL_TYPE_* - NMP_OBJECT_TYPE_* For IP addresses, routes and routing policy rules, when we request a refresh-all (NLM_F_DUMP), we want to specify the address family. For addresses and routes that is currently solved by having two sets of NMPObject types, for each address family one. I think that is cumbersome because the implementations of both address families are quite similar. By implementing both families as different object types, we have a lot of duplicate code and it's hard to see where the families actually differ. It would be better to have only one NMPObject type, but then when we "refresh-all" such types, we still want to be able to dump all (AF_UNSPEC) or only a particular address family (AF_INET, AF_INET6). Decouple REFRESH_ALL_TYPE_* from NMP_OBJECT_TYPE_* to make that possible. --- src/platform/nm-linux-platform.c | 296 ++++++++++++++++++++----------- src/platform/nmp-object.c | 20 ++- src/platform/nmp-object.h | 30 +++- 3 files changed, 232 insertions(+), 114 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 777c866472..7d299aba9d 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -283,41 +283,56 @@ typedef struct { }; } ChangeLinkData; -enum { - REFRESH_ALL_TYPE_LINKS, - REFRESH_ALL_TYPE_IP4_ADDRESSES, - REFRESH_ALL_TYPE_IP6_ADDRESSES, - REFRESH_ALL_TYPE_IP4_ROUTES, - REFRESH_ALL_TYPE_IP6_ROUTES, - REFRESH_ALL_TYPE_QDISCS, - REFRESH_ALL_TYPE_TFILTERS, +typedef enum { + _REFRESH_ALL_TYPE_FIRST = 0, + + REFRESH_ALL_TYPE_LINKS = 0, + REFRESH_ALL_TYPE_IP4_ADDRESSES = 1, + REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, + REFRESH_ALL_TYPE_IP4_ROUTES = 3, + REFRESH_ALL_TYPE_IP6_ROUTES = 4, + REFRESH_ALL_TYPE_QDISCS = 5, + REFRESH_ALL_TYPE_TFILTERS = 6, + _REFRESH_ALL_TYPE_NUM, -}; +} RefreshAllType; + +typedef struct { + NMPObjectType obj_type; + + /* for NLM_F_DUMP, which address family to request. */ + int addr_family; +} RefreshAllInfo; typedef enum { - DELAYED_ACTION_TYPE_NONE = 0, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = (1LL << /* 0 */ REFRESH_ALL_TYPE_LINKS), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = (1LL << /* 1 */ REFRESH_ALL_TYPE_IP4_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = (1LL << /* 2 */ REFRESH_ALL_TYPE_IP6_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << /* 3 */ REFRESH_ALL_TYPE_IP4_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << /* 4 */ REFRESH_ALL_TYPE_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = (1LL << /* 5 */ REFRESH_ALL_TYPE_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = (1LL << /* 6 */ REFRESH_ALL_TYPE_TFILTERS), - DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 7), - DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 11), - DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 12), - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = (1LL << 13), + DELAYED_ACTION_TYPE_NONE = 0, + +#define F(val, name) ((sizeof (char[(((val)) == (name)) ? 1 : -1]) * 0) + (val)) + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = 1 << F (0, REFRESH_ALL_TYPE_LINKS), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = 1 << F (1, REFRESH_ALL_TYPE_IP4_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F (2, REFRESH_ALL_TYPE_IP6_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F (3, REFRESH_ALL_TYPE_IP4_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F (4, REFRESH_ALL_TYPE_IP6_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (5, REFRESH_ALL_TYPE_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (6, REFRESH_ALL_TYPE_TFILTERS), +#undef F + + DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 7, + DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 8, + DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 9, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 10, + __DELAYED_ACTION_TYPE_MAX, - DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, + DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, } DelayedActionType; #define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ @@ -387,7 +402,7 @@ typedef struct { GIOChannel *event_channel; guint event_id; - bool pruning[_REFRESH_ALL_TYPE_NUM]; + guint32 pruning[_REFRESH_ALL_TYPE_NUM]; GHashTable *sysctl_get_prev_values; CList sysctl_list; @@ -4347,55 +4362,109 @@ process_events (NMPlatform *platform) /*****************************************************************************/ -_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), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ROUTE, DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ROUTE, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_QDISC, DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS), - NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_TFILTER, DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), -); +static const RefreshAllInfo * +refresh_all_type_get_info (RefreshAllType refresh_all_type) +{ + static const RefreshAllInfo infos[] = { +#define R(_refresh_all_type, _obj_type, _addr_family) [_refresh_all_type] = { .obj_type = _obj_type, .addr_family = _addr_family, } + R (REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), + R (REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), + R (REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), + R (REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), +#undef R + }; -_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_to_object_type, DelayedActionType, NMPObjectType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (NMP_OBJECT_TYPE_UNKNOWN), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, NMP_OBJECT_TYPE_LINK), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, NMP_OBJECT_TYPE_QDISC), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NMP_OBJECT_TYPE_TFILTER), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), -); + nm_assert (_NM_INT_NOT_NEGATIVE (refresh_all_type)); + nm_assert (refresh_all_type < G_N_ELEMENTS (infos)); + nm_assert (nmp_class_from_type (infos[refresh_all_type].obj_type)); -_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_all_to_idx, DelayedActionType, guint, + return &infos[refresh_all_type]; +} + +_NM_UTILS_LOOKUP_DEFINE (static, delayed_action_type_to_refresh_all_type, DelayedActionType, RefreshAllType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (0), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, REFRESH_ALL_TYPE_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, REFRESH_ALL_TYPE_IP4_ADDRESSES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), ); +static DelayedActionType +delayed_action_type_from_refresh_all_type (RefreshAllType refresh_all_type) +{ + DelayedActionType t; + + nm_assert (refresh_all_type_get_info (refresh_all_type)); + + t = (((DelayedActionType) 1) << refresh_all_type); + + nm_assert (refresh_all_type == delayed_action_type_to_refresh_all_type (t)); + + return t; +} + +static DelayedActionType +refresh_all_type_from_needle_object (const NMPObject *obj_needle) +{ + switch (NMP_OBJECT_GET_TYPE (obj_needle)) { + case NMP_OBJECT_TYPE_LINK: return REFRESH_ALL_TYPE_LINKS; + case NMP_OBJECT_TYPE_IP4_ADDRESS: return REFRESH_ALL_TYPE_IP4_ADDRESSES; + case NMP_OBJECT_TYPE_IP6_ADDRESS: return REFRESH_ALL_TYPE_IP6_ADDRESSES; + case NMP_OBJECT_TYPE_IP4_ROUTE: return REFRESH_ALL_TYPE_IP4_ROUTES; + case NMP_OBJECT_TYPE_IP6_ROUTE: return REFRESH_ALL_TYPE_IP6_ROUTES; + case NMP_OBJECT_TYPE_QDISC: return REFRESH_ALL_TYPE_QDISCS; + case NMP_OBJECT_TYPE_TFILTER: return REFRESH_ALL_TYPE_TFILTERS; + default: + nm_assert_not_reached (); + return 0; + } +} + +static const NMPLookup * +refresh_all_type_init_lookup (RefreshAllType refresh_all_type, + NMPLookup *lookup) +{ + const RefreshAllInfo *refresh_all_info; + + nm_assert (lookup); + + refresh_all_info = refresh_all_type_get_info (refresh_all_type); + + nm_assert (refresh_all_info); + + /* not yet implemented. */ + nm_assert (refresh_all_info->addr_family == AF_UNSPEC); + + return nmp_lookup_init_obj_type (lookup, + refresh_all_info->obj_type); +} + +static DelayedActionType +delayed_action_refresh_from_needle_object (const NMPObject *obj_needle) +{ + return delayed_action_type_from_refresh_all_type (refresh_all_type_from_needle_object (obj_needle)); +} + NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, "refresh-all-ip4-addresses"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, "refresh-all-ip4-addresses"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX), @@ -4455,6 +4524,7 @@ static gboolean delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType action_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + RefreshAllType refresh_all_type; nm_assert (nm_utils_is_power_of_two (action_type)); nm_assert (NM_FLAGS_ANY (action_type, DELAYED_ACTION_TYPE_REFRESH_ALL)); @@ -4463,10 +4533,8 @@ delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType if (NM_FLAGS_ANY (priv->delayed_action.flags, action_type)) return TRUE; - if (priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (action_type)] > 0) - return TRUE; - - return FALSE; + refresh_all_type = delayed_action_type_to_refresh_all_type (action_type); + return (priv->delayed_action.refresh_all_in_progress[refresh_all_type] > 0); } static void @@ -4764,25 +4832,33 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, /*****************************************************************************/ static void -cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) +cache_prune_one_type (NMPlatform *platform, + const NMPLookup *lookup) { NMDedupMultiIter iter; const NMPObject *obj; NMPCacheOpsType cache_op; - NMPLookup lookup; NMPCache *cache = nm_platform_get_cache (platform); - nmp_lookup_init_obj_type (&lookup, - obj_type); nm_dedup_multi_iter_init (&iter, nmp_cache_lookup (cache, - &lookup)); + lookup)); while (nm_dedup_multi_iter_next (&iter)) { - if (iter.current->dirty) { + const NMDedupMultiEntry *main_entry; + + /* we only track the dirty flag for the OBJECT-TYPE index. That means, + * for other lookup types we need to check the dirty flag of the main-entry. */ + main_entry = nmp_cache_reresolve_main_entry (cache, iter.current, lookup); + if (!main_entry->dirty) + continue; + + obj = main_entry->obj; + + _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + + { nm_auto_nmpobj const NMPObject *obj_old = NULL; - 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, TRUE, &obj_old); nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); cache_on_change (platform, cache_op, obj_old, NULL); @@ -4795,16 +4871,19 @@ static void cache_prune_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionType iflags, action_type; + RefreshAllType refresh_all_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)]; + for (refresh_all_type = _REFRESH_ALL_TYPE_FIRST; refresh_all_type < _REFRESH_ALL_TYPE_NUM; refresh_all_type++) { + NMPLookup lookup; - if (*p) { - *p = FALSE; - cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags)); - } + if (priv->pruning[refresh_all_type] == 0) + continue; + priv->pruning[refresh_all_type] -= 1; + if (priv->pruning[refresh_all_type] > 0) + continue; + refresh_all_type_init_lookup (refresh_all_type, + &lookup); + cache_prune_one_type (platform, &lookup); } } @@ -5166,7 +5245,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); if (entry) { - priv->pruning[REFRESH_ALL_TYPE_LINKS] = TRUE; + priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1; nm_dedup_multi_entry_set_dirty (entry, TRUE); } } @@ -5260,21 +5339,23 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; FOR_EACH_DELAYED_ACTION (iflags, action_type) { + RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); NMPLookup lookup; - priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE; - nmp_lookup_init_obj_type (&lookup, - delayed_action_refresh_to_object_type (iflags)); - nmp_cache_dirty_set_all (nm_platform_get_cache (platform), - &lookup); + priv->pruning[refresh_all_type] += 1; + refresh_all_type_init_lookup (refresh_all_type, + &lookup); + nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), + &lookup); } FOR_EACH_DELAYED_ACTION (iflags, action_type) { - NMPObjectType obj_type = delayed_action_refresh_to_object_type (iflags); + RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); + const RefreshAllInfo *refresh_all_info = refresh_all_type_get_info (refresh_all_type); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; int *out_refresh_all_in_progress; - out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (iflags)]; + out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[refresh_all_type]; nm_assert (*out_refresh_all_in_progress >= 0); *out_refresh_all_in_progress += 1; @@ -5282,7 +5363,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio priv->delayed_action.flags &= ~iflags; _LOGt_delayed_action (iflags, NULL, "handle (do-request-all)"); - if (obj_type == NMP_OBJECT_TYPE_LINK) { + if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) { nm_assert ( (priv->delayed_action.list_refresh_link->len > 0) == NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { @@ -5294,7 +5375,8 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio event_handler_read_netlink (platform, FALSE); - nlmsg = _nl_msg_new_dump (obj_type, AF_UNSPEC); + nlmsg = _nl_msg_new_dump (refresh_all_info->obj_type, + refresh_all_info->addr_family); if (!nlmsg) goto next_after_fail; @@ -5315,9 +5397,9 @@ next_after_fail: } static void -do_request_one_type (NMPlatform *platform, NMPObjectType obj_type) +do_request_one_type_by_needle_object (NMPlatform *platform, const NMPObject *obj_needle) { - do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_object_type (obj_type)); + do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_needle_object (obj_needle)); delayed_action_handle_all (platform, FALSE); } @@ -5434,7 +5516,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event RTM_NEWQDISC, RTM_NEWTFILTER)) { is_dump = delayed_action_refresh_all_in_progress (platform, - delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); + delayed_action_refresh_from_needle_object (obj)); } _LOGT ("event-notification: %s%s: %s", @@ -5542,7 +5624,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event * netlink events. This needs investigation. */ _LOGT ("schedule resync of routes after RTM_NEWROUTE"); delayed_action_schedule (platform, - delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)), + delayed_action_refresh_from_needle_object (obj), NULL); } break; @@ -5662,7 +5744,7 @@ do_add_addrroute (NMPlatform *platform, * * rh#1484434 */ if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + do_request_one_type_by_needle_object (platform, obj_id); } return wait_for_nl_response_to_nmerr (seq_result); @@ -5727,7 +5809,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * * * rh#1484434 */ if (nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + do_request_one_type_by_needle_object (platform, obj_id); } return success; diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index e3563d716f..f6ba0542cf 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -2911,15 +2911,25 @@ nmp_cache_update_link_master_connected (NMPCache *cache, /*****************************************************************************/ void -nmp_cache_dirty_set_all (NMPCache *cache, - const NMPLookup *lookup) +nmp_cache_dirty_set_all_main (NMPCache *cache, + const NMPLookup *lookup) { + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + nm_assert (cache); nm_assert (lookup); - nm_dedup_multi_index_dirty_set_head (cache->multi_idx, - _idx_type_get (cache, lookup->cache_id_type), - &lookup->selector_obj); + head_entry = nmp_cache_lookup (cache, lookup); + + nm_dedup_multi_iter_init (&iter, head_entry); + while (nm_dedup_multi_iter_next (&iter)) { + const NMDedupMultiEntry *main_entry; + + main_entry = nmp_cache_reresolve_main_entry (cache, iter.current, lookup); + + nm_dedup_multi_entry_set_dirty (main_entry, TRUE); + } } /*****************************************************************************/ diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 5c5b510ad0..15f660a042 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -853,8 +853,34 @@ NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, const NMPObject **out_obj_old, const NMPObject **out_obj_new); -void nmp_cache_dirty_set_all (NMPCache *cache, - const NMPLookup *lookup); +static inline const NMDedupMultiEntry * +nmp_cache_reresolve_main_entry (NMPCache *cache, + const NMDedupMultiEntry *entry, + const NMPLookup *lookup) +{ + const NMDedupMultiEntry *main_entry; + + nm_assert (cache); + nm_assert (entry); + nm_assert (lookup); + + if (lookup->cache_id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) { + nm_assert (entry == nmp_cache_lookup_entry (cache, entry->obj)); + return entry; + } + + /* we only track the dirty flag for the OBJECT-TYPE index. That means, + * for other lookup types we need to check the dirty flag of the main-entry. */ + main_entry = nmp_cache_lookup_entry (cache, entry->obj); + + nm_assert (main_entry); + nm_assert (main_entry->obj == entry->obj); + + return main_entry; +} + +void nmp_cache_dirty_set_all_main (NMPCache *cache, + const NMPLookup *lookup); NMPCache *nmp_cache_new (NMDedupMultiIndex *multi_idx, gboolean use_udev); void nmp_cache_free (NMPCache *cache); From 9934a6a0e3e60368cd3c7423a6b21fa5e92d740c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 14 Feb 2019 13:08:12 +0100 Subject: [PATCH 13/16] platform: add support for routing-rule objects and cache them in platform Add and implement NMPlatformRoutingRule types and let the platform cache handle rules. Rules are special in two ways: - they don't have an ifindex. That makes them different from all other currently existing NMPlatform* types, which have an "ifindex" field and "implement" NMPlatformObjWithIfindex. - they have an address family, but contrary to addresses and routes, there is only one NMPlatformRoutingRule object to handle both address families. Both of these points require some special considerations. Kernel treats routing-rules quite similar to routes. That is, kernel allows to add different rules/routes, as long as they differ in certain fields. These "fields" make up the identity of the rules/routes. But in practice, it's not defined which fields contribute to the identity of these objects. That makes using the netlink API very hard. For example, when kernel gains support for a new attribute which NetworkManager does not know yet, then users can add two rules/routes that look the same to NetworkManager. That can easily result in cache inconsistencies. Another problem is, that older kernel versions may not yet support all fields, which NetworkManager (and newer kernels) considers for identity. The older kernel will not simply reject netlink messages with these unknown keys, instead it will proceed adding the route/rule without it. That means, the added route/rule will have a different identity than what NetworkManager intended to add. --- src/nm-types.h | 1 + src/platform/nm-linux-platform.c | 270 ++++++++++++++++-- src/platform/nm-platform.c | 457 ++++++++++++++++++++++++++++++- src/platform/nm-platform.h | 62 +++++ src/platform/nmp-object.c | 87 ++++++ src/platform/nmp-object.h | 23 ++ 6 files changed, 874 insertions(+), 26 deletions(-) diff --git a/src/nm-types.h b/src/nm-types.h index f30cc19920..03ee99299b 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -193,6 +193,7 @@ typedef enum { NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE, NMP_OBJECT_TYPE_QDISC, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 7d299aba9d..18c9847324 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,19 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +#define FRA_TUN_ID 12 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_PAD 18 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define FRA_PROTOCOL 21 +#define FRA_IP_PROTO 22 +#define FRA_SPORT_RANGE 23 +#define FRA_DPORT_RANGE 24 + +/*****************************************************************************/ + #define IFLA_MACSEC_UNSPEC 0 #define IFLA_MACSEC_SCI 1 #define IFLA_MACSEC_PORT 2 @@ -291,8 +305,10 @@ typedef enum { REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, REFRESH_ALL_TYPE_IP4_ROUTES = 3, REFRESH_ALL_TYPE_IP6_ROUTES = 4, - REFRESH_ALL_TYPE_QDISCS = 5, - REFRESH_ALL_TYPE_TFILTERS = 6, + REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, + REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, + REFRESH_ALL_TYPE_QDISCS = 7, + REFRESH_ALL_TYPE_TFILTERS = 8, _REFRESH_ALL_TYPE_NUM, } RefreshAllType; @@ -313,22 +329,28 @@ typedef enum { DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F (2, REFRESH_ALL_TYPE_IP6_ADDRESSES), DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F (3, REFRESH_ALL_TYPE_IP4_ROUTES), DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F (4, REFRESH_ALL_TYPE_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (5, REFRESH_ALL_TYPE_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (6, REFRESH_ALL_TYPE_TFILTERS), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 << F (5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 << F (6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (7, REFRESH_ALL_TYPE_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (8, REFRESH_ALL_TYPE_TFILTERS), #undef F - DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 7, - DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 8, - DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 9, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 10, + DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, + DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, + DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, __DELAYED_ACTION_TYPE_MAX, + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, + DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, @@ -3354,6 +3376,165 @@ rta_multipath_done: return g_steal_pointer (&obj); } +static NMPObject * +_new_from_nl_routing_rule (struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [FRA_UNSPEC] = { }, + [FRA_DST] = { /* struct in_addr, struct in6_addr */ }, + [FRA_SRC] = { /* struct in_addr, struct in6_addr */ }, + [FRA_IIFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [FRA_GOTO] = { .type = NLA_U32, }, + [FRA_UNUSED2] = { }, + [FRA_PRIORITY] = { .type = NLA_U32, }, + [FRA_UNUSED3] = { }, + [FRA_UNUSED4] = { }, + [FRA_UNUSED5] = { }, + [FRA_FWMARK] = { .type = NLA_U32, }, + [FRA_FLOW] = { .type = NLA_U32, }, + [FRA_TUN_ID] = { .type = NLA_U64, }, + [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32, }, + [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32, }, + [FRA_TABLE] = { .type = NLA_U32, }, + [FRA_FWMASK] = { .type = NLA_U32, }, + [FRA_OIFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [FRA_PAD] = { .type = NLA_U32, }, + [FRA_L3MDEV] = { .type = NLA_U8, }, + [FRA_UID_RANGE] = { .minlen = sizeof(NMFibRuleUidRange), + .maxlen = sizeof(NMFibRuleUidRange), }, + [FRA_PROTOCOL] = { .type = NLA_U8, }, + [FRA_IP_PROTO] = { .type = NLA_U8, }, + [FRA_SPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), }, + [FRA_DPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), }, + }; + struct nlattr *tb[G_N_ELEMENTS (policy)]; + const struct fib_rule_hdr *frh; + NMPlatformRoutingRule *props; + nm_auto_nmpobj NMPObject *obj = NULL; + int addr_family; + guint8 addr_size; + + if (nlmsg_parse_arr (nlh, sizeof (*frh), tb, policy) < 0) + return NULL; + + frh = nlmsg_data (nlh); + + addr_family = frh->family; + + if (!NM_IN_SET (addr_family, AF_INET, AF_INET6)) { + /* we don't care about other address families. */ + return NULL; + } + + addr_size = nm_utils_addr_family_to_size (addr_family); + + obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL); + props = &obj->routing_rule; + + props->addr_family = addr_family; + props->action = frh->action; + props->flags = frh->flags; + props->tos = frh->tos; + + props->table = tb[FRA_TABLE] + ? nla_get_u32 (tb[FRA_TABLE]) + : frh->table; + + if (tb[FRA_SUPPRESS_PREFIXLEN]) + props->suppress_prefixlen_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_PREFIXLEN]); + + if (tb[FRA_SUPPRESS_IFGROUP]) + props->suppress_ifgroup_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_IFGROUP]); + + if (tb[FRA_IIFNAME]) + nla_strlcpy (props->iifname, tb[FRA_IIFNAME], sizeof (props->iifname)); + + if (tb[FRA_OIFNAME]) + nla_strlcpy (props->oifname, tb[FRA_OIFNAME], sizeof (props->oifname)); + + if (tb[FRA_PRIORITY]) + props->priority = nla_get_u32 (tb[FRA_PRIORITY]); + + if (tb[FRA_FWMARK]) + props->fwmark = nla_get_u32 (tb[FRA_FWMARK]); + + if (tb[FRA_FWMASK]) + props->fwmask = nla_get_u32 (tb[FRA_FWMASK]); + + if (tb[FRA_GOTO]) + props->goto_target = nla_get_u32 (tb[FRA_GOTO]); + + props->src_len = frh->src_len; + if (props->src_len > addr_size * 8) + return NULL; + if (!tb[FRA_SRC]) { + if (props->src_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted (addr_family, + &props->src, + nla_data (tb[FRA_SRC]), + nla_len (tb[FRA_SRC]), + NULL)) + return NULL; + + props->dst_len = frh->dst_len; + if (props->dst_len > addr_size * 8) + return NULL; + if (!tb[FRA_DST]) { + if (props->dst_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted (addr_family, + &props->dst, + nla_data (tb[FRA_DST]), + nla_len (tb[FRA_DST]), + NULL)) + return NULL; + + if (tb[FRA_FLOW]) + props->flow = nla_get_u32 (tb[FRA_FLOW]); + + if (tb[FRA_TUN_ID]) + props->tun_id = nla_get_be64 (tb[FRA_TUN_ID]); + + if (tb[FRA_L3MDEV]) { + /* actually, kernel only allows this attribute to be missing or + * "1". Still, encode it as full uint8. + * + * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */ + props->l3mdev = nla_get_u8 (tb[FRA_L3MDEV]); + } + + if (tb[FRA_PROTOCOL]) + props->protocol = nla_get_u8 (tb[FRA_PROTOCOL]); + else + nm_assert (props->protocol == RTPROT_UNSPEC); + + if (tb[FRA_IP_PROTO]) + props->ip_proto = nla_get_u8 (tb[FRA_IP_PROTO]); + + G_STATIC_ASSERT_EXPR (sizeof (NMFibRulePortRange) == 4); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, start) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, end) == 2); + + nla_memcpy_checked_size (&props->sport_range, tb[FRA_SPORT_RANGE], sizeof (props->sport_range)); + nla_memcpy_checked_size (&props->dport_range, tb[FRA_DPORT_RANGE], sizeof (props->dport_range)); + + G_STATIC_ASSERT_EXPR (sizeof (NMFibRuleUidRange) == 8); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, start) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, end) == 4); + + if (tb[FRA_UID_RANGE]) { + nla_memcpy_checked_size (&props->uid_range, tb[FRA_UID_RANGE], sizeof (props->uid_range)); + props->uid_range_has = TRUE; + } + + return g_steal_pointer (&obj); +} + static NMPObject * _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) { @@ -3453,6 +3634,10 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m case RTM_DELROUTE: case RTM_GETROUTE: return _new_from_nl_route (msghdr, id_only); + case RTM_NEWRULE: + case RTM_DELRULE: + case RTM_GETRULE: + return _new_from_nl_routing_rule (msghdr, id_only); case RTM_NEWQDISC: case RTM_DELQDISC: case RTM_GETQDISC: @@ -4372,6 +4557,8 @@ refresh_all_type_get_info (RefreshAllType refresh_all_type) R (REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), R (REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), R (REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), + R (REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), + R (REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), R (REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), R (REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), #undef R @@ -4391,6 +4578,8 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_type_to_refresh_all_type, Delaye NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), @@ -4410,7 +4599,7 @@ delayed_action_type_from_refresh_all_type (RefreshAllType refresh_all_type) return t; } -static DelayedActionType +static RefreshAllType refresh_all_type_from_needle_object (const NMPObject *obj_needle) { switch (NMP_OBJECT_GET_TYPE (obj_needle)) { @@ -4421,6 +4610,13 @@ refresh_all_type_from_needle_object (const NMPObject *obj_needle) case NMP_OBJECT_TYPE_IP6_ROUTE: return REFRESH_ALL_TYPE_IP6_ROUTES; case NMP_OBJECT_TYPE_QDISC: return REFRESH_ALL_TYPE_QDISCS; case NMP_OBJECT_TYPE_TFILTER: return REFRESH_ALL_TYPE_TFILTERS; + case NMP_OBJECT_TYPE_ROUTING_RULE: + switch (NMP_OBJECT_CAST_ROUTING_RULE (obj_needle)->addr_family) { + case AF_INET: return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; + case AF_INET6: return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; + } + nm_assert_not_reached (); + return 0; default: nm_assert_not_reached (); return 0; @@ -4439,6 +4635,12 @@ refresh_all_type_init_lookup (RefreshAllType refresh_all_type, nm_assert (refresh_all_info); + if (NM_IN_SET (refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { + return nmp_lookup_init_object_by_addr_family (lookup, + refresh_all_info->obj_type, + refresh_all_info->addr_family); + } + /* not yet implemented. */ nm_assert (refresh_all_info->addr_family == AF_UNSPEC); @@ -4459,6 +4661,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, "refresh-all-routing-rules-ip4"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, "refresh-all-routing-rules-ip6"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), @@ -4467,6 +4671,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX), ); @@ -4955,6 +5160,7 @@ cache_on_change (NMPlatform *platform, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -5313,6 +5519,7 @@ _nl_msg_new_dump (NMPObjectType obj_type, case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: { const struct rtgenmsg gmsg = { .rtgen_family = preferred_addr_family, @@ -5333,12 +5540,30 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionType action_type_prune; DelayedActionType iflags; nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; - FOR_EACH_DELAYED_ACTION (iflags, action_type) { + action_type_prune = action_type; + + /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra + * cache lookup for every entry. + * + * Avoid that, by special casing routing-rules here. */ + if (NM_FLAGS_ALL (action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { + NMPLookup lookup; + + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), + &lookup); + action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; + } + + FOR_EACH_DELAYED_ACTION (iflags, action_type_prune) { RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); NMPLookup lookup; @@ -5495,6 +5720,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, RTM_DELADDR, RTM_DELROUTE, + RTM_DELRULE, RTM_DELQDISC, RTM_DELTFILTER)) { /* The event notifies about a deleted object. We don't need to initialize all @@ -5513,6 +5739,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event && NM_IN_SET (msghdr->nlmsg_type, RTM_NEWADDR, RTM_NEWLINK, RTM_NEWROUTE, + RTM_NEWRULE, RTM_NEWQDISC, RTM_NEWTFILTER)) { is_dump = delayed_action_refresh_all_in_progress (platform, @@ -5532,10 +5759,11 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event switch (msghdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: case RTM_GETLINK: + case RTM_NEWADDR: + case RTM_NEWLINK: case RTM_NEWQDISC: + case RTM_NEWRULE: case RTM_NEWTFILTER: cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { @@ -5630,10 +5858,11 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event break; } - case RTM_DELLINK: case RTM_DELADDR: - case RTM_DELROUTE: + case RTM_DELLINK: case RTM_DELQDISC: + case RTM_DELROUTE: + case RTM_DELRULE: case RTM_DELTFILTER: cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { @@ -7817,7 +8046,8 @@ qdisc_add (NMPlatform *platform, if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) return 0; - + if (seq_result < 0) + return seq_result; return -NME_UNSPEC; } @@ -8134,6 +8364,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -8399,9 +8630,13 @@ constructed (GObject *_object) g_assert (!nle); nle = nl_socket_add_memberships (priv->nlh, + RTNLGRP_IPV4_IFADDR, + RTNLGRP_IPV4_ROUTE, + RTNLGRP_IPV4_RULE, + RTNLGRP_IPV6_RULE, + RTNLGRP_IPV6_IFADDR, + RTNLGRP_IPV6_ROUTE, RTNLGRP_LINK, - RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, - RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, RTNLGRP_TC, 0); g_assert (!nle); @@ -8428,6 +8663,7 @@ constructed (GObject *_object) DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f0fb2a528d..5e6744db09 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -6063,6 +6064,253 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi return buf; } +static void +_routing_rule_addr_to_string (char **buf, + gsize *len, + int addr_family, + const NMIPAddr *addr, + guint8 plen, + gboolean is_src) +{ + char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + gboolean is_zero; + gsize addr_size; + + nm_assert_addr_family (addr_family); + nm_assert (addr); + + addr_size = nm_utils_addr_family_to_size (addr_family); + + is_zero = nm_utils_memeqzero (addr, addr_size); + + if ( plen == 0 + && is_zero) { + if (is_src) + nm_utils_strbuf_append_str (buf, len, " from all"); + else + nm_utils_strbuf_append_str (buf, len, ""); + return; + } + + nm_utils_strbuf_append_str (buf, len, is_src ? " from " : " to "); + + nm_utils_strbuf_append_str (buf, len, nm_utils_inet_ntop (addr_family, addr, s_addr)); + + if (plen != (addr_size * 8)) + nm_utils_strbuf_append (buf, len, "/%u", plen); +} + +static void +_routing_rule_port_range_to_string (char **buf, + gsize *len, + const NMFibRulePortRange *port_range, + const char *name) +{ + if ( port_range->start == 0 + && port_range->end == 0) + nm_utils_strbuf_append_str (buf, len, ""); + else { + nm_utils_strbuf_append (buf, len, " %s %u", name, port_range->start); + if (port_range->start != port_range->end) + nm_utils_strbuf_append (buf, len, "-%u", port_range->end); + } +} + +const char * +nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *routing_rule, char *buf, gsize len) +{ + const char *buf0; + guint32 rr_flags; + + if (!nm_utils_to_string_buffer_init_null (routing_rule, &buf, &len)) + return buf; + + if (!NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6)) { + /* invalid addr-family. The other fields are undefined. */ + if (routing_rule->addr_family == AF_UNSPEC) + g_snprintf (buf, len, "[routing-rule]"); + else + g_snprintf (buf, len, "[routing-rule family:%u]", routing_rule->addr_family); + return buf; + } + + buf0 = buf; + + rr_flags = routing_rule->flags; + + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_INVERT); + nm_utils_strbuf_append (&buf, &len, + "[%c] " /* addr-family */ + "%u:" /* priority */ + "%s", /* not/FIB_RULE_INVERT */ + nm_utils_addr_family_to_char (routing_rule->addr_family), + routing_rule->priority, + ( NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_INVERT) + ? " not" + : "")); + + _routing_rule_addr_to_string (&buf, &len, + routing_rule->addr_family, + &routing_rule->src, + routing_rule->src_len, + TRUE); + + _routing_rule_addr_to_string (&buf, &len, + routing_rule->addr_family, + &routing_rule->dst, + routing_rule->dst_len, + FALSE); + + if (routing_rule->tos) + nm_utils_strbuf_append (&buf, &len, " tos 0x%02x", routing_rule->tos); + + if ( routing_rule->fwmark != 0 + || routing_rule->fwmask != 0) { + nm_utils_strbuf_append (&buf, &len, " fwmark %#x", (unsigned) routing_rule->fwmark); + if (routing_rule->fwmark != 0xFFFFFFFFu) + nm_utils_strbuf_append (&buf, &len, "/%#x", (unsigned) routing_rule->fwmask); + } + + if (routing_rule->iifname[0]) { + nm_utils_strbuf_append (&buf, &len, " iif %s", routing_rule->iifname); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_IIF_DETACHED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_IIF_DETACHED)) + nm_utils_strbuf_append_str (&buf, &len, " [detached]"); + } + + if (routing_rule->oifname[0]) { + nm_utils_strbuf_append (&buf, &len, " oif %s", routing_rule->oifname); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_OIF_DETACHED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_OIF_DETACHED)) + nm_utils_strbuf_append_str (&buf, &len, " [detached]"); + } + + if (routing_rule->l3mdev != 0) { + if (routing_rule->l3mdev == 1) + nm_utils_strbuf_append_str (&buf, &len, " lookup [l3mdev-table]"); + else { + nm_utils_strbuf_append (&buf, &len, " lookup [l3mdev-table/%u]", (unsigned) routing_rule->l3mdev); + } + } + + if ( routing_rule->uid_range_has + || routing_rule->uid_range.start + || routing_rule->uid_range.end) { + nm_utils_strbuf_append (&buf, &len, + " uidrange %u-%u%s", + routing_rule->uid_range.start, + routing_rule->uid_range.end, + routing_rule->uid_range_has ? "" : "(?)"); + } + + if (routing_rule->ip_proto != 0) { + /* we don't call getprotobynumber(), just print the numeric value. + * This differs from what ip-rule prints. */ + nm_utils_strbuf_append (&buf, &len, + " ipproto %u", + routing_rule->ip_proto); + } + + _routing_rule_port_range_to_string (&buf, &len, + &routing_rule->sport_range, + "sport"); + + _routing_rule_port_range_to_string (&buf, &len, + &routing_rule->dport_range, + "dport"); + + if (routing_rule->tun_id != 0) { + nm_utils_strbuf_append (&buf, &len, + " tun_id %"G_GUINT64_FORMAT, + routing_rule->tun_id); + } + + if (routing_rule->table != 0) { + nm_utils_strbuf_append (&buf, &len, + " lookup %u", + routing_rule->table); + } + + if (routing_rule->suppress_prefixlen_inverse != 0) { + nm_utils_strbuf_append (&buf, &len, + " suppress_prefixlen %d", + (int) (~routing_rule->suppress_prefixlen_inverse)); + } + + if (routing_rule->suppress_ifgroup_inverse != 0) { + nm_utils_strbuf_append (&buf, &len, + " suppress_ifgroup %d", + (int) (~routing_rule->suppress_ifgroup_inverse)); + } + + if (routing_rule->flow) { + /* FRA_FLOW is only for IPv4, but we want to print the value for all address-families, + * to see when it is set. In practice, this should not be set except for IPv4. + * + * We don't follow the style how ip-rule prints flow/realms. It's confusing. Just + * print the value hex. */ + nm_utils_strbuf_append (&buf, &len, + " realms 0x%08x", + routing_rule->flow); + } + + if (routing_rule->action == RTN_NAT) { + G_STATIC_ASSERT_EXPR (RTN_NAT == 10); + + /* NAT is deprecated for many years. We don't support RTA_GATEWAY/FRA_UNUSED2 + * for the gateway, and so do recent kernels ignore that parameter. */ + nm_utils_strbuf_append_str (&buf, &len, " masquerade"); + } else if (routing_rule->action == FR_ACT_GOTO) { + if (routing_rule->goto_target != 0) + nm_utils_strbuf_append (&buf, &len, " goto %u", routing_rule->goto_target); + else + nm_utils_strbuf_append_str (&buf, &len, " goto none"); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_UNRESOLVED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_UNRESOLVED)) + nm_utils_strbuf_append_str (&buf, &len, " unresolved"); + } else if (routing_rule->action != FR_ACT_TO_TBL) { + const char *ss; + char ss_buf[60]; + +#define _V(v1, v2) ((sizeof (char[(((int) (v1)) == ((int) (v2))) ? 1 : -1]) * 0) + (v1)) + switch (routing_rule->action) { + case _V (FR_ACT_UNSPEC, RTN_UNSPEC) : ss = "none"; break; + case _V (FR_ACT_TO_TBL, RTN_UNICAST) : ss = "unicast"; break; + case _V (FR_ACT_GOTO, RTN_LOCAL) : ss = "local"; break; + case _V (FR_ACT_NOP, RTN_BROADCAST) : ss = "nop"; break; + case _V (FR_ACT_RES3, RTN_ANYCAST) : ss = "anycast"; break; + case _V (FR_ACT_RES4, RTN_MULTICAST) : ss = "multicast"; break; + case _V (FR_ACT_BLACKHOLE, RTN_BLACKHOLE) : ss = "blackhole"; break; + case _V (FR_ACT_UNREACHABLE, RTN_UNREACHABLE) : ss = "unreachable"; break; + case _V (FR_ACT_PROHIBIT, RTN_PROHIBIT) : ss = "prohibit"; break; + case RTN_THROW : ss = "throw"; break; + case RTN_NAT : ss = "nat"; break; + case RTN_XRESOLVE : ss = "xresolve"; break; + default: + ss = nm_sprintf_buf (ss_buf, "action-%u", routing_rule->action); + break; + } +#undef _V + nm_utils_strbuf_append (&buf, &len, " %s", ss); + } + + if (routing_rule->protocol != RTPROT_UNSPEC) + nm_utils_strbuf_append (&buf, &len, " protocol %u", routing_rule->protocol); + + if ( routing_rule->goto_target != 0 + && routing_rule->action != FR_ACT_GOTO) { + /* a trailing target is set for an unexpected action. Print it. */ + nm_utils_strbuf_append (&buf, &len, " goto-target %u", routing_rule->goto_target); + } + + if (rr_flags != 0) { + /* we have some flags we didn't print about yet. */ + nm_utils_strbuf_append (&buf, &len, " remaining-flags %x", rr_flags); + } + + return buf0; +} + const char * nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) { @@ -7021,6 +7269,186 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route return 0; } +#define _ROUTING_RULE_FLAGS_IGNORE ( FIB_RULE_UNRESOLVED \ + | FIB_RULE_IIF_DETACHED \ + | FIB_RULE_OIF_DETACHED) + +void +nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, + NMPlatformRoutingRuleCmpType cmp_type, + NMHashState *h) +{ + gboolean cmp_full = TRUE; + gsize addr_size; + guint32 flags_mask = G_MAXUINT32; + + if (G_UNLIKELY (!NM_IN_SET (obj->addr_family, AF_INET, AF_INET6))) { + /* the address family is not one of the supported ones. That means, the + * instance will only compare equal to itself (pointer-equality). */ + nm_hash_update_val (h, (gconstpointer) obj); + return; + } + + switch (cmp_type) { + + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: + + flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: + + cmp_full = FALSE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: + + + nm_hash_update_vals (h, + obj->addr_family, + obj->tun_id, + obj->table, + obj->flags & flags_mask, + obj->priority, + obj->fwmark, + obj->fwmask, + ( ( cmp_full + || ( cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY + && obj->action == FR_ACT_GOTO)) + ? obj->goto_target + : (guint32) 0u), + ( ( cmp_full + || obj->addr_family == AF_INET) + ? obj->flow + : (guint32) 0u), + NM_HASH_COMBINE_BOOLS (guint8, + obj->uid_range_has), + obj->suppress_prefixlen_inverse, + obj->suppress_ifgroup_inverse, + ( cmp_full + ? obj->l3mdev + : (guint8) !!obj->l3mdev), + obj->action, + obj->tos, + obj->src_len, + obj->dst_len, + obj->protocol, + obj->ip_proto); + addr_size = nm_utils_addr_family_to_size (obj->addr_family); + if (cmp_full || obj->src_len > 0) + nm_hash_update (h, &obj->src, addr_size); + if (cmp_full || obj->dst_len > 0) + nm_hash_update (h, &obj->dst, addr_size); + if (cmp_full || obj->uid_range_has) + nm_hash_update_valp (h, &obj->uid_range); + nm_hash_update_valp (h, &obj->sport_range); + nm_hash_update_valp (h, &obj->dport_range); + nm_hash_update_str (h, obj->iifname); + nm_hash_update_str (h, obj->oifname); + return; + } + + nm_assert_not_reached (); +} + +int +nm_platform_routing_rule_cmp (const NMPlatformRoutingRule *a, + const NMPlatformRoutingRule *b, + NMPlatformRoutingRuleCmpType cmp_type) +{ + gboolean cmp_full = TRUE; + gsize addr_size; + bool valid; + guint32 flags_mask = G_MAXUINT32; + + NM_CMP_SELF (a, b); + + valid = NM_IN_SET (a->addr_family, AF_INET, AF_INET6); + NM_CMP_DIRECT (valid, + (bool) NM_IN_SET (b->addr_family, AF_INET, AF_INET6)); + + if (G_UNLIKELY (!valid)) { + /* the address family is not one of the supported ones. That means, the + * instance will only compare equal to itself. */ + NM_CMP_DIRECT ((uintptr_t) a, (uintptr_t) b); + nm_assert_not_reached (); + return 0; + } + + switch (cmp_type) { + + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: + + flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: + + cmp_full = FALSE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: + NM_CMP_FIELD (a, b, addr_family); + NM_CMP_FIELD (a, b, action); + NM_CMP_FIELD (a, b, priority); + NM_CMP_FIELD (a, b, tun_id); + + if (cmp_full) + NM_CMP_FIELD (a, b, l3mdev); + else + NM_CMP_FIELD_BOOL (a, b, l3mdev); + + if (cmp_full || !a->l3mdev) + NM_CMP_FIELD (a, b, table); + + NM_CMP_DIRECT (a->flags & flags_mask, b->flags & flags_mask); + + NM_CMP_FIELD (a, b, fwmark); + NM_CMP_FIELD (a, b, fwmask); + + if ( cmp_full + || ( cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY + && a->action == FR_ACT_GOTO)) + NM_CMP_FIELD (a, b, goto_target); + + NM_CMP_FIELD (a, b, suppress_prefixlen_inverse); + NM_CMP_FIELD (a, b, suppress_ifgroup_inverse); + NM_CMP_FIELD (a, b, tos); + + if (cmp_full || a->addr_family == AF_INET) + NM_CMP_FIELD (a, b, flow); + + NM_CMP_FIELD (a, b, protocol); + NM_CMP_FIELD (a, b, ip_proto); + addr_size = nm_utils_addr_family_to_size (a->addr_family); + + NM_CMP_FIELD (a, b, src_len); + if (cmp_full || a->src_len > 0) + NM_CMP_FIELD_MEMCMP_LEN (a, b, src, addr_size); + + NM_CMP_FIELD (a, b, dst_len); + if (cmp_full || a->dst_len > 0) + NM_CMP_FIELD_MEMCMP_LEN (a, b, dst, addr_size); + + NM_CMP_FIELD_UNSAFE (a, b, uid_range_has); + if (cmp_full || a->uid_range_has) { + NM_CMP_FIELD (a, b, uid_range.start); + NM_CMP_FIELD (a, b, uid_range.end); + } + + NM_CMP_FIELD (a, b, sport_range.start); + NM_CMP_FIELD (a, b, sport_range.end); + NM_CMP_FIELD (a, b, dport_range.start); + NM_CMP_FIELD (a, b, dport_range.end); + NM_CMP_FIELD_STR (a, b, iifname); + NM_CMP_FIELD_STR (a, b, oifname); + return 0; + } + + nm_assert_not_reached (); + return 0; +} + /** * nm_platform_ip_address_cmp_expiry: * @a: a NMPlatformIPAddress to compare @@ -7117,6 +7545,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform _LOG3D ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0)); } +static void +log_routing_rule (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformRoutingRule *routing_rule, NMPlatformSignalChangeType change_type, gpointer user_data) +{ + /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */ + _LOG3D ("signal: rt-rule %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_routing_rule_to_string (routing_rule, NULL, 0)); +} + static void log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data) { @@ -7183,10 +7618,13 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, return; } - ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (o)->ifindex; - klass = NMP_OBJECT_GET_CLASS (o); + if (klass->obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) + ifindex = 0; + else + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (o)->ifindex; + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) @@ -7400,11 +7838,12 @@ nm_platform_class_init (NMPlatformClass *platform_class) } G_STMT_END /* Signals */ - SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); - SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); - SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); + SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, log_routing_rule); + SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); + SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 9b626fb4a0..4e7cd5cd59 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -151,6 +151,14 @@ typedef enum { } NMPlatformIPRouteCmpType; +typedef enum { + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, +} NMPlatformRoutingRuleCmpType; + typedef enum { /* match-flags are strictly inclusive. That means, @@ -256,6 +264,7 @@ typedef enum { /*< skip >*/ NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, + NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_ID_TFILTER, _NM_PLATFORM_SIGNAL_ID_LAST, @@ -545,6 +554,48 @@ typedef union { #undef __NMPlatformIPRoute_COMMON +typedef struct { + /* struct fib_rule_uid_range */ + guint32 start; + guint32 end; +} NMFibRuleUidRange; + +typedef struct { + /* struct fib_rule_port_range */ + guint16 start; + guint16 end; +} NMFibRulePortRange; + +typedef struct { + NMIPAddr src; /* FRA_SRC */ + NMIPAddr dst; /* FRA_DST */ + guint64 tun_id; /* betoh64(FRA_TUN_ID) */ + guint32 table; /* (struct fib_rule_hdr).table, FRA_TABLE */ + guint32 flags; /* (struct fib_rule_hdr).flags */ + guint32 priority; /* RA_PRIORITY */ + guint32 fwmark; /* FRA_FWMARK */ + guint32 fwmask; /* FRA_FWMASK */ + guint32 goto_target; /* FRA_GOTO */ + guint32 flow; /* FRA_FLOW */ + guint32 suppress_prefixlen_inverse; /* ~(FRA_SUPPRESS_PREFIXLEN) */ + guint32 suppress_ifgroup_inverse; /* ~(FRA_SUPPRESS_IFGROUP) */ + NMFibRuleUidRange uid_range; /* FRA_UID_RANGE */ + NMFibRulePortRange sport_range; /* FRA_SPORT_RANGE */ + NMFibRulePortRange dport_range; /* FRA_DPORT_RANGE */ + char iifname[NMP_IFNAMSIZ]; /* FRA_IIFNAME */ + char oifname[NMP_IFNAMSIZ]; /* FRA_OIFNAME */ + guint8 addr_family; /* (struct fib_rule_hdr).family */ + guint8 action; /* (struct fib_rule_hdr).action */ + guint8 tos; /* (struct fib_rule_hdr).tos */ + guint8 src_len; /* (struct fib_rule_hdr).src_len */ + guint8 dst_len; /* (struct fib_rule_hdr).dst_len */ + guint8 l3mdev; /* FRA_L3MDEV */ + guint8 protocol; /* FRA_PROTOCOL */ + guint8 ip_proto; /* FRA_IP_PROTO */ + + bool uid_range_has:1; /* has(FRA_UID_RANGE) */ +} NMPlatformRoutingRule; + typedef struct { __NMPlatformObjWithIfindex_COMMON; const char *kind; @@ -1012,6 +1063,7 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED "ip6-address-changed" #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" +#define NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED "routing-rule-changed" #define NM_PLATFORM_SIGNAL_QDISC_CHANGED "qdisc-changed" #define NM_PLATFORM_SIGNAL_TFILTER_CHANGED "tfilter-changed" @@ -1521,6 +1573,7 @@ const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *addre const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len); const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len); const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len); +const char *nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *routing_rule, char *buf, gsize len); const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len); const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len); const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len); @@ -1565,6 +1618,14 @@ nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6 return nm_platform_ip6_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } +int nm_platform_routing_rule_cmp (const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b, NMPlatformRoutingRuleCmpType cmp_type); + +static inline int +nm_platform_routing_rule_cmp_full (const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b) +{ + return nm_platform_routing_rule_cmp (a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); +} + int nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b); int nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b); @@ -1573,6 +1634,7 @@ void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHas void nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h); void nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); void nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, NMPlatformRoutingRuleCmpType cmp_type, NMHashState *h); void nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h); void nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h); void nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index f6ba0542cf..ae8fb6d4bb 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -427,6 +427,25 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, } return 1; + case NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY: + obj_type = NMP_OBJECT_GET_TYPE (obj_a); + /* currently, only routing rules are supported for this cache-id-type. */ + if ( obj_type != NMP_OBJECT_TYPE_ROUTING_RULE + || !NM_IN_SET (obj_a->routing_rule.addr_family, AF_INET, AF_INET6)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_b) == NMP_OBJECT_TYPE_ROUTING_RULE + && obj_a->routing_rule.addr_family == obj_b->routing_rule.addr_family; + } + if (h) { + nm_hash_update_vals (h, idx_type->cache_id_type, + obj_a->routing_rule.addr_family); + } + return 1; + case NMP_CACHE_ID_TYPE_NONE: case __NMP_CACHE_ID_TYPE_MAX: break; @@ -1359,6 +1378,10 @@ _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { *dst = *src; nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); +_vt_cmd_plobj_id_copy (routing_rule, NMPlatformRoutingRule, { + *dst = *src; + nm_assert (nm_platform_routing_rule_cmp (dst, src, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +}); /* Uses internally nmp_object_copy(), hence it also violates the const * promise for @obj. @@ -1459,6 +1482,12 @@ _vt_cmd_plobj_id_cmp_ip6_route (const NMPlatformObject *obj1, const NMPlatformOb return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } +static int +_vt_cmd_plobj_id_cmp_routing_rule (const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_routing_rule_cmp ((NMPlatformRoutingRule *) obj1, (NMPlatformRoutingRule *) obj2, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); +} + void nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h) { @@ -1524,6 +1553,9 @@ _vt_cmd_plobj_id_hash_update (ip4_route, NMPlatformIP4Route, { _vt_cmd_plobj_id_hash_update (ip6_route, NMPlatformIP6Route, { nm_platform_ip6_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) +_vt_cmd_plobj_id_hash_update (routing_rule, NMPlatformRoutingRule, { + nm_platform_routing_rule_hash_update (obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h); +}) _vt_cmd_plobj_id_hash_update (qdisc, NMPlatformQdisc, { nm_hash_update_vals (h, obj->ifindex, @@ -1547,6 +1579,12 @@ _vt_cmd_plobj_hash_update_ip6_route (const NMPlatformObject *obj, NMHashState *h return nm_platform_ip6_route_hash_update ((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); } +static void +_vt_cmd_plobj_hash_update_routing_rule (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_routing_rule_hash_update ((const NMPlatformRoutingRule *) obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, h); +} + gboolean nmp_object_is_alive (const NMPObject *obj) { @@ -1596,6 +1634,12 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) && !NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED); } +static gboolean +_vt_cmd_obj_is_alive_routing_rule (const NMPObject *obj) +{ + return NM_IN_SET (obj->routing_rule.addr_family, AF_INET, AF_INET6); +} + static gboolean _vt_cmd_obj_is_alive_qdisc (const NMPObject *obj) { @@ -1663,6 +1707,12 @@ static const guint8 _supported_cache_ids_ipx_route[] = { 0, }; +static const guint8 _supported_cache_ids_routing_rules[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, + 0, +}; + /*****************************************************************************/ static void @@ -1940,6 +1990,7 @@ nmp_lookup_init_obj_type (NMPLookup *lookup, case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: case NMP_OBJECT_TYPE_QDISC: case NMP_OBJECT_TYPE_TFILTER: _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); @@ -2088,6 +2139,23 @@ nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, return _L (lookup); } +const NMPLookup * +nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, + NMPObjectType obj_type, + int addr_family) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert_addr_family (addr_family); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family = addr_family; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; + return _L (lookup); +} + /*****************************************************************************/ GArray * @@ -3077,6 +3145,25 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full, }, + [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_ROUTING_RULE, + .sizeof_data = sizeof (NMPObjectRoutingRule), + .sizeof_public = sizeof (NMPlatformRoutingRule), + .obj_type_name = "routing-rule", + .rtm_gettype = RTM_GETRULE, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, + .signal_type = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, + .supported_cache_ids = _supported_cache_ids_routing_rules, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_routing_rule, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_routing_rule, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_routing_rule, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_routing_rule_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_routing_rule_to_string, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_routing_rule_cmp_full, + }, [NMP_OBJECT_TYPE_QDISC - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_QDISC, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 15f660a042..8bbbcd9e6e 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -174,6 +174,11 @@ typedef enum { /*< skip >*/ * cache-resync. */ NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + /* a filter for objects that track an explicit address family. + * + * Note that currently on NMPObjectRoutingRule is indexed by this filter. */ + NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, + __NMP_CACHE_ID_TYPE_MAX, NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, } NMPCacheIdType; @@ -324,6 +329,10 @@ typedef struct { NMPlatformIP6Route _public; } NMPObjectIP6Route; +typedef struct { + NMPlatformRoutingRule _public; +} NMPObjectRoutingRule; + typedef struct { NMPlatformQdisc _public; } NMPObjectQdisc; @@ -392,6 +401,9 @@ struct _NMPObject { NMPObjectIP4Route _ip4_route; NMPObjectIP6Route _ip6_route; + NMPlatformRoutingRule routing_rule; + NMPObjectRoutingRule _routing_rule; + NMPlatformQdisc qdisc; NMPObjectQdisc _qdisc; NMPlatformTfilter tfilter; @@ -595,6 +607,14 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ }) +#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \ + }) + #define NMP_OBJECT_CAST_QDISC(obj) \ ({ \ typeof (obj) _obj = (obj); \ @@ -764,6 +784,9 @@ const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, guint32 metric, const struct in6_addr *src, guint8 src_plen); +const NMPLookup *nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, + NMPObjectType obj_type, + int addr_family); GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, NMPObjectType obj_type, From 9992ac1cf83bd8657aef23a828c4dc3048d7b915 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 5 Mar 2019 12:13:04 +0100 Subject: [PATCH 14/16] platform: add routing-rule add/delete netlink functions --- contrib/scripts/NM-log | 2 +- src/platform/nm-linux-platform.c | 159 +++++++++++++++++++++++++++++++ src/platform/nm-platform.c | 42 ++++++-- src/platform/nm-platform.h | 8 ++ 4 files changed, 200 insertions(+), 11 deletions(-) diff --git a/contrib/scripts/NM-log b/contrib/scripts/NM-log index 903d68ed86..89f7021328 100755 --- a/contrib/scripts/NM-log +++ b/contrib/scripts/NM-log @@ -38,7 +38,7 @@ NM-colorize() { GREP_COLOR='01;31' grep -a --color=always '^\|^\(.* \)\?<\(warn> \|error>\) \[[0-9.]*\]' | \ GREP_COLOR='01;33' grep -a --color=always '^\|^\(.* \)\? \[[0-9.]*\]\( .*\.*$\)\?' | \ GREP_COLOR='01;37' grep -a --color=always '^\|\\|\\|\addr_family); + guint32 table; + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + + table = routing_rule->table; + + if ( NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6) + && routing_rule->action == FR_ACT_TO_TBL + && routing_rule->l3mdev == 0 + && table == RT_TABLE_UNSPEC) { + /* for IPv6, this setting is invalid and rejected by kernel. That's fine. + * + * for IPv4, kernel will automatically assign an unused table. That's not + * fine, because we don't know what we will get. + * + * The caller must not allow that to happen. */ + nm_assert_not_reached (); + } + + { + const struct fib_rule_hdr frh = { + .family = routing_rule->addr_family, + .src_len = routing_rule->src_len, + .dst_len = routing_rule->dst_len, + .tos = routing_rule->tos, + .table = table, + .action = routing_rule->action, + + /* we only allow setting the "not" flag. */ + .flags = routing_rule->flags & ((guint32) FIB_RULE_INVERT), + }; + + if (nlmsg_append_struct (msg, &frh) < 0) + goto nla_put_failure; + } + + if (table > G_MAXINT8) + NLA_PUT_U32 (msg, FRA_TABLE, table); + + if (routing_rule->suppress_prefixlen_inverse != 0) + NLA_PUT_U32 (msg, FRA_SUPPRESS_PREFIXLEN, ~routing_rule->suppress_prefixlen_inverse); + + if (routing_rule->suppress_ifgroup_inverse != 0) + NLA_PUT_U32 (msg, FRA_SUPPRESS_IFGROUP, ~routing_rule->suppress_ifgroup_inverse); + + if (routing_rule->iifname[0] != '\0') + NLA_PUT_STRING (msg, FRA_IIFNAME, routing_rule->iifname); + + if (routing_rule->oifname[0] != '\0') + NLA_PUT_STRING (msg, FRA_OIFNAME, routing_rule->oifname); + + /* we always set the priority and don't support letting kernel pick one. */ + NLA_PUT_U32 (msg, FRA_PRIORITY, routing_rule->priority); + + if ( routing_rule->fwmark != 0 + || routing_rule->fwmask != 0) { + NLA_PUT_U32 (msg, FRA_FWMARK, routing_rule->fwmark); + NLA_PUT_U32 (msg, FRA_FWMASK, routing_rule->fwmask); + } + + if (routing_rule->src_len > 0) + NLA_PUT (msg, FRA_SRC, addr_size, &routing_rule->src); + + if (routing_rule->dst_len > 0) + NLA_PUT (msg, FRA_DST, addr_size, &routing_rule->dst); + + if (routing_rule->flow != 0) { + /* only relevant for IPv4. */ + NLA_PUT_U32 (msg, FRA_FLOW, routing_rule->flow); + } + + if (routing_rule->tun_id != 0) + NLA_PUT_U64 (msg, FRA_TUN_ID, htobe64 (routing_rule->tun_id)); + + if (routing_rule->l3mdev) + NLA_PUT_U8 (msg, FRA_L3MDEV, routing_rule->l3mdev); + + if (routing_rule->protocol != RTPROT_UNSPEC) + NLA_PUT_U8 (msg, FRA_PROTOCOL, routing_rule->protocol); + + if (routing_rule->ip_proto != 0) + NLA_PUT_U8 (msg, FRA_IP_PROTO, routing_rule->ip_proto); + + if ( routing_rule->sport_range.start + || routing_rule->sport_range.end) + NLA_PUT (msg, FRA_SPORT_RANGE, sizeof (routing_rule->sport_range), &routing_rule->sport_range); + + if ( routing_rule->dport_range.start + || routing_rule->dport_range.end) + NLA_PUT (msg, FRA_DPORT_RANGE, sizeof (routing_rule->dport_range), &routing_rule->dport_range); + + if (routing_rule->uid_range_has) + NLA_PUT (msg, FRA_UID_RANGE, sizeof (routing_rule->uid_range), &routing_rule->uid_range); + + switch (routing_rule->action) { + case FR_ACT_GOTO: + NLA_PUT_U32 (msg, FRA_GOTO, routing_rule->goto_target); + break; + } + + return g_steal_pointer (&msg); + +nla_put_failure: + g_return_val_if_reached (NULL); +} + static struct nl_msg * _nl_msg_new_qdisc (int nlmsg_type, int nlmsg_flags, @@ -7916,6 +8029,9 @@ object_delete (NMPlatform *platform, case NMP_OBJECT_TYPE_IP6_ROUTE: nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj); break; + case NMP_OBJECT_TYPE_ROUTING_RULE: + nlmsg = _nl_msg_new_routing_rule (RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + break; case NMP_OBJECT_TYPE_QDISC: nlmsg = _nl_msg_new_qdisc (RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC (obj)); break; @@ -8012,6 +8128,47 @@ ip_route_get (NMPlatform *platform, /*****************************************************************************/ +static int +routing_rule_add (NMPlatform *platform, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule) +{ + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nm_auto_nlmsg struct nl_msg *msg = NULL; + gs_free char *errmsg = NULL; + char s_buf[256]; + int nle; + + msg = _nl_msg_new_routing_rule (RTM_NEWRULE, flags, routing_rule); + + event_handler_read_netlink (platform, FALSE); + + nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE ("do-add-rule: failed sending netlink request \"%s\" (%d)", + nm_strerror (nle), -nle); + return -NME_PL_NETLINK; + } + + delayed_action_handle_all (platform, FALSE); + + nm_assert (seq_result); + + _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + ? LOGL_DEBUG + : LOGL_WARN, + "do-add-rule: %s", + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return 0; + if (seq_result < 0) + return seq_result; + return -NME_UNSPEC; +} + +/*****************************************************************************/ + static int qdisc_add (NMPlatform *platform, NMPNlmFlags flags, @@ -8859,6 +9016,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip_route_add = ip_route_add; platform_class->ip_route_get = ip_route_get; + platform_class->routing_rule_add = routing_rule_add; + platform_class->qdisc_add = qdisc_add; platform_class->tfilter_add = tfilter_add; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 5e6744db09..2f27c861f3 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4619,17 +4619,24 @@ nm_platform_object_delete (NMPlatform *self, _CHECK_SELF (self, klass, FALSE); - if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER)) + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_ROUTING_RULE: + _LOGD ("%s: delete %s", + NMP_OBJECT_GET_CLASS (obj)->obj_type_name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_QDISC: + case NMP_OBJECT_TYPE_TFILTER: + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj)->ifindex; + _LOG3D ("%s: delete %s", + NMP_OBJECT_GET_CLASS (obj)->obj_type_name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + break; + default: g_return_val_if_reached (FALSE); - - ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj)->ifindex; - - _LOG3D ("%s: delete %s", - NMP_OBJECT_GET_CLASS (obj)->obj_type_name, - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + } return klass->object_delete (self, obj); } @@ -4958,6 +4965,21 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, /*****************************************************************************/ +int +nm_platform_routing_rule_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule) +{ + _CHECK_SELF (self, klass, -NME_BUG); + + g_return_val_if_fail (routing_rule, -NME_BUG); + + _LOGD ("routing-rule: adding or updating: %s", nm_platform_routing_rule_to_string (routing_rule, NULL, 0)); + return klass->routing_rule_add (self, flags, routing_rule); +} + +/*****************************************************************************/ + int nm_platform_qdisc_add (NMPlatform *self, NMPNlmFlags flags, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 4e7cd5cd59..06231a1dc9 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1035,6 +1035,10 @@ typedef struct { int oif_ifindex, NMPObject **out_route); + int (*routing_rule_add) (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule); + int (*qdisc_add) (NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc); @@ -1543,6 +1547,10 @@ int nm_platform_ip_route_get (NMPlatform *self, int oif_ifindex, NMPObject **out_route); +int nm_platform_routing_rule_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformRoutingRule *routing_rule); + int nm_platform_qdisc_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc); From 5ae2431b0f9e8dc2fbba5c9783852735a8db7c27 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 5 Mar 2019 18:01:28 +0100 Subject: [PATCH 15/16] platform/tests: add tests for handling policy routing rules --- src/platform/tests/test-common.h | 52 +++ src/platform/tests/test-route.c | 744 +++++++++++++++++++++++++++++++ 2 files changed, 796 insertions(+) diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index aa1f54606b..e4eaec2933 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -273,6 +273,58 @@ nmtstp_ip6_route_get_all (NMPlatform *platform, GArray *nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex); GArray *nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex); +/*****************************************************************************/ + +static inline gboolean +_nmtstp_platform_routing_rules_get_all_predicate (const NMPObject *obj, + gpointer user_data) +{ + int addr_family = GPOINTER_TO_INT (user_data); + + g_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + + return addr_family == AF_UNSPEC + || NMP_OBJECT_CAST_ROUTING_RULE (obj)->addr_family == addr_family; +} + +static inline GPtrArray * +nmtstp_platform_routing_rules_get_all (NMPlatform *platform, int addr_family) +{ + NMPLookup lookup; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + return nm_platform_lookup_clone (platform, + &lookup, + _nmtstp_platform_routing_rules_get_all_predicate, + GINT_TO_POINTER (addr_family)); +} + +static inline guint +nmtstp_platform_routing_rules_get_count (NMPlatform *platform, int addr_family) +{ + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *obj; + NMPLookup lookup; + guint n; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + + n = 0; + nmp_cache_iter_for_each (&iter, head_entry, &obj) { + if (_nmtstp_platform_routing_rules_get_all_predicate (obj, GINT_TO_POINTER (addr_family))) + n++; + } + return n; +} + gboolean nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric); gboolean nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 2619ec52c2..1be6e87a97 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -20,6 +20,7 @@ #include "nm-default.h" #include +#include #include "nm-core-utils.h" #include "platform/nm-platform-utils.h" @@ -873,6 +874,743 @@ again_find_idx: /*****************************************************************************/ +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define FRA_PROTOCOL 21 +#define FRA_IP_PROTO 22 +#define FRA_SPORT_RANGE 23 +#define FRA_DPORT_RANGE 24 + +static const NMPObject * +_rule_find_by_priority (NMPlatform *platform, + guint32 priority) +{ + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + const NMPObject *obj = NULL; + NMPLookup lookup; + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + if (NMP_OBJECT_CAST_ROUTING_RULE (o)->priority != priority) + continue; + g_assert (!obj); + obj = o; + } + return obj; +} + +static const NMPObject * +_rule_check_kernel_support_one (NMPlatform *platform, + const NMPlatformRoutingRule *rr) +{ + nm_auto_nmpobj const NMPObject *obj = NULL; + int r; + + g_assert (!_rule_find_by_priority (platform, rr->priority)); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, rr); + g_assert_cmpint (r, ==, 0); + + obj = nmp_object_ref (_rule_find_by_priority (platform, rr->priority)); + g_assert (obj); + + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); + + g_assert (!_rule_find_by_priority (platform, rr->priority)); + + return g_steal_pointer (&obj); +} + +static gboolean +_rule_check_kernel_support (NMPlatform *platform, + int attribute) +{ + static int support[] = { + [FRA_SUPPRESS_IFGROUP] = 0, + [FRA_SUPPRESS_PREFIXLEN] = 0, + [FRA_L3MDEV] = 0, + [FRA_UID_RANGE] = 0, + [FRA_PROTOCOL] = 0, + [FRA_IP_PROTO] = 0, + [FRA_SPORT_RANGE] = 0, + [FRA_DPORT_RANGE] = 0, + }; + const guint32 PROBE_PRORITY = 12033; + gboolean sup; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (attribute >= 0 && attribute < G_N_ELEMENTS (support)); + + if (support[attribute] != 0) + return support[attribute] >= 0; + + switch (attribute) { + case FRA_SUPPRESS_IFGROUP: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .suppress_ifgroup_inverse = ~((guint32) 1245), + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->suppress_prefixlen_inverse == rr.suppress_ifgroup_inverse; + break; + } + case FRA_SUPPRESS_PREFIXLEN: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .suppress_prefixlen_inverse = ~((guint32) 1245), + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->suppress_prefixlen_inverse == rr.suppress_prefixlen_inverse; + break; + } + case FRA_L3MDEV: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .l3mdev = TRUE, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->l3mdev != 0; + break; + } + case FRA_UID_RANGE: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .uid_range = { + .start = 0, + .end = 0, + }, + .uid_range_has = TRUE, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->uid_range_has; + break; + } + case FRA_PROTOCOL: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .protocol = 30, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->protocol == 30; + break; + } + case FRA_IP_PROTO: { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformRoutingRule rr = { + .addr_family = AF_INET, + .priority = PROBE_PRORITY, + .ip_proto = 30, + }; + + obj = _rule_check_kernel_support_one (platform, &rr); + + sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->ip_proto == 30; + break; + } + case FRA_SPORT_RANGE: + case FRA_DPORT_RANGE: + /* these were added at the same time as FRA_IP_PROTO. */ + sup = _rule_check_kernel_support (platform, FRA_IP_PROTO); + break; + default: + g_assert_not_reached (); + return FALSE; + } + + support[attribute] = sup ? 1 : -1; + + _LOGD ("kernel support for routing rule attribute #%d %s", attribute, sup ? "detected" : "not detected"); + return sup; +} + +static const NMPObject * +_platform_has_routing_rule (NMPlatform *platform, + const NMPObject *obj) +{ + const NMPObject *o; + + g_assert (NM_IS_PLATFORM (platform)); + g_assert (NMP_OBJECT_IS_VALID (obj)); + g_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + + o = nm_platform_lookup_obj (platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj); + if (o) + g_assert (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (obj), NMP_OBJECT_CAST_ROUTING_RULE (o), NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); + + return o; +} + +static const NMPObject * +_rule_create_random (NMPlatform *platform) +{ + NMPObject *obj; + NMPlatformRoutingRule *rr; + guint32 p; + int addr_size; + guint i; + char saddr[NM_UTILS_INET_ADDRSTRLEN]; + static struct { + guint32 uid; + guint32 euid; + bool initialized; + } uids; + + if (G_UNLIKELY (!uids.initialized)) { + uids.uid = getuid (); + uids.euid = geteuid (); + uids.initialized = TRUE; + } + + obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL); + rr = NMP_OBJECT_CAST_ROUTING_RULE (obj); + + rr->addr_family = nmtst_rand_select (AF_INET, AF_INET6); + + addr_size = nm_utils_addr_family_to_size (rr->addr_family); + + p = nmtst_get_rand_int () % 1000u; + if (p < 250) + rr->priority = 10000 + (p / 50); + + p = nmtst_get_rand_int () % 1000u; + if (p < 60) + nm_sprintf_buf (rr->iifname, "t-iif-%u", p / 20); + else if (p < 120) + nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME); + + p = nmtst_get_rand_int () % 1000u; + if (p < 60) + nm_sprintf_buf (rr->oifname, "t-oif-%d", p / 20); + else if (p < 120) + nm_sprintf_buf (rr->oifname, "%s", DEVICE_NAME); + + p = nmtst_get_rand_int () % 1000u; + if (p < 60) + nm_sprintf_buf (rr->iifname, "t-iif-%d", p / 20); + else if (p < 120) + nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME); + + for (i = 0; i < 2; i++) { + NMIPAddr *p_addr = i ? &rr->src : &rr->dst; + guint8 *p_len = i ? &rr->src_len : &rr->dst_len; + + p = nmtst_get_rand_int () % 1000u; + if (p < 300) { + /* if we set src_len/dst_len to zero, the src/dst is actually ignored. + * + * For fuzzying, still set the address. It shall have no further effect. + * */ + *p_len = p % (addr_size * 8 + 1); + p = nmtst_get_rand_int () % 750; + if (p <= 255) { + if (rr->addr_family == AF_INET) + p_addr->addr4 = nmtst_inet4_from_string (nm_sprintf_buf (saddr, "192.192.5.%u", p)); + else + p_addr->addr6 = *nmtst_inet6_from_string (nm_sprintf_buf (saddr, "1:2:3:4::f:%02x", p)); + } else if (p <= 512) + nmtst_rand_buf (NULL, p_addr, addr_size); + } + } + + p = nmtst_get_rand_int () % 1000u; + if (p < 50) + rr->tun_id = 10000 + p; + +again_action: + p = nmtst_get_rand_int () % 1000u; + if (p < 300) + rr->action = FR_ACT_UNSPEC; + else if (p < 700) + rr->action = p % 12; + else + rr->action = p % 0xFF; + + rr->priority = nmtst_rand_select (0, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int ()); + if ( rr->action == FR_ACT_GOTO + && rr->priority == G_MAXINT32) + goto again_action; + +again_goto_target: + rr->goto_target = nmtst_rand_select (0, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int (), + rr->priority + 1); + if ( rr->action == FR_ACT_GOTO + && rr->goto_target <= rr->priority) + goto again_goto_target; + + p = nmtst_get_rand_int () % 1000u; + if (p < 50) { + if (_rule_check_kernel_support (platform, FRA_L3MDEV)) { + rr->l3mdev = TRUE; + rr->table = RT_TABLE_UNSPEC; + } + } + +again_table: + if (!rr->l3mdev) { + rr->table = nmtst_rand_select (RT_TABLE_UNSPEC, + RT_TABLE_MAIN, + 10000 + nmtst_get_rand_int () % 10); + if ( rr->action == FR_ACT_TO_TBL + && rr->table == RT_TABLE_UNSPEC) + goto again_table; + } + + rr->fwmark = nmtst_rand_select (0, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int ()); + rr->fwmask = nmtst_rand_select (0u, + 0xFFFFFFFFu, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int ()); + + rr->flow = nmtst_rand_select (0u, + 0xFFFFFFFFu, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_PROTOCOL)) { + rr->protocol = nmtst_rand_select (0u, + 255u, + nmtst_get_rand_int () % 256); + } + +#define IPTOS_TOS_MASK 0x1E + +again_tos: + rr->tos = nmtst_rand_select (0u, + 255u, + nmtst_get_rand_int () % 256); + if ( rr->addr_family == AF_INET + && rr->tos & ~IPTOS_TOS_MASK) + goto again_tos; + + if (_rule_check_kernel_support (platform, FRA_IP_PROTO)) { + rr->ip_proto = nmtst_rand_select (0u, + 255u, + nmtst_get_rand_int () % 256); + } + + if (_rule_check_kernel_support (platform, FRA_SUPPRESS_PREFIXLEN)) { + rr->suppress_prefixlen_inverse = ~((guint32) nmtst_rand_select (0u, + 0xFFFFFFFFu, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int ())); + } + + if (_rule_check_kernel_support (platform, FRA_SUPPRESS_IFGROUP)) { + rr->suppress_ifgroup_inverse = ~((guint32) nmtst_rand_select (0u, + 0xFFFFFFFFu, + nmtst_get_rand_int () % 100, + nmtst_get_rand_int ())); + } + +again_uid_range: + p = nmtst_get_rand_int () % 1000u; + if (p < 100) { + if (_rule_check_kernel_support (platform, FRA_UID_RANGE)) + rr->uid_range_has = TRUE; + } + + rr->uid_range.start = nmtst_rand_select (0u, uids.uid, uids.euid); + rr->uid_range.end = nmtst_rand_select (0u, uids.uid, uids.euid); + if (rr->uid_range.end < rr->uid_range.start) + NMTST_SWAP (rr->uid_range.start, rr->uid_range.end); + + if ( rr->uid_range.start == ((guint32) -1) + || rr->uid_range.end == ((guint32) -1)) + goto again_uid_range; + + for (i = 0; i < 2; i++) { + NMFibRulePortRange *range = i ? &rr->sport_range : &rr->dport_range; + int attribute = i ? FRA_SPORT_RANGE : FRA_DPORT_RANGE; + + if (!_rule_check_kernel_support (platform, attribute)) + continue; + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 100) { + while (range->start == 0) { + p = p ^ nmtst_get_rand_int (); + range->start = nmtst_rand_select (1u, 0xFFFEu, ((p ) % 0xFFFEu) + 1); + range->end = nmtst_rand_select (1u, 0xFFFEu, ((p >> 16) % 0xFFFEu) + 1, range->start); + if (range->end < range->start) + NMTST_SWAP (range->start, range->end); + } + } + } + + p = nmtst_get_rand_int () % 1000u; + if (p < 100) + rr->flags |= FIB_RULE_INVERT; + + return obj; +} + +static gboolean +_rule_fuzzy_equal (const NMPObject *obj, + const NMPObject *obj_comp, + int op_type) +{ + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (obj); + NMPlatformRoutingRule rr_co = *NMP_OBJECT_CAST_ROUTING_RULE (obj_comp); + + switch (op_type) { + case RTM_NEWRULE: + /* when adding rules with RTM_NEWRULE, kernel checks whether an existing + * rule already exists and may fail with EEXIST. This check has issues + * and reject legitimate rules (rh#1686075). + * + * Work around that. */ + if (rr->src_len == 0) + rr_co.src_len = 0; + if (rr->dst_len == 0) + rr_co.dst_len = 0; + if (rr->flow == 0) + rr_co.flow = 0; + if (rr->tos == 0) + rr_co.tos = 0; + break; + case RTM_DELRULE: + /* when deleting a rule with RTM_DELRULE, kernel tries to find the + * cadidate to delete. It might delete the wrong rule (rh#1685816). */ + if (rr->action == FR_ACT_UNSPEC) + rr_co.action = FR_ACT_UNSPEC; + if (rr->iifname[0] == '\0') + rr_co.iifname[0] = '\0'; + if (rr->oifname[0] == '\0') + rr_co.oifname[0] = '\0'; + if (rr->src_len == 0) + rr_co.src_len = 0; + if (rr->dst_len == 0) + rr_co.dst_len = 0; + if (rr->tun_id == 0) + rr_co.tun_id = 0; + if (rr->fwmark == 0) + rr_co.fwmark = 0; + if (rr->fwmask == 0) + rr_co.fwmask = 0; + if (rr->flow == 0) + rr_co.flow = 0; + if (rr->protocol == 0) + rr_co.protocol = 0; + if (rr->table == RT_TABLE_UNSPEC) + rr_co.table = RT_TABLE_UNSPEC; + if (rr->l3mdev == 0) + rr_co.l3mdev = 0; + if (rr->tos == 0) + rr_co.tos = 0; + if (rr->ip_proto == 0) + rr_co.ip_proto = 0; + if (rr->suppress_prefixlen_inverse == 0) + rr_co.suppress_prefixlen_inverse = 0; + if (rr->suppress_ifgroup_inverse == 0) + rr_co.suppress_ifgroup_inverse = 0; + if (!rr->uid_range_has) + rr_co.uid_range_has = FALSE; + if (rr->sport_range.start == 0 && rr->sport_range.end == 0) { + rr_co.sport_range.start = 0; + rr_co.sport_range.end = 0; + } + if (rr->dport_range.start == 0 && rr->dport_range.end == 0) { + rr_co.dport_range.start = 0; + rr_co.dport_range.end = 0; + } + if (!NM_FLAGS_HAS (rr->flags, FIB_RULE_INVERT)) + rr_co.flags &= ~((guint32) FIB_RULE_INVERT); + else + rr_co.flags |= ((guint32) FIB_RULE_INVERT); + break; + default: + g_assert_not_reached (); + break; + } + + return nm_platform_routing_rule_cmp (rr, &rr_co, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0; +} + +static void +test_rule (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + gs_unref_ptrarray GPtrArray *objs = NULL; + gs_unref_ptrarray GPtrArray *objs_initial = NULL; + NMPlatform *platform = NM_PLATFORM_GET; + guint i, j, n; + int r; + + nm_platform_process_events (platform); + + objs_initial = nmtstp_platform_routing_rules_get_all (platform, AF_UNSPEC); + g_assert (objs_initial); + g_assert_cmpint (objs_initial->len, ==, 5); + + nmtstp_run_command_check ("ip rule add table 766"); + nm_platform_process_events (platform); + + for (i = 6; i > 0; i--) { + gs_unref_ptrarray GPtrArray *objs_extern = NULL; + const NMPObject *obj; + + objs_extern = nmtstp_platform_routing_rules_get_all (platform, AF_UNSPEC); + + g_assert (objs_extern); + g_assert_cmpint (objs_extern->len, ==, i); + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs_extern->pdata, NULL, sizeof (gpointer), objs_extern->len); + + obj = objs_extern->pdata[0]; + + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); + + g_assert (!_platform_has_routing_rule (platform, obj)); + } + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, 0); + +#define RR(...) \ + nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, \ + (const NMPlatformObject *) &((NMPlatformRoutingRule) { \ + __VA_ARGS__ \ + })) + + objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 10, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 400, + .action = FR_ACT_GOTO, + .goto_target = 10000, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .action = FR_ACT_TO_TBL, + .table = RT_TABLE_MAIN, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .priority = 30, + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .priority = 50, + .iifname = "t-iif-1", + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET6, + .priority = 50, + .iifname = "t-oif-1", + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 50, + .iifname = "t-oif-2", + )); + + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .priority = 51, + .iifname = DEVICE_NAME, + )); + + if (TEST_IDX == 1) { + g_ptr_array_add (objs, RR ( + .addr_family = AF_INET, + .table = 10000, + )); + } + + if (TEST_IDX != 1) { + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + g_ptr_array_set_size (objs, nmtst_get_rand_int () % (objs->len + 1)); + } + + n = (TEST_IDX != 1) ? nmtst_get_rand_int () % 50u : 0u; + for (i = 0; i < n; i++) { + nm_auto_nmpobj const NMPObject *o = NULL; + guint try = 0; + +again: + o = _rule_create_random (platform); + for (j = 0; j < objs->len; j++) { + if (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (o), + NMP_OBJECT_CAST_ROUTING_RULE (objs->pdata[j]), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0) { + try++; + g_assert (try < 200); + nm_clear_pointer (&o, nmp_object_unref); + goto again; + } + } + g_ptr_array_add (objs, (gpointer) g_steal_pointer (&o)); + } + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + + for (i = 0; i < objs->len;) { + const NMPObject *obj = objs->pdata[i]; + + for (j = 0; j < objs->len; j++) + g_assert ((j < i) == (!!_platform_has_routing_rule (platform, objs->pdata[j]))); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + + if (r == -EEXIST) { + g_assert (!_platform_has_routing_rule (platform, obj)); + /* this should not happen, but there are bugs in kernel (rh#1686075). */ + for (j = 0; j < i; j++) { + const NMPObject *obj2 = objs->pdata[j]; + + g_assert (_platform_has_routing_rule (platform, obj2)); + + if (_rule_fuzzy_equal (obj, obj2, RTM_NEWRULE)) { + r = 0; + break; + } + } + if (r == 0) { + /* OK, the rule is shadowed by another rule, and kernel does not allow + * us to add this one (rh#1686075). Drop this from the test. */ + g_ptr_array_remove_index (objs, i); + continue; + } + } + + if (r != 0) { + g_print (">>> failing...\n"); + nmtstp_run_command_check ("ip rule"); + nmtstp_run_command_check ("ip -6 rule"); + g_assert_cmpint (r, ==, 0); + } + + g_assert (_platform_has_routing_rule (platform, obj)); + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); + + i++; + } + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + + if (_LOGD_ENABLED ()) { + nmtstp_run_command_check ("ip rule"); + nmtstp_run_command_check ("ip -6 rule"); + } + + for (i = 0; i < objs->len; i++) { + const NMPObject *obj = objs->pdata[i]; + const NMPObject *obj2; + + for (j = 0; j < objs->len; j++) + g_assert ((j < i) == (!_platform_has_routing_rule (platform, objs->pdata[j]))); + + g_assert (_platform_has_routing_rule (platform, obj)); + + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); + + obj2 = _platform_has_routing_rule (platform, obj); + + if (obj2) { + guint k; + + /* When deleting a rule, kernel does a fuzzy match, ignoring for example: + * - action, if it is FR_ACT_UNSPEC + * - iifname,oifname if it is unspecified + * rh#1685816 + * + * That means, we may have deleted the wrong rule. Which one? */ + k = i; + for (j = i + 1; j < objs->len; j++) { + if (!_platform_has_routing_rule (platform, objs->pdata[j])) { + g_assert_cmpint (k, ==, i); + k = j; + } + } + g_assert_cmpint (k, >, i); + + if (!_rule_fuzzy_equal (obj, objs->pdata[k], RTM_DELRULE)) { + g_print (">>> failing...\n"); + g_print (">>> no fuzzy match between: %s\n", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_print (">>> and: %s\n", nmp_object_to_string (objs->pdata[k], NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_assert_not_reached (); + } + + objs->pdata[i] = objs->pdata[k]; + objs->pdata[k] = (gpointer) obj; + obj2 = NULL; + } + + g_assert (!obj2); + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs->len -i - 1); + } + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, 0); + + for (i = 0; i < objs_initial->len; i++) { + const NMPObject *obj = objs_initial->pdata[i]; + + for (j = 0; j < objs_initial->len; j++) + g_assert ((j < i) == (!!_platform_has_routing_rule (platform, objs_initial->pdata[j]))); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + g_assert_cmpint (r, ==, 0); + } + for (j = 0; j < objs_initial->len; j++) + g_assert (_platform_has_routing_rule (platform, objs_initial->pdata[j])); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_initial->len); +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -902,4 +1640,10 @@ _nmtstp_setup_tests (void) add_test_func ("/route/ip6_route_get", test_ip6_route_get); add_test_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); } + + if (nmtstp_is_root_test ()) { + add_test_func_data ("/route/rule/1", test_rule, GINT_TO_POINTER (1)); + add_test_func_data ("/route/rule/2", test_rule, GINT_TO_POINTER (2)); + add_test_func_data ("/route/rule/3", test_rule, GINT_TO_POINTER (3)); + } } From b8398b9e7948caefbdc93d5bff208e8caf308a80 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 11 Mar 2019 11:37:40 +0100 Subject: [PATCH 16/16] platform: add NMPRulesManager for syncing routing rules Routing rules are unlike addresses or routes not tied to an interface. NetworkManager thinks in terms of connection profiles. That works well for addresses and routes, as one profile configures addresses and routes for one device. For example, when activating a profile on a device, the configuration does not interfere with the addresses/routes of other devices. That is not the case for routing rules, which are global, netns-wide entities. When one connection profile specifies rules, then this per-device configuration must be merged with the global configuration. And when a device disconnects later, the rules must be removed. Add a new NMPRulesManager API to track/untrack routing rules. Devices can register/add there the routing rules they require. And the sync method will apply the configuration. This is be implemented on top of NMPlatform's caching API. --- Makefile.am | 2 + src/meson.build | 1 + src/nm-netns.c | 16 +- src/nm-netns.h | 2 + src/platform/nm-platform.c | 3 +- src/platform/nmp-rules-manager.c | 660 +++++++++++++++++++++++++++++++ src/platform/nmp-rules-manager.h | 61 +++ src/platform/tests/test-route.c | 235 +++++++---- 8 files changed, 900 insertions(+), 80 deletions(-) create mode 100644 src/platform/nmp-rules-manager.c create mode 100644 src/platform/nmp-rules-manager.h diff --git a/Makefile.am b/Makefile.am index b180466adc..7064eb15c0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1795,6 +1795,8 @@ src_libNetworkManagerBase_la_SOURCES = \ src/platform/nm-platform-private.h \ src/platform/nm-linux-platform.c \ src/platform/nm-linux-platform.h \ + src/platform/nmp-rules-manager.c \ + src/platform/nmp-rules-manager.h \ src/platform/wifi/nm-wifi-utils-nl80211.c \ src/platform/wifi/nm-wifi-utils-nl80211.h \ src/platform/wifi/nm-wifi-utils-private.h \ diff --git a/src/meson.build b/src/meson.build index 06a0dc57da..20a14f9f00 100644 --- a/src/meson.build +++ b/src/meson.build @@ -35,6 +35,7 @@ sources = files( 'platform/nm-platform-utils.c', 'platform/nmp-netns.c', 'platform/nmp-object.c', + 'platform/nmp-rules-manager.c', 'main-utils.c', 'NetworkManagerUtils.c', 'nm-core-utils.c', diff --git a/src/nm-netns.c b/src/nm-netns.c index 5952a490f4..ce32ac6b6f 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -24,10 +24,11 @@ #include "nm-utils/nm-dedup-multi.h" +#include "NetworkManagerUtils.h" +#include "nm-core-internal.h" #include "platform/nm-platform.h" #include "platform/nmp-netns.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" +#include "platform/nmp-rules-manager.h" /*****************************************************************************/ @@ -38,6 +39,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; + NMPRulesManager *rules_manager; } NMNetnsPrivate; struct _NMNetns { @@ -71,6 +73,12 @@ nm_netns_get_platform (NMNetns *self) return NM_NETNS_GET_PRIVATE (self)->platform; } +NMPRulesManager * +nm_netns_get_rules_manager (NMNetns *self) +{ + return NM_NETNS_GET_PRIVATE (self)->rules_manager; +} + NMDedupMultiIndex * nm_netns_get_multi_idx (NMNetns *self) { @@ -118,6 +126,8 @@ constructed (GObject *object) priv->platform_netns = nm_platform_netns_get (priv->platform); + priv->rules_manager = nmp_rules_manager_new (priv->platform, TRUE); + G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); } @@ -137,6 +147,8 @@ dispose (GObject *object) g_clear_object (&priv->platform); + nm_clear_pointer (&priv->rules_manager, nmp_rules_manager_unref); + G_OBJECT_CLASS (nm_netns_parent_class)->dispose (object); } diff --git a/src/nm-netns.h b/src/nm-netns.h index ae343ccebf..250d114921 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -40,6 +40,8 @@ NMNetns *nm_netns_new (NMPlatform *platform); NMPlatform *nm_netns_get_platform (NMNetns *self); NMPNetns *nm_netns_get_platform_netns (NMNetns *self); +struct _NMPRulesManager *nm_netns_get_rules_manager (NMNetns *self); + struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); #define NM_NETNS_GET (nm_netns_get ()) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 2f27c861f3..8500c2faa4 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -7787,8 +7787,9 @@ constructor (GType type, priv->multi_idx = nm_dedup_multi_index_new (); - priv->cache = nmp_cache_new (nm_platform_get_multi_idx (self), + priv->cache = nmp_cache_new (priv->multi_idx, priv->use_udev); + return object; } diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c new file mode 100644 index 0000000000..2c486fdaa6 --- /dev/null +++ b/src/platform/nmp-rules-manager.c @@ -0,0 +1,660 @@ +/* + * 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. + */ + +#include "nm-default.h" + +#include "nmp-rules-manager.h" + +#include +#include + +#include "nm-utils/c-list-util.h" +#include "nmp-object.h" + +/*****************************************************************************/ + +struct _NMPRulesManager { + NMPlatform *platform; + GHashTable *by_obj; + GHashTable *by_user_tag; + GHashTable *by_data; + guint ref_count; + bool track_default:1; +}; + +/*****************************************************************************/ + +static void _rules_init (NMPRulesManager *self); + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG_PREFIX_NAME "rules-manager" + +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +static gboolean +NMP_IS_RULES_MANAGER (gpointer self) +{ + return self + && ((NMPRulesManager *) self)->ref_count > 0 + && NM_IS_PLATFORM (((NMPRulesManager *) self)->platform); +} + +#define _USER_TAG_LOG(user_tag) nm_hash_obfuscate_ptr (1240261787u, (user_tag)) + +/*****************************************************************************/ + +typedef struct { + const NMPObject *obj; + gconstpointer user_tag; + CList obj_lst; + CList user_tag_lst; + + guint32 priority_val; + bool priority_present; + + bool dirty:1; +} RulesData; + +typedef struct { + const NMPObject *obj; + CList obj_lst_head; + + /* indicates that we configured the rule (during sync()). We need that, so + * if the rule gets untracked, that we know to remove it on the next + * sync(). + * + * This makes NMPRulesManager stateful (beyond the configuration that indicates + * which rules are tracked). + * After a restart, NetworkManager would no longer remember which rules were added + * by us. That would need to be fixed by persisting the state and reloading it after + * restart. */ + bool added_by_us:1; +} RulesObjData; + +typedef struct { + gconstpointer user_tag; + CList user_tag_lst_head; +} RulesUserTagData; + +static void +_rules_data_assert (const RulesData *rules_data, gboolean linked) +{ + nm_assert (rules_data); + nm_assert (NMP_OBJECT_GET_TYPE (rules_data->obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + nm_assert (nmp_object_is_visible (rules_data->obj)); + nm_assert (rules_data->user_tag); + nm_assert (!linked || !c_list_is_empty (&rules_data->obj_lst)); + nm_assert (!linked || !c_list_is_empty (&rules_data->user_tag_lst)); +} + +static guint +_rules_data_hash (gconstpointer data) +{ + const RulesData *rules_data = data; + NMHashState h; + + _rules_data_assert (rules_data, FALSE); + + nm_hash_init (&h, 269297543u); + nm_platform_routing_rule_hash_update (NMP_OBJECT_CAST_ROUTING_RULE (rules_data->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + &h); + nm_hash_update_val (&h, rules_data->user_tag); + return nm_hash_complete (&h); +} + +static gboolean +_rules_data_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesData *rules_data_a = data_a; + const RulesData *rules_data_b = data_b; + + _rules_data_assert (rules_data_a, FALSE); + _rules_data_assert (rules_data_b, FALSE); + + return rules_data_a->user_tag == rules_data_b->user_tag + && (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (rules_data_a->obj), + NMP_OBJECT_CAST_ROUTING_RULE (rules_data_b->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +} + +static void +_rules_data_destroy (gpointer data) +{ + RulesData *rules_data = data; + + _rules_data_assert (rules_data, FALSE); + + c_list_unlink_stale (&rules_data->obj_lst); + c_list_unlink_stale (&rules_data->user_tag_lst); + nmp_object_unref (rules_data->obj); + g_slice_free (RulesData, rules_data); +} + +static const RulesData * +_rules_obj_get_best_data (RulesObjData *obj_data) +{ + RulesData *rules_data; + const RulesData *rd_best = NULL; + + nm_assert (!c_list_is_empty (&obj_data->obj_lst_head)); + + c_list_for_each_entry (rules_data, &obj_data->obj_lst_head, obj_lst) { + + _rules_data_assert (rules_data, TRUE); + + if (rd_best) { + if (rd_best->priority_val > rules_data->priority_val) + continue; + if (rd_best->priority_val == rules_data->priority_val) { + if ( rd_best->priority_present + || !rules_data->priority_present) + continue; + } + } + + rd_best = rules_data; + } + + return rd_best; +} + +static guint +_rules_obj_hash (gconstpointer data) +{ + const RulesObjData *obj_data = data; + NMHashState h; + + nm_hash_init (&h, 432817559u); + nm_platform_routing_rule_hash_update (NMP_OBJECT_CAST_ROUTING_RULE (obj_data->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + &h); + return nm_hash_complete (&h); +} + +static gboolean +_rules_obj_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesObjData *obj_data_a = data_a; + const RulesObjData *obj_data_b = data_b; + + return (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (obj_data_a->obj), + NMP_OBJECT_CAST_ROUTING_RULE (obj_data_b->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +} + +static void +_rules_obj_destroy (gpointer data) +{ + RulesObjData *obj_data = data; + + c_list_unlink_stale (&obj_data->obj_lst_head); + nmp_object_unref (obj_data->obj); + g_slice_free (RulesObjData, obj_data); +} + +static guint +_rules_user_tag_hash (gconstpointer data) +{ + const RulesUserTagData *user_tag_data = data; + + return nm_hash_val (644693447u, user_tag_data->user_tag); +} + +static gboolean +_rules_user_tag_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesUserTagData *user_tag_data_a = data_a; + const RulesUserTagData *user_tag_data_b = data_b; + + return user_tag_data_a->user_tag == user_tag_data_b->user_tag; +} + +static void +_rules_user_tag_destroy (gpointer data) +{ + RulesUserTagData *user_tag_data = data; + + c_list_unlink_stale (&user_tag_data->user_tag_lst_head); + g_slice_free (RulesUserTagData, user_tag_data); +} + +static RulesData * +_rules_data_lookup (GHashTable *by_data, + const NMPObject *obj, + gconstpointer user_tag) +{ + RulesData rules_data_needle = { + .obj = obj, + .user_tag = user_tag, + }; + + return g_hash_table_lookup (by_data, &rules_data_needle); +} + +void +nmp_rules_manager_track (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gint32 priority, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + RulesData *rules_data; + RulesObjData *obj_data; + RulesUserTagData *user_tag_data; + gboolean changed = FALSE; + guint32 priority_val; + gboolean priority_present; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (routing_rule); + g_return_if_fail (user_tag); + nm_assert (priority != G_MININT32); + + _rules_init (self); + + p_obj_stack = nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); + + nm_assert (nmp_object_is_visible (p_obj_stack)); + + if (priority >= 0) { + priority_val = priority; + priority_present = TRUE; + } else { + priority_val = -priority; + priority_present = FALSE; + } + + rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); + + if (!rules_data) { + rules_data = g_slice_new (RulesData); + *rules_data = (RulesData) { + .obj = nm_dedup_multi_index_obj_intern (nm_platform_get_multi_idx (self->platform), + p_obj_stack), + .user_tag = user_tag, + .priority_val = priority_val, + .priority_present = priority_present, + .dirty = FALSE, + }; + g_hash_table_add (self->by_data, rules_data); + + obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); + if (!obj_data) { + obj_data = g_slice_new (RulesObjData); + *obj_data = (RulesObjData) { + .obj = nmp_object_ref (rules_data->obj), + .obj_lst_head = C_LIST_INIT (obj_data->obj_lst_head), + .added_by_us = FALSE, + }; + g_hash_table_add (self->by_obj, obj_data); + } + c_list_link_tail (&obj_data->obj_lst_head, &rules_data->obj_lst); + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &rules_data->user_tag); + if (!user_tag_data) { + user_tag_data = g_slice_new (RulesUserTagData); + *user_tag_data = (RulesUserTagData) { + .user_tag = user_tag, + .user_tag_lst_head = C_LIST_INIT (user_tag_data->user_tag_lst_head), + }; + g_hash_table_add (self->by_user_tag, user_tag_data); + } + c_list_link_tail (&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst); + changed = TRUE; + } else { + rules_data->dirty = FALSE; + if ( rules_data->priority_val != priority_val + || rules_data->priority_present != priority_present) { + rules_data->priority_val = priority_val; + rules_data->priority_present = priority_present; + changed = TRUE; + } + } + + _rules_data_assert (rules_data, TRUE); + + if (changed) { + _LOGD ("routing-rule: track ["NM_HASH_OBFUSCATE_PTR_FMT",%c%u] \"%s\")", + _USER_TAG_LOG (rules_data->user_tag), + rules_data->priority_present ? '+' : '-', + (guint) rules_data->priority_val, + nmp_object_to_string (rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + } +} + +static void +_rules_data_untrack (NMPRulesManager *self, + RulesData *rules_data, + gboolean remove_user_tag_data) +{ + RulesObjData *obj_data; + + nm_assert (NMP_IS_RULES_MANAGER (self)); + _rules_data_assert (rules_data, TRUE); + nm_assert (self->by_data); + nm_assert (g_hash_table_lookup (self->by_data, rules_data) == rules_data); + + _LOGD ("routing-rule: untrack ["NM_HASH_OBFUSCATE_PTR_FMT"] \"%s\"", + _USER_TAG_LOG (rules_data->user_tag), + nmp_object_to_string (rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + +#if NM_MORE_ASSERTS + { + RulesUserTagData *user_tag_data; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &rules_data->user_tag); + nm_assert (user_tag_data); + nm_assert (c_list_contains (&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst)); + } +#endif + + nm_assert (!c_list_is_empty (&rules_data->user_tag_lst)); + if ( remove_user_tag_data + && c_list_length_is (&rules_data->user_tag_lst, 1)) + g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); + + obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); + nm_assert (obj_data); + nm_assert (c_list_contains (&obj_data->obj_lst_head, &rules_data->obj_lst)); + nm_assert (obj_data == g_hash_table_lookup (self->by_obj, &rules_data->obj)); + + /* if obj_data is marked to be "added_by_us", we need to keep this entry around + * for the next sync -- so that we can remove the rule that was added. */ + if ( !obj_data->added_by_us + && c_list_length_is (&rules_data->obj_lst, 1)) + g_hash_table_remove (self->by_obj, &rules_data->obj); + + g_hash_table_remove (self->by_data, rules_data); +} + +void +nmp_rules_manager_untrack (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + RulesData *rules_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (routing_rule); + g_return_if_fail (user_tag); + + _rules_init (self); + + p_obj_stack = nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); + + nm_assert (nmp_object_is_visible (p_obj_stack)); + + rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); + if (rules_data) + _rules_data_untrack (self, rules_data, TRUE); +} + +void +nmp_rules_manager_set_dirty (NMPRulesManager *self, + gconstpointer user_tag) +{ + RulesData *rules_data; + RulesUserTagData *user_tag_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (user_tag); + + if (!self->by_data) + return; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry (rules_data, &user_tag_data->user_tag_lst_head, user_tag_lst) + rules_data->dirty = TRUE; +} + +void +nmp_rules_manager_untrack_all (NMPRulesManager *self, + gconstpointer user_tag, + gboolean all /* or only dirty */) +{ + RulesData *rules_data; + RulesData *rules_data_safe; + RulesUserTagData *user_tag_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (user_tag); + + if (!self->by_data) + return; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry_safe (rules_data, rules_data_safe, &user_tag_data->user_tag_lst_head, user_tag_lst) { + if ( all + || rules_data->dirty) + _rules_data_untrack (self, rules_data, FALSE); + } + if (c_list_is_empty (&user_tag_data->user_tag_lst_head)) + g_hash_table_remove (self->by_user_tag, user_tag_data); +} + +void +nmp_rules_manager_sync (NMPRulesManager *self) +{ + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter pl_iter; + const NMPObject *plobj; + gs_unref_ptrarray GPtrArray *rules_to_delete = NULL; + RulesObjData *obj_data; + GHashTableIter h_iter; + guint i; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (!self->by_data) + return; + + _LOGD ("sync"); + + pl_head_entry = nm_platform_lookup_obj_type (self->platform, NMP_OBJECT_TYPE_ROUTING_RULE); + if (pl_head_entry) { + nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) { + obj_data = g_hash_table_lookup (self->by_obj, &plobj); + + if (!obj_data) { + /* this rule is not tracked. It was externally added, hence we + * ignore it. */ + continue; + } + + if (c_list_is_empty (&obj_data->obj_lst_head)) { + nm_assert (obj_data->added_by_us); + g_hash_table_remove (self->by_obj, obj_data); + } else { + if (_rules_obj_get_best_data (obj_data)->priority_present) + continue; + obj_data->added_by_us = FALSE; + } + + if (!rules_to_delete) + rules_to_delete = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (rules_to_delete, (gpointer) nmp_object_ref (plobj)); + } + } + + if (rules_to_delete) { + for (i = 0; i < rules_to_delete->len; i++) + nm_platform_object_delete (self->platform, rules_to_delete->pdata[i]); + } + + g_hash_table_iter_init (&h_iter, self->by_obj); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &obj_data, NULL)) { + + if (c_list_is_empty (&obj_data->obj_lst_head)) { + nm_assert (obj_data->added_by_us); + g_hash_table_iter_remove (&h_iter); + continue; + } + + if (!_rules_obj_get_best_data (obj_data)->priority_present) + continue; + + plobj = nm_platform_lookup_obj (self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj); + if (plobj) + continue; + + obj_data->added_by_us = TRUE; + nm_platform_routing_rule_add (self->platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj_data->obj)); + } +} + +/*****************************************************************************/ + +void +nmp_rules_manager_track_default (NMPRulesManager *self, + int addr_family, + int priority, + gconstpointer user_tag) +{ + /* track the default rules. See also `man ip-rule`. */ + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + }), + priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + }), + priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 32767, + .table = RT_TABLE_DEFAULT, + .action = FR_ACT_TO_TBL, + }), + priority, + user_tag); + } + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET6, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + }), + priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET6, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + }), + priority, + user_tag); + } +} + +static void +_rules_init (NMPRulesManager *self) +{ + if (self->by_data) + return; + + self->by_data = g_hash_table_new_full (_rules_data_hash, _rules_data_equal, NULL, _rules_data_destroy); + self->by_obj = g_hash_table_new_full (_rules_obj_hash, _rules_obj_equal, NULL, _rules_obj_destroy); + self->by_user_tag = g_hash_table_new_full (_rules_user_tag_hash, _rules_user_tag_equal, NULL, _rules_user_tag_destroy); + + if (self->track_default) + nmp_rules_manager_track_default (self, AF_UNSPEC, 0, &self->by_data); +} + +/*****************************************************************************/ + +NMPRulesManager * +nmp_rules_manager_new (NMPlatform *platform, + gboolean track_default) +{ + NMPRulesManager *self; + + g_return_val_if_fail (NM_IS_PLATFORM (platform), NULL); + + self = g_slice_new (NMPRulesManager); + *self = (NMPRulesManager) { + .ref_count = 1, + .platform = g_object_ref (platform), + .track_default = track_default, + }; + return self; +} + +void +nmp_rules_manager_ref (NMPRulesManager *self) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + self->ref_count++; +} + +void nmp_rules_manager_unref (NMPRulesManager *self) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (--self->ref_count > 0) + return; + + if (self->by_data) { + g_hash_table_destroy (self->by_user_tag); + g_hash_table_destroy (self->by_obj); + g_hash_table_destroy (self->by_data); + } + g_object_unref (self->platform); + g_slice_free (NMPRulesManager, self); +} diff --git a/src/platform/nmp-rules-manager.h b/src/platform/nmp-rules-manager.h new file mode 100644 index 0000000000..491df31d4a --- /dev/null +++ b/src/platform/nmp-rules-manager.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef __NMP_RULES_MANAGER_H__ +#define __NMP_RULES_MANAGER_H__ + +#include "nm-platform.h" + +/*****************************************************************************/ + +typedef struct _NMPRulesManager NMPRulesManager; + +NMPRulesManager *nmp_rules_manager_new (NMPlatform *platform, + gboolean track_default); + +void nmp_rules_manager_ref (NMPRulesManager *self); +void nmp_rules_manager_unref (NMPRulesManager *self); + +#define nm_auto_unref_rules_manager nm_auto (_nmp_rules_manager_unref) +NM_AUTO_DEFINE_FCN0 (NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_manager_unref) + +void nmp_rules_manager_track (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gint32 priority, + gconstpointer user_tag); + +void nmp_rules_manager_track_default (NMPRulesManager *self, + int addr_family, + int priority, + gconstpointer user_tag); + +void nmp_rules_manager_untrack (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gconstpointer user_tag); + +void nmp_rules_manager_set_dirty (NMPRulesManager *self, + gconstpointer user_tag); + +void nmp_rules_manager_untrack_all (NMPRulesManager *self, + gconstpointer user_tag, + gboolean all /* or only dirty */); + +void nmp_rules_manager_sync (NMPRulesManager *self); + +/*****************************************************************************/ + +#endif /* __NMP_RULES_MANAGER_H__ */ diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 1be6e87a97..c164ef120e 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -24,6 +24,7 @@ #include "nm-core-utils.h" #include "platform/nm-platform-utils.h" +#include "platform/nmp-rules-manager.h" #include "test-common.h" @@ -1362,6 +1363,7 @@ static void test_rule (gconstpointer test_data) { const int TEST_IDX = GPOINTER_TO_INT (test_data); + const gboolean TEST_SYNC = (TEST_IDX == 4); gs_unref_ptrarray GPtrArray *objs = NULL; gs_unref_ptrarray GPtrArray *objs_initial = NULL; NMPlatform *platform = NM_PLATFORM_GET; @@ -1493,104 +1495,182 @@ again: if (TEST_IDX != 1) nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); - for (i = 0; i < objs->len;) { - const NMPObject *obj = objs->pdata[i]; + if (TEST_SYNC) { + gs_unref_hashtable GHashTable *unique_priorities = g_hash_table_new (NULL, NULL); + nm_auto_unref_rules_manager NMPRulesManager *rules_manager = nmp_rules_manager_new (platform, FALSE); + gs_unref_ptrarray GPtrArray *objs_sync = NULL; + gconstpointer USER_TAG_1 = &platform; + gconstpointer USER_TAG_2 = &unique_priorities; - for (j = 0; j < objs->len; j++) - g_assert ((j < i) == (!!_platform_has_routing_rule (platform, objs->pdata[j]))); + objs_sync = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); - r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + /* ensure that priorities are unique. Otherwise it confuses the test, because + * kernel may wrongly be unable to add/delete routes based on a wrong match + * (rh#1685816, rh#1685816). */ + for (i = 0; i < objs->len; i++) { + const NMPObject *obj = objs->pdata[i]; + guint32 prio = NMP_OBJECT_CAST_ROUTING_RULE (obj)->priority; - if (r == -EEXIST) { - g_assert (!_platform_has_routing_rule (platform, obj)); - /* this should not happen, but there are bugs in kernel (rh#1686075). */ - for (j = 0; j < i; j++) { - const NMPObject *obj2 = objs->pdata[j]; - - g_assert (_platform_has_routing_rule (platform, obj2)); - - if (_rule_fuzzy_equal (obj, obj2, RTM_NEWRULE)) { - r = 0; - break; - } - } - if (r == 0) { - /* OK, the rule is shadowed by another rule, and kernel does not allow - * us to add this one (rh#1686075). Drop this from the test. */ - g_ptr_array_remove_index (objs, i); - continue; + if ( !NM_IN_SET (prio, 0, 32766, 32767) + && !g_hash_table_contains (unique_priorities, GUINT_TO_POINTER (prio))) { + g_hash_table_add (unique_priorities, GUINT_TO_POINTER (prio)); + g_ptr_array_add (objs_sync, (gpointer) nmp_object_ref (obj)); } } - if (r != 0) { - g_print (">>> failing...\n"); + for (i = 0; i < objs_sync->len; i++) { + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + 1, + USER_TAG_1); + if (nmtst_get_rand_bool ()) { + /* this has no effect, because a negative priority (of same absolute value) + * has lower priority than the positive priority above. */ + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + -1, + USER_TAG_2); + } + if (nmtst_get_rand_int () % objs_sync->len == 0) { + nmp_rules_manager_sync (rules_manager); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); + } + } + + nmp_rules_manager_sync (rules_manager); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len); + + for (i = 0; i < objs_sync->len; i++) { + switch (nmtst_get_rand_int () % 3) { + case 0: + nmp_rules_manager_untrack (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + USER_TAG_1); + nmp_rules_manager_untrack (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + USER_TAG_1); + break; + case 1: + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + -1, + USER_TAG_1); + break; + case 2: + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), + -2, + USER_TAG_2); + break; + } + if (nmtst_get_rand_int () % objs_sync->len == 0) { + nmp_rules_manager_sync (rules_manager); + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len - i - 1); + } + } + + nmp_rules_manager_sync (rules_manager); + + } else { + for (i = 0; i < objs->len;) { + const NMPObject *obj = objs->pdata[i]; + + for (j = 0; j < objs->len; j++) + g_assert ((j < i) == (!!_platform_has_routing_rule (platform, objs->pdata[j]))); + + r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj)); + + if (r == -EEXIST) { + g_assert (!_platform_has_routing_rule (platform, obj)); + /* this should not happen, but there are bugs in kernel (rh#1686075). */ + for (j = 0; j < i; j++) { + const NMPObject *obj2 = objs->pdata[j]; + + g_assert (_platform_has_routing_rule (platform, obj2)); + + if (_rule_fuzzy_equal (obj, obj2, RTM_NEWRULE)) { + r = 0; + break; + } + } + if (r == 0) { + /* OK, the rule is shadowed by another rule, and kernel does not allow + * us to add this one (rh#1686075). Drop this from the test. */ + g_ptr_array_remove_index (objs, i); + continue; + } + } + + if (r != 0) { + g_print (">>> failing...\n"); + nmtstp_run_command_check ("ip rule"); + nmtstp_run_command_check ("ip -6 rule"); + g_assert_cmpint (r, ==, 0); + } + + g_assert (_platform_has_routing_rule (platform, obj)); + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); + + i++; + } + + if (TEST_IDX != 1) + nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + + if (_LOGD_ENABLED ()) { nmtstp_run_command_check ("ip rule"); nmtstp_run_command_check ("ip -6 rule"); - g_assert_cmpint (r, ==, 0); } - g_assert (_platform_has_routing_rule (platform, obj)); + for (i = 0; i < objs->len; i++) { + const NMPObject *obj = objs->pdata[i]; + const NMPObject *obj2; - g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); + for (j = 0; j < objs->len; j++) + g_assert ((j < i) == (!_platform_has_routing_rule (platform, objs->pdata[j]))); - i++; - } + g_assert (_platform_has_routing_rule (platform, obj)); - if (TEST_IDX != 1) - nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); + r = nm_platform_object_delete (platform, obj); + g_assert_cmpint (r, ==, TRUE); - if (_LOGD_ENABLED ()) { - nmtstp_run_command_check ("ip rule"); - nmtstp_run_command_check ("ip -6 rule"); - } + obj2 = _platform_has_routing_rule (platform, obj); - for (i = 0; i < objs->len; i++) { - const NMPObject *obj = objs->pdata[i]; - const NMPObject *obj2; + if (obj2) { + guint k; - for (j = 0; j < objs->len; j++) - g_assert ((j < i) == (!_platform_has_routing_rule (platform, objs->pdata[j]))); - - g_assert (_platform_has_routing_rule (platform, obj)); - - r = nm_platform_object_delete (platform, obj); - g_assert_cmpint (r, ==, TRUE); - - obj2 = _platform_has_routing_rule (platform, obj); - - if (obj2) { - guint k; - - /* When deleting a rule, kernel does a fuzzy match, ignoring for example: - * - action, if it is FR_ACT_UNSPEC - * - iifname,oifname if it is unspecified - * rh#1685816 - * - * That means, we may have deleted the wrong rule. Which one? */ - k = i; - for (j = i + 1; j < objs->len; j++) { - if (!_platform_has_routing_rule (platform, objs->pdata[j])) { - g_assert_cmpint (k, ==, i); - k = j; + /* When deleting a rule, kernel does a fuzzy match, ignoring for example: + * - action, if it is FR_ACT_UNSPEC + * - iifname,oifname if it is unspecified + * rh#1685816 + * + * That means, we may have deleted the wrong rule. Which one? */ + k = i; + for (j = i + 1; j < objs->len; j++) { + if (!_platform_has_routing_rule (platform, objs->pdata[j])) { + g_assert_cmpint (k, ==, i); + k = j; + } } - } - g_assert_cmpint (k, >, i); + g_assert_cmpint (k, >, i); - if (!_rule_fuzzy_equal (obj, objs->pdata[k], RTM_DELRULE)) { - g_print (">>> failing...\n"); - g_print (">>> no fuzzy match between: %s\n", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_print (">>> and: %s\n", nmp_object_to_string (objs->pdata[k], NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_assert_not_reached (); + if (!_rule_fuzzy_equal (obj, objs->pdata[k], RTM_DELRULE)) { + g_print (">>> failing...\n"); + g_print (">>> no fuzzy match between: %s\n", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_print (">>> and: %s\n", nmp_object_to_string (objs->pdata[k], NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_assert_not_reached (); + } + + objs->pdata[i] = objs->pdata[k]; + objs->pdata[k] = (gpointer) obj; + obj2 = NULL; } - objs->pdata[i] = objs->pdata[k]; - objs->pdata[k] = (gpointer) obj; - obj2 = NULL; + g_assert (!obj2); + + g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs->len -i - 1); } - - g_assert (!obj2); - - g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs->len -i - 1); } g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, 0); @@ -1645,5 +1725,6 @@ _nmtstp_setup_tests (void) add_test_func_data ("/route/rule/1", test_rule, GINT_TO_POINTER (1)); add_test_func_data ("/route/rule/2", test_rule, GINT_TO_POINTER (2)); add_test_func_data ("/route/rule/3", test_rule, GINT_TO_POINTER (3)); + add_test_func_data ("/route/rule/4", test_rule, GINT_TO_POINTER (4)); } }