
We handle cloned routes (that have rtm_flags RTM_F_CLONED) differently. We used to mark such routes by hacking NMIPConfigSource to have a special value. No longer do this, because it mixes different concepts. Note that the rt_cloned filed fits into a hole in the aligment of NMPlatformIPRoute. Thus there is almost no overhead to this change.
2286 lines
80 KiB
C
2286 lines
80 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/* nm-platform.c - Handle runtime kernel networking configuration
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nmp-object.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "nm-utils.h"
|
|
|
|
#include "nm-core-utils.h"
|
|
#include "nm-platform-utils.h"
|
|
|
|
/*********************************************************************************************/
|
|
|
|
#define _NMLOG_DOMAIN LOGD_PLATFORM
|
|
#define _NMLOG(level, obj, ...) \
|
|
G_STMT_START { \
|
|
const NMLogLevel __level = (level); \
|
|
\
|
|
if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
|
|
const NMPObject *const __obj = (obj); \
|
|
\
|
|
_nm_log (__level, _NMLOG_DOMAIN, 0, \
|
|
"nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
|
|
__obj, \
|
|
(__obj ? NMP_OBJECT_GET_CLASS (__obj)->obj_type_name : "???") \
|
|
_NM_UTILS_MACRO_REST (__VA_ARGS__)); \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
/*********************************************************************************************/
|
|
|
|
struct _NMPCache {
|
|
/* the cache contains only one hash table for all object types, and similarly
|
|
* it contains only one NMMultiIndex.
|
|
* This works, because different object types don't ever compare equal and
|
|
* because their index ids also don't overlap.
|
|
*
|
|
* For routes and addresses, the cache contains an address if (and only if) the
|
|
* object was reported via netlink.
|
|
* For links, the cache contain a link if it was reported by either netlink
|
|
* or udev. That means, a link object can be alive, even if it was already
|
|
* removed via netlink.
|
|
*
|
|
* This effectively merges the udev-device cache into the NMPCache.
|
|
*/
|
|
|
|
GHashTable *idx_main;
|
|
NMMultiIndex *idx_multi;
|
|
|
|
gboolean use_udev;
|
|
};
|
|
|
|
/******************************************************************/
|
|
|
|
static inline guint
|
|
_id_hash_ip6_addr (const struct in6_addr *addr)
|
|
{
|
|
guint hash = (guint) 0x897da53981a13ULL;
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof (*addr); i++)
|
|
hash = (hash * 33) + ((const guint8 *) addr)[i];
|
|
return hash;
|
|
}
|
|
|
|
static int
|
|
_vlan_xgress_qos_mappings_cmp (guint n_map,
|
|
const NMVlanQosMapping *map1,
|
|
const NMVlanQosMapping *map2)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < n_map; i++) {
|
|
if (map1[i].from != map2[i].from)
|
|
return map1[i].from < map2[i].from ? -1 : 1;
|
|
if (map1[i].to != map2[i].to)
|
|
return map1[i].to < map2[i].to ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_vlan_xgress_qos_mappings_cpy (guint *dst_n_map,
|
|
const NMVlanQosMapping **dst_map,
|
|
guint src_n_map,
|
|
const NMVlanQosMapping *src_map)
|
|
{
|
|
if (src_n_map == 0) {
|
|
g_clear_pointer (dst_map, g_free);
|
|
*dst_n_map = 0;
|
|
} else if ( src_n_map != *dst_n_map
|
|
|| _vlan_xgress_qos_mappings_cmp (src_n_map, *dst_map, src_map) != 0) {
|
|
g_clear_pointer (dst_map, g_free);
|
|
*dst_n_map = src_n_map;
|
|
if (src_n_map > 0)
|
|
*dst_map = g_memdup (src_map, sizeof (*src_map) * src_n_map);
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
static const char *
|
|
_link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname)
|
|
{
|
|
const char *driver = NULL;
|
|
|
|
nm_assert (kind == g_intern_string (kind));
|
|
|
|
if (udev_device) {
|
|
driver = nmp_utils_udev_get_driver (udev_device);
|
|
if (driver)
|
|
return driver;
|
|
}
|
|
|
|
if (kind)
|
|
return kind;
|
|
|
|
if (ifname) {
|
|
char *d;
|
|
|
|
if (nmp_utils_ethtool_get_driver_info (ifname, &d, NULL, NULL)) {
|
|
driver = d && d[0] ? g_intern_string (d) : NULL;
|
|
g_free (d);
|
|
if (driver)
|
|
return driver;
|
|
}
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
void
|
|
_nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev)
|
|
{
|
|
const char *driver = NULL;
|
|
gboolean initialized = FALSE;
|
|
|
|
nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
/* The link contains internal fields that are combined by
|
|
* properties from netlink and udev. Update those properties */
|
|
|
|
/* When a link is not in netlink, it's udev fields don't matter. */
|
|
if (obj->_link.netlink.is_in_netlink) {
|
|
driver = _link_get_driver (obj->_link.udev.device,
|
|
obj->link.kind,
|
|
obj->link.name);
|
|
if (obj->_link.udev.device)
|
|
initialized = TRUE;
|
|
else if (!use_udev) {
|
|
/* If we don't use udev, we immediately mark the link as initialized.
|
|
*
|
|
* For that, we consult @use_udev argument, that is cached via
|
|
* nmp_cache_use_udev_get(). It is on purpose not to test
|
|
* for a writable /sys on every call. A minor reason for that is
|
|
* performance, but the real reason is reproducibility.
|
|
* */
|
|
initialized = TRUE;
|
|
}
|
|
}
|
|
|
|
obj->link.driver = driver;
|
|
obj->link.initialized = initialized;
|
|
}
|
|
|
|
static void
|
|
_nmp_object_fixup_link_master_connected (NMPObject *obj, const NMPCache *cache)
|
|
{
|
|
nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL))
|
|
obj->link.connected = !obj->link.connected;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
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];
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
NMPObject *
|
|
nmp_object_ref (NMPObject *obj)
|
|
{
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL);
|
|
g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL);
|
|
obj->_ref_count++;
|
|
|
|
_LOGt (obj, "ref: %d", obj->_ref_count);
|
|
|
|
return obj;
|
|
}
|
|
|
|
void
|
|
nmp_object_unref (NMPObject *obj)
|
|
{
|
|
if (obj) {
|
|
g_return_if_fail (obj->_ref_count > 0);
|
|
g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT);
|
|
_LOGt (obj, "%s: %d",
|
|
obj->_ref_count <= 1 ? "destroy" : "unref",
|
|
obj->_ref_count - 1);
|
|
if (--obj->_ref_count <= 0) {
|
|
const NMPClass *klass = obj->_class;
|
|
|
|
nm_assert (!obj->is_cached);
|
|
if (klass->cmd_obj_dispose)
|
|
klass->cmd_obj_dispose (obj);
|
|
g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_dispose_link (NMPObject *obj)
|
|
{
|
|
g_clear_object (&obj->_link.udev.device);
|
|
nmp_object_unref (obj->_link.netlink.lnk);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_dispose_lnk_vlan (NMPObject *obj)
|
|
{
|
|
g_free ((gpointer) obj->_lnk_vlan.ingress_qos_map);
|
|
g_free ((gpointer) obj->_lnk_vlan.egress_qos_map);
|
|
}
|
|
|
|
static NMPObject *
|
|
_nmp_object_new_from_class (const NMPClass *klass)
|
|
{
|
|
NMPObject *obj;
|
|
|
|
nm_assert (klass);
|
|
nm_assert (klass->sizeof_data > 0);
|
|
nm_assert (klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data);
|
|
|
|
obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object));
|
|
obj->_class = klass;
|
|
obj->_ref_count = 1;
|
|
_LOGt (obj, "new");
|
|
return obj;
|
|
}
|
|
|
|
NMPObject *
|
|
nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plobj)
|
|
{
|
|
const NMPClass *klass = nmp_class_from_type (obj_type);
|
|
NMPObject *obj;
|
|
|
|
obj = _nmp_object_new_from_class (klass);
|
|
if (plobj)
|
|
memcpy (&obj->object, plobj, klass->sizeof_public);
|
|
return obj;
|
|
}
|
|
|
|
NMPObject *
|
|
nmp_object_new_link (int ifindex)
|
|
{
|
|
NMPObject *obj;
|
|
|
|
obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL);
|
|
obj->link.ifindex = ifindex;
|
|
return obj;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
static const NMPObject *
|
|
_nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass)
|
|
{
|
|
nm_assert (klass);
|
|
|
|
memset (obj, 0, sizeof (NMPObject));
|
|
obj->_class = klass;
|
|
obj->_ref_count = NMP_REF_COUNT_STACKINIT;
|
|
return obj;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj)
|
|
{
|
|
const NMPClass *klass = nmp_class_from_type (obj_type);
|
|
|
|
_nmp_object_stackinit_from_class (obj, klass);
|
|
if (plobj)
|
|
memcpy (&obj->object, plobj, klass->sizeof_public);
|
|
return obj;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src)
|
|
{
|
|
const NMPClass *klass;
|
|
|
|
nm_assert (NMP_OBJECT_IS_VALID (src));
|
|
nm_assert (obj);
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (src);
|
|
if (!klass->cmd_obj_stackinit_id)
|
|
nmp_object_stackinit (obj, klass->obj_type, NULL);
|
|
else
|
|
klass->cmd_obj_stackinit_id (obj, src);
|
|
return obj;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_link (NMPObject *obj, int ifindex)
|
|
{
|
|
nmp_object_stackinit (obj, NMP_OBJECT_TYPE_LINK, NULL);
|
|
obj->link.ifindex = ifindex;
|
|
return obj;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src)
|
|
{
|
|
nmp_object_stackinit_id_link (obj, src->link.ifindex);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address)
|
|
{
|
|
nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL);
|
|
obj->ip4_address.ifindex = ifindex;
|
|
obj->ip4_address.address = address;
|
|
obj->ip4_address.plen = plen;
|
|
obj->ip4_address.peer_address = peer_address;
|
|
return obj;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_stackinit_id_ip4_address (NMPObject *obj, const NMPObject *src)
|
|
{
|
|
nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen, src->ip4_address.peer_address);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, guint8 plen)
|
|
{
|
|
nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL);
|
|
obj->ip4_address.ifindex = ifindex;
|
|
if (address)
|
|
obj->ip6_address.address = *address;
|
|
obj->ip6_address.plen = plen;
|
|
return obj;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_stackinit_id_ip6_address (NMPObject *obj, const NMPObject *src)
|
|
{
|
|
nmp_object_stackinit_id_ip6_address (obj, src->ip_address.ifindex, &src->ip6_address.address, src->ip_address.plen);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric)
|
|
{
|
|
nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ROUTE, NULL);
|
|
obj->ip4_route.ifindex = ifindex;
|
|
obj->ip4_route.network = network;
|
|
obj->ip4_route.plen = plen;
|
|
obj->ip4_route.metric = metric;
|
|
return obj;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_stackinit_id_ip4_route (NMPObject *obj, const NMPObject *src)
|
|
{
|
|
nmp_object_stackinit_id_ip4_route (obj, src->ip_route.ifindex, src->ip4_route.network, src->ip_route.plen, src->ip_route.metric);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric)
|
|
{
|
|
nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL);
|
|
obj->ip6_route.ifindex = ifindex;
|
|
if (network)
|
|
obj->ip6_route.network = *network;
|
|
obj->ip6_route.plen = plen;
|
|
obj->ip6_route.metric = metric;
|
|
return obj;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_stackinit_id_ip6_route (NMPObject *obj, const NMPObject *src)
|
|
{
|
|
nmp_object_stackinit_id_ip6_route (obj, src->ip_route.ifindex, &src->ip6_route.network, src->ip_route.plen, src->ip_route.metric);
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
const char *
|
|
nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size)
|
|
{
|
|
const NMPClass *klass;
|
|
char buf2[sizeof (_nm_utils_to_string_buffer)];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (obj, &buf, &buf_size))
|
|
return buf;
|
|
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL);
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (obj);
|
|
|
|
if (klass->cmd_obj_to_string)
|
|
return klass->cmd_obj_to_string (obj, to_string_mode, buf, buf_size);
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
if (!klass->cmd_plobj_to_string_id) {
|
|
g_snprintf (buf, buf_size, "%p", obj);
|
|
return buf;
|
|
}
|
|
return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size);
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
g_snprintf (buf, buf_size,
|
|
"[%s,%p,%d,%ccache,%calive,%cvisible; %s]",
|
|
klass->obj_type_name, obj, obj->_ref_count,
|
|
obj->is_cached ? '+' : '-',
|
|
nmp_object_is_alive (obj) ? '+' : '-',
|
|
nmp_object_is_visible (obj) ? '+' : '-',
|
|
NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2)));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size);
|
|
return buf;
|
|
default:
|
|
g_return_val_if_reached ("ERROR");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
_vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size)
|
|
{
|
|
const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj);
|
|
char buf2[sizeof (_nm_utils_to_string_buffer)];
|
|
char buf3[sizeof (_nm_utils_to_string_buffer)];
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size);
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
g_snprintf (buf, buf_size,
|
|
"[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]",
|
|
klass->obj_type_name, obj, obj->_ref_count,
|
|
obj->is_cached ? '+' : '-',
|
|
nmp_object_is_alive (obj) ? '+' : '-',
|
|
nmp_object_is_visible (obj) ? '+' : '-',
|
|
obj->_link.netlink.is_in_netlink ? '+' : '-',
|
|
obj->_link.udev.device,
|
|
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2)));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
if (obj->_link.netlink.lnk) {
|
|
NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2));
|
|
nmp_object_to_string (obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, buf3, sizeof (buf3));
|
|
g_snprintf (buf, buf_size,
|
|
"%s; %s",
|
|
buf2, buf3);
|
|
} else
|
|
NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size);
|
|
return buf;
|
|
default:
|
|
g_return_val_if_reached ("ERROR");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
_vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size)
|
|
{
|
|
const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj);
|
|
char buf2[sizeof (_nm_utils_to_string_buffer)];
|
|
char *b;
|
|
gsize l;
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (obj);
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
g_snprintf (buf, buf_size, "%p", obj);
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
|
|
g_snprintf (buf, buf_size,
|
|
"[%s,%p,%d,%ccache,%calive,%cvisible; %s]",
|
|
klass->obj_type_name, obj, obj->_ref_count,
|
|
obj->is_cached ? '+' : '-',
|
|
nmp_object_is_alive (obj) ? '+' : '-',
|
|
nmp_object_is_visible (obj) ? '+' : '-',
|
|
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2)));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size);
|
|
|
|
b = buf;
|
|
l = strlen (b);
|
|
b += l;
|
|
buf_size -= l;
|
|
|
|
if (obj->_lnk_vlan.n_ingress_qos_map) {
|
|
nm_platform_vlan_qos_mapping_to_string (" ingress-qos-map",
|
|
obj->_lnk_vlan.ingress_qos_map,
|
|
obj->_lnk_vlan.n_ingress_qos_map,
|
|
b,
|
|
buf_size);
|
|
l = strlen (b);
|
|
b += l;
|
|
buf_size -= l;
|
|
}
|
|
if (obj->_lnk_vlan.n_egress_qos_map) {
|
|
nm_platform_vlan_qos_mapping_to_string (" egress-qos-map",
|
|
obj->_lnk_vlan.egress_qos_map,
|
|
obj->_lnk_vlan.n_egress_qos_map,
|
|
b,
|
|
buf_size);
|
|
l = strlen (b);
|
|
b += l;
|
|
buf_size -= l;
|
|
}
|
|
|
|
return buf;
|
|
default:
|
|
g_return_val_if_reached ("ERROR");
|
|
}
|
|
}
|
|
|
|
#define _vt_cmd_plobj_to_string_id(type, plat_type, ...) \
|
|
static const char * \
|
|
_vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsize buf_len) \
|
|
{ \
|
|
plat_type *const obj = (plat_type *) _obj; \
|
|
char buf1[NM_UTILS_INET_ADDRSTRLEN]; \
|
|
char buf2[NM_UTILS_INET_ADDRSTRLEN]; \
|
|
\
|
|
(void) buf1; \
|
|
(void) buf2; \
|
|
g_snprintf (buf, buf_len, \
|
|
__VA_ARGS__); \
|
|
return buf; \
|
|
}
|
|
_vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex);
|
|
_vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen,
|
|
obj->peer_address != obj->address ? "," : "",
|
|
obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : "");
|
|
_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1));
|
|
_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric);
|
|
_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric);
|
|
|
|
int
|
|
nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2)
|
|
{
|
|
const NMPClass *klass1, *klass2;
|
|
|
|
if (obj1 == obj2)
|
|
return 0;
|
|
if (!obj1)
|
|
return -1;
|
|
if (!obj2)
|
|
return 1;
|
|
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), -1);
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), 1);
|
|
|
|
klass1 = NMP_OBJECT_GET_CLASS (obj1);
|
|
klass2 = NMP_OBJECT_GET_CLASS (obj2);
|
|
|
|
if (klass1 != klass2)
|
|
return klass1->obj_type < klass2->obj_type ? -1 : 1;
|
|
|
|
if (klass1->cmd_obj_cmp)
|
|
return klass1->cmd_obj_cmp (obj1, obj2);
|
|
return klass1->cmd_plobj_cmp (&obj1->object, &obj2->object);
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2)
|
|
{
|
|
int i;
|
|
|
|
i = nm_platform_link_cmp (&obj1->link, &obj2->link);
|
|
if (i)
|
|
return i;
|
|
if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink)
|
|
return obj1->_link.netlink.is_in_netlink ? -1 : 1;
|
|
i = nmp_object_cmp (obj1->_link.netlink.lnk, obj2->_link.netlink.lnk);
|
|
if (i)
|
|
return i;
|
|
if (obj1->_link.udev.device != obj2->_link.udev.device) {
|
|
if (!obj1->_link.udev.device)
|
|
return -1;
|
|
if (!obj2->_link.udev.device)
|
|
return 1;
|
|
|
|
/* Only compare based on pointer values. That is ugly because it's not a
|
|
* stable sort order, but probably udev gives us always the same GUdevDevice
|
|
* instance.
|
|
*
|
|
* Have this check as very last. */
|
|
return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2)
|
|
{
|
|
int c;
|
|
|
|
c = nm_platform_lnk_vlan_cmp (&obj1->lnk_vlan, &obj2->lnk_vlan);
|
|
if (c)
|
|
return c;
|
|
|
|
if (obj1->_lnk_vlan.n_ingress_qos_map != obj2->_lnk_vlan.n_ingress_qos_map)
|
|
return obj1->_lnk_vlan.n_ingress_qos_map < obj2->_lnk_vlan.n_ingress_qos_map ? -1 : 1;
|
|
if (obj1->_lnk_vlan.n_egress_qos_map != obj2->_lnk_vlan.n_egress_qos_map)
|
|
return obj1->_lnk_vlan.n_egress_qos_map < obj2->_lnk_vlan.n_egress_qos_map ? -1 : 1;
|
|
|
|
c = _vlan_xgress_qos_mappings_cmp (obj1->_lnk_vlan.n_ingress_qos_map, obj1->_lnk_vlan.ingress_qos_map, obj2->_lnk_vlan.ingress_qos_map);
|
|
if (c)
|
|
return c;
|
|
c = _vlan_xgress_qos_mappings_cmp (obj1->_lnk_vlan.n_egress_qos_map, obj1->_lnk_vlan.egress_qos_map, obj2->_lnk_vlan.egress_qos_map);
|
|
|
|
return c;
|
|
}
|
|
|
|
gboolean
|
|
nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2)
|
|
{
|
|
return nmp_object_cmp (obj1, obj2) == 0;
|
|
}
|
|
|
|
/* @src is a const object, which is not entirely correct for link types, where
|
|
* we increase the ref count for src->_link.udev.device.
|
|
* Hence, nmp_object_copy() can violate the const promise of @src.
|
|
* */
|
|
void
|
|
nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only)
|
|
{
|
|
g_return_if_fail (NMP_OBJECT_IS_VALID (dst));
|
|
g_return_if_fail (NMP_OBJECT_IS_VALID (src));
|
|
g_return_if_fail (!NMP_OBJECT_IS_STACKINIT (dst));
|
|
|
|
if (src != dst) {
|
|
const NMPClass *klass = NMP_OBJECT_GET_CLASS (dst);
|
|
|
|
g_return_if_fail (klass == NMP_OBJECT_GET_CLASS (src));
|
|
|
|
if (id_only) {
|
|
if (klass->cmd_plobj_id_copy)
|
|
klass->cmd_plobj_id_copy (&dst->object, &src->object);
|
|
} else if (klass->cmd_obj_copy)
|
|
klass->cmd_obj_copy (dst, src);
|
|
else
|
|
memcpy (&dst->object, &src->object, klass->sizeof_data);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src)
|
|
{
|
|
if (dst->_link.udev.device != src->_link.udev.device) {
|
|
if (src->_link.udev.device)
|
|
g_object_ref (src->_link.udev.device);
|
|
if (dst->_link.udev.device)
|
|
g_object_unref (dst->_link.udev.device);
|
|
}
|
|
if (dst->_link.netlink.lnk != src->_link.netlink.lnk) {
|
|
if (src->_link.netlink.lnk)
|
|
nmp_object_ref (src->_link.netlink.lnk);
|
|
if (dst->_link.netlink.lnk)
|
|
nmp_object_unref (dst->_link.netlink.lnk);
|
|
}
|
|
dst->_link = src->_link;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src)
|
|
{
|
|
dst->lnk_vlan = src->lnk_vlan;
|
|
_vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_ingress_qos_map,
|
|
&dst->_lnk_vlan.ingress_qos_map,
|
|
src->_lnk_vlan.n_ingress_qos_map,
|
|
src->_lnk_vlan.ingress_qos_map);
|
|
_vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_egress_qos_map,
|
|
&dst->_lnk_vlan.egress_qos_map,
|
|
src->_lnk_vlan.n_egress_qos_map,
|
|
src->_lnk_vlan.egress_qos_map);
|
|
}
|
|
|
|
#define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \
|
|
static void \
|
|
_vt_cmd_plobj_id_copy_##type (NMPlatformObject *_dst, const NMPlatformObject *_src) \
|
|
{ \
|
|
plat_type *const dst = (plat_type *) _dst; \
|
|
const plat_type *const src = (const plat_type *) _src; \
|
|
{ cmd } \
|
|
}
|
|
_vt_cmd_plobj_id_copy (link, NMPlatformLink, {
|
|
dst->ifindex = src->ifindex;
|
|
});
|
|
_vt_cmd_plobj_id_copy (ip4_address, NMPlatformIP4Address, {
|
|
dst->ifindex = src->ifindex;
|
|
dst->plen = src->plen;
|
|
dst->address = src->address;
|
|
dst->peer_address = src->peer_address;
|
|
});
|
|
_vt_cmd_plobj_id_copy (ip6_address, NMPlatformIP6Address, {
|
|
dst->ifindex = src->ifindex;
|
|
dst->address = src->address;
|
|
});
|
|
_vt_cmd_plobj_id_copy (ip4_route, NMPlatformIP4Route, {
|
|
dst->ifindex = src->ifindex;
|
|
dst->plen = src->plen;
|
|
dst->metric = src->metric;
|
|
dst->network = src->network;
|
|
});
|
|
_vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, {
|
|
dst->ifindex = src->ifindex;
|
|
dst->plen = src->plen;
|
|
dst->metric = src->metric;
|
|
dst->network = src->network;
|
|
});
|
|
|
|
/* Uses internally nmp_object_copy(), hence it also violates the const
|
|
* promise for @obj.
|
|
* */
|
|
NMPObject *
|
|
nmp_object_clone (const NMPObject *obj, gboolean id_only)
|
|
{
|
|
NMPObject *dst;
|
|
|
|
if (!obj)
|
|
return NULL;
|
|
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL);
|
|
|
|
dst = _nmp_object_new_from_class (NMP_OBJECT_GET_CLASS (obj));
|
|
nmp_object_copy (dst, obj, id_only);
|
|
return dst;
|
|
}
|
|
|
|
gboolean
|
|
nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2)
|
|
{
|
|
const NMPClass *klass;
|
|
|
|
if (obj1 == obj2)
|
|
return TRUE;
|
|
if (!obj1 || !obj2)
|
|
return FALSE;
|
|
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE);
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE);
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (obj1);
|
|
return klass == NMP_OBJECT_GET_CLASS (obj2)
|
|
&& klass->cmd_plobj_id_equal
|
|
&& klass->cmd_plobj_id_equal (&obj1->object, &obj2->object);
|
|
}
|
|
|
|
#define _vt_cmd_plobj_id_equal(type, plat_type, cmd) \
|
|
static gboolean \
|
|
_vt_cmd_plobj_id_equal_##type (const NMPlatformObject *_obj1, const NMPlatformObject *_obj2) \
|
|
{ \
|
|
const plat_type *const obj1 = (const plat_type *) _obj1; \
|
|
const plat_type *const obj2 = (const plat_type *) _obj2; \
|
|
return (cmd); \
|
|
}
|
|
_vt_cmd_plobj_id_equal (link, NMPlatformLink,
|
|
obj1->ifindex == obj2->ifindex);
|
|
_vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address,
|
|
obj1->ifindex == obj2->ifindex
|
|
&& obj1->plen == obj2->plen
|
|
&& obj1->address == obj2->address
|
|
/* for IPv4 addresses, you can add the same local address with differing peer-adddress
|
|
* (IFA_ADDRESS), provided that their net-part differs. */
|
|
&& ((obj1->peer_address ^ obj2->peer_address) & nm_utils_ip4_prefix_to_netmask (obj1->plen)) == 0);
|
|
_vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address,
|
|
obj1->ifindex == obj2->ifindex
|
|
/* for IPv6 addresses, the prefix length is not part of the primary identifier. */
|
|
&& IN6_ARE_ADDR_EQUAL (&obj1->address, &obj2->address));
|
|
_vt_cmd_plobj_id_equal (ip4_route, NMPlatformIP4Route,
|
|
obj1->ifindex == obj2->ifindex
|
|
&& obj1->plen == obj2->plen
|
|
&& obj1->metric == obj2->metric
|
|
&& obj1->network == obj2->network);
|
|
_vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route,
|
|
obj1->ifindex == obj2->ifindex
|
|
&& obj1->plen == obj2->plen
|
|
&& obj1->metric == obj2->metric
|
|
&& IN6_ARE_ADDR_EQUAL( &obj1->network, &obj2->network));
|
|
|
|
guint
|
|
nmp_object_id_hash (const NMPObject *obj)
|
|
{
|
|
const NMPClass *klass;
|
|
|
|
if (!obj)
|
|
return 0;
|
|
|
|
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0);
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (obj);
|
|
|
|
if (klass->cmd_plobj_id_hash)
|
|
return klass->cmd_plobj_id_hash (&obj->object);
|
|
|
|
/* unhashable objects implement pointer equality. */
|
|
return g_direct_hash (obj);
|
|
}
|
|
|
|
#define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \
|
|
static guint \
|
|
_vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \
|
|
{ \
|
|
const plat_type *const obj = (const plat_type *) _obj; \
|
|
guint hash; \
|
|
{ cmd; } \
|
|
return hash; \
|
|
}
|
|
_vt_cmd_plobj_id_hash (link, NMPlatformLink, {
|
|
hash = (guint) 3982791431u;
|
|
hash = hash + ((guint) obj->ifindex);
|
|
})
|
|
_vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, {
|
|
hash = (guint) 3591309853u;
|
|
hash = hash + ((guint) obj->ifindex);
|
|
hash = hash * 33 + ((guint) obj->plen);
|
|
hash = hash * 33 + ((guint) obj->address);
|
|
|
|
/* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */
|
|
hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen)));
|
|
})
|
|
_vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, {
|
|
hash = (guint) 2907861637u;
|
|
hash = hash + ((guint) obj->ifindex);
|
|
/* for IPv6 addresses, the prefix length is not part of the primary identifier. */
|
|
hash = hash * 33 + _id_hash_ip6_addr (&obj->address);
|
|
})
|
|
_vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, {
|
|
hash = (guint) 2569857221u;
|
|
hash = hash + ((guint) obj->ifindex);
|
|
hash = hash * 33 + ((guint) obj->plen);
|
|
hash = hash * 33 + ((guint) obj->metric);
|
|
hash = hash * 33 + ((guint) obj->network);
|
|
})
|
|
_vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, {
|
|
hash = (guint) 3999787007u;
|
|
hash = hash + ((guint) obj->ifindex);
|
|
hash = hash * 33 + ((guint) obj->plen);
|
|
hash = hash * 33 + ((guint) obj->metric);
|
|
hash = hash * 33 + _id_hash_ip6_addr (&obj->network);
|
|
})
|
|
|
|
gboolean
|
|
nmp_object_is_alive (const NMPObject *obj)
|
|
{
|
|
const NMPClass *klass;
|
|
|
|
/* for convenience, allow NULL. */
|
|
if (!obj)
|
|
return FALSE;
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (obj);
|
|
return !klass->cmd_obj_is_alive
|
|
|| klass->cmd_obj_is_alive (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);
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_ipx_address (const NMPObject *obj)
|
|
{
|
|
return obj->object.ifindex > 0;
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj)
|
|
{
|
|
/* We want to ignore routes that are RTM_F_CLONED but we still
|
|
* let nmp_object_from_nl() create such route objects, instead of
|
|
* returning NULL right away.
|
|
*
|
|
* The idea is, that if we have the same route (according to its id)
|
|
* in the cache with !RTM_F_CLONED, an update that changes the route
|
|
* to be RTM_F_CLONED must remove the instance.
|
|
*
|
|
* If nmp_object_from_nl() would just return NULL, we couldn't look
|
|
* into the cache to see if it contains a route that now disappears
|
|
* (because it changed to be cloned).
|
|
*
|
|
* Instead we create a dead object, and nmp_cache_update_netlink()
|
|
* will remove the old version of the update.
|
|
**/
|
|
return obj->object.ifindex > 0 && !obj->ip_route.rt_cloned;
|
|
}
|
|
|
|
gboolean
|
|
nmp_object_is_visible (const NMPObject *obj)
|
|
{
|
|
const NMPClass *klass;
|
|
|
|
/* for convenience, allow NULL. */
|
|
if (!obj)
|
|
return FALSE;
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (obj);
|
|
|
|
/* a dead object is never visible. */
|
|
if ( klass->cmd_obj_is_alive
|
|
&& !klass->cmd_obj_is_alive (obj))
|
|
return FALSE;
|
|
|
|
return !klass->cmd_obj_is_visible
|
|
|| klass->cmd_obj_is_visible (obj);
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_visible_link (const NMPObject *obj)
|
|
{
|
|
return obj->_link.netlink.is_in_netlink
|
|
&& obj->link.name[0];
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
#define _STRUCT_SIZE(struct_type, field) \
|
|
(G_STRUCT_OFFSET (struct_type, field) + sizeof (((struct_type *) NULL)->field))
|
|
|
|
_NM_UTILS_LOOKUP_DEFINE (static, _nmp_cache_id_size_by_type, NMPCacheIdType, guint,
|
|
NM_UTILS_LOOKUP_DEFAULT (({ nm_assert_not_reached (); sizeof (NMPCacheId); })),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE, _STRUCT_SIZE (NMPCacheId, object_type)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, _STRUCT_SIZE (NMPCacheId, object_type)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, _STRUCT_SIZE (NMPCacheId, object_type)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, _STRUCT_SIZE (NMPCacheId, object_type)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, _STRUCT_SIZE (NMPCacheId, object_type_by_ifindex)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, _STRUCT_SIZE (NMPCacheId, object_type_by_ifindex)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, _STRUCT_SIZE (NMPCacheId, object_type_by_ifindex)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, _STRUCT_SIZE (NMPCacheId, link_by_ifname)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4, _STRUCT_SIZE (NMPCacheId, routes_by_destination_ip4)),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6, _STRUCT_SIZE (NMPCacheId, routes_by_destination_ip6)),
|
|
NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_CACHE_ID_TYPE_NONE),
|
|
NM_UTILS_LOOKUP_ITEM_IGNORE (__NMP_CACHE_ID_TYPE_MAX),
|
|
);
|
|
|
|
gboolean
|
|
nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b)
|
|
{
|
|
if (a->_id_type != b->_id_type)
|
|
return FALSE;
|
|
return memcmp (a, b, _nmp_cache_id_size_by_type (a->_id_type)) == 0;
|
|
}
|
|
|
|
guint
|
|
nmp_cache_id_hash (const NMPCacheId *id)
|
|
{
|
|
guint hash = 5381;
|
|
guint i, n;
|
|
|
|
/* for hashing we only iterate over the actually set bytes and skip the
|
|
* zero padding at the end (which depends on the type of the id).
|
|
*
|
|
* For the equal implementation, we don't care about that and compare the
|
|
* entire NMPCacheId sized struct. */
|
|
n = _nmp_cache_id_size_by_type (id->_id_type);
|
|
for (i = 0; i < n; i++)
|
|
hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */
|
|
return hash;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_clone (const NMPCacheId *id)
|
|
{
|
|
NMPCacheId *id2;
|
|
guint n;
|
|
|
|
n = _nmp_cache_id_size_by_type (id->_id_type);
|
|
id2 = g_slice_alloc (n);
|
|
memcpy (id2, id, n);
|
|
return id2;
|
|
}
|
|
|
|
void
|
|
nmp_cache_id_destroy (NMPCacheId *id)
|
|
{
|
|
guint n;
|
|
|
|
n = _nmp_cache_id_size_by_type (id->_id_type);
|
|
g_slice_free1 (n, id);
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
NMPCacheId _nmp_cache_id_static;
|
|
|
|
static void
|
|
_nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type)
|
|
{
|
|
memset (id, 0, sizeof (NMPCacheId));
|
|
id->_id_type = id_type;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_copy (NMPCacheId *id, const NMPCacheId *src)
|
|
{
|
|
guint n;
|
|
|
|
memset (id, 0, sizeof (NMPCacheId));
|
|
n = _nmp_cache_id_size_by_type (src->_id_type);
|
|
memcpy (id, src, n);
|
|
return id;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only)
|
|
{
|
|
_nmp_cache_id_init (id, visible_only
|
|
? NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY
|
|
: NMP_CACHE_ID_TYPE_OBJECT_TYPE);
|
|
id->object_type.obj_type = obj_type;
|
|
return id;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id,
|
|
NMPObjectType obj_type,
|
|
int ifindex)
|
|
{
|
|
g_return_val_if_fail (NM_IN_SET (obj_type,
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP6_ROUTE), NULL);
|
|
|
|
if (ifindex <= 0)
|
|
return nmp_cache_id_init_object_type (id, obj_type, TRUE);
|
|
|
|
_nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX);
|
|
id->object_type_by_ifindex.obj_type = obj_type;
|
|
memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int));
|
|
return id;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_init_routes_visible (NMPCacheId *id,
|
|
NMPObjectType obj_type,
|
|
gboolean with_default,
|
|
gboolean with_non_default,
|
|
int ifindex)
|
|
{
|
|
g_return_val_if_fail (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE), NULL);
|
|
|
|
if (with_default && with_non_default) {
|
|
if (ifindex <= 0)
|
|
return nmp_cache_id_init_object_type (id, obj_type, TRUE);
|
|
return nmp_cache_id_init_addrroute_visible_by_ifindex (id, obj_type, ifindex);
|
|
}
|
|
|
|
if (with_default)
|
|
_nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT);
|
|
else if (with_non_default)
|
|
_nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT);
|
|
else
|
|
g_return_val_if_reached (NULL);
|
|
|
|
id->object_type_by_ifindex.obj_type = obj_type;
|
|
memcpy (&id->object_type_by_ifindex._misaligned_ifindex, &ifindex, sizeof (int));
|
|
return id;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_init_link_by_ifname (NMPCacheId *id,
|
|
const char *ifname)
|
|
{
|
|
gsize l;
|
|
|
|
_nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME);
|
|
|
|
if ( !ifname
|
|
|| (l = strlen (ifname)) > sizeof (id->link_by_ifname.ifname_short))
|
|
g_return_val_if_reached (id);
|
|
|
|
/* the trailing NUL is dropped!! */
|
|
memcpy (id->link_by_ifname.ifname_short, ifname, l);
|
|
|
|
return id;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_init_routes_by_destination_ip4 (NMPCacheId *id,
|
|
guint32 network,
|
|
guint8 plen,
|
|
guint32 metric)
|
|
{
|
|
_nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4);
|
|
id->routes_by_destination_ip4.plen = plen;
|
|
memcpy (&id->routes_by_destination_ip4._misaligned_metric, &metric, sizeof (guint32));
|
|
memcpy (&id->routes_by_destination_ip4._misaligned_network, &network, sizeof (guint32));
|
|
return id;
|
|
}
|
|
|
|
NMPCacheId *
|
|
nmp_cache_id_init_routes_by_destination_ip6 (NMPCacheId *id,
|
|
const struct in6_addr *network,
|
|
guint8 plen,
|
|
guint32 metric)
|
|
{
|
|
_nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6);
|
|
id->routes_by_destination_ip4.plen = plen;
|
|
memcpy (&id->routes_by_destination_ip6._misaligned_metric, &metric, sizeof (guint32));
|
|
if (network)
|
|
memcpy (&id->routes_by_destination_ip6._misaligned_network, network, sizeof (struct in6_addr));
|
|
return id;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
static gboolean
|
|
_nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id)
|
|
{
|
|
const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj);
|
|
|
|
switch (id_type) {
|
|
case NMP_CACHE_ID_TYPE_OBJECT_TYPE:
|
|
*out_id = nmp_cache_id_init_object_type (id, klass->obj_type, FALSE);
|
|
return TRUE;
|
|
case NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY:
|
|
if (nmp_object_is_visible (obj))
|
|
*out_id = nmp_cache_id_init_object_type (id, klass->obj_type, TRUE);
|
|
else
|
|
*out_id = NULL;
|
|
return TRUE;
|
|
default:
|
|
return klass->cmd_obj_init_cache_id
|
|
&& klass->cmd_obj_init_cache_id (obj, id_type, id, out_id);
|
|
}
|
|
}
|
|
|
|
static const guint8 _supported_cache_ids_link[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY,
|
|
NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
|
|
0,
|
|
};
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id)
|
|
{
|
|
switch (id_type) {
|
|
case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME:
|
|
if (obj->link.name[0]) {
|
|
*out_id = nmp_cache_id_init_link_by_ifname (id, obj->link.name);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
*out_id = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
static const guint8 _supported_cache_ids_ipx_address[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY,
|
|
NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX,
|
|
0,
|
|
};
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id)
|
|
{
|
|
switch (id_type) {
|
|
case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX:
|
|
if (nmp_object_is_visible (obj)) {
|
|
nm_assert (obj->object.ifindex > 0);
|
|
*out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
*out_id = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
static const guint8 _supported_cache_ids_ip4_route[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY,
|
|
NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4,
|
|
0,
|
|
};
|
|
|
|
static const guint8 _supported_cache_ids_ip6_route[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY,
|
|
NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT,
|
|
NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6,
|
|
0,
|
|
};
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id)
|
|
{
|
|
switch (id_type) {
|
|
case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX:
|
|
if (nmp_object_is_visible (obj)) {
|
|
nm_assert (obj->object.ifindex > 0);
|
|
*out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT:
|
|
if ( nmp_object_is_visible (obj)
|
|
&& !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) {
|
|
nm_assert (obj->object.ifindex > 0);
|
|
*out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT:
|
|
if ( nmp_object_is_visible (obj)
|
|
&& NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) {
|
|
nm_assert (obj->object.ifindex > 0);
|
|
*out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT:
|
|
if ( nmp_object_is_visible (obj)
|
|
&& !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) {
|
|
nm_assert (obj->object.ifindex > 0);
|
|
*out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT:
|
|
if ( nmp_object_is_visible (obj)
|
|
&& NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) {
|
|
nm_assert (obj->object.ifindex > 0);
|
|
*out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP4:
|
|
if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) {
|
|
*out_id = nmp_cache_id_init_routes_by_destination_ip4 (id, obj->ip4_route.network, obj->ip_route.plen, obj->ip_route.metric);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION_IP6:
|
|
if (NMP_OBJECT_GET_CLASS (obj)->obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) {
|
|
*out_id = nmp_cache_id_init_routes_by_destination_ip6 (id, &obj->ip6_route.network, obj->ip_route.plen, obj->ip_route.metric);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
*out_id = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
gboolean
|
|
nmp_cache_use_udev_get (const NMPCache *cache)
|
|
{
|
|
g_return_val_if_fail (cache, TRUE);
|
|
|
|
return cache->use_udev;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
/**
|
|
* nmp_cache_link_connected_needs_toggle:
|
|
* @cache: the platform cache
|
|
* @master: the link object, that is checked whether its connected property
|
|
* needs to be toggled.
|
|
* @potential_slave: (allow-none): an additional link object that is treated
|
|
* as if it was inside @cache. If given, it shaddows a link in the cache
|
|
* with the same ifindex.
|
|
* @ignore_slave: (allow-none): if set, the check will pretend that @ignore_slave
|
|
* is not in the cache.
|
|
*
|
|
* NMPlatformLink has two connected flags: (master->link.flags&IFF_LOWER_UP) (as reported
|
|
* from netlink) and master->link.connected. For bond and bridge master, kernel reports
|
|
* those links as IFF_LOWER_UP if they have no slaves attached. We want to present instead
|
|
* a combined @connected flag that shows masters without slaves as down.
|
|
*
|
|
* Check if the connected flag of @master should be toggled according to the content
|
|
* of @cache (including @potential_slave).
|
|
*
|
|
* Returns: %TRUE, if @master->link.connected should be flipped/toggled.
|
|
**/
|
|
gboolean
|
|
nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave)
|
|
{
|
|
const NMPlatformLink *const *links;
|
|
gboolean is_lower_up = FALSE;
|
|
guint len, i;
|
|
|
|
if ( !master
|
|
|| NMP_OBJECT_GET_TYPE (master) != NMP_OBJECT_TYPE_LINK
|
|
|| master->link.ifindex <= 0
|
|
|| !nmp_object_is_visible (master)
|
|
|| !NM_IN_SET (master->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND))
|
|
return FALSE;
|
|
|
|
/* if native IFF_LOWER_UP is down, link.connected must also be down
|
|
* regardless of the slaves. */
|
|
if (!NM_FLAGS_HAS (master->link.n_ifi_flags, IFF_LOWER_UP))
|
|
return !!master->link.connected;
|
|
|
|
if (potential_slave && NMP_OBJECT_GET_TYPE (potential_slave) != NMP_OBJECT_TYPE_LINK)
|
|
potential_slave = NULL;
|
|
|
|
if ( potential_slave
|
|
&& nmp_object_is_visible (potential_slave)
|
|
&& potential_slave->link.ifindex > 0
|
|
&& potential_slave->link.master == master->link.ifindex
|
|
&& potential_slave->link.connected) {
|
|
is_lower_up = TRUE;
|
|
} else {
|
|
links = (const NMPlatformLink *const *) nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, NMP_OBJECT_TYPE_LINK, FALSE), &len);
|
|
for (i = 0; i < len; i++) {
|
|
const NMPlatformLink *link = links[i];
|
|
const NMPObject *obj = NMP_OBJECT_UP_CAST ((NMPlatformObject *) link);
|
|
|
|
nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST ((NMPlatformObject *) link)) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
if ( (!potential_slave || potential_slave->link.ifindex != link->ifindex)
|
|
&& ignore_slave != obj
|
|
&& link->ifindex > 0
|
|
&& link->master == master->link.ifindex
|
|
&& nmp_object_is_visible (obj)
|
|
&& link->connected) {
|
|
is_lower_up = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return !!master->link.connected != is_lower_up;
|
|
}
|
|
|
|
/**
|
|
* nmp_cache_link_connected_needs_toggle_by_ifindex:
|
|
* @cache:
|
|
* @master_ifindex: the ifindex of a potential master that should be checked
|
|
* whether it needs toggling.
|
|
* @potential_slave: (allow-none): passed to nmp_cache_link_connected_needs_toggle().
|
|
* It considers @potential_slave as being inside the cache, replacing an existing
|
|
* link with the same ifindex.
|
|
* @ignore_slave: (allow-onne): passed to nmp_cache_link_connected_needs_toggle().
|
|
*
|
|
* The flag obj->link.connected depends on the state of other links in the
|
|
* @cache. See also nmp_cache_link_connected_needs_toggle(). Given an ifindex
|
|
* of a master, check if the cache contains such a master link that needs
|
|
* toogling of the connected flag.
|
|
*
|
|
* Returns: NULL if there is no master link with ifindex @master_ifindex that should be toggled.
|
|
* Otherwise, return the link object from inside the cache with the given ifindex.
|
|
* The connected flag of that master should be toggled.
|
|
*/
|
|
const NMPObject *
|
|
nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave)
|
|
{
|
|
const NMPObject *master;
|
|
|
|
if (master_ifindex > 0) {
|
|
master = nmp_cache_lookup_link (cache, master_ifindex);
|
|
if (nmp_cache_link_connected_needs_toggle (cache, master, potential_slave, ignore_slave))
|
|
return master;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
const NMPlatformObject *const *
|
|
nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len)
|
|
{
|
|
return (const NMPlatformObject *const *) nm_multi_index_lookup (cache->idx_multi,
|
|
(const NMMultiIndexId *) cache_id,
|
|
out_len);
|
|
}
|
|
|
|
GArray *
|
|
nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id)
|
|
{
|
|
const NMPClass *klass = nmp_class_from_type (obj_type);
|
|
guint len, i;
|
|
const NMPlatformObject *const *objects;
|
|
GArray *array;
|
|
|
|
g_return_val_if_fail (klass, NULL);
|
|
|
|
objects = nmp_cache_lookup_multi (cache, cache_id, &len);
|
|
array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[i])) == klass);
|
|
g_array_append_vals (array, objects[i], 1);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj)
|
|
{
|
|
g_return_val_if_fail (obj, NULL);
|
|
|
|
return g_hash_table_lookup (cache->idx_main, obj);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_cache_lookup_link (const NMPCache *cache, int ifindex)
|
|
{
|
|
NMPObject obj_needle;
|
|
|
|
return nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex));
|
|
}
|
|
|
|
/**
|
|
* nmp_cache_find_other_route_for_same_destination:
|
|
* @cache:
|
|
* @route:
|
|
*
|
|
* Look into the cache whether there is a route to the same destination,
|
|
* in terms of network/plen,metric.
|
|
*
|
|
* Returns: (transfer none): the first found route object from the cache
|
|
* that has the same (network/plen,metric) values as @route, but has different
|
|
* ID. Or %NULL, if no such route exists.
|
|
*/
|
|
const NMPObject *
|
|
nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route)
|
|
{
|
|
NMPCacheId cache_id;
|
|
const NMPlatformObject *const *list;
|
|
|
|
nm_assert (cache);
|
|
|
|
switch (NMP_OBJECT_GET_TYPE (route)) {
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
nmp_cache_id_init_routes_by_destination_ip4 (&cache_id, route->ip4_route.network, route->ip_route.plen, route->ip_route.metric);
|
|
break;
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
nmp_cache_id_init_routes_by_destination_ip6 (&cache_id, &route->ip6_route.network, route->ip_route.plen, route->ip_route.metric);
|
|
break;
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
list = nmp_cache_lookup_multi (cache, &cache_id, NULL);
|
|
if (list) {
|
|
for (; *list; list++) {
|
|
const NMPObject *candidate = NMP_OBJECT_UP_CAST (*list);
|
|
|
|
nm_assert (NMP_OBJECT_GET_CLASS (route) == NMP_OBJECT_GET_CLASS (candidate));
|
|
|
|
if (!nmp_object_id_equal (route, candidate))
|
|
return candidate;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
NMPObject obj_needle;
|
|
const NMPObject *obj;
|
|
const NMPlatformObject *const *list;
|
|
guint i, len;
|
|
NMPCacheId cache_id, *p_cache_id;
|
|
|
|
if (ifindex > 0) {
|
|
obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex));
|
|
|
|
if ( !obj
|
|
|| (visible_only && !nmp_object_is_visible (obj))
|
|
|| (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type)
|
|
|| (ifname && strcmp (obj->link.name, ifname))
|
|
|| (match_fn && !match_fn (obj, user_data)))
|
|
return NULL;
|
|
return obj;
|
|
} else if (!ifname && !match_fn)
|
|
return NULL;
|
|
else {
|
|
if ( ifname
|
|
&& strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) {
|
|
p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname);
|
|
ifname = NULL;
|
|
} else
|
|
p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only);
|
|
|
|
list = nmp_cache_lookup_multi (cache, p_cache_id, &len);
|
|
for (i = 0; i < len; i++) {
|
|
obj = NMP_OBJECT_UP_CAST (list[i]);
|
|
|
|
if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type)
|
|
continue;
|
|
if (ifname && strcmp (ifname, obj->link.name))
|
|
continue;
|
|
if (match_fn && !match_fn (obj, user_data))
|
|
continue;
|
|
|
|
return obj;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
GHashTable *
|
|
nmp_cache_lookup_all_to_hash (const NMPCache *cache,
|
|
NMPCacheId *cache_id,
|
|
GHashTable *hash)
|
|
{
|
|
NMMultiIndexIdIter iter;
|
|
gpointer plobj;
|
|
|
|
nm_multi_index_id_iter_init (&iter, cache->idx_multi, (const NMMultiIndexId *) cache_id);
|
|
|
|
if (nm_multi_index_id_iter_next (&iter, &plobj)) {
|
|
if (!hash)
|
|
hash = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL);
|
|
|
|
do {
|
|
g_hash_table_add (hash, nmp_object_ref (NMP_OBJECT_UP_CAST (plobj)));
|
|
} while (nm_multi_index_id_iter_next (&iter, &plobj));
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
static void
|
|
_nmp_cache_update_cache (NMPCache *cache, NMPObject *obj, gboolean remove)
|
|
{
|
|
const guint8 *id_type;
|
|
|
|
for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) {
|
|
NMPCacheId cache_id_storage;
|
|
const NMPCacheId *cache_id;
|
|
|
|
if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id))
|
|
continue;
|
|
if (!cache_id)
|
|
continue;
|
|
|
|
/* We don't put @obj itself into the multi index, but &obj->object. As of now, all
|
|
* users expect a pointer to NMPlatformObject, not NMPObject.
|
|
* You can use NMP_OBJECT_UP_CAST() to retrieve the original @obj pointer.
|
|
*
|
|
* If need be, we could determine based on @id_type which pointer we want to store. */
|
|
|
|
if (remove) {
|
|
if (!nm_multi_index_remove (cache->idx_multi, &cache_id->base, &obj->object))
|
|
g_assert_not_reached ();
|
|
} else {
|
|
if (!nm_multi_index_add (cache->idx_multi, &cache_id->base, &obj->object))
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_nmp_cache_update_add (NMPCache *cache, NMPObject *obj)
|
|
{
|
|
nm_assert (!obj->is_cached);
|
|
nmp_object_ref (obj);
|
|
nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object));
|
|
if (!nm_g_hash_table_add (cache->idx_main, obj))
|
|
g_assert_not_reached ();
|
|
obj->is_cached = TRUE;
|
|
_nmp_cache_update_cache (cache, obj, FALSE);
|
|
}
|
|
|
|
static void
|
|
_nmp_cache_update_remove (NMPCache *cache, NMPObject *obj)
|
|
{
|
|
nm_assert (obj->is_cached);
|
|
_nmp_cache_update_cache (cache, obj, TRUE);
|
|
obj->is_cached = FALSE;
|
|
if (!g_hash_table_remove (cache->idx_main, obj))
|
|
g_assert_not_reached ();
|
|
|
|
/* @obj is possibly a dangling pointer at this point. No problem, multi-index doesn't dereference. */
|
|
nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object));
|
|
}
|
|
|
|
static void
|
|
_nmp_cache_update_update (NMPCache *cache, NMPObject *obj, const NMPObject *new)
|
|
{
|
|
const guint8 *id_type;
|
|
|
|
nm_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (new));
|
|
nm_assert (obj->is_cached);
|
|
nm_assert (!new->is_cached);
|
|
|
|
for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) {
|
|
NMPCacheId cache_id_storage_obj, cache_id_storage_new;
|
|
const NMPCacheId *cache_id_obj, *cache_id_new;
|
|
|
|
if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage_obj, &cache_id_obj))
|
|
continue;
|
|
if (!_nmp_object_init_cache_id (new, *id_type, &cache_id_storage_new, &cache_id_new))
|
|
g_assert_not_reached ();
|
|
if (!nm_multi_index_move (cache->idx_multi, (NMMultiIndexId *) cache_id_obj, (NMMultiIndexId *) cache_id_new, &obj->object))
|
|
g_assert_not_reached ();
|
|
}
|
|
nmp_object_copy (obj, new, FALSE);
|
|
}
|
|
|
|
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)
|
|
{
|
|
NMPObject *old;
|
|
|
|
nm_assert (NMP_OBJECT_IS_VALID (obj));
|
|
|
|
old = g_hash_table_lookup (cache->idx_main, obj);
|
|
if (!old) {
|
|
if (out_obj)
|
|
*out_obj = NULL;
|
|
if (out_was_visible)
|
|
*out_was_visible = FALSE;
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (old);
|
|
if (out_was_visible)
|
|
*out_was_visible = nmp_object_is_visible (old);
|
|
if (equals_by_ptr && old != obj) {
|
|
/* We found an identical object, but we only delete it if it's the same pointer as
|
|
* @obj. */
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
if (pre_hook)
|
|
pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data);
|
|
_nmp_cache_update_remove (cache, old);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data)
|
|
{
|
|
if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) {
|
|
NMPObject *old;
|
|
nm_auto_nmpobj NMPObject *obj = NULL;
|
|
|
|
/* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be
|
|
* removed from netlink. Link objects are alive without being in netlink when they
|
|
* have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink
|
|
* flag. */
|
|
|
|
old = (NMPObject *) nmp_cache_lookup_link (cache, obj_needle->link.ifindex);
|
|
if (!old) {
|
|
if (out_obj)
|
|
*out_obj = NULL;
|
|
if (out_was_visible)
|
|
*out_was_visible = FALSE;
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (old);
|
|
if (out_was_visible)
|
|
*out_was_visible = nmp_object_is_visible (old);
|
|
|
|
if (!old->_link.netlink.is_in_netlink) {
|
|
nm_assert (old->_link.udev.device);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
if (!old->_link.udev.device) {
|
|
/* the update would make @old invalid. Remove it. */
|
|
if (pre_hook)
|
|
pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data);
|
|
_nmp_cache_update_remove (cache, old);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
obj = nmp_object_clone (old, FALSE);
|
|
obj->_link.netlink.is_in_netlink = FALSE;
|
|
|
|
_nmp_object_fixup_link_master_connected (obj, cache);
|
|
_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
|
|
|
|
if (pre_hook)
|
|
pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data);
|
|
_nmp_cache_update_update (cache, old, obj);
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
} else
|
|
return nmp_cache_remove (cache, obj_needle, FALSE, out_obj, out_was_visible, pre_hook, user_data);
|
|
}
|
|
|
|
/**
|
|
* nmp_cache_update_netlink:
|
|
* @cache: the platform cache
|
|
* @obj: a #NMPObject instance as received from netlink and created via
|
|
* nmp_object_from_nl(). Especially for link, it must not have the udev
|
|
* replated fields set.
|
|
* This instance will be modified and might be put into the cache. When
|
|
* calling nmp_cache_update_netlink() you hand @obj over to the cache.
|
|
* Except, that the cache will increment the ref count as appropriate. You
|
|
* must still unref the obj to release your part of the ownership.
|
|
* @out_obj: (allow-none): (out): return the object instance that is inside
|
|
* the cache. If you specify non %NULL, you must always unref the returned
|
|
* instance. If the return value indicates that the object was removed,
|
|
* the object is no longer in the cache. Even if the return value indicates
|
|
* that the object was unchanged, it will still return @out_obj -- if
|
|
* such an object is in the cache.
|
|
* @out_was_visible: (allow-none): (out): whether the object was visible before
|
|
* the update operation.
|
|
* @pre_hook: (allow-none): a callback *before* the object gets updated. You cannot
|
|
* influence the outcome and must not do anything beyong inspecting the changes.
|
|
* @user_data:
|
|
*
|
|
* Returns: how the cache changed.
|
|
**/
|
|
NMPCacheOpsType
|
|
nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data)
|
|
{
|
|
NMPObject *old;
|
|
|
|
nm_assert (NMP_OBJECT_IS_VALID (obj));
|
|
nm_assert (!NMP_OBJECT_IS_STACKINIT (obj));
|
|
nm_assert (!obj->is_cached);
|
|
|
|
/* A link object from netlink must have the udev related fields unset.
|
|
* We could implement to handle that, but there is no need to support such
|
|
* a use-case */
|
|
nm_assert (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_LINK ||
|
|
( !obj->_link.udev.device
|
|
&& !obj->link.driver));
|
|
|
|
old = g_hash_table_lookup (cache->idx_main, obj);
|
|
|
|
if (out_obj)
|
|
*out_obj = NULL;
|
|
if (out_was_visible)
|
|
*out_was_visible = FALSE;
|
|
|
|
if (!old) {
|
|
if (!nmp_object_is_alive (obj))
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
|
|
if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) {
|
|
_nmp_object_fixup_link_master_connected (obj, cache);
|
|
_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
|
|
}
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (obj);
|
|
|
|
if (pre_hook)
|
|
pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data);
|
|
_nmp_cache_update_add (cache, obj);
|
|
return NMP_CACHE_OPS_ADDED;
|
|
} else if (old == obj) {
|
|
/* updating a cached object inplace is not supported because the object contributes to hash-key
|
|
* for NMMultiIndex. Modifying an object that is inside NMMultiIndex means that these
|
|
* keys change.
|
|
* The problem is, that for a given object NMMultiIndex does not support (efficient)
|
|
* reverse lookup to get all the NMPCacheIds to which it belongs. If that would be implemented,
|
|
* it would be possible to implement inplace-update.
|
|
*
|
|
* There is an un-optimized reverse lookup via nm_multi_index_iter_init(), but we don't want
|
|
* that because we might have a large number of indexes to search.
|
|
*
|
|
* We could add efficient reverse lookup by adding a reverse index to NMMultiIndex. But that
|
|
* also adds some cost to support an (uncommon?) usage pattern.
|
|
*
|
|
* Instead we just don't support it, instead we expect the user to
|
|
* create a new instance from netlink.
|
|
*
|
|
* TL;DR: a cached object must never be modified.
|
|
*/
|
|
g_assert_not_reached ();
|
|
} else {
|
|
gboolean is_alive = FALSE;
|
|
|
|
nm_assert (old->is_cached);
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (old);
|
|
if (out_was_visible)
|
|
*out_was_visible = nmp_object_is_visible (old);
|
|
|
|
if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) {
|
|
if (!obj->_link.netlink.is_in_netlink) {
|
|
if (!old->_link.netlink.is_in_netlink) {
|
|
nm_assert (old->_link.udev.device);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
if (old->_link.udev.device) {
|
|
/* @obj is not in netlink.
|
|
*
|
|
* This is similar to nmp_cache_remove_netlink(), but there we preserve the
|
|
* preexisting netlink properties. The use case of that is when kernel_get_object()
|
|
* cannot load an object (based on the id of a needle).
|
|
*
|
|
* Here we keep the data provided from @obj. The usecase is when receiving
|
|
* a valid @obj instance from netlink with RTM_DELROUTE.
|
|
*/
|
|
is_alive = TRUE;
|
|
}
|
|
} else
|
|
is_alive = TRUE;
|
|
|
|
if (is_alive) {
|
|
_nmp_object_fixup_link_master_connected (obj, cache);
|
|
|
|
/* Merge the netlink parts with what we have from udev. */
|
|
g_clear_object (&obj->_link.udev.device);
|
|
obj->_link.udev.device = old->_link.udev.device ? g_object_ref (old->_link.udev.device) : NULL;
|
|
_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
|
|
}
|
|
} else
|
|
is_alive = nmp_object_is_alive (obj);
|
|
|
|
if (!is_alive) {
|
|
/* the update would make @old invalid. Remove it. */
|
|
if (pre_hook)
|
|
pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data);
|
|
_nmp_cache_update_remove (cache, old);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
if (nmp_object_equal (old, obj))
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
|
|
if (pre_hook)
|
|
pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data);
|
|
_nmp_cache_update_update (cache, old, obj);
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
NMPObject *old;
|
|
nm_auto_nmpobj NMPObject *obj = NULL;
|
|
|
|
old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex);
|
|
|
|
if (out_obj)
|
|
*out_obj = NULL;
|
|
if (out_was_visible)
|
|
*out_was_visible = FALSE;
|
|
|
|
if (!old) {
|
|
if (!udev_device)
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
|
|
obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL);
|
|
obj->link.ifindex = ifindex;
|
|
obj->_link.udev.device = g_object_ref (udev_device);
|
|
|
|
_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
|
|
|
|
nm_assert (nmp_object_is_alive (obj));
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (obj);
|
|
|
|
if (pre_hook)
|
|
pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data);
|
|
_nmp_cache_update_add (cache, obj);
|
|
return NMP_CACHE_OPS_ADDED;
|
|
} else {
|
|
nm_assert (old->is_cached);
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (old);
|
|
if (out_was_visible)
|
|
*out_was_visible = nmp_object_is_visible (old);
|
|
|
|
if (old->_link.udev.device == udev_device)
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
|
|
if (!udev_device && !old->_link.netlink.is_in_netlink) {
|
|
/* the update would make @old invalid. Remove it. */
|
|
if (pre_hook)
|
|
pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data);
|
|
_nmp_cache_update_remove (cache, old);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
obj = nmp_object_clone (old, FALSE);
|
|
|
|
g_clear_object (&obj->_link.udev.device);
|
|
obj->_link.udev.device = udev_device ? g_object_ref (udev_device) : NULL;
|
|
|
|
_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
|
|
|
|
nm_assert (nmp_object_is_alive (obj));
|
|
|
|
if (pre_hook)
|
|
pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data);
|
|
_nmp_cache_update_update (cache, old, obj);
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data)
|
|
{
|
|
NMPObject *old;
|
|
nm_auto_nmpobj NMPObject *obj = NULL;
|
|
|
|
old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex);
|
|
|
|
if (!old) {
|
|
if (out_obj)
|
|
*out_obj = NULL;
|
|
if (out_was_visible)
|
|
*out_was_visible = FALSE;
|
|
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
nm_assert (old->is_cached);
|
|
|
|
if (out_obj)
|
|
*out_obj = nmp_object_ref (old);
|
|
if (out_was_visible)
|
|
*out_was_visible = nmp_object_is_visible (old);
|
|
|
|
if (!nmp_cache_link_connected_needs_toggle (cache, old, NULL, NULL))
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
|
|
obj = nmp_object_clone (old, FALSE);
|
|
obj->link.connected = !old->link.connected;
|
|
|
|
nm_assert (nmp_object_is_alive (obj));
|
|
|
|
if (pre_hook)
|
|
pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data);
|
|
_nmp_cache_update_update (cache, old, obj);
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
NMPCache *
|
|
nmp_cache_new (gboolean use_udev)
|
|
{
|
|
NMPCache *cache = g_new (NMPCache, 1);
|
|
|
|
cache->idx_main = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash,
|
|
(GEqualFunc) nmp_object_id_equal,
|
|
(GDestroyNotify) nmp_object_unref,
|
|
NULL);
|
|
cache->idx_multi = nm_multi_index_new ((NMMultiIndexFuncHash) nmp_cache_id_hash,
|
|
(NMMultiIndexFuncEqual) nmp_cache_id_equal,
|
|
(NMMultiIndexFuncClone) nmp_cache_id_clone,
|
|
(NMMultiIndexFuncDestroy) nmp_cache_id_destroy);
|
|
cache->use_udev = !!use_udev;
|
|
return cache;
|
|
}
|
|
|
|
void
|
|
nmp_cache_free (NMPCache *cache)
|
|
{
|
|
GHashTableIter iter;
|
|
NMPObject *obj;
|
|
|
|
/* No need to cumbersomely remove the objects properly. They are not hooked up
|
|
* in a complicated way, we can just unref them together with cache->idx_main.
|
|
*
|
|
* But we must clear the @is_cached flag. */
|
|
g_hash_table_iter_init (&iter, cache->idx_main);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) {
|
|
nm_assert (obj->is_cached);
|
|
obj->is_cached = FALSE;
|
|
}
|
|
|
|
nm_multi_index_free (cache->idx_multi);
|
|
g_hash_table_unref (cache->idx_main);
|
|
|
|
g_free (cache);
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
void
|
|
ASSERT_nmp_cache_is_consistent (const NMPCache *cache)
|
|
{
|
|
#if NM_MORE_ASSERTS
|
|
NMMultiIndexIter iter_multi;
|
|
GHashTableIter iter_hash;
|
|
guint i, len;
|
|
NMPCacheId cache_id_storage;
|
|
const NMPCacheId *cache_id, *cache_id2;
|
|
const NMPlatformObject *const *objects;
|
|
const NMPObject *obj;
|
|
|
|
g_assert (cache);
|
|
|
|
g_hash_table_iter_init (&iter_hash, cache->idx_main);
|
|
while (g_hash_table_iter_next (&iter_hash, (gpointer *) &obj, NULL)) {
|
|
const guint8 *id_type;
|
|
|
|
g_assert (NMP_OBJECT_IS_VALID (obj));
|
|
g_assert (nmp_object_is_alive (obj));
|
|
|
|
for (id_type = NMP_OBJECT_GET_CLASS (obj)->supported_cache_ids; *id_type; id_type++) {
|
|
if (!_nmp_object_init_cache_id (obj, *id_type, &cache_id_storage, &cache_id))
|
|
continue;
|
|
if (!cache_id)
|
|
continue;
|
|
g_assert (nm_multi_index_contains (cache->idx_multi, &cache_id->base, &obj->object));
|
|
}
|
|
}
|
|
|
|
nm_multi_index_iter_init (&iter_multi, cache->idx_multi, NULL);
|
|
while (nm_multi_index_iter_next (&iter_multi,
|
|
(const NMMultiIndexId **) &cache_id,
|
|
(void *const**) &objects,
|
|
&len)) {
|
|
g_assert (len > 0 && objects && objects[len] == NULL);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
g_assert (objects[i]);
|
|
obj = NMP_OBJECT_UP_CAST (objects[i]);
|
|
g_assert (NMP_OBJECT_IS_VALID (obj));
|
|
|
|
/* for now, enforce that all objects for a certain index are of the same type. */
|
|
g_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[0])));
|
|
|
|
if (!_nmp_object_init_cache_id (obj, cache_id->_id_type, &cache_id_storage, &cache_id2))
|
|
g_assert_not_reached ();
|
|
g_assert (cache_id2);
|
|
g_assert (nmp_cache_id_equal (cache_id, cache_id2));
|
|
g_assert_cmpint (nmp_cache_id_hash (cache_id), ==, nmp_cache_id_hash (cache_id2));
|
|
|
|
g_assert (obj == g_hash_table_lookup (cache->idx_main, obj));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
/******************************************************************/
|
|
|
|
const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
|
|
[NMP_OBJECT_TYPE_LINK - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LINK,
|
|
.sizeof_data = sizeof (NMPObjectLink),
|
|
.sizeof_public = sizeof (NMPlatformLink),
|
|
.obj_type_name = "link",
|
|
.addr_family = AF_UNSPEC,
|
|
.rtm_gettype = RTM_GETLINK,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK,
|
|
.signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_link,
|
|
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link,
|
|
.cmd_obj_cmp = _vt_cmd_obj_cmp_link,
|
|
.cmd_obj_copy = _vt_cmd_obj_copy_link,
|
|
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link,
|
|
.cmd_obj_dispose = _vt_cmd_obj_dispose_link,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_link,
|
|
.cmd_obj_is_visible = _vt_cmd_obj_is_visible_link,
|
|
.cmd_obj_to_string = _vt_cmd_obj_to_string_link,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link,
|
|
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link,
|
|
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_link_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
.sizeof_data = sizeof (NMPObjectIP4Address),
|
|
.sizeof_public = sizeof (NMPlatformIP4Address),
|
|
.obj_type_name = "ip4-address",
|
|
.addr_family = AF_INET,
|
|
.rtm_gettype = RTM_GETADDR,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS,
|
|
.signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_ipx_address,
|
|
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address,
|
|
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address,
|
|
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_address,
|
|
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_address_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
.sizeof_data = sizeof (NMPObjectIP6Address),
|
|
.sizeof_public = sizeof (NMPlatformIP6Address),
|
|
.obj_type_name = "ip6-address",
|
|
.addr_family = AF_INET6,
|
|
.rtm_gettype = RTM_GETADDR,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
|
|
.signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_ipx_address,
|
|
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address,
|
|
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address,
|
|
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_address,
|
|
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_address_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp
|
|
},
|
|
[NMP_OBJECT_TYPE_IP4_ROUTE - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
.sizeof_data = sizeof (NMPObjectIP4Route),
|
|
.sizeof_public = sizeof (NMPlatformIP4Route),
|
|
.obj_type_name = "ip4-route",
|
|
.addr_family = AF_INET,
|
|
.rtm_gettype = RTM_GETROUTE,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,
|
|
.signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_ip4_route,
|
|
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route,
|
|
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route,
|
|
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_route,
|
|
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP6_ROUTE - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_IP6_ROUTE,
|
|
.sizeof_data = sizeof (NMPObjectIP6Route),
|
|
.sizeof_public = sizeof (NMPlatformIP6Route),
|
|
.obj_type_name = "ip6-route",
|
|
.addr_family = AF_INET6,
|
|
.rtm_gettype = RTM_GETROUTE,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,
|
|
.signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_ip6_route,
|
|
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route,
|
|
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route,
|
|
.cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_route,
|
|
.cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_GRE - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_GRE,
|
|
.sizeof_data = sizeof (NMPObjectLnkGre),
|
|
.sizeof_public = sizeof (NMPlatformLnkGre),
|
|
.obj_type_name = "gre",
|
|
.lnk_link_type = NM_LINK_TYPE_GRE,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND,
|
|
.sizeof_data = sizeof (NMPObjectLnkInfiniband),
|
|
.sizeof_public = sizeof (NMPlatformLnkInfiniband),
|
|
.obj_type_name = "infiniband",
|
|
.lnk_link_type = NM_LINK_TYPE_INFINIBAND,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_infiniband_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_infiniband_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IP6TNL - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_IP6TNL,
|
|
.sizeof_data = sizeof (NMPObjectLnkIp6Tnl),
|
|
.sizeof_public = sizeof (NMPlatformLnkIp6Tnl),
|
|
.obj_type_name = "ip6tnl",
|
|
.lnk_link_type = NM_LINK_TYPE_IP6TNL,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IPIP - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_IPIP,
|
|
.sizeof_data = sizeof (NMPObjectLnkIpIp),
|
|
.sizeof_public = sizeof (NMPlatformLnkIpIp),
|
|
.obj_type_name = "ipip",
|
|
.lnk_link_type = NM_LINK_TYPE_IPIP,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ipip_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ipip_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN,
|
|
.sizeof_data = sizeof (NMPObjectLnkMacvlan),
|
|
.sizeof_public = sizeof (NMPlatformLnkMacvlan),
|
|
.obj_type_name = "macvlan",
|
|
.lnk_link_type = NM_LINK_TYPE_MACVLAN,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP,
|
|
.sizeof_data = sizeof (NMPObjectLnkMacvtap),
|
|
.sizeof_public = sizeof (NMPlatformLnkMacvtap),
|
|
.obj_type_name = "macvtap",
|
|
.lnk_link_type = NM_LINK_TYPE_MACVTAP,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_SIT - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_SIT,
|
|
.sizeof_data = sizeof (NMPObjectLnkSit),
|
|
.sizeof_public = sizeof (NMPlatformLnkSit),
|
|
.obj_type_name = "sit",
|
|
.lnk_link_type = NM_LINK_TYPE_SIT,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_sit_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VLAN - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_VLAN,
|
|
.sizeof_data = sizeof (NMPObjectLnkVlan),
|
|
.sizeof_public = sizeof (NMPlatformLnkVlan),
|
|
.obj_type_name = "vlan",
|
|
.lnk_link_type = NM_LINK_TYPE_VLAN,
|
|
.cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan,
|
|
.cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan,
|
|
.cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan,
|
|
.cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vlan_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VXLAN - 1] = {
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_VXLAN,
|
|
.sizeof_data = sizeof (NMPObjectLnkVxlan),
|
|
.sizeof_public = sizeof (NMPlatformLnkVxlan),
|
|
.obj_type_name = "vxlan",
|
|
.lnk_link_type = NM_LINK_TYPE_VXLAN,
|
|
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vxlan_to_string,
|
|
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vxlan_cmp,
|
|
},
|
|
};
|
|
|