platform: implement NMPObject and NMPCache

NMPObject is a simple "object" implemenation around NMPlatformObject.
They are ref-counted and have a class-pointer. Several basic functions
like equality, hash, to-string are implemented.

NMPCache is can be used to store the NMPObject. Objects are indexed
via their primary id, but there is also multi-lookup via NMCacheId
and NMMultiIndex.

Part of the implementation is inside "nm-linux-platform.c",
because it depends on utility functions from there.
This commit is contained in:
Thomas Haller
2015-04-14 23:14:06 +02:00
parent d1e7554a90
commit 53f98e7f9e
7 changed files with 3064 additions and 5 deletions

View File

@@ -52,6 +52,9 @@ typedef enum {
/* In priority order; higher number == higher priority */ /* In priority order; higher number == higher priority */
NM_IP_CONFIG_SOURCE_UNKNOWN, NM_IP_CONFIG_SOURCE_UNKNOWN,
/* platform internal flag used to mark routes with RTM_F_CLONED. */
_NM_IP_CONFIG_SOURCE_RTM_F_CLONED,
/* platform internal flag used to mark routes with protocol RTPROT_KERNEL. */ /* platform internal flag used to mark routes with protocol RTPROT_KERNEL. */
_NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL,

View File

@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* Copyright (C) 2012-2013 Red Hat, Inc. * Copyright (C) 2012-2015 Red Hat, Inc.
*/ */
#include "config.h" #include "config.h"
@@ -436,7 +436,7 @@ nm_linux_platform_setup (void)
/******************************************************************/ /******************************************************************/
static ObjectType ObjectType
_nlo_get_object_type (const struct nl_object *object) _nlo_get_object_type (const struct nl_object *object)
{ {
const char *type_str; const char *type_str;
@@ -1011,6 +1011,86 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin
return TRUE; return TRUE;
} }
gboolean
_nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache)
{
NMPlatformLink *obj = (NMPlatformLink *) _obj;
NMPObjectLink *obj_priv = (NMPObjectLink *) _obj;
struct rtnl_link *nlo = (struct rtnl_link *) _nlo;
const char *name;
struct nl_addr *nladdr;
nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0);
obj->ifindex = rtnl_link_get_ifindex (nlo);
if (id_only)
return TRUE;
name = rtnl_link_get_name (nlo);
if (name)
g_strlcpy (obj->name, name, sizeof (obj->name));
obj->type = link_extract_type (platform, nlo);
obj->kind = g_intern_string (rtnl_link_get_type (nlo));
obj->flags = rtnl_link_get_flags (nlo);
obj->up = NM_FLAGS_HAS (obj->flags, IFF_UP);
obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP);
obj->arp = !NM_FLAGS_HAS (obj->flags, IFF_NOARP);
obj->master = rtnl_link_get_master (nlo);
obj->parent = rtnl_link_get_link (nlo);
obj->mtu = rtnl_link_get_mtu (nlo);
obj->arptype = rtnl_link_get_arptype (nlo);
if (obj->type == NM_LINK_TYPE_VLAN)
obj->vlan_id = rtnl_link_vlan_get_id (nlo);
if ((nladdr = rtnl_link_get_addr (nlo))) {
unsigned int l = 0;
l = nl_addr_get_len (nladdr);
if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) {
G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->addr.data));
memcpy (obj->addr.data, nl_addr_get_binary_addr (nladdr), l);
obj->addr.len = l;
}
}
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
if (_support_user_ipv6ll_get ()) {
guint8 mode = 0;
if (rtnl_link_inet6_get_addr_gen_mode (nlo, &mode) == 0)
obj->inet6_addr_gen_mode_inv = ~mode;
}
#endif
#if HAVE_LIBNL_INET6_TOKEN
if ((rtnl_link_inet6_get_token (nlo, &nladdr)) == 0) {
if ( nl_addr_get_family (nladdr) == AF_INET6
&& nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) {
struct in6_addr *addr;
NMUtilsIPv6IfaceId *iid = &obj->inet6_token.iid;
addr = nl_addr_get_binary_addr (nladdr);
iid->id_u8[7] = addr->s6_addr[15];
iid->id_u8[6] = addr->s6_addr[14];
iid->id_u8[5] = addr->s6_addr[13];
iid->id_u8[4] = addr->s6_addr[12];
iid->id_u8[3] = addr->s6_addr[11];
iid->id_u8[2] = addr->s6_addr[10];
iid->id_u8[1] = addr->s6_addr[9];
iid->id_u8[0] = addr->s6_addr[8];
obj->inet6_token.is_valid = TRUE;
}
nl_addr_put (nladdr);
}
#endif
obj_priv->netlink.is_in_netlink = TRUE;
return TRUE;
}
/* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break /* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break
* the carrier detection. This hack makes nm-platform think they don't have the * the carrier detection. This hack makes nm-platform think they don't have the
* IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all * IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all
@@ -1194,6 +1274,66 @@ _init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr
address->preferred = _get_remaining_time (address->timestamp, a_preferred); address->preferred = _get_remaining_time (address->timestamp, a_preferred);
} }
static guint32
_extend_lifetime (guint32 lifetime, guint32 seconds)
{
guint64 v;
if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT
|| seconds == 0)
return lifetime;
v = (guint64) lifetime + (guint64) seconds;
return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1);
}
/* The rtnl_addr object contains relative lifetimes @valid and @preferred
* that count in seconds, starting from the moment when the kernel constructed
* the netlink message.
*
* There is also a field rtnl_addr_last_update_time(), which is the absolute
* time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address
* was modified (wrapping every 497 days).
* Immediately at the time when the address was last modified, #NOW and @last_update_time
* are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time.
* However, this is not true in general. As time goes by, whenever kernel sends a new address
* via netlink, the lifetimes keep counting down.
**/
static void
_nlo_rtnl_addr_get_lifetimes (const struct rtnl_addr *rtnladdr,
guint32 *out_timestamp,
guint32 *out_lifetime,
guint32 *out_preferred)
{
guint32 timestamp = 0;
gint32 now;
guint32 lifetime = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr);
guint32 preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr);
if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT
|| preferred != NM_PLATFORM_LIFETIME_PERMANENT) {
if (preferred > lifetime)
preferred = lifetime;
timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr, &now);
if (now == 0) {
/* strange. failed to detect the last-update time and assumed that timestamp is 1. */
nm_assert (timestamp == 1);
now = nm_utils_get_monotonic_timestamp_s ();
}
if (timestamp < now) {
guint32 diff = now - timestamp;
lifetime = _extend_lifetime (lifetime, diff);
preferred = _extend_lifetime (preferred, diff);
} else
nm_assert (timestamp == now);
}
*out_timestamp = timestamp;
*out_lifetime = lifetime;
*out_preferred = preferred;
}
static gboolean static gboolean
init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr)
{ {
@@ -1229,6 +1369,44 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr)
return TRUE; return TRUE;
} }
gboolean
_nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache)
{
NMPlatformIP4Address *obj = (NMPlatformIP4Address *) _obj;
struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo;
struct nl_addr *nladdr = rtnl_addr_get_local (nlo);
struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo);
const char *label;
if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address))
g_return_val_if_reached (FALSE);
obj->ifindex = rtnl_addr_get_ifindex (nlo);
obj->plen = rtnl_addr_get_prefixlen (nlo);
memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address));
if (id_only)
return TRUE;
obj->source = NM_IP_CONFIG_SOURCE_KERNEL;
_nlo_rtnl_addr_get_lifetimes (nlo,
&obj->timestamp,
&obj->lifetime,
&obj->preferred);
if (nlpeer) {
if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address))
g_warn_if_reached ();
else
memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address));
}
label = rtnl_addr_get_label (nlo);
/* Check for ':'; we're only interested in labels used as interface aliases */
if (label && strchr (label, ':'))
g_strlcpy (obj->label, label, sizeof (obj->label));
return TRUE;
}
static gboolean static gboolean
init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
{ {
@@ -1258,6 +1436,41 @@ init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
return TRUE; return TRUE;
} }
gboolean
_nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache)
{
NMPlatformIP6Address *obj = (NMPlatformIP6Address *) _obj;
struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo;
struct nl_addr *nladdr = rtnl_addr_get_local (nlo);
struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo);
if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address))
g_return_val_if_reached (FALSE);
obj->ifindex = rtnl_addr_get_ifindex (nlo);
obj->plen = rtnl_addr_get_prefixlen (nlo);
memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address));
if (id_only)
return TRUE;
obj->source = NM_IP_CONFIG_SOURCE_KERNEL;
_nlo_rtnl_addr_get_lifetimes (nlo,
&obj->timestamp,
&obj->lifetime,
&obj->preferred);
obj->flags = rtnl_addr_get_flags (nlo);
if (nlpeer) {
if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address))
g_warn_if_reached ();
else
memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address));
}
return TRUE;
}
static guint static guint
source_to_rtprot (NMIPConfigSource source) source_to_rtprot (NMIPConfigSource source)
{ {
@@ -1350,6 +1563,61 @@ init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute)
return TRUE; return TRUE;
} }
gboolean
_nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache)
{
NMPlatformIP4Route *obj = (NMPlatformIP4Route *) _obj;
struct rtnl_route *nlo = (struct rtnl_route *) _nlo;
struct nl_addr *dst, *gw;
struct rtnl_nexthop *nexthop;
if (rtnl_route_get_type (nlo) != RTN_UNICAST ||
rtnl_route_get_table (nlo) != RT_TABLE_MAIN ||
rtnl_route_get_tos (nlo) != 0 ||
rtnl_route_get_nnexthops (nlo) != 1)
return FALSE;
nexthop = rtnl_route_nexthop_n (nlo, 0);
if (!nexthop)
g_return_val_if_reached (FALSE);
dst = rtnl_route_get_dst (nlo);
if (!dst)
g_return_val_if_reached (FALSE);
if (nl_addr_get_len (dst)) {
if (nl_addr_get_len (dst) != sizeof (obj->network))
g_return_val_if_reached (FALSE);
memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network));
}
obj->ifindex = rtnl_route_nh_get_ifindex (nexthop);
obj->plen = nl_addr_get_prefixlen (dst);
obj->metric = rtnl_route_get_priority (nlo);
obj->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (nlo));
gw = rtnl_route_nh_get_gateway (nexthop);
if (gw) {
if (nl_addr_get_len (gw) != sizeof (obj->gateway))
g_warn_if_reached ();
else
memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway));
}
rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss);
if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) {
/* we must not straight way reject cloned routes, because we might have cached
* a non-cloned route. If we now receive an update of the route with the route
* being cloned, we must still return the object, so that we can remove the old
* one from the cache.
*
* This happens, because this route is not nmp_object_is_alive().
* */
obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED;
} else
obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo), TRUE);
return TRUE;
}
static gboolean static gboolean
init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
{ {
@@ -1390,6 +1658,56 @@ init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
return TRUE; return TRUE;
} }
gboolean
_nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache)
{
NMPlatformIP6Route *obj = (NMPlatformIP6Route *) _obj;
struct rtnl_route *nlo = (struct rtnl_route *) _nlo;
struct nl_addr *dst, *gw;
struct rtnl_nexthop *nexthop;
if (rtnl_route_get_type (nlo) != RTN_UNICAST ||
rtnl_route_get_table (nlo) != RT_TABLE_MAIN ||
rtnl_route_get_tos (nlo) != 0 ||
rtnl_route_get_nnexthops (nlo) != 1)
return FALSE;
nexthop = rtnl_route_nexthop_n (nlo, 0);
if (!nexthop)
g_return_val_if_reached (FALSE);
dst = rtnl_route_get_dst (nlo);
if (!dst)
g_return_val_if_reached (FALSE);
if (nl_addr_get_len (dst)) {
if (nl_addr_get_len (dst) != sizeof (obj->network))
g_return_val_if_reached (FALSE);
memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network));
}
obj->ifindex = rtnl_route_nh_get_ifindex (nexthop);
obj->plen = nl_addr_get_prefixlen (dst);
obj->metric = rtnl_route_get_priority (nlo);
if (id_only)
return TRUE;
gw = rtnl_route_nh_get_gateway (nexthop);
if (gw) {
if (nl_addr_get_len (gw) != sizeof (obj->gateway))
g_warn_if_reached ();
else
memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway));
}
rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss);
if (rtnl_route_get_flags (nlo) & RTM_F_CLONED)
obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED;
else
obj->source = rtprot_to_source (rtnl_route_get_protocol (nlo), TRUE);
return TRUE;
}
static char to_string_buffer[255]; static char to_string_buffer[255];
#define SET_AND_RETURN_STRING_BUFFER(...) \ #define SET_AND_RETURN_STRING_BUFFER(...) \
@@ -2296,6 +2614,16 @@ build_rtnl_link (int ifindex, const char *name, NMLinkType type)
return (struct nl_object *) rtnllink; return (struct nl_object *) rtnllink;
} }
struct nl_object *
_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
const NMPlatformLink *obj = (const NMPlatformLink *) _obj;
return build_rtnl_link (obj->ifindex,
obj->name[0] ? obj->name : NULL,
obj->type);
}
static gboolean static gboolean
link_get_by_name (NMPlatform *platform, const char *name, NMPlatformLink *out_link) link_get_by_name (NMPlatform *platform, const char *name, NMPlatformLink *out_link)
{ {
@@ -3782,6 +4110,40 @@ build_rtnl_addr (NMPlatform *platform,
return (struct nl_object *) rtnladdr_copy; return (struct nl_object *) rtnladdr_copy;
} }
struct nl_object *
_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
const NMPlatformIP4Address *obj = (const NMPlatformIP4Address *) _obj;
return build_rtnl_addr (platform,
AF_INET,
obj->ifindex,
&obj->address,
obj->peer_address ? &obj->peer_address : NULL,
obj->plen,
obj->lifetime,
obj->preferred,
0,
obj->label[0] ? obj->label : NULL);
}
struct nl_object *
_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
const NMPlatformIP6Address *obj = (const NMPlatformIP6Address *) _obj;
return build_rtnl_addr (platform,
AF_INET6,
obj->ifindex,
&obj->address,
!IN6_IS_ADDR_UNSPECIFIED (&obj->peer_address) ? &obj->peer_address : NULL,
obj->plen,
obj->lifetime,
obj->preferred,
0,
NULL);
}
static gboolean static gboolean
ip4_address_add (NMPlatform *platform, ip4_address_add (NMPlatform *platform,
int ifindex, int ifindex,
@@ -4036,6 +4398,38 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source,
return (struct nl_object *) rtnlroute; return (struct nl_object *) rtnlroute;
} }
struct nl_object *
_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
const NMPlatformIP4Route *obj = (const NMPlatformIP4Route *) _obj;
return build_rtnl_route (AF_INET,
obj->ifindex,
obj->source,
&obj->network,
obj->plen,
&obj->gateway,
NULL,
obj->metric,
obj->mss);
}
struct nl_object *
_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only)
{
const NMPlatformIP6Route *obj = (const NMPlatformIP6Route *) _obj;
return build_rtnl_route (AF_INET6,
obj->ifindex,
obj->source,
&obj->network,
obj->plen,
&obj->gateway,
NULL,
obj->metric,
obj->mss);
}
static gboolean static gboolean
ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
in_addr_t network, int plen, in_addr_t gateway, in_addr_t network, int plen, in_addr_t gateway,

