release: bump version to 1.17.1-dev after 1.16.0 release

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.
This commit is contained in:
Thomas Haller 2019-03-15 16:22:18 +01:00
commit e491b46800
26 changed files with 3175 additions and 274 deletions

View file

@ -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 \

10
NEWS
View file

@ -1,3 +1,13 @@
=============================================
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

View file

@ -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])

View file

@ -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

View file

@ -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 */

View file

@ -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',

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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.
*

View file

@ -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;

View file

@ -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',

View file

@ -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);

View file

@ -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);
}

View file

@ -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 ())

View file

@ -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,

View file

@ -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));

View file

@ -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_MAX = __DELAYED_ACTION_TYPE_MAX -1,
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,
} 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;
@ -3339,6 +3376,165 @@ rta_multipath_done:
return g_steal_pointer (&obj);
}
static NMPObject *
_new_from_nl_routing_rule (struct nlmsghdr *nlh, gboolean id_only)
{
static const struct nla_policy policy[] = {
[FRA_UNSPEC] = { },
[FRA_DST] = { /* struct in_addr, struct in6_addr */ },
[FRA_SRC] = { /* struct in_addr, struct in6_addr */ },
[FRA_IIFNAME] = { .type = NLA_STRING,
.maxlen = IFNAMSIZ, },
[FRA_GOTO] = { .type = NLA_U32, },
[FRA_UNUSED2] = { },
[FRA_PRIORITY] = { .type = NLA_U32, },
[FRA_UNUSED3] = { },
[FRA_UNUSED4] = { },
[FRA_UNUSED5] = { },
[FRA_FWMARK] = { .type = NLA_U32, },
[FRA_FLOW] = { .type = NLA_U32, },
[FRA_TUN_ID] = { .type = NLA_U64, },
[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32, },
[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32, },
[FRA_TABLE] = { .type = NLA_U32, },
[FRA_FWMASK] = { .type = NLA_U32, },
[FRA_OIFNAME] = { .type = NLA_STRING,
.maxlen = IFNAMSIZ, },
[FRA_PAD] = { .type = NLA_U32, },
[FRA_L3MDEV] = { .type = NLA_U8, },
[FRA_UID_RANGE] = { .minlen = sizeof(NMFibRuleUidRange),
.maxlen = sizeof(NMFibRuleUidRange), },
[FRA_PROTOCOL] = { .type = NLA_U8, },
[FRA_IP_PROTO] = { .type = NLA_U8, },
[FRA_SPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange),
.maxlen = sizeof(NMFibRulePortRange), },
[FRA_DPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange),
.maxlen = sizeof(NMFibRulePortRange), },
};
struct nlattr *tb[G_N_ELEMENTS (policy)];
const struct fib_rule_hdr *frh;
NMPlatformRoutingRule *props;
nm_auto_nmpobj NMPObject *obj = NULL;
int addr_family;
guint8 addr_size;
if (nlmsg_parse_arr (nlh, sizeof (*frh), tb, policy) < 0)
return NULL;
frh = nlmsg_data (nlh);
addr_family = frh->family;
if (!NM_IN_SET (addr_family, AF_INET, AF_INET6)) {
/* we don't care about other address families. */
return NULL;
}
addr_size = nm_utils_addr_family_to_size (addr_family);
obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL);
props = &obj->routing_rule;
props->addr_family = addr_family;
props->action = frh->action;
props->flags = frh->flags;
props->tos = frh->tos;
props->table = tb[FRA_TABLE]
? nla_get_u32 (tb[FRA_TABLE])
: frh->table;
if (tb[FRA_SUPPRESS_PREFIXLEN])
props->suppress_prefixlen_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_PREFIXLEN]);
if (tb[FRA_SUPPRESS_IFGROUP])
props->suppress_ifgroup_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_IFGROUP]);
if (tb[FRA_IIFNAME])
nla_strlcpy (props->iifname, tb[FRA_IIFNAME], sizeof (props->iifname));
if (tb[FRA_OIFNAME])
nla_strlcpy (props->oifname, tb[FRA_OIFNAME], sizeof (props->oifname));
if (tb[FRA_PRIORITY])
props->priority = nla_get_u32 (tb[FRA_PRIORITY]);
if (tb[FRA_FWMARK])
props->fwmark = nla_get_u32 (tb[FRA_FWMARK]);
if (tb[FRA_FWMASK])
props->fwmask = nla_get_u32 (tb[FRA_FWMASK]);
if (tb[FRA_GOTO])
props->goto_target = nla_get_u32 (tb[FRA_GOTO]);
props->src_len = frh->src_len;
if (props->src_len > addr_size * 8)
return NULL;
if (!tb[FRA_SRC]) {
if (props->src_len > 0)
return NULL;
} else if (!nm_ip_addr_set_from_untrusted (addr_family,
&props->src,
nla_data (tb[FRA_SRC]),
nla_len (tb[FRA_SRC]),
NULL))
return NULL;
props->dst_len = frh->dst_len;
if (props->dst_len > addr_size * 8)
return NULL;
if (!tb[FRA_DST]) {
if (props->dst_len > 0)
return NULL;
} else if (!nm_ip_addr_set_from_untrusted (addr_family,
&props->dst,
nla_data (tb[FRA_DST]),
nla_len (tb[FRA_DST]),
NULL))
return NULL;
if (tb[FRA_FLOW])
props->flow = nla_get_u32 (tb[FRA_FLOW]);
if (tb[FRA_TUN_ID])
props->tun_id = nla_get_be64 (tb[FRA_TUN_ID]);
if (tb[FRA_L3MDEV]) {
/* actually, kernel only allows this attribute to be missing or
* "1". Still, encode it as full uint8.
*
* Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */
props->l3mdev = nla_get_u8 (tb[FRA_L3MDEV]);
}
if (tb[FRA_PROTOCOL])
props->protocol = nla_get_u8 (tb[FRA_PROTOCOL]);
else
nm_assert (props->protocol == RTPROT_UNSPEC);
if (tb[FRA_IP_PROTO])
props->ip_proto = nla_get_u8 (tb[FRA_IP_PROTO]);
G_STATIC_ASSERT_EXPR (sizeof (NMFibRulePortRange) == 4);
G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, start) == 0);
G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, end) == 2);
nla_memcpy_checked_size (&props->sport_range, tb[FRA_SPORT_RANGE], sizeof (props->sport_range));
nla_memcpy_checked_size (&props->dport_range, tb[FRA_DPORT_RANGE], sizeof (props->dport_range));
G_STATIC_ASSERT_EXPR (sizeof (NMFibRuleUidRange) == 8);
G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, start) == 0);
G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, end) == 4);
if (tb[FRA_UID_RANGE]) {
nla_memcpy_checked_size (&props->uid_range, tb[FRA_UID_RANGE], sizeof (props->uid_range));
props->uid_range_has = TRUE;
}
return g_steal_pointer (&obj);
}
static NMPObject *
_new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only)
{
@ -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:
@ -3892,6 +4092,119 @@ nla_put_failure:
g_return_val_if_reached (NULL);
}
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,
@ -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[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_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[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;
@ -7702,6 +8128,47 @@ ip_route_get (NMPlatform *platform,
/*****************************************************************************/
static int
routing_rule_add (NMPlatform *platform,
NMPNlmFlags flags,
const NMPlatformRoutingRule *routing_rule)
{
WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
nm_auto_nlmsg struct nl_msg *msg = NULL;
gs_free char *errmsg = NULL;
char s_buf[256];
int nle;
msg = _nl_msg_new_routing_rule (RTM_NEWRULE, flags, routing_rule);
event_handler_read_netlink (platform, FALSE);
nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
if (nle < 0) {
_LOGE ("do-add-rule: failed sending netlink request \"%s\" (%d)",
nm_strerror (nle), -nle);
return -NME_PL_NETLINK;
}
delayed_action_handle_all (platform, FALSE);
nm_assert (seq_result);
_NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK
? LOGL_DEBUG
: LOGL_WARN,
"do-add-rule: %s",
wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)));
if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
return 0;
if (seq_result < 0)
return seq_result;
return -NME_UNSPEC;
}
/*****************************************************************************/
static int
qdisc_add (NMPlatform *platform,
NMPNlmFlags flags,
@ -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;

View file

@ -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>
@ -1188,21 +1189,6 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex)
return (nm_platform_link_get_type (self, ifindex) & 0x20000);
}
/**
* nm_platform_refresh_all:
* @self: platform instance
* @obj_type: The object type to request.
*
* Resync and re-request all objects from kernel of a certain @obj_type.
*/
void
nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type)
{
_CHECK_SELF_VOID (self, klass);
klass->refresh_all (self, obj_type);
}
/**
* nm_platform_link_refresh:
* @self: platform instance
@ -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);
}
@ -4970,6 +4965,21 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self,
/*****************************************************************************/
int
nm_platform_routing_rule_add (NMPlatform *self,
NMPNlmFlags flags,
const NMPlatformRoutingRule *routing_rule)
{
_CHECK_SELF (self, klass, -NME_BUG);
g_return_val_if_fail (routing_rule, -NME_BUG);
_LOGD ("routing-rule: adding or updating: %s", nm_platform_routing_rule_to_string (routing_rule, NULL, 0));
return klass->routing_rule_add (self, flags, routing_rule);
}
/*****************************************************************************/
int
nm_platform_qdisc_add (NMPlatform *self,
NMPNlmFlags flags,
@ -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
@ -7130,6 +7567,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform
_LOG3D ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0));
}
static void
log_routing_rule (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformRoutingRule *routing_rule, NMPlatformSignalChangeType change_type, gpointer user_data)
{
/* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */
_LOG3D ("signal: rt-rule %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_routing_rule_to_string (routing_rule, NULL, 0));
}
static void
log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data)
{
@ -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);
}

View file

@ -151,6 +151,14 @@ typedef enum {
} NMPlatformIPRouteCmpType;
typedef enum {
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID,
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY,
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL,
} NMPlatformRoutingRuleCmpType;
typedef enum {
/* match-flags are strictly inclusive. That means,
@ -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);

View file

@ -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,

View file

@ -174,6 +174,11 @@ typedef enum { /*< skip >*/
* cache-resync. */
NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
/* a filter for objects that track an explicit address family.
*
* Note that currently on NMPObjectRoutingRule is indexed by this filter. */
NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY,
__NMP_CACHE_ID_TYPE_MAX,
NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1,
} NMPCacheIdType;
@ -324,6 +329,10 @@ typedef struct {
NMPlatformIP6Route _public;
} NMPObjectIP6Route;
typedef struct {
NMPlatformRoutingRule _public;
} NMPObjectRoutingRule;
typedef struct {
NMPlatformQdisc _public;
} NMPObjectQdisc;
@ -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);

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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);

View file

@ -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);

View file

@ -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));
}
}