core: merge branch 'th/routing-rule-pt1'

https://github.com/NetworkManager/NetworkManager/pull/306
This commit is contained in:
Thomas Haller 2019-03-13 20:17:23 +01:00
commit d62dd09772
20 changed files with 3141 additions and 265 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 \

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

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

@ -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,822 @@ again_find_idx:
/*****************************************************************************/
#define FRA_SUPPRESS_IFGROUP 13
#define FRA_SUPPRESS_PREFIXLEN 14
#define FRA_L3MDEV 19
#define FRA_UID_RANGE 20
#define FRA_PROTOCOL 21
#define FRA_IP_PROTO 22
#define FRA_SPORT_RANGE 23
#define FRA_DPORT_RANGE 24
static const NMPObject *
_rule_find_by_priority (NMPlatform *platform,
guint32 priority)
{
const NMDedupMultiHeadEntry *head_entry;
NMDedupMultiIter iter;
const NMPObject *o;
const NMPObject *obj = NULL;
NMPLookup lookup;
nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE);
head_entry = nm_platform_lookup (platform, &lookup);
nmp_cache_iter_for_each (&iter, head_entry, &o) {
if (NMP_OBJECT_CAST_ROUTING_RULE (o)->priority != priority)
continue;
g_assert (!obj);
obj = o;
}
return obj;
}
static const NMPObject *
_rule_check_kernel_support_one (NMPlatform *platform,
const NMPlatformRoutingRule *rr)
{
nm_auto_nmpobj const NMPObject *obj = NULL;
int r;
g_assert (!_rule_find_by_priority (platform, rr->priority));
r = nm_platform_routing_rule_add (platform, NMP_NLM_FLAG_ADD, rr);
g_assert_cmpint (r, ==, 0);
obj = nmp_object_ref (_rule_find_by_priority (platform, rr->priority));
g_assert (obj);
r = nm_platform_object_delete (platform, obj);
g_assert_cmpint (r, ==, TRUE);
g_assert (!_rule_find_by_priority (platform, rr->priority));
return g_steal_pointer (&obj);
}
static gboolean
_rule_check_kernel_support (NMPlatform *platform,
int attribute)
{
static int support[] = {
[FRA_SUPPRESS_IFGROUP] = 0,
[FRA_SUPPRESS_PREFIXLEN] = 0,
[FRA_L3MDEV] = 0,
[FRA_UID_RANGE] = 0,
[FRA_PROTOCOL] = 0,
[FRA_IP_PROTO] = 0,
[FRA_SPORT_RANGE] = 0,
[FRA_DPORT_RANGE] = 0,
};
const guint32 PROBE_PRORITY = 12033;
gboolean sup;
g_assert (NM_IS_PLATFORM (platform));
g_assert (attribute >= 0 && attribute < G_N_ELEMENTS (support));
if (support[attribute] != 0)
return support[attribute] >= 0;
switch (attribute) {
case FRA_SUPPRESS_IFGROUP: {
nm_auto_nmpobj const NMPObject *obj = NULL;
const NMPlatformRoutingRule rr = {
.addr_family = AF_INET,
.priority = PROBE_PRORITY,
.suppress_ifgroup_inverse = ~((guint32) 1245),
};
obj = _rule_check_kernel_support_one (platform, &rr);
sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->suppress_prefixlen_inverse == rr.suppress_ifgroup_inverse;
break;
}
case FRA_SUPPRESS_PREFIXLEN: {
nm_auto_nmpobj const NMPObject *obj = NULL;
const NMPlatformRoutingRule rr = {
.addr_family = AF_INET,
.priority = PROBE_PRORITY,
.suppress_prefixlen_inverse = ~((guint32) 1245),
};
obj = _rule_check_kernel_support_one (platform, &rr);
sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->suppress_prefixlen_inverse == rr.suppress_prefixlen_inverse;
break;
}
case FRA_L3MDEV: {
nm_auto_nmpobj const NMPObject *obj = NULL;
const NMPlatformRoutingRule rr = {
.addr_family = AF_INET,
.priority = PROBE_PRORITY,
.l3mdev = TRUE,
};
obj = _rule_check_kernel_support_one (platform, &rr);
sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->l3mdev != 0;
break;
}
case FRA_UID_RANGE: {
nm_auto_nmpobj const NMPObject *obj = NULL;
const NMPlatformRoutingRule rr = {
.addr_family = AF_INET,
.priority = PROBE_PRORITY,
.uid_range = {
.start = 0,
.end = 0,
},
.uid_range_has = TRUE,
};
obj = _rule_check_kernel_support_one (platform, &rr);
sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->uid_range_has;
break;
}
case FRA_PROTOCOL: {
nm_auto_nmpobj const NMPObject *obj = NULL;
const NMPlatformRoutingRule rr = {
.addr_family = AF_INET,
.priority = PROBE_PRORITY,
.protocol = 30,
};
obj = _rule_check_kernel_support_one (platform, &rr);
sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->protocol == 30;
break;
}
case FRA_IP_PROTO: {
nm_auto_nmpobj const NMPObject *obj = NULL;
const NMPlatformRoutingRule rr = {
.addr_family = AF_INET,
.priority = PROBE_PRORITY,
.ip_proto = 30,
};
obj = _rule_check_kernel_support_one (platform, &rr);
sup = NMP_OBJECT_CAST_ROUTING_RULE (obj)->ip_proto == 30;
break;
}
case FRA_SPORT_RANGE:
case FRA_DPORT_RANGE:
/* these were added at the same time as FRA_IP_PROTO. */
sup = _rule_check_kernel_support (platform, FRA_IP_PROTO);
break;
default:
g_assert_not_reached ();
return FALSE;
}
support[attribute] = sup ? 1 : -1;
_LOGD ("kernel support for routing rule attribute #%d %s", attribute, sup ? "detected" : "not detected");
return sup;
}
static const NMPObject *
_platform_has_routing_rule (NMPlatform *platform,
const NMPObject *obj)
{
const NMPObject *o;
g_assert (NM_IS_PLATFORM (platform));
g_assert (NMP_OBJECT_IS_VALID (obj));
g_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_ROUTING_RULE);
o = nm_platform_lookup_obj (platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj);
if (o)
g_assert (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (obj), NMP_OBJECT_CAST_ROUTING_RULE (o), NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0);
return o;
}
static const NMPObject *
_rule_create_random (NMPlatform *platform)
{
NMPObject *obj;
NMPlatformRoutingRule *rr;
guint32 p;
int addr_size;
guint i;
char saddr[NM_UTILS_INET_ADDRSTRLEN];
static struct {
guint32 uid;
guint32 euid;
bool initialized;
} uids;
if (G_UNLIKELY (!uids.initialized)) {
uids.uid = getuid ();
uids.euid = geteuid ();
uids.initialized = TRUE;
}
obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL);
rr = NMP_OBJECT_CAST_ROUTING_RULE (obj);
rr->addr_family = nmtst_rand_select (AF_INET, AF_INET6);
addr_size = nm_utils_addr_family_to_size (rr->addr_family);
p = nmtst_get_rand_int () % 1000u;
if (p < 250)
rr->priority = 10000 + (p / 50);
p = nmtst_get_rand_int () % 1000u;
if (p < 60)
nm_sprintf_buf (rr->iifname, "t-iif-%u", p / 20);
else if (p < 120)
nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME);
p = nmtst_get_rand_int () % 1000u;
if (p < 60)
nm_sprintf_buf (rr->oifname, "t-oif-%d", p / 20);
else if (p < 120)
nm_sprintf_buf (rr->oifname, "%s", DEVICE_NAME);
p = nmtst_get_rand_int () % 1000u;
if (p < 60)
nm_sprintf_buf (rr->iifname, "t-iif-%d", p / 20);
else if (p < 120)
nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME);
for (i = 0; i < 2; i++) {
NMIPAddr *p_addr = i ? &rr->src : &rr->dst;
guint8 *p_len = i ? &rr->src_len : &rr->dst_len;
p = nmtst_get_rand_int () % 1000u;
if (p < 300) {
/* if we set src_len/dst_len to zero, the src/dst is actually ignored.
*
* For fuzzying, still set the address. It shall have no further effect.
* */
*p_len = p % (addr_size * 8 + 1);
p = nmtst_get_rand_int () % 750;
if (p <= 255) {
if (rr->addr_family == AF_INET)
p_addr->addr4 = nmtst_inet4_from_string (nm_sprintf_buf (saddr, "192.192.5.%u", p));
else
p_addr->addr6 = *nmtst_inet6_from_string (nm_sprintf_buf (saddr, "1:2:3:4::f:%02x", p));
} else if (p <= 512)
nmtst_rand_buf (NULL, p_addr, addr_size);
}
}
p = nmtst_get_rand_int () % 1000u;
if (p < 50)
rr->tun_id = 10000 + p;
again_action:
p = nmtst_get_rand_int () % 1000u;
if (p < 300)
rr->action = FR_ACT_UNSPEC;
else if (p < 700)
rr->action = p % 12;
else
rr->action = p % 0xFF;
rr->priority = nmtst_rand_select (0,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int ());
if ( rr->action == FR_ACT_GOTO
&& rr->priority == G_MAXINT32)
goto again_action;
again_goto_target:
rr->goto_target = nmtst_rand_select (0,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int (),
rr->priority + 1);
if ( rr->action == FR_ACT_GOTO
&& rr->goto_target <= rr->priority)
goto again_goto_target;
p = nmtst_get_rand_int () % 1000u;
if (p < 50) {
if (_rule_check_kernel_support (platform, FRA_L3MDEV)) {
rr->l3mdev = TRUE;
rr->table = RT_TABLE_UNSPEC;
}
}
again_table:
if (!rr->l3mdev) {
rr->table = nmtst_rand_select (RT_TABLE_UNSPEC,
RT_TABLE_MAIN,
10000 + nmtst_get_rand_int () % 10);
if ( rr->action == FR_ACT_TO_TBL
&& rr->table == RT_TABLE_UNSPEC)
goto again_table;
}
rr->fwmark = nmtst_rand_select (0,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int ());
rr->fwmask = nmtst_rand_select (0u,
0xFFFFFFFFu,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int ());
rr->flow = nmtst_rand_select (0u,
0xFFFFFFFFu,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int ());
if (_rule_check_kernel_support (platform, FRA_PROTOCOL)) {
rr->protocol = nmtst_rand_select (0u,
255u,
nmtst_get_rand_int () % 256);
}
#define IPTOS_TOS_MASK 0x1E
again_tos:
rr->tos = nmtst_rand_select (0u,
255u,
nmtst_get_rand_int () % 256);
if ( rr->addr_family == AF_INET
&& rr->tos & ~IPTOS_TOS_MASK)
goto again_tos;
if (_rule_check_kernel_support (platform, FRA_IP_PROTO)) {
rr->ip_proto = nmtst_rand_select (0u,
255u,
nmtst_get_rand_int () % 256);
}
if (_rule_check_kernel_support (platform, FRA_SUPPRESS_PREFIXLEN)) {
rr->suppress_prefixlen_inverse = ~((guint32) nmtst_rand_select (0u,
0xFFFFFFFFu,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int ()));
}
if (_rule_check_kernel_support (platform, FRA_SUPPRESS_IFGROUP)) {
rr->suppress_ifgroup_inverse = ~((guint32) nmtst_rand_select (0u,
0xFFFFFFFFu,
nmtst_get_rand_int () % 100,
nmtst_get_rand_int ()));
}
again_uid_range:
p = nmtst_get_rand_int () % 1000u;
if (p < 100) {
if (_rule_check_kernel_support (platform, FRA_UID_RANGE))
rr->uid_range_has = TRUE;
}
rr->uid_range.start = nmtst_rand_select (0u, uids.uid, uids.euid);
rr->uid_range.end = nmtst_rand_select (0u, uids.uid, uids.euid);
if (rr->uid_range.end < rr->uid_range.start)
NMTST_SWAP (rr->uid_range.start, rr->uid_range.end);
if ( rr->uid_range.start == ((guint32) -1)
|| rr->uid_range.end == ((guint32) -1))
goto again_uid_range;
for (i = 0; i < 2; i++) {
NMFibRulePortRange *range = i ? &rr->sport_range : &rr->dport_range;
int attribute = i ? FRA_SPORT_RANGE : FRA_DPORT_RANGE;
if (!_rule_check_kernel_support (platform, attribute))
continue;
p = nmtst_get_rand_int ();
if ((p % 1000u) < 100) {
while (range->start == 0) {
p = p ^ nmtst_get_rand_int ();
range->start = nmtst_rand_select (1u, 0xFFFEu, ((p ) % 0xFFFEu) + 1);
range->end = nmtst_rand_select (1u, 0xFFFEu, ((p >> 16) % 0xFFFEu) + 1, range->start);
if (range->end < range->start)
NMTST_SWAP (range->start, range->end);
}
}
}
p = nmtst_get_rand_int () % 1000u;
if (p < 100)
rr->flags |= FIB_RULE_INVERT;
return obj;
}
static gboolean
_rule_fuzzy_equal (const NMPObject *obj,
const NMPObject *obj_comp,
int op_type)
{
const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (obj);
NMPlatformRoutingRule rr_co = *NMP_OBJECT_CAST_ROUTING_RULE (obj_comp);
switch (op_type) {
case RTM_NEWRULE:
/* when adding rules with RTM_NEWRULE, kernel checks whether an existing
* rule already exists and may fail with EEXIST. This check has issues
* and reject legitimate rules (rh#1686075).
*
* Work around that. */
if (rr->src_len == 0)
rr_co.src_len = 0;
if (rr->dst_len == 0)
rr_co.dst_len = 0;
if (rr->flow == 0)
rr_co.flow = 0;
if (rr->tos == 0)
rr_co.tos = 0;
break;
case RTM_DELRULE:
/* when deleting a rule with RTM_DELRULE, kernel tries to find the
* cadidate to delete. It might delete the wrong rule (rh#1685816). */
if (rr->action == FR_ACT_UNSPEC)
rr_co.action = FR_ACT_UNSPEC;
if (rr->iifname[0] == '\0')
rr_co.iifname[0] = '\0';
if (rr->oifname[0] == '\0')
rr_co.oifname[0] = '\0';
if (rr->src_len == 0)
rr_co.src_len = 0;
if (rr->dst_len == 0)
rr_co.dst_len = 0;
if (rr->tun_id == 0)
rr_co.tun_id = 0;
if (rr->fwmark == 0)
rr_co.fwmark = 0;
if (rr->fwmask == 0)
rr_co.fwmask = 0;
if (rr->flow == 0)
rr_co.flow = 0;
if (rr->protocol == 0)
rr_co.protocol = 0;
if (rr->table == RT_TABLE_UNSPEC)
rr_co.table = RT_TABLE_UNSPEC;
if (rr->l3mdev == 0)
rr_co.l3mdev = 0;
if (rr->tos == 0)
rr_co.tos = 0;
if (rr->ip_proto == 0)
rr_co.ip_proto = 0;
if (rr->suppress_prefixlen_inverse == 0)
rr_co.suppress_prefixlen_inverse = 0;
if (rr->suppress_ifgroup_inverse == 0)
rr_co.suppress_ifgroup_inverse = 0;
if (!rr->uid_range_has)
rr_co.uid_range_has = FALSE;
if (rr->sport_range.start == 0 && rr->sport_range.end == 0) {
rr_co.sport_range.start = 0;
rr_co.sport_range.end = 0;
}
if (rr->dport_range.start == 0 && rr->dport_range.end == 0) {
rr_co.dport_range.start = 0;
rr_co.dport_range.end = 0;
}
if (!NM_FLAGS_HAS (rr->flags, FIB_RULE_INVERT))
rr_co.flags &= ~((guint32) FIB_RULE_INVERT);
else
rr_co.flags |= ((guint32) FIB_RULE_INVERT);
break;
default:
g_assert_not_reached ();
break;
}
return nm_platform_routing_rule_cmp (rr, &rr_co, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0;
}
static void
test_rule (gconstpointer test_data)
{
const int TEST_IDX = GPOINTER_TO_INT (test_data);
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 +1720,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));
}
}