View File

@@ -2393,6 +2393,8 @@ source_to_string (NMIPConfigSource source)
switch (source) { switch (source) {
case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: case _NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
return "rtprot-kernel"; return "rtprot-kernel";
case _NM_IP_CONFIG_SOURCE_RTM_F_CLONED:
return "rtm-f-cloned";
case NM_IP_CONFIG_SOURCE_KERNEL: case NM_IP_CONFIG_SOURCE_KERNEL:
return "kernel"; return "kernel";
case NM_IP_CONFIG_SOURCE_SHARED: case NM_IP_CONFIG_SOURCE_SHARED:
@@ -2844,7 +2846,11 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b)
_CMP_FIELD (a, b, inet6_addr_gen_mode_inv); _CMP_FIELD (a, b, inet6_addr_gen_mode_inv);
_CMP_FIELD (a, b, inet6_token.is_valid); _CMP_FIELD (a, b, inet6_token.is_valid);
_CMP_FIELD_STR_INTERNED (a, b, kind); _CMP_FIELD_STR_INTERNED (a, b, kind);
_CMP_FIELD_STR0 (a, b, udi);
/* udi is not an interned string, but NMRefString. Hence,
* do a pointer comparison first. */
_CMP_FIELD_STR_INTERNED (a, b, udi);
_CMP_FIELD_STR_INTERNED (a, b, driver); _CMP_FIELD_STR_INTERNED (a, b, driver);
if (a->addr.len) if (a->addr.len)
_CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len);

