diff options
author | Thomas Haller <thaller@redhat.com> | 2019-03-15 16:22:18 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-03-15 16:23:02 +0100 |
commit | e491b46800e2334368100a9d4c614e67ddb14a36 (patch) | |
tree | faef29876faf5d3200ed514515c1bdfedbfc789f | |
parent | 15a6b41239e9abc44f128d2721f63c2ce09a40ec (diff) | |
parent | fd2106dbd647fb70a1cae89515fdb51c00a337ca (diff) |
release: bump version to 1.17.1-dev after 1.16.0 release1.17.1-dev
After 1.16.0 is released, merge it back into master so that
1.16.0 is part of the history of master. That means,
$ git log --first-parent master
will also traverse 1.16.0 and 1.16-rc*.
Also bump the micro version to 1.17.1-dev to indicate that this is
after 1.16.0 is out.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | NEWS | 10 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rwxr-xr-x | contrib/scripts/NM-log | 4 | ||||
-rw-r--r-- | libnm-core/nm-version.h | 14 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | shared/nm-utils/c-list-util.h | 23 | ||||
-rw-r--r-- | shared/nm-utils/nm-hash-utils.h | 27 | ||||
-rw-r--r-- | shared/nm-version-macros.h.in | 1 | ||||
-rw-r--r-- | src/devices/wwan/nm-device-modem.c | 13 | ||||
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/nm-ip4-config.c | 6 | ||||
-rw-r--r-- | src/nm-netns.c | 16 | ||||
-rw-r--r-- | src/nm-netns.h | 2 | ||||
-rw-r--r-- | src/nm-types.h | 20 | ||||
-rw-r--r-- | src/platform/nm-fake-platform.c | 2 | ||||
-rw-r--r-- | src/platform/nm-linux-platform.c | 729 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 563 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 113 | ||||
-rw-r--r-- | src/platform/nmp-object.c | 154 | ||||
-rw-r--r-- | src/platform/nmp-object.h | 144 | ||||
-rw-r--r-- | src/platform/nmp-rules-manager.c | 660 | ||||
-rw-r--r-- | src/platform/nmp-rules-manager.h | 61 | ||||
-rw-r--r-- | src/platform/tests/test-common.h | 52 | ||||
-rw-r--r-- | src/platform/tests/test-nmp-object.c | 2 | ||||
-rw-r--r-- | src/platform/tests/test-route.c | 824 |
26 files changed, 3175 insertions, 274 deletions
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 \ @@ -1,4 +1,14 @@ ============================================= +NetworkManager-1.18 +Overview of changes since NetworkManager-1.16 +============================================= + +This is a snapshot of NetworkManager 1.18 development series. +The API is subject to change and not guaranteed to be compatible +with the later release. +USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE! + +============================================= NetworkManager-1.16 Overview of changes since NetworkManager-1.14 ============================================= diff --git a/configure.ac b/configure.ac index 967eac032f..1c00e4b81b 100644 --- a/configure.ac +++ b/configure.ac @@ -7,8 +7,8 @@ dnl - add corresponding NM_VERSION_x_y_z macros in dnl "shared/nm-version-macros.h.in" dnl - update number in meson.build m4_define([nm_major_version], [1]) -m4_define([nm_minor_version], [16]) -m4_define([nm_micro_version], [0]) +m4_define([nm_minor_version], [17]) +m4_define([nm_micro_version], [1]) m4_define([nm_version], [nm_major_version.nm_minor_version.nm_micro_version]) diff --git a/contrib/scripts/NM-log b/contrib/scripts/NM-log index f81ef8441c..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 '^\|^\(.* \)\?<info> \[[0-9.]*\]\( .*\<is starting\>.*$\)\?' | \ GREP_COLOR='01;37' grep -a --color=always '^\|\<platform:\( (.*)\)\? signal: .*$' | \ - GREP_COLOR='01;34' grep -a --color=always '^\|\<platform\(-linux\)\?:\( (.*)\)\? link: \(add\|adding\|change\|setting\|deleting\)\>\|\<platform:\( (.*)\)\? address: \(deleting\|adding or updating\) IPv. address:\? \|\<platform:\( (.*)\)\? \(route\|ip4-route\|ip6-route\|qdisc\|tfilter\): \([a-z]\+\|adding or updating\|new\[0x[0-9A-Za-z]*\]\) \|\<platform-linux: sysctl: setting ' | \ + GREP_COLOR='01;34' grep -a --color=always '^\|\<platform\(-linux\)\?:\( (.*)\)\? link: \(add\|adding\|change\|setting\|deleting\)\>\|\<platform: routing-rule: \(adding or updating:\|delete \)\|\<platform:\( (.*)\)\? address: \(deleting\|adding or updating\) IPv. address:\? \|\<platform:\( (.*)\)\? \(route\|ip4-route\|ip6-route\|qdisc\|tfilter\): \([a-z]\+\|adding or updating\|new\[0x[0-9A-Za-z]*\]\) \|\<platform-linux: sysctl: setting ' | \ GREP_COLOR='01;35' grep -a --color=always '^\|\<audit: .*$' | \ GREP_COLOR='01;32' grep -a --color=always '^\|\<device (.*): state change: ' | if [[ "$NM_LOG_GREP" != "" ]]; then @@ -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 diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h index 7fe2e45b59..0ac2955baa 100644 --- a/libnm-core/nm-version.h +++ b/libnm-core/nm-version.h @@ -188,4 +188,18 @@ # define NM_AVAILABLE_IN_1_16 #endif +#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_18 +# define NM_DEPRECATED_IN_1_18 G_DEPRECATED +# define NM_DEPRECATED_IN_1_18_FOR(f) G_DEPRECATED_FOR(f) +#else +# define NM_DEPRECATED_IN_1_18 +# define NM_DEPRECATED_IN_1_18_FOR(f) +#endif + +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_18 +# define NM_AVAILABLE_IN_1_18 G_UNAVAILABLE(1,18) +#else +# define NM_AVAILABLE_IN_1_18 +#endif + #endif /* NM_VERSION_H */ diff --git a/meson.build b/meson.build index 4981879147..ee74e54d76 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,7 @@ project( # - add corresponding NM_VERSION_x_y_z macros in # "shared/nm-version-macros.h.in" # - update number in configure.ac - version: '1.16.0', + version: '1.17.1', license: 'GPL2+', default_options: [ 'buildtype=debugoptimized', 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__ */ 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__ */ diff --git a/shared/nm-version-macros.h.in b/shared/nm-version-macros.h.in index 22af1428be..4b57529a30 100644 --- a/shared/nm-version-macros.h.in +++ b/shared/nm-version-macros.h.in @@ -75,6 +75,7 @@ #define NM_VERSION_1_12 (NM_ENCODE_VERSION (1, 12, 0)) #define NM_VERSION_1_14 (NM_ENCODE_VERSION (1, 14, 0)) #define NM_VERSION_1_16 (NM_ENCODE_VERSION (1, 16, 0)) +#define NM_VERSION_1_18 (NM_ENCODE_VERSION (1, 18, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 1e31628080..876f75d7e5 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -473,11 +473,6 @@ check_connection_available (NMDevice *device, } state = nm_modem_get_state (priv->modem); - if (state <= NM_MODEM_STATE_INITIALIZING) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "modem not initialized"); - return FALSE; - } if (state == NM_MODEM_STATE_LOCKED) { if (!nm_connection_get_setting_gsm (connection)) { @@ -487,6 +482,12 @@ check_connection_available (NMDevice *device, } } + if (state < NM_MODEM_STATE_REGISTERED) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "modem not registered"); + return FALSE; + } + return TRUE; } @@ -658,7 +659,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) g_assert (priv->modem); modem_state = nm_modem_get_state (priv->modem); - if (modem_state <= NM_MODEM_STATE_INITIALIZING) + if (modem_state < NM_MODEM_STATE_REGISTERED) return FALSE; return TRUE; 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-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-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/nm-types.h b/src/nm-types.h index b6b49028ca..03ee99299b 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 @@ -192,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-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-linux-platform.c b/src/platform/nm-linux-platform.c index 2f5c75b055..977ee885df 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -26,6 +26,7 @@ #include <endian.h> #include <fcntl.h> #include <libudev.h> +#include <linux/fib_rules.h> #include <linux/ip.h> #include <linux/if_arp.h> #include <linux/if_link.h> @@ -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 @@ -283,41 +297,64 @@ 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, -}; +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_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; + +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 */ 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_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_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 << 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 = 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_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, - 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 +424,7 @@ typedef struct { GIOChannel *event_channel; guint event_id; - bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + guint32 pruning[_REFRESH_ALL_TYPE_NUM]; GHashTable *sysctl_get_prev_values; CList sysctl_list; @@ -401,7 +438,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; @@ -3340,6 +3377,165 @@ rta_multipath_done: } 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) { static const struct nla_policy policy[] = { @@ -3438,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: @@ -3893,6 +4093,119 @@ nla_put_failure: } static struct nl_msg * +_nl_msg_new_routing_rule (int nlmsg_type, + int nlmsg_flags, + const NMPlatformRoutingRule *routing_rule) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + const guint8 addr_size = nm_utils_addr_family_to_size (routing_rule->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, const NMPlatformQdisc *qdisc) @@ -4347,57 +4660,131 @@ 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_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 + }; -_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, 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_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 (), ); +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 RefreshAllType +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; + 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; + } +} + +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); + + 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); + + 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_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"), + 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_REFRESH_ALL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX), ); @@ -4455,6 +4842,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 +4851,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 +5150,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 +5189,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); } } @@ -4876,6 +5273,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); @@ -5166,7 +5564,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] += 1; nm_dedup_multi_entry_set_dirty (entry, TRUE); } } @@ -5234,6 +5632,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, @@ -5254,42 +5653,68 @@ 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; - 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; /* 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)"); + + 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)) { + _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); - 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; @@ -5310,9 +5735,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); } @@ -5408,6 +5833,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 @@ -5426,10 +5852,11 @@ 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, - delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); + delayed_action_refresh_from_needle_object (obj)); } _LOGT ("event-notification: %s%s: %s", @@ -5445,10 +5872,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) { @@ -5537,16 +5965,17 @@ 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; } - 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) { @@ -5657,7 +6086,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); @@ -5722,7 +6151,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; @@ -5891,12 +6320,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, @@ -7606,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; @@ -7703,6 +8129,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, const NMPlatformQdisc *qdisc) @@ -7736,7 +8203,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; } @@ -8053,6 +8521,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); @@ -8318,9 +8787,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); @@ -8347,6 +8820,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); @@ -8457,7 +8931,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; @@ -8543,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 fe0cb662a2..8500c2faa4 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -28,6 +28,7 @@ #include <arpa/inet.h> #include <sys/socket.h> #include <netdb.h> +#include <linux/fib_rules.h> #include <linux/ip.h> #include <linux/if.h> #include <linux/if_tun.h> @@ -1189,21 +1190,6 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) } /** - * nm_platform_refresh_all: - * @self: platform instance - * @obj_type: The object type to request. - * - * Resync and re-request all objects from kernel of a certain @obj_type. - */ -void -nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type) -{ - _CHECK_SELF_VOID (self, klass); - - klass->refresh_all (self, obj_type); -} - -/** * nm_platform_link_refresh: * @self: platform instance * @ifindex: Interface index @@ -4264,14 +4250,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++) { @@ -4578,7 +4563,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), @@ -4630,18 +4615,28 @@ 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, - 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); - - _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); } @@ -4971,6 +4966,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, const NMPlatformQdisc *qdisc) @@ -6076,6 +6086,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) { @@ -7034,6 +7291,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 @@ -7131,6 +7568,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform } 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) { _LOG3D ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0)); @@ -7196,9 +7640,13 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, return; } - ifindex = o->object.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)) @@ -7214,7 +7662,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); @@ -7261,24 +7709,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, + }, }; /*****************************************************************************/ @@ -7338,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; } @@ -7411,11 +7861,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 37aa58fdc5..06231a1dc9 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -152,6 +152,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, * by default nothing is matched, but if you enable a particular @@ -181,12 +189,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; @@ -246,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, @@ -260,15 +279,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 +385,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 +555,49 @@ typedef union { #undef __NMPlatformIPRoute_COMMON typedef struct { - __NMPlatformObject_COMMON; + /* 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; int addr_family; guint32 handle; @@ -562,7 +619,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 +628,7 @@ typedef struct { NMPlatformAction action; } NMPlatformTfilter; -#undef __NMPlatformObject_COMMON +#undef __NMPlatformObjWithIfindex_COMMON typedef struct { gboolean is_ip4; @@ -583,8 +640,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; @@ -793,8 +857,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, @@ -973,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); @@ -1001,6 +1067,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" @@ -1167,8 +1234,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); @@ -1482,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); @@ -1512,6 +1581,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); @@ -1556,6 +1626,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); @@ -1564,6 +1642,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 6ec6fea138..ae8fb6d4bb 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)); @@ -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; @@ -712,16 +731,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) { @@ -1369,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. @@ -1469,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) { @@ -1534,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, @@ -1557,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) { @@ -1574,13 +1602,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 @@ -1601,20 +1630,26 @@ _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_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) { - 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 @@ -1672,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 @@ -1949,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); @@ -1998,7 +2040,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); } @@ -2014,7 +2056,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); } @@ -2062,9 +2104,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; @@ -2085,9 +2127,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) @@ -2097,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 * @@ -2920,15 +2979,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); + } } /*****************************************************************************/ @@ -3076,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 525e144001..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; @@ -325,6 +330,10 @@ typedef struct { } NMPObjectIP6Route; typedef struct { + NMPlatformRoutingRule _public; +} NMPObjectRoutingRule; + +typedef struct { NMPlatformQdisc _public; } NMPObjectQdisc; @@ -340,6 +349,8 @@ struct _NMPObject { union { NMPlatformObject object; + NMPlatformObjWithIfindex obj_with_ifindex; + NMPlatformLink link; NMPObjectLink _link; @@ -390,6 +401,9 @@ struct _NMPObject { NMPObjectIP4Route _ip4_route; NMPObjectIP6Route _ip6_route; + NMPlatformRoutingRule routing_rule; + NMPObjectRoutingRule _routing_rule; + NMPlatformQdisc qdisc; NMPObjectQdisc _qdisc; NMPlatformTfilter tfilter; @@ -397,6 +411,8 @@ struct _NMPObject { }; }; +/*****************************************************************************/ + static inline gboolean NMP_CLASS_IS_VALID (const NMPClass *klass) { @@ -405,6 +421,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) { @@ -454,11 +481,65 @@ 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); \ \ - 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 +563,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 +571,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 +595,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,15 +603,23 @@ 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; \ }) +#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); \ \ - 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,12 +627,10 @@ 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; \ }) -const NMPClass *nmp_class_from_type (NMPObjectType obj_type); - static inline const NMPObject * nmp_object_ref (const NMPObject *obj) { @@ -565,9 +652,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) \ @@ -695,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, @@ -784,8 +876,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); 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 <linux/fib_rules.h> +#include <linux/rtnetlink.h> + +#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-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-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); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 2619ec52c2..995da9ff21 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -20,9 +20,11 @@ #include "nm-default.h" #include <linux/rtnetlink.h> +#include <linux/fib_rules.h> #include "nm-core-utils.h" #include "platform/nm-platform-utils.h" +#include "platform/nmp-rules-manager.h" #include "test-common.h" @@ -873,6 +875,821 @@ 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 guint32 +_rr_rand_choose_u32 (guint32 p) +{ + /* mostly, we just return zero. We want that each rule only has few + * fields set -- having most fields at zero. */ + if ((p % 10000u) < 7500u) + return 0; + + /* give 0xFFFFFFFFu extra probability. */ + if ((p % 10000u) < 8250u) + return 0xFFFFFFFFu; + + /* choose a small number. */ + if ((p % 10000u) < 9125u) + return (~p) % 10; + + /* finally, full random number. */ + return ~p; +} + +#define _rr_rand_choose_u8(p) ((guint8) _rr_rand_choose_u32 ((p))) + +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 (); + if ((p % 1000u) < 50) + rr->priority = 10000 + ((~p) % 20u); + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 40) + nm_sprintf_buf (rr->iifname, "t-iif-%u", (~p) % 20); + else if ((p % 1000u) < 80) + nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME); + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 40) + nm_sprintf_buf (rr->oifname, "t-oif-%d", (~p) % 20); + else if ((p % 1000u) < 80) + nm_sprintf_buf (rr->oifname, "%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 (); + if ((p % 1000u) < 100) { + /* 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 (); + if ((p % 3u) == 0) { + if (rr->addr_family == AF_INET) + p_addr->addr4 = nmtst_inet4_from_string (nm_sprintf_buf (saddr, "192.192.5.%u", (~p) % 256u)); + else + p_addr->addr6 = *nmtst_inet6_from_string (nm_sprintf_buf (saddr, "1:2:3:4::f:%02x", (~p) % 256u)); + } else if ((p % 3u) == 1) + nmtst_rand_buf (NULL, p_addr, addr_size); + } + } + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 50) + rr->tun_id = 10000 + ((~p) % 20); + +again_action: + p = nmtst_get_rand_int (); + if ((p % 1000u) < 500) + rr->action = FR_ACT_UNSPEC; + else if ((p % 1000u) < 750) + rr->action = (~p) % 12u; + else + rr->action = (~p) % 0x100u; + + rr->priority = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if ( rr->action == FR_ACT_GOTO + && rr->priority == G_MAXINT32) + goto again_action; + + p = nmtst_get_rand_int (); + if ((p % 10000u) < 100) + rr->goto_target = rr->priority + 1; + else + rr->goto_target = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + if ( rr->action == FR_ACT_GOTO + && rr->goto_target <= rr->priority) + goto again_action; + + p = nmtst_get_rand_int (); + if ((p % 1000u) < 25) { + if (_rule_check_kernel_support (platform, FRA_L3MDEV)) { + rr->l3mdev = TRUE; + rr->table = RT_TABLE_UNSPEC; + } + } + +again_table: + if (!rr->l3mdev) { + p = nmtst_get_rand_int (); + if ((p % 1000u) < 700) + rr->table = RT_TABLE_UNSPEC; + else if ((p % 1000u) < 850) + rr->table = RT_TABLE_MAIN; + else + rr->table = 10000 + ((~p) % 10); + if ( rr->action == FR_ACT_TO_TBL + && rr->table == RT_TABLE_UNSPEC) + goto again_table; + } + + rr->fwmark = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->fwmask = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + + rr->flow = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_PROTOCOL)) + rr->protocol = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + +#define IPTOS_TOS_MASK 0x1E + +again_tos: + rr->tos = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + 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 = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_SUPPRESS_PREFIXLEN)) + rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_SUPPRESS_IFGROUP)) + rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + + if (_rule_check_kernel_support (platform, FRA_UID_RANGE)) { + p = nmtst_get_rand_int (); + rr->uid_range_has = (p % 10000u) < 200; + } + +again_uid_range: + 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_has) { + 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 % 10000u) < 300) { + 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; + 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; + 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); + 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; + 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); + + 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; + + objs_sync = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + /* 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 ( !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)); + } + } + + 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"); + } + + 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 +1719,11 @@ _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)); + add_test_func_data ("/route/rule/4", test_rule, GINT_TO_POINTER (4)); + } } |