summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-03-15 16:22:18 +0100
committerThomas Haller <thaller@redhat.com>2019-03-15 16:23:02 +0100
commite491b46800e2334368100a9d4c614e67ddb14a36 (patch)
treefaef29876faf5d3200ed514515c1bdfedbfc789f
parent15a6b41239e9abc44f128d2721f63c2ce09a40ec (diff)
parentfd2106dbd647fb70a1cae89515fdb51c00a337ca (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.am2
-rw-r--r--NEWS10
-rw-r--r--configure.ac4
-rwxr-xr-xcontrib/scripts/NM-log4
-rw-r--r--libnm-core/nm-version.h14
-rw-r--r--meson.build2
-rw-r--r--shared/nm-utils/c-list-util.h23
-rw-r--r--shared/nm-utils/nm-hash-utils.h27
-rw-r--r--shared/nm-version-macros.h.in1
-rw-r--r--src/devices/wwan/nm-device-modem.c13
-rw-r--r--src/meson.build1
-rw-r--r--src/nm-ip4-config.c6
-rw-r--r--src/nm-netns.c16
-rw-r--r--src/nm-netns.h2
-rw-r--r--src/nm-types.h20
-rw-r--r--src/platform/nm-fake-platform.c2
-rw-r--r--src/platform/nm-linux-platform.c729
-rw-r--r--src/platform/nm-platform.c563
-rw-r--r--src/platform/nm-platform.h113
-rw-r--r--src/platform/nmp-object.c154
-rw-r--r--src/platform/nmp-object.h144
-rw-r--r--src/platform/nmp-rules-manager.c660
-rw-r--r--src/platform/nmp-rules-manager.h61
-rw-r--r--src/platform/tests/test-common.h52
-rw-r--r--src/platform/tests/test-nmp-object.c2
-rw-r--r--src/platform/tests/test-route.c824
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 \
diff --git a/NEWS b/NEWS
index 9a72dcb893..e060a3d90e 100644
--- a/NEWS
+++ b/NEWS
@@ -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));
+ }
}