View File

@@ -91,8 +91,10 @@ struct _NMPlatformLink {
/* NMPlatform initializes this field with a static string. */ /* NMPlatform initializes this field with a static string. */
const char *kind; const char *kind;
/* Beware: NMPlatform initializes this string with an allocated string. /* Beware: NMPlatform initializes this string with an allocated string
* Handle it properly (i.e. don't keep a reference to it). */ * (NMRefString). Handle it properly (i.e. don't keep a reference to it
* without incrementing the ref-counter).
* This property depends on @initialized. */
const char *udi; const char *udi;
/* NMPlatform initializes this field with a static string. */ /* NMPlatform initializes this field with a static string. */

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,12 @@
#include "config.h" #include "config.h"
#include "nm-platform.h" #include "nm-platform.h"
#include "nm-multi-index.h"
#include "nm-macros-internal.h"
#include <netlink/netlink.h>
#include <gudev/gudev.h>
typedef enum { /*< skip >*/ typedef enum { /*< skip >*/
OBJECT_TYPE_UNKNOWN, OBJECT_TYPE_UNKNOWN,
@@ -36,4 +42,342 @@ typedef enum { /*< skip >*/
OBJECT_TYPE_MAX = __OBJECT_TYPE_LAST - 1, OBJECT_TYPE_MAX = __OBJECT_TYPE_LAST - 1,
} ObjectType; } ObjectType;
typedef enum { /*< skip >*/
NMP_OBJECT_TO_STRING_ID,
NMP_OBJECT_TO_STRING_PUBLIC,
NMP_OBJECT_TO_STRING_ALL,
} NMPObjectToStringMode;
typedef enum { /*< skip >*/
NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE,
NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED,
NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED,
NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED,
} NMPCacheOpsType;
/* The NMPCacheIdType are the different index types.
*
* An object of a certain object-type, can be candidate to being
* indexed by a certain NMPCacheIdType or not. For example, all
* objects are indexed via an index of type NMP_CACHE_ID_TYPE_OBJECT_TYPE,
* but only route objects are indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL.
*
* Of one index type, there can be different indexes or not.
* For example, there is only one single instance of
* NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY, because this instance is
* applicable for all link objects.
* But there are different instances of NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX
* type index, which differ in v4/v6, the ifindex, and whether the index
* is for routes or address instances.
*
* But one object, can only be indexed by one particular index of one
* type. For example, a certain address instance is only indexed by
* the index NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX with matching v4/v6
* and ifindex.
* */
typedef enum { /*< skip >*/
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY,
NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX,
/* three indeces for the visibile routes. */
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL,
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT,
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT,
__NMP_CACHE_ID_TYPE_MAX,
NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1,
} NMPCacheIdType;
typedef struct _NMPObject NMPObject;
typedef struct {
union {
NMMultiIndexId base;
guint8 _id_type; /* NMPCacheIdType as guint8 */
struct {
/* NMP_CACHE_ID_TYPE_OBJECT_TYPE */
guint8 _id_type;
guint8 obj_type; /* ObjectType as guint8 */
} object_type;
struct {
/* NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY */
guint8 _id_type;
/* the @_global_id is only defined by it's type and has no arguments.
* It is used by NMP_CACHE_ID_TYPE_LINKS_VISIBLE_ONLY. There is only
* one single occurence of an index of this type. */
} _global_id;
struct {
/* NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX */
guint8 _id_type;
guint8 obj_type; /* ObjectType as guint8 */
int ifindex;
} addrroute_by_ifindex;
struct {
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ALL */
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */
/* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */
guint8 _id_type;
guint8 is_v4;
int ifindex;
} routes_visible;
};
} NMPCacheId;
extern NMPCacheId _nmp_cache_id_static;
#define NMP_CACHE_ID_STATIC (&_nmp_cache_id_static)
typedef struct {
ObjectType obj_type;
int addr_family;
int rtm_gettype;
int sizeof_data;
int sizeof_public;
const char *obj_type_name;
const char *nl_type;
const char *signal_type;
/* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index,
* initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */
gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id);
gboolean (*cmd_obj_equal) (const NMPObject *obj1, const NMPObject *obj2);
void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src);
void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src);
void (*cmd_obj_dispose) (NMPObject *obj);
gboolean (*cmd_obj_is_alive) (const NMPObject *obj);
gboolean (*cmd_obj_is_visible) (const NMPObject *obj);
/* functions that operate on NMPlatformObject */
gboolean (*cmd_plobj_init_from_nl) (NMPlatform *platform, NMPlatformObject *obj, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache);
struct nl_object *(*cmd_plobj_to_nl) (NMPlatform *platform, const NMPlatformObject *obj, gboolean id_only);
void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src);
gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2);
guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj);
const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size);
const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj);
int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2);
} NMPClass;
extern const NMPClass _nmp_classes[OBJECT_TYPE_MAX];
typedef struct {
NMPlatformLink _public;
struct {
guint8 is_in_netlink;
} netlink;
struct {
GUdevDevice *device;
} udev;
} NMPObjectLink;
typedef struct {
NMPlatformIP4Address _public;
} NMPObjectIP4Address;
typedef struct {
NMPlatformIP4Route _public;
} NMPObjectIP4Route;
typedef struct {
NMPlatformIP6Address _public;
} NMPObjectIP6Address;
typedef struct {
NMPlatformIP6Route _public;
} NMPObjectIP6Route;
struct _NMPObject {
const NMPClass *_class;
int _ref_count;
guint8 is_cached;
union {
NMPlatformObject object;
NMPlatformLink link;
NMPObjectLink _link;
NMPlatformIPAddress ip_address;
NMPlatformIPXAddress ipx_address;
NMPlatformIP4Address ip4_address;
NMPlatformIP6Address ip6_address;
NMPObjectIP4Address _ip4_address;
NMPObjectIP6Address _ip6_address;
NMPlatformIPRoute ip_route;
NMPlatformIPXRoute ipx_route;
NMPlatformIP4Route ip4_route;
NMPlatformIP6Route ip6_route;
NMPObjectIP4Route _ip4_route;
NMPObjectIP6Route _ip6_route;
};
};
static inline gboolean
NMP_CLASS_IS_VALID (const NMPClass *klass)
{
return klass >= &_nmp_classes[0]
&& klass <= &_nmp_classes[G_N_ELEMENTS (_nmp_classes)]
&& ((((char *) klass) - ((char *) NULL)) % (&_nmp_classes[1] - &_nmp_classes[0])) == 0;
}
#define NMP_REF_COUNT_STACKINIT (G_MAXINT)
static inline NMPObject *
NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj)
{
NMPObject *obj;
obj = plobj
? (NMPObject *) ( &(((char *) plobj)[-((int) G_STRUCT_OFFSET (NMPObject, object))]) )
: NULL;
nm_assert (!obj || (obj->_ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class)));
return obj;
}
#define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST ((const NMPlatformObject *) (plobj)))
static inline gboolean
NMP_OBJECT_IS_VALID (const NMPObject *obj)
{
nm_assert (!obj || ( obj
&& obj->_ref_count > 0
&& NMP_CLASS_IS_VALID (obj->_class)));
/* There isn't really much to check. Either @obj is NULL, or we must
* assume that it points to valid memory. */
return obj != NULL;
}
static inline gboolean
NMP_OBJECT_IS_STACKINIT (const NMPObject *obj)
{
nm_assert (!obj || NMP_OBJECT_IS_VALID (obj));
return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT;
}
static inline const NMPClass *
NMP_OBJECT_GET_CLASS (const NMPObject *obj)
{
nm_assert (NMP_OBJECT_IS_VALID (obj));
return obj->_class;
}
static inline ObjectType
NMP_OBJECT_GET_TYPE (const NMPObject *obj)
{
nm_assert (!obj || NMP_OBJECT_IS_VALID (obj));
return obj ? obj->_class->obj_type : OBJECT_TYPE_UNKNOWN;
}
const NMPClass *nmp_class_from_type (ObjectType obj_type);
NMPObject *nmp_object_ref (NMPObject *object);
void nmp_object_unref (NMPObject *object);
NMPObject *nmp_object_new (ObjectType obj_type, const NMPlatformObject *plob);
NMPObject *nmp_object_new_link (int ifindex);
const NMPObject *nmp_object_stackinit (NMPObject *obj, ObjectType obj_type, const NMPlatformObject *plobj);
const NMPObject *nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src);
const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex);
const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen);
const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, int plen);
const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, int plen, guint32 metric);
const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, int plen, guint32 metric);
const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size);
int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2);
gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2);
void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only);
NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only);
gboolean nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2);
guint nmp_object_id_hash (const NMPObject *obj);
gboolean nmp_object_is_alive (const NMPObject *obj);
gboolean nmp_object_is_visible (const NMPObject *obj);
void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev);
#define auto_nmp_obj __attribute__((cleanup(_nmp_auto_obj_cleanup)))
static inline void
_nmp_auto_obj_cleanup (NMPObject **pobj)
{
nmp_object_unref (*pobj);
}
typedef struct _NMPCache NMPCache;
typedef void (*NMPCachePreHook) (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
typedef gboolean (*NMPObjectMatchFn) (const NMPObject *obj, gpointer user_data);
gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b);
guint nmp_cache_id_hash (const NMPCacheId *id);
NMPCacheId *nmp_cache_id_clone (const NMPCacheId *id);
void nmp_cache_id_destroy (NMPCacheId *id);
NMPCacheId *nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type);
NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, ObjectType obj_type);
NMPCacheId *nmp_cache_id_init_links (NMPCacheId *id, gboolean visible_only);
NMPCacheId *nmp_cache_id_init_addrroute_by_ifindex (NMPCacheId *id, ObjectType obj_type, int ifindex);
NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPCacheIdType id_type, gboolean is_v4, int ifindex);
const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len);
GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, ObjectType obj_type, const NMPCacheId *cache_id);
const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj);
const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex);
const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache,
int ifindex,
const char *ifname,
gboolean visible_only,
NMLinkType link_type,
NMPObjectMatchFn match_fn,
gpointer user_data);
GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache,
NMPCacheId *cache_id,
GHashTable *hash);
gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave);
const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave);
gboolean nmp_cache_use_udev_detect (void);
gboolean nmp_cache_use_udev_get (const NMPCache *cache);
gboolean nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev);
void ASSERT_nmp_cache_is_consistent (const NMPCache *cache);
NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
NMPCache *nmp_cache_new (void);
void nmp_cache_free (NMPCache *cache);
NMPObject *nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache);
struct nl_object *nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only);
/* the following functions are currently implemented inside nm-linux-platform, because
* they depend on utility functions there. */
ObjectType _nlo_get_object_type (const struct nl_object *nlo);
gboolean _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache);
gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache);
gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache);
gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache);
gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only);
#endif /* __NMP_OBJECT_H__ */ #endif /* __NMP_OBJECT_H__ */

