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

@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 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"
@@ -436,7 +436,7 @@ nm_linux_platform_setup (void)
/******************************************************************/
static ObjectType
ObjectType
_nlo_get_object_type (const struct nl_object *object)
{
const char *type_str;
@@ -1011,6 +1011,86 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin
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
* 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
@@ -1194,6 +1274,66 @@ _init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr
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
init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr)
{
@@ -1229,6 +1369,44 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr)
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
init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
{
@@ -1258,6 +1436,41 @@ init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
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
source_to_rtprot (NMIPConfigSource source)
{
@@ -1350,6 +1563,61 @@ init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute)
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
init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
{
@@ -1390,6 +1658,56 @@ init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
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];
#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;
}
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
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;
}
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
ip4_address_add (NMPlatform *platform,
int ifindex,
@@ -4036,6 +4398,38 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source,
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
ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
in_addr_t network, int plen, in_addr_t gateway,