View File

@@ -24,6 +24,366 @@
#include "nm-test-utils.h" #include "nm-test-utils.h"
struct {
GList *udev_devices;
} global;
/******************************************************************/
static gboolean
_nmp_object_id_equal (const NMPObject *a, const NMPObject *b)
{
gboolean a_b = nmp_object_id_equal (a, b);
g_assert (NM_IN_SET (a_b, FALSE, TRUE) && a_b == nmp_object_id_equal (b, a));
return a_b;
}
#define nmp_object_id_equal _nmp_object_id_equal
static gboolean
_nmp_object_equal (const NMPObject *a, const NMPObject *b)
{
gboolean a_b = nmp_object_equal (a, b);
g_assert (NM_IN_SET (a_b, FALSE, TRUE) && a_b == nmp_object_equal (b, a));
return a_b;
}
#define nmp_object_equal _nmp_object_equal
/******************************************************************/
static void
_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains)
{
const NMPlatformObject *const *objects;
guint i, len;
gboolean found;
g_assert (cache_id);
g_assert (NMP_OBJECT_IS_VALID (obj));
g_assert (nmp_cache_lookup_obj (cache, obj) == obj);
objects = nmp_cache_lookup_multi (cache, cache_id, &len);
g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len]));
found = FALSE;
for (i = 0; i < len; i++) {
NMPObject *o;
g_assert (objects[i]);
o = NMP_OBJECT_UP_CAST (objects[i]);
g_assert (NMP_OBJECT_IS_VALID (o));
if (obj == o) {
g_assert (!found);
found = TRUE;
}
}
g_assert (!!contains == found);
}
/******************************************************************/
typedef struct {
NMPCache *cache;
NMPCacheOpsType expected_ops_type;
const NMPObject *obj_clone;
NMPObject *new_clone;
gboolean was_visible;
gboolean called;
} _NMPCacheUpdateData;
static void
_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data)
{
_NMPCacheUpdateData *data = user_data;
g_assert (data);
g_assert (!data->called);
g_assert (data->cache == cache);
g_assert_cmpint (data->expected_ops_type, ==, ops_type);
switch (ops_type) {
case NMP_CACHE_OPS_ADDED:
g_assert (!old);
g_assert (NMP_OBJECT_IS_VALID (new));
g_assert (nmp_object_is_alive (new));
g_assert (nmp_object_id_equal (data->obj_clone, new));
g_assert (nmp_object_equal (data->obj_clone, new));
break;
case NMP_CACHE_OPS_UPDATED:
g_assert (NMP_OBJECT_IS_VALID (old));
g_assert (NMP_OBJECT_IS_VALID (new));
g_assert (nmp_object_is_alive (old));
g_assert (nmp_object_is_alive (new));
g_assert (nmp_object_id_equal (data->obj_clone, new));
g_assert (nmp_object_id_equal (data->obj_clone, old));
g_assert (nmp_object_id_equal (old, new));
g_assert (nmp_object_equal (data->obj_clone, new));
g_assert (!nmp_object_equal (data->obj_clone, old));
g_assert (!nmp_object_equal (old, new));
break;
case NMP_CACHE_OPS_REMOVED:
g_assert (!new);
g_assert (NMP_OBJECT_IS_VALID (old));
g_assert (nmp_object_is_alive (old));
g_assert (nmp_object_id_equal (data->obj_clone, old));
break;
default:
g_assert_not_reached ();
}
data->was_visible = old ? nmp_object_is_visible (old) : FALSE;
data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL;
data->called = TRUE;
}
static void
_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type)
{
NMPCacheOpsType ops_type;
NMPObject *obj2;
gboolean was_visible;
auto_nmp_obj NMPObject *obj_clone = nmp_object_clone (obj, FALSE);
auto_nmp_obj NMPObject *new_clone = NULL;
const NMPObject *obj_old;
_NMPCacheUpdateData data = {
.cache = cache,
.expected_ops_type = expected_ops_type,
.obj_clone = obj_clone,
};
obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex);
if (obj_old && obj_old->_link.udev.device)
obj_clone->_link.udev.device = g_object_ref (obj_old->_link.udev.device);
_nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache));
g_assert (cache);
g_assert (NMP_OBJECT_IS_VALID (obj));
ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data);
new_clone = data.new_clone;
g_assert_cmpint (ops_type, ==, expected_ops_type);
if (ops_type != NMP_CACHE_OPS_UNCHANGED) {
g_assert (NMP_OBJECT_IS_VALID (obj2));
g_assert (data.called);
g_assert_cmpint (data.was_visible, ==, was_visible);
if (ops_type == NMP_CACHE_OPS_REMOVED)
g_assert (!data.new_clone);
else {
g_assert (data.new_clone);
g_assert (nmp_object_equal (obj2, data.new_clone));
}
} else {
g_assert (!data.called);
g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2));
}
g_assert (!obj2 || nmp_object_id_equal (obj, obj2));
if (ops_type != NMP_CACHE_OPS_REMOVED && obj2)
g_assert (nmp_object_equal (obj, obj2));
if (out_obj)
*out_obj = obj2;
else
nmp_object_unref (obj2);
if (out_was_visible)
*out_was_visible = was_visible;
}
static const NMPlatformLink pl_link_2 = {
.ifindex = 2,
.name = "eth0",
.type = NM_LINK_TYPE_ETHERNET,
};
static const NMPlatformLink pl_link_3 = {
.ifindex = 3,
.name = "wlan0",
.type = NM_LINK_TYPE_WIFI,
};
static void
test_cache_link ()
{
NMPCache *cache;
NMPObject *obj1, *obj2;
NMPObject objs1;
gboolean was_visible;
NMPCacheId cache_id_storage;
GUdevDevice *udev_device_2 = g_list_nth_data (global.udev_devices, 0);
GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
NMPCacheOpsType ops_type;
cache = nmp_cache_new ();
nmp_cache_use_udev_set (cache, g_rand_int_range (nmtst_get_rand (), 0, 2));
/* if we have a link, and don't set is_in_netlink, adding it has no effect. */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1);
g_assert (!nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (!obj2);
g_assert (!was_visible);
g_assert (!nmp_cache_lookup_obj (cache, obj1));
g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)));
nmp_object_unref (obj1);
/* Only when setting @is_in_netlink the link is added. */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
/* updating the same link with identical value, has no effect. */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
/* remove the link from netlink */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
g_assert (!nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (was_visible);
g_assert (!nmp_cache_lookup_obj (cache, obj1));
g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)));
nmp_object_unref (obj1);
nmp_object_unref (obj2);
udev_device_2 = NULL;
if (udev_device_2) {
/* now add the link only with aspect UDEV. */
ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL);
ASSERT_nmp_cache_is_consistent (cache);
g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED);
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (!nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, FALSE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
nmp_object_unref (obj2);
}
/* add it in netlink too. */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
/* remove again from netlink. */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
obj1->_link.netlink.is_in_netlink = FALSE;
g_assert (!nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (was_visible);
if (udev_device_2) {
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
g_assert (!nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, FALSE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
} else {
g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL);
g_assert (nmp_object_is_visible (obj2));
}
nmp_object_unref (obj1);
nmp_object_unref (obj2);
/* now another link only with aspect UDEV. */
if (udev_device_3) {
/* now add the link only with aspect UDEV. */
ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL);
g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (NMP_OBJECT_IS_VALID (obj2));
g_assert (!was_visible);
g_assert (!nmp_object_is_visible (obj2));
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, FALSE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE);
g_assert_cmpint (obj2->link.initialized, ==, FALSE);
nmp_object_unref (obj2);
/* add it in netlink too. */
obj1 = nmp_object_new (OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3);
obj1->_link.netlink.is_in_netlink = TRUE;
g_assert (nmp_object_is_alive (obj1));
_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (obj2 != obj1);
g_assert (nmp_object_equal (obj1, obj2));
g_assert (!was_visible);
g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE);
g_assert_cmpint (obj2->link.initialized, ==, TRUE);
nmp_object_unref (obj1);
nmp_object_unref (obj2);
/* remove UDEV. */
ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL);
g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED);
ASSERT_nmp_cache_is_consistent (cache);
g_assert (was_visible);
g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
g_assert (nmp_object_is_visible (obj2));
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, TRUE), obj2, TRUE);
_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_links (&cache_id_storage, FALSE), obj2, TRUE);
g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE);
g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache));
nmp_object_unref (obj2);
}
nmp_cache_free (cache);
}
/******************************************************************/ /******************************************************************/
@@ -33,11 +393,34 @@ int
main (int argc, char **argv) main (int argc, char **argv)
{ {
int result; int result;
gs_unref_object GUdevClient *udev_client = NULL;
nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
udev_client = g_udev_client_new ((const char *[]) { "net", NULL });
{
gs_unref_object GUdevEnumerator *udev_enumerator = g_udev_enumerator_new (udev_client);
g_udev_enumerator_add_match_subsystem (udev_enumerator, "net");
/* Demand that the device is initialized (udev rules ran,
* device has a stable name now) in case udev is running
* (not in a container). */
if (access ("/sys", W_OK) == 0)
g_udev_enumerator_add_match_is_initialized (udev_enumerator);
global.udev_devices = g_udev_enumerator_execute (udev_enumerator);
}
g_test_add_func ("/nmp-object/cache_link", test_cache_link);
result = g_test_run (); result = g_test_run ();
while (global.udev_devices) {
g_object_unref (global.udev_devices->data);
global.udev_devices = g_list_remove (global.udev_devices, global.udev_devices->data);
}
return result; return result;
} }