mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-24 06:40:16 +01:00
The function should modify the "ip6_address" member of the union. In practice, it doesn't matter because the ifindex is the first member of both "ip4_address" and "ip6_address".
3745 lines
144 KiB
C
3745 lines
144 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2015 - 2018 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
|
|
|
#include "nmp-object.h"
|
|
|
|
#include <unistd.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/if.h>
|
|
#include <libudev.h>
|
|
|
|
#include "libnm-glib-aux/nm-secret-utils.h"
|
|
#include "libnm-platform/nm-platform-utils.h"
|
|
#include "libnm-platform/wifi/nm-wifi-utils.h"
|
|
#include "libnm-platform/wpan/nm-wpan-utils.h"
|
|
#include "libnm-platform/nmp-ethtool-ioctl.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, \
|
|
NULL, \
|
|
NULL, \
|
|
"nmp-object[" NM_HASH_OBFUSCATE_PTR_FMT "" \
|
|
"/%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
NM_HASH_OBFUSCATE_PTR(__obj), \
|
|
(__obj ? NMP_OBJECT_GET_CLASS(__obj)->obj_type_name \
|
|
: "???") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
|
} \
|
|
} \
|
|
G_STMT_END
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
NMDedupMultiIdxType parent;
|
|
NMPCacheIdType cache_id_type;
|
|
} DedupMultiIdxType;
|
|
|
|
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.
|
|
*/
|
|
|
|
NMDedupMultiIndex *multi_idx;
|
|
|
|
/* an idx_type entry for each NMP_CACHE_ID_TYPE. Note that NONE (zero)
|
|
* is skipped, so the index is shifted by one: idx_type[cache_id_type - 1].
|
|
*
|
|
* Don't bother, use _idx_type_get() instead! */
|
|
DedupMultiIdxType idx_types[NMP_CACHE_ID_TYPE_MAX];
|
|
|
|
gboolean use_udev;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
nm_sock_addr_union_cmp(const NMSockAddrUnion *a, const NMSockAddrUnion *b)
|
|
{
|
|
nm_assert(!a || NM_IN_SET(a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6));
|
|
nm_assert(!b || NM_IN_SET(b->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6));
|
|
|
|
NM_CMP_SELF(a, b);
|
|
|
|
NM_CMP_FIELD(a, b, sa.sa_family);
|
|
switch (a->sa.sa_family) {
|
|
case AF_INET:
|
|
NM_CMP_DIRECT(ntohl(a->in.sin_addr.s_addr), ntohl(b->in.sin_addr.s_addr));
|
|
NM_CMP_DIRECT(htons(a->in.sin_port), htons(b->in.sin_port));
|
|
break;
|
|
case AF_INET6:
|
|
NM_CMP_DIRECT_IN6ADDR(&a->in6.sin6_addr, &b->in6.sin6_addr);
|
|
NM_CMP_DIRECT(htons(a->in6.sin6_port), htons(b->in6.sin6_port));
|
|
NM_CMP_FIELD(a, b, in6.sin6_scope_id);
|
|
NM_CMP_FIELD(a, b, in6.sin6_flowinfo);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
nm_sock_addr_union_hash_update(const NMSockAddrUnion *a, NMHashState *h)
|
|
{
|
|
if (!a) {
|
|
nm_hash_update_val(h, 1241364739u);
|
|
return;
|
|
}
|
|
|
|
nm_assert(NM_IN_SET(a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6));
|
|
|
|
switch (a->sa.sa_family) {
|
|
case AF_INET:
|
|
nm_hash_update_vals(h, a->in.sin_family, a->in.sin_addr.s_addr, a->in.sin_port);
|
|
return;
|
|
case AF_INET6:
|
|
nm_hash_update_vals(h,
|
|
a->in6.sin6_family,
|
|
a->in6.sin6_addr,
|
|
a->in6.sin6_port,
|
|
a->in6.sin6_scope_id,
|
|
a->in6.sin6_flowinfo);
|
|
return;
|
|
default:
|
|
nm_hash_update_val(h, a->sa.sa_family);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nm_sock_addr_union_cpy:
|
|
* @dst: the destination #NMSockAddrUnion. It will always be fully initialized,
|
|
* to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of
|
|
* error).
|
|
* @src: (nullable): the source buffer with an sockaddr to copy. It may be unaligned in
|
|
* memory. If not %NULL, the buffer must be at least large enough to contain
|
|
* sa.sa_family, and then, depending on sa.sa_family, it must be large enough
|
|
* to hold struct sockaddr_in or struct sockaddr_in6.
|
|
*
|
|
* @dst will always be fully initialized (including setting all un-used bytes to zero).
|
|
*/
|
|
void
|
|
nm_sock_addr_union_cpy(NMSockAddrUnion *dst,
|
|
gconstpointer src /* unaligned (const NMSockAddrUnion *) */)
|
|
{
|
|
struct sockaddr sa;
|
|
gsize src_len;
|
|
|
|
nm_assert(dst);
|
|
|
|
*dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC;
|
|
|
|
if (!src)
|
|
return;
|
|
|
|
memcpy(&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof(sa.sa_family));
|
|
|
|
if (sa.sa_family == AF_INET)
|
|
src_len = sizeof(struct sockaddr_in);
|
|
else if (sa.sa_family == AF_INET6)
|
|
src_len = sizeof(struct sockaddr_in6);
|
|
else
|
|
return;
|
|
|
|
memcpy(dst, src, src_len);
|
|
nm_assert(dst->sa.sa_family == sa.sa_family);
|
|
}
|
|
|
|
/**
|
|
* nm_sock_addr_union_cpy_untrusted:
|
|
* @dst: the destination #NMSockAddrUnion. It will always be fully initialized,
|
|
* to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of
|
|
* error).
|
|
* @src: the source buffer with an sockaddr to copy. It may be unaligned in
|
|
* memory.
|
|
* @src_len: the length of @src in bytes.
|
|
*
|
|
* The function requires @src_len to be either sizeof(struct sockaddr_in) or sizeof (struct sockaddr_in6).
|
|
* If that's the case, then @src will be interpreted as such structure (unaligned), and
|
|
* accessed. It will check sa.sa_family to match the expected sizes, and if it does, the
|
|
* struct will be copied.
|
|
*
|
|
* On any failure, @dst will be set to sa.sa_family AF_UNSPEC.
|
|
* @dst will always be fully initialized (including setting all un-used bytes to zero).
|
|
*/
|
|
void
|
|
nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst,
|
|
gconstpointer src /* unaligned (const NMSockAddrUnion *) */,
|
|
gsize src_len)
|
|
{
|
|
int f_expected;
|
|
struct sockaddr sa;
|
|
|
|
nm_assert(dst);
|
|
|
|
*dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC;
|
|
|
|
if (src_len == sizeof(struct sockaddr_in))
|
|
f_expected = AF_INET;
|
|
else if (src_len == sizeof(struct sockaddr_in6))
|
|
f_expected = AF_INET6;
|
|
else
|
|
return;
|
|
|
|
memcpy(&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof(sa.sa_family));
|
|
|
|
if (sa.sa_family != f_expected)
|
|
return;
|
|
|
|
memcpy(dst, src, src_len);
|
|
nm_assert(dst->sa.sa_family == sa.sa_family);
|
|
}
|
|
|
|
const char *
|
|
nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len)
|
|
{
|
|
char s_addr[NM_INET_ADDRSTRLEN];
|
|
char s_scope_id[40];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null(sa, &buf, &len))
|
|
return buf;
|
|
|
|
/* maybe we should use getnameinfo(), but here implement it ourself.
|
|
*
|
|
* We want to see the actual bytes for debugging (as we understand them),
|
|
* and now what getnameinfo() makes of it. Also, it's simpler this way. */
|
|
|
|
switch (sa->sa.sa_family) {
|
|
case AF_INET:
|
|
g_snprintf(buf,
|
|
len,
|
|
"%s:%u",
|
|
nm_inet4_ntop(sa->in.sin_addr.s_addr, s_addr),
|
|
(guint) htons(sa->in.sin_port));
|
|
break;
|
|
case AF_INET6:
|
|
g_snprintf(buf,
|
|
len,
|
|
"[%s%s]:%u",
|
|
nm_inet6_ntop(&sa->in6.sin6_addr, s_addr),
|
|
(sa->in6.sin6_scope_id != 0
|
|
? nm_sprintf_buf(s_scope_id, "%u", sa->in6.sin6_scope_id)
|
|
: ""),
|
|
(guint) htons(sa->in6.sin6_port));
|
|
break;
|
|
case AF_UNSPEC:
|
|
g_snprintf(buf, len, "unspec");
|
|
break;
|
|
default:
|
|
g_snprintf(buf, len, "{addr-family:%u}", (unsigned) sa->sa.sa_family);
|
|
break;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nmp_object_link_udev_device_get_property_value(const NMPObject *obj, const char *key)
|
|
{
|
|
nm_assert(key);
|
|
|
|
if (!obj)
|
|
return nm_assert_unreachable_val(NULL);
|
|
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
if (!obj->_link.udev.device)
|
|
return NULL;
|
|
|
|
return udev_device_get_property_value(obj->_link.udev.device, key);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class;
|
|
|
|
static void
|
|
_idx_obj_id_hash_update(const NMDedupMultiIdxType *idx_type,
|
|
const NMDedupMultiObj *obj,
|
|
NMHashState *h)
|
|
{
|
|
const NMPObject *o = (NMPObject *) obj;
|
|
|
|
nm_assert(idx_type && idx_type->klass == &_dedup_multi_idx_type_class);
|
|
nm_assert(NMP_OBJECT_GET_TYPE(o) != NMP_OBJECT_TYPE_UNKNOWN);
|
|
|
|
nmp_object_id_hash_update(o, h);
|
|
}
|
|
|
|
static gboolean
|
|
_idx_obj_id_equal(const NMDedupMultiIdxType *idx_type,
|
|
const NMDedupMultiObj *obj_a,
|
|
const NMDedupMultiObj *obj_b)
|
|
{
|
|
const NMPObject *o_a = (NMPObject *) obj_a;
|
|
const NMPObject *o_b = (NMPObject *) obj_b;
|
|
|
|
nm_assert(idx_type && idx_type->klass == &_dedup_multi_idx_type_class);
|
|
nm_assert(NMP_OBJECT_GET_TYPE(o_a) != NMP_OBJECT_TYPE_UNKNOWN);
|
|
nm_assert(NMP_OBJECT_GET_TYPE(o_b) != NMP_OBJECT_TYPE_UNKNOWN);
|
|
|
|
return nmp_object_id_equal(o_a, o_b);
|
|
}
|
|
|
|
static guint
|
|
_idx_obj_part(const DedupMultiIdxType *idx_type,
|
|
const NMPObject *obj_a,
|
|
const NMPObject *obj_b,
|
|
NMHashState *h)
|
|
{
|
|
NMPObjectType obj_type;
|
|
|
|
/* the hash/equals functions are strongly related. So, keep them
|
|
* side-by-side and do it all in _idx_obj_part(). */
|
|
|
|
nm_assert(idx_type);
|
|
nm_assert(idx_type->parent.klass == &_dedup_multi_idx_type_class);
|
|
nm_assert(obj_a);
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj_a) != NMP_OBJECT_TYPE_UNKNOWN);
|
|
nm_assert(!obj_b || (NMP_OBJECT_GET_TYPE(obj_b) != NMP_OBJECT_TYPE_UNKNOWN));
|
|
nm_assert(!h || !obj_b);
|
|
|
|
switch (idx_type->cache_id_type) {
|
|
case NMP_CACHE_ID_TYPE_OBJECT_TYPE:
|
|
if (obj_b)
|
|
return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b);
|
|
if (h) {
|
|
nm_hash_update_vals(h, idx_type->cache_id_type, NMP_OBJECT_GET_TYPE(obj_a));
|
|
}
|
|
return 1;
|
|
|
|
case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME:
|
|
if (NMP_OBJECT_GET_TYPE(obj_a) != NMP_OBJECT_TYPE_LINK) {
|
|
/* first check, whether obj_a is suitable for this idx_type.
|
|
* If not, return 0 (which is correct for partitionable(), hash() and equal()
|
|
* functions. */
|
|
if (h)
|
|
nm_hash_update_val(h, obj_a);
|
|
return 0;
|
|
}
|
|
if (obj_b) {
|
|
/* we are in equal() mode. Compare obj_b with obj_a. */
|
|
return NMP_OBJECT_GET_TYPE(obj_b) == NMP_OBJECT_TYPE_LINK
|
|
&& nm_streq(obj_a->link.name, obj_b->link.name);
|
|
}
|
|
if (h) {
|
|
nm_hash_update_val(h, idx_type->cache_id_type);
|
|
nm_hash_update_strarr(h, obj_a->link.name);
|
|
}
|
|
/* just return 1, to indicate that obj_a is partitionable by this idx_type. */
|
|
return 1;
|
|
|
|
case NMP_CACHE_ID_TYPE_DEFAULT_ROUTES:
|
|
if (!NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a),
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE)
|
|
|| !NM_PLATFORM_IP_ROUTE_IS_DEFAULT(&obj_a->ip_route)
|
|
|| !nmp_object_is_visible(obj_a)) {
|
|
if (h)
|
|
nm_hash_update_val(h, obj_a);
|
|
return 0;
|
|
}
|
|
if (obj_b) {
|
|
return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b)
|
|
&& NM_PLATFORM_IP_ROUTE_IS_DEFAULT(&obj_b->ip_route)
|
|
&& nmp_object_is_visible(obj_b);
|
|
}
|
|
if (h) {
|
|
nm_hash_update_vals(h, idx_type->cache_id_type, NMP_OBJECT_GET_TYPE(obj_a));
|
|
}
|
|
return 1;
|
|
|
|
case NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX:
|
|
if (!NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a),
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE,
|
|
NMP_OBJECT_TYPE_QDISC,
|
|
NMP_OBJECT_TYPE_TFILTER,
|
|
NMP_OBJECT_TYPE_MPTCP_ADDR)
|
|
|| !nmp_object_is_visible(obj_a)) {
|
|
if (h)
|
|
nm_hash_update_val(h, obj_a);
|
|
return 0;
|
|
}
|
|
nm_assert(NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex > 0
|
|
|| (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex == 0
|
|
&& NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a),
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE)));
|
|
if (obj_b) {
|
|
return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b)
|
|
&& NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex
|
|
== NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_b)->ifindex
|
|
&& nmp_object_is_visible(obj_b);
|
|
}
|
|
if (h) {
|
|
nm_hash_update_vals(h, idx_type->cache_id_type, obj_a->obj_with_ifindex.ifindex);
|
|
}
|
|
return 1;
|
|
|
|
case NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID:
|
|
obj_type = NMP_OBJECT_GET_TYPE(obj_a);
|
|
if (!NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)
|
|
|| NMP_OBJECT_CAST_IP_ROUTE(obj_a)->ifindex < 0) {
|
|
if (h)
|
|
nm_hash_update_val(h, obj_a);
|
|
return 0;
|
|
}
|
|
if (obj_b) {
|
|
return obj_type == NMP_OBJECT_GET_TYPE(obj_b)
|
|
&& NMP_OBJECT_CAST_IP_ROUTE(obj_b)->ifindex >= 0
|
|
&& (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE
|
|
? (nm_platform_ip4_route_cmp(&obj_a->ip4_route,
|
|
&obj_b->ip4_route,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID)
|
|
== 0)
|
|
: (nm_platform_ip6_route_cmp(&obj_a->ip6_route,
|
|
&obj_b->ip6_route,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID)
|
|
== 0));
|
|
}
|
|
if (h) {
|
|
nm_hash_update_val(h, idx_type->cache_id_type);
|
|
if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE)
|
|
nm_platform_ip4_route_hash_update(&obj_a->ip4_route,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID,
|
|
h);
|
|
else
|
|
nm_platform_ip6_route_hash_update(&obj_a->ip6_route,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID,
|
|
h);
|
|
}
|
|
return 1;
|
|
|
|
case NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY:
|
|
obj_type = NMP_OBJECT_GET_TYPE(obj_a);
|
|
/* currently, only routing rules are supported for this cache-id-type. */
|
|
if (obj_type != NMP_OBJECT_TYPE_ROUTING_RULE
|
|
|| !NM_IN_SET(obj_a->routing_rule.addr_family, AF_INET, AF_INET6)) {
|
|
if (h)
|
|
nm_hash_update_val(h, obj_a);
|
|
return 0;
|
|
}
|
|
if (obj_b) {
|
|
return NMP_OBJECT_GET_TYPE(obj_b) == NMP_OBJECT_TYPE_ROUTING_RULE
|
|
&& obj_a->routing_rule.addr_family == obj_b->routing_rule.addr_family;
|
|
}
|
|
if (h) {
|
|
nm_hash_update_vals(h, idx_type->cache_id_type, obj_a->routing_rule.addr_family);
|
|
}
|
|
return 1;
|
|
|
|
case NMP_CACHE_ID_TYPE_NONE:
|
|
case __NMP_CACHE_ID_TYPE_MAX:
|
|
break;
|
|
}
|
|
nm_assert_not_reached();
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
_idx_obj_partitionable(const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj)
|
|
{
|
|
return _idx_obj_part((DedupMultiIdxType *) idx_type, (NMPObject *) obj, NULL, NULL) != 0;
|
|
}
|
|
|
|
static void
|
|
_idx_obj_partition_hash_update(const NMDedupMultiIdxType *idx_type,
|
|
const NMDedupMultiObj *obj,
|
|
NMHashState *h)
|
|
{
|
|
_idx_obj_part((DedupMultiIdxType *) idx_type, (NMPObject *) obj, NULL, h);
|
|
}
|
|
|
|
static gboolean
|
|
_idx_obj_partition_equal(const NMDedupMultiIdxType *idx_type,
|
|
const NMDedupMultiObj *obj_a,
|
|
const NMDedupMultiObj *obj_b)
|
|
{
|
|
return _idx_obj_part((DedupMultiIdxType *) idx_type,
|
|
(NMPObject *) obj_a,
|
|
(NMPObject *) obj_b,
|
|
NULL);
|
|
}
|
|
|
|
static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = {
|
|
.idx_obj_id_hash_update = _idx_obj_id_hash_update,
|
|
.idx_obj_id_equal = _idx_obj_id_equal,
|
|
.idx_obj_partitionable = _idx_obj_partitionable,
|
|
.idx_obj_partition_hash_update = _idx_obj_partition_hash_update,
|
|
.idx_obj_partition_equal = _idx_obj_partition_equal,
|
|
};
|
|
|
|
static void
|
|
_dedup_multi_idx_type_init(DedupMultiIdxType *idx_type, NMPCacheIdType cache_id_type)
|
|
{
|
|
nm_dedup_multi_idx_type_init((NMDedupMultiIdxType *) idx_type, &_dedup_multi_idx_type_class);
|
|
idx_type->cache_id_type = cache_id_type;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_vlan_xgress_qos_mappings_hash_update(guint n_map, const NMVlanQosMapping *map, NMHashState *h)
|
|
{
|
|
/* ensure no padding. */
|
|
G_STATIC_ASSERT(sizeof(NMVlanQosMapping) == 2 * sizeof(guint32));
|
|
|
|
nm_hash_update_val(h, n_map);
|
|
if (n_map)
|
|
nm_hash_update(h, map, n_map * sizeof(*map));
|
|
}
|
|
|
|
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,
|
|
NMVlanQosMapping **dst_map,
|
|
guint src_n_map,
|
|
const NMVlanQosMapping *src_map)
|
|
{
|
|
if (src_n_map == 0) {
|
|
nm_clear_g_free(dst_map);
|
|
*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) {
|
|
nm_clear_g_free(dst_map);
|
|
*dst_n_map = src_n_map;
|
|
*dst_map = nm_memdup(src_map, sizeof(*src_map) * src_n_map);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_wireguard_allowed_ip_hash_update(const NMPWireGuardAllowedIP *ip, NMHashState *h)
|
|
{
|
|
nm_hash_update_vals(h, ip->family, ip->mask);
|
|
|
|
if (ip->family == AF_INET)
|
|
nm_hash_update_val(h, ip->addr.addr4);
|
|
else if (ip->family == AF_INET6)
|
|
nm_hash_update_val(h, ip->addr.addr6);
|
|
}
|
|
|
|
static int
|
|
_wireguard_allowed_ip_cmp(const NMPWireGuardAllowedIP *a, const NMPWireGuardAllowedIP *b)
|
|
{
|
|
NM_CMP_SELF(a, b);
|
|
|
|
NM_CMP_FIELD(a, b, family);
|
|
NM_CMP_FIELD(a, b, mask);
|
|
|
|
if (a->family == AF_INET)
|
|
NM_CMP_FIELD(a, b, addr.addr4);
|
|
else if (a->family == AF_INET6)
|
|
NM_CMP_FIELD_IN6ADDR(a, b, addr.addr6);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_wireguard_peer_hash_update(const NMPWireGuardPeer *peer, NMHashState *h)
|
|
{
|
|
guint i;
|
|
|
|
nm_hash_update(h, peer->public_key, sizeof(peer->public_key));
|
|
nm_hash_update(h, peer->preshared_key, sizeof(peer->preshared_key));
|
|
nm_hash_update_vals(h,
|
|
peer->persistent_keepalive_interval,
|
|
peer->allowed_ips_len,
|
|
peer->rx_bytes,
|
|
peer->tx_bytes,
|
|
peer->last_handshake_time.tv_sec,
|
|
peer->last_handshake_time.tv_nsec);
|
|
|
|
nm_sock_addr_union_hash_update(&peer->endpoint, h);
|
|
|
|
for (i = 0; i < peer->allowed_ips_len; i++)
|
|
_wireguard_allowed_ip_hash_update(&peer->allowed_ips[i], h);
|
|
}
|
|
|
|
static int
|
|
_wireguard_peer_cmp(const NMPWireGuardPeer *a, const NMPWireGuardPeer *b)
|
|
{
|
|
guint i;
|
|
|
|
NM_CMP_SELF(a, b);
|
|
|
|
NM_CMP_FIELD(a, b, last_handshake_time.tv_sec);
|
|
NM_CMP_FIELD(a, b, last_handshake_time.tv_nsec);
|
|
NM_CMP_FIELD(a, b, rx_bytes);
|
|
NM_CMP_FIELD(a, b, tx_bytes);
|
|
NM_CMP_FIELD(a, b, allowed_ips_len);
|
|
NM_CMP_FIELD(a, b, persistent_keepalive_interval);
|
|
NM_CMP_FIELD(a, b, endpoint.sa.sa_family);
|
|
NM_CMP_FIELD_MEMCMP(a, b, public_key);
|
|
NM_CMP_FIELD_MEMCMP(a, b, preshared_key);
|
|
|
|
NM_CMP_RETURN(nm_sock_addr_union_cmp(&a->endpoint, &b->endpoint));
|
|
|
|
for (i = 0; i < a->allowed_ips_len; i++) {
|
|
NM_CMP_RETURN(_wireguard_allowed_ip_cmp(&a->allowed_ips[i], &b->allowed_ips[i]));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const char *
|
|
_link_get_driver(struct udev_device *udevice, const char *kind, int ifindex)
|
|
{
|
|
const char *driver = NULL;
|
|
|
|
nm_assert(kind == g_intern_string(kind));
|
|
|
|
if (udevice) {
|
|
driver = nmp_utils_udev_get_driver(udevice);
|
|
if (driver)
|
|
return driver;
|
|
}
|
|
|
|
if (kind)
|
|
return kind;
|
|
|
|
if (ifindex > 0) {
|
|
NMPUtilsEthtoolDriverInfo driver_info;
|
|
|
|
if (nmp_ethtool_ioctl_get_driver_info(ifindex, &driver_info)) {
|
|
if (driver_info.driver[0])
|
|
return g_intern_string(driver_info.driver);
|
|
}
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
void
|
|
_nmp_object_fixup_link_udev_fields(NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev)
|
|
{
|
|
const char *driver = NULL;
|
|
gboolean initialized = FALSE;
|
|
NMPObject *obj;
|
|
|
|
nm_assert(obj_orig || *obj_new);
|
|
nm_assert(obj_new);
|
|
nm_assert(!obj_orig || NMP_OBJECT_GET_TYPE(obj_orig) == NMP_OBJECT_TYPE_LINK);
|
|
nm_assert(!*obj_new || NMP_OBJECT_GET_TYPE(*obj_new) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
obj = *obj_new ?: obj_orig;
|
|
|
|
/* The link contains internal fields that are combined by
|
|
* properties from netlink and udev. Update those properties */
|
|
|
|
/* When a link is not in netlink, its 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.ifindex);
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (nm_streq0(obj->link.driver, driver) && obj->link.initialized == initialized)
|
|
return;
|
|
|
|
if (!*obj_new)
|
|
obj = *obj_new = nmp_object_clone(obj, FALSE);
|
|
|
|
obj->link.driver = driver;
|
|
obj->link.initialized = initialized;
|
|
}
|
|
|
|
static void
|
|
_nmp_object_fixup_link_controller_connected(NMPObject **obj_new,
|
|
NMPObject *obj_orig,
|
|
const NMPCache *cache)
|
|
{
|
|
NMPObject *obj;
|
|
|
|
nm_assert(obj_orig || *obj_new);
|
|
nm_assert(obj_new);
|
|
nm_assert(!obj_orig || NMP_OBJECT_GET_TYPE(obj_orig) == NMP_OBJECT_TYPE_LINK);
|
|
nm_assert(!*obj_new || NMP_OBJECT_GET_TYPE(*obj_new) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
obj = *obj_new ?: obj_orig;
|
|
|
|
if (nmp_cache_link_connected_needs_toggle(cache, obj, NULL, NULL)) {
|
|
if (!*obj_new)
|
|
obj = *obj_new = nmp_object_clone(obj, FALSE);
|
|
obj->link.connected = !obj->link.connected;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_vt_cmd_obj_dispose_link(NMPObject *obj)
|
|
{
|
|
if (obj->_link.udev.device) {
|
|
udev_device_unref(obj->_link.udev.device);
|
|
obj->_link.udev.device = NULL;
|
|
}
|
|
g_clear_object(&obj->_link.ext_data);
|
|
nmp_object_unref(obj->_link.netlink.lnk);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_dispose_ip4_route(NMPObject *obj)
|
|
{
|
|
nm_clear_g_free((gpointer *) &obj->_ip4_route.extra_nexthops);
|
|
}
|
|
|
|
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 void
|
|
_wireguard_clear(NMPObjectLnkWireGuard *lnk)
|
|
{
|
|
guint i;
|
|
|
|
nm_explicit_bzero(lnk->_public.private_key, sizeof(lnk->_public.private_key));
|
|
for (i = 0; i < lnk->peers_len; i++) {
|
|
NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &lnk->peers[i];
|
|
|
|
nm_explicit_bzero(peer->preshared_key, sizeof(peer->preshared_key));
|
|
}
|
|
g_free((gpointer) lnk->peers);
|
|
g_free((gpointer) lnk->_allowed_ips_buf);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_dispose_lnk_wireguard(NMPObject *obj)
|
|
{
|
|
_wireguard_clear(&obj->_lnk_wireguard);
|
|
}
|
|
|
|
static gsize
|
|
_NMP_OBJECT_STRUCT_SIZE(const NMPClass *klass)
|
|
{
|
|
nm_assert(klass);
|
|
nm_assert(klass->sizeof_public > 0);
|
|
nm_assert(klass->sizeof_public <= klass->sizeof_data);
|
|
|
|
return klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object);
|
|
}
|
|
|
|
static NMPObject *
|
|
_nmp_object_new_from_class(const NMPClass *klass)
|
|
{
|
|
NMPObject *obj;
|
|
|
|
obj = g_slice_alloc0(_NMP_OBJECT_STRUCT_SIZE(klass));
|
|
obj->_class = klass;
|
|
obj->parent._ref_count = 1;
|
|
return obj;
|
|
}
|
|
|
|
NMPObject *
|
|
nmp_object_new(NMPObjectType obj_type, gconstpointer 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 NMPObject *
|
|
_nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass)
|
|
{
|
|
nm_assert(obj);
|
|
|
|
memset(obj, 0, _NMP_OBJECT_STRUCT_SIZE(klass));
|
|
obj->_class = klass;
|
|
obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT;
|
|
return obj;
|
|
}
|
|
|
|
static NMPObject *
|
|
_nmp_object_stackinit_from_type(NMPObject *obj, NMPObjectType obj_type)
|
|
{
|
|
return _nmp_object_stackinit_from_class(obj, nmp_class_from_type(obj_type));
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit(NMPObject *obj, NMPObjectType obj_type, gconstpointer 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);
|
|
_nmp_object_stackinit_from_class(obj, klass);
|
|
if (klass->cmd_plobj_id_copy)
|
|
klass->cmd_plobj_id_copy(&obj->object, &src->object);
|
|
else {
|
|
/* plain memcpy.
|
|
*
|
|
* Note that for NMPObjectIP4Route this also copies extra_nexthops
|
|
* pointer, aliasing it without taking ownership. That is potentially
|
|
* dangerous, but when using a stack allocated instance, you must
|
|
* always take care of ownership. */
|
|
memcpy(&obj->object, &src->object, klass->sizeof_data);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_link(NMPObject *obj, int ifindex)
|
|
{
|
|
_nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_LINK);
|
|
obj->link.ifindex = ifindex;
|
|
return obj;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_ip4_address(NMPObject *obj,
|
|
int ifindex,
|
|
guint32 address,
|
|
guint8 plen,
|
|
guint32 peer_address)
|
|
{
|
|
_nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP4_ADDRESS);
|
|
obj->ip4_address.ifindex = ifindex;
|
|
obj->ip4_address.address = address;
|
|
obj->ip4_address.plen = plen;
|
|
obj->ip4_address.peer_address = peer_address;
|
|
return obj;
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address)
|
|
{
|
|
_nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP6_ADDRESS);
|
|
obj->ip6_address.ifindex = ifindex;
|
|
if (address)
|
|
obj->ip6_address.address = *address;
|
|
return obj;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nmp_object_to_string(const NMPObject *obj,
|
|
NMPObjectToStringMode to_string_mode,
|
|
char *buf,
|
|
gsize buf_size)
|
|
{
|
|
const NMPClass *klass;
|
|
char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
|
|
|
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) {
|
|
nm_assert(!klass->cmd_plobj_to_string);
|
|
nm_assert(!klass->cmd_plobj_to_string_id);
|
|
return klass->cmd_obj_to_string(obj, to_string_mode, buf, buf_size);
|
|
}
|
|
|
|
nm_assert(klass->cmd_plobj_to_string);
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
if (klass->cmd_plobj_to_string_id)
|
|
return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size);
|
|
g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
g_snprintf(buf,
|
|
buf_size,
|
|
"[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]",
|
|
klass->obj_type_name,
|
|
NM_HASH_OBFUSCATE_PTR(obj),
|
|
obj->parent._ref_count,
|
|
nmp_object_is_alive(obj) ? '+' : '-',
|
|
nmp_object_is_visible(obj) ? '+' : '-',
|
|
klass->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2)));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
klass->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 *b = buf;
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
g_snprintf(buf, buf_size, "%d", obj->link.ifindex);
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
nm_strbuf_append(&b,
|
|
&buf_size,
|
|
"[%s," NM_HASH_OBFUSCATE_PTR_FMT
|
|
",%u,%calive,%cvisible,%cin-nl," NM_HASH_OBFUSCATE_PTR_FMT "; ",
|
|
klass->obj_type_name,
|
|
NM_HASH_OBFUSCATE_PTR(obj),
|
|
obj->parent._ref_count,
|
|
nmp_object_is_alive(obj) ? '+' : '-',
|
|
nmp_object_is_visible(obj) ? '+' : '-',
|
|
obj->_link.netlink.is_in_netlink ? '+' : '-',
|
|
NM_HASH_OBFUSCATE_PTR(obj->_link.udev.device));
|
|
nm_platform_link_to_string(&obj->link, b, buf_size);
|
|
nm_strbuf_seek_end(&b, &buf_size);
|
|
if (obj->_link.netlink.lnk) {
|
|
nm_strbuf_append_str(&b, &buf_size, "; ");
|
|
nmp_object_to_string(obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_ALL, b, buf_size);
|
|
nm_strbuf_seek_end(&b, &buf_size);
|
|
}
|
|
nm_strbuf_append_c(&b, &buf_size, ']');
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
nm_platform_link_to_string(&obj->link, b, buf_size);
|
|
if (obj->_link.netlink.lnk) {
|
|
nm_strbuf_seek_end(&b, &buf_size);
|
|
nm_strbuf_append_str(&b, &buf_size, "; ");
|
|
nmp_object_to_string(obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, b, buf_size);
|
|
}
|
|
return buf;
|
|
default:
|
|
g_return_val_if_reached("ERROR");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
_vt_cmd_obj_to_string_ip4_route(const NMPObject *obj,
|
|
NMPObjectToStringMode to_string_mode,
|
|
char *buf,
|
|
gsize buf_size)
|
|
{
|
|
const NMPClass *klass;
|
|
char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
|
|
|
klass = NMP_OBJECT_GET_CLASS(obj);
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
nm_platform_ip4_route_to_string_full(&obj->ip4_route,
|
|
obj->_ip4_route.extra_nexthops,
|
|
buf,
|
|
buf_size);
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
g_snprintf(buf,
|
|
buf_size,
|
|
"[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]",
|
|
klass->obj_type_name,
|
|
NM_HASH_OBFUSCATE_PTR(obj),
|
|
obj->parent._ref_count,
|
|
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;
|
|
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;
|
|
char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
|
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, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
|
|
g_snprintf(buf,
|
|
buf_size,
|
|
"[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]",
|
|
klass->obj_type_name,
|
|
NM_HASH_OBFUSCATE_PTR(obj),
|
|
obj->parent._ref_count,
|
|
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:
|
|
nm_platform_lnk_vlan_to_string(&obj->lnk_vlan, 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");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
_vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj,
|
|
NMPObjectToStringMode to_string_mode,
|
|
char *buf,
|
|
gsize buf_size)
|
|
{
|
|
const NMPClass *klass;
|
|
char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
|
char *b;
|
|
guint i;
|
|
|
|
klass = NMP_OBJECT_GET_CLASS(obj);
|
|
|
|
switch (to_string_mode) {
|
|
case NMP_OBJECT_TO_STRING_ID:
|
|
g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj));
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_ALL:
|
|
b = buf;
|
|
|
|
nm_strbuf_append(&b,
|
|
&buf_size,
|
|
"[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s"
|
|
"%s",
|
|
klass->obj_type_name,
|
|
NM_HASH_OBFUSCATE_PTR(obj),
|
|
obj->parent._ref_count,
|
|
nmp_object_is_alive(obj) ? '+' : '-',
|
|
nmp_object_is_visible(obj) ? '+' : '-',
|
|
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2)),
|
|
obj->_lnk_wireguard.peers_len > 0 ? " peers {" : "");
|
|
|
|
for (i = 0; i < obj->_lnk_wireguard.peers_len; i++) {
|
|
const NMPWireGuardPeer *peer = &obj->_lnk_wireguard.peers[i];
|
|
|
|
nm_strbuf_append_str(&b, &buf_size, " { ");
|
|
nm_platform_wireguard_peer_to_string(peer, b, buf_size);
|
|
nm_strbuf_seek_end(&b, &buf_size);
|
|
nm_strbuf_append_str(&b, &buf_size, " }");
|
|
}
|
|
if (obj->_lnk_wireguard.peers_len)
|
|
nm_strbuf_append_str(&b, &buf_size, " }");
|
|
|
|
return buf;
|
|
case NMP_OBJECT_TO_STRING_PUBLIC:
|
|
nm_platform_lnk_wireguard_to_string(&obj->lnk_wireguard, buf, buf_size);
|
|
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; \
|
|
_nm_unused char buf1[NM_INET_ADDRSTRLEN]; \
|
|
_nm_unused char buf2[NM_INET_ADDRSTRLEN]; \
|
|
\
|
|
g_snprintf(buf, buf_len, __VA_ARGS__); \
|
|
return buf; \
|
|
} \
|
|
_NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
|
|
|
|
_vt_cmd_plobj_to_string_id(
|
|
ip4_address,
|
|
NMPlatformIP4Address,
|
|
"%d: %s/%d%s%s",
|
|
obj->ifindex,
|
|
nm_inet4_ntop(obj->address, buf1),
|
|
obj->plen,
|
|
obj->peer_address != obj->address ? "," : "",
|
|
obj->peer_address != obj->address
|
|
? nm_inet4_ntop(nm_ip4_addr_clear_host_address(obj->peer_address, obj->plen), buf2)
|
|
: "");
|
|
|
|
_vt_cmd_plobj_to_string_id(ip6_address,
|
|
NMPlatformIP6Address,
|
|
"%d: %s",
|
|
obj->ifindex,
|
|
nm_inet6_ntop(&obj->address, buf1));
|
|
|
|
_vt_cmd_plobj_to_string_id(qdisc, NMPlatformQdisc, "%d: %d", obj->ifindex, obj->parent);
|
|
|
|
_vt_cmd_plobj_to_string_id(tfilter, NMPlatformTfilter, "%d: %d", obj->ifindex, obj->parent);
|
|
|
|
void
|
|
nmp_object_hash_update_full(const NMPObject *obj, gboolean for_id, NMHashState *h)
|
|
{
|
|
const NMPClass *klass;
|
|
|
|
g_return_if_fail(NMP_OBJECT_IS_VALID(obj));
|
|
|
|
klass = NMP_OBJECT_GET_CLASS(obj);
|
|
|
|
nm_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update));
|
|
nm_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update));
|
|
nm_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update));
|
|
|
|
nm_assert((!!klass->cmd_obj_hash_update) ^ (!!klass->cmd_plobj_hash_update));
|
|
nm_assert((!klass->cmd_obj_hash_update) || (!klass->cmd_plobj_id_hash_update));
|
|
|
|
nm_hash_update_val(h, klass->obj_type);
|
|
|
|
if (for_id) {
|
|
if (klass->cmd_obj_hash_update)
|
|
klass->cmd_obj_hash_update(obj, TRUE, h);
|
|
else if (klass->cmd_plobj_id_hash_update)
|
|
klass->cmd_plobj_id_hash_update(&obj->object, h);
|
|
else {
|
|
/* The klass doesn't implement ID compare. It means, to use pointer
|
|
* equality. */
|
|
nm_hash_update_val(h, obj);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (klass->cmd_obj_hash_update)
|
|
klass->cmd_obj_hash_update(obj, FALSE, h);
|
|
else
|
|
klass->cmd_plobj_hash_update(&obj->object, h);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_hash_update_link(const NMPObject *obj, gboolean for_id, NMHashState *h)
|
|
{
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
if (for_id) {
|
|
nm_hash_update_val(h, obj->link.ifindex);
|
|
return;
|
|
}
|
|
|
|
nm_platform_link_hash_update(&obj->link, h);
|
|
nm_hash_update_vals(h, obj->_link.netlink.is_in_netlink, obj->_link.udev.device);
|
|
if (obj->_link.netlink.lnk)
|
|
nmp_object_hash_update(obj->_link.netlink.lnk, h);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_hash_update_ip4_route(const NMPObject *obj, gboolean for_id, NMHashState *h)
|
|
{
|
|
guint i;
|
|
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE);
|
|
|
|
nm_platform_ip4_route_hash_update(&obj->ip4_route,
|
|
for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
|
|
: NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL,
|
|
h);
|
|
for (i = 1u; i < obj->ip4_route.n_nexthops; i++)
|
|
nm_platform_ip4_rt_nexthop_hash_update(&obj->_ip4_route.extra_nexthops[i - 1u], for_id, h);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, gboolean for_id, NMHashState *h)
|
|
{
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_VLAN);
|
|
|
|
if (for_id) {
|
|
nm_hash_update_val(h, obj);
|
|
return;
|
|
}
|
|
|
|
nm_platform_lnk_vlan_hash_update(&obj->lnk_vlan, h);
|
|
_vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_ingress_qos_map,
|
|
obj->_lnk_vlan.ingress_qos_map,
|
|
h);
|
|
_vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_egress_qos_map,
|
|
obj->_lnk_vlan.egress_qos_map,
|
|
h);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, gboolean for_id, NMHashState *h)
|
|
{
|
|
guint i;
|
|
|
|
nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_WIREGUARD);
|
|
|
|
if (for_id) {
|
|
nm_hash_update_val(h, obj);
|
|
return;
|
|
}
|
|
|
|
nm_platform_lnk_wireguard_hash_update(&obj->lnk_wireguard, h);
|
|
|
|
nm_hash_update_val(h, obj->_lnk_wireguard.peers_len);
|
|
for (i = 0; i < obj->_lnk_wireguard.peers_len; i++)
|
|
_wireguard_peer_hash_update(&obj->_lnk_wireguard.peers[i], h);
|
|
}
|
|
|
|
int
|
|
nmp_object_cmp_full(const NMPObject *obj1, const NMPObject *obj2, NMPObjectCmpFlags flags)
|
|
{
|
|
const NMPClass *klass;
|
|
const NMPClass *klass2;
|
|
NMPObject obj_stackcopy;
|
|
|
|
nm_assert(
|
|
!NM_FLAGS_ANY(flags, ~(NMP_OBJECT_CMP_FLAGS_ID | NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX)));
|
|
|
|
/* The ID flag (currently) cannot be combined with other flags. That's partly because
|
|
* it's not implemented, but also because some objects use only pointer equality. So it's
|
|
* not clear how that combines with other flags (well, they'd be ignored). */
|
|
nm_assert(!NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_ID) || flags == NMP_OBJECT_CMP_FLAGS_ID);
|
|
|
|
NM_CMP_SELF(obj1, obj2);
|
|
|
|
g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), -1);
|
|
g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), 1);
|
|
|
|
klass = NMP_OBJECT_GET_CLASS(obj1);
|
|
|
|
nm_assert(klass);
|
|
|
|
nm_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update));
|
|
nm_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update));
|
|
nm_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update));
|
|
|
|
nm_assert((!!klass->cmd_obj_cmp) ^ (!!klass->cmd_plobj_cmp));
|
|
nm_assert((!klass->cmd_obj_cmp) || (!klass->cmd_plobj_id_cmp));
|
|
|
|
klass2 = NMP_OBJECT_GET_CLASS(obj2);
|
|
|
|
if (klass != klass2) {
|
|
nm_assert(klass2);
|
|
NM_CMP_DIRECT(klass->obj_type, klass2->obj_type);
|
|
return nm_assert_unreachable_val(0);
|
|
}
|
|
|
|
if (NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_ID)) {
|
|
if (klass->cmd_obj_cmp)
|
|
return klass->cmd_obj_cmp(obj1, obj2, TRUE);
|
|
if (klass->cmd_plobj_id_cmp)
|
|
return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object);
|
|
|
|
/* the klass doesn't implement ID cmp(). That means, different objects
|
|
* never compare equal, but the cmp() according to their pointer value. */
|
|
NM_CMP_DIRECT_PTR(obj1, obj2);
|
|
return nm_assert_unreachable_val(0);
|
|
}
|
|
|
|
if (NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX)) {
|
|
if (!NM_IN_SET(klass->obj_type,
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE)) {
|
|
/* This flag is currently only implemented for certain types.
|
|
* That is, because we just create a stack copy, and that naive
|
|
* approach only knows for types where we know that it works. */
|
|
} else if (obj1->obj_with_ifindex.ifindex != obj2->obj_with_ifindex.ifindex) {
|
|
nmp_object_stackinit(&obj_stackcopy, klass->obj_type, &obj2->obj_with_ifindex);
|
|
obj_stackcopy.obj_with_ifindex.ifindex = obj1->obj_with_ifindex.ifindex;
|
|
if (klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE)
|
|
obj_stackcopy._ip4_route.extra_nexthops = obj2->_ip4_route.extra_nexthops;
|
|
obj2 = &obj_stackcopy;
|
|
}
|
|
}
|
|
|
|
if (klass->cmd_obj_cmp)
|
|
return klass->cmd_obj_cmp(obj1, obj2, FALSE);
|
|
return klass->cmd_plobj_cmp(&obj1->object, &obj2->object);
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id)
|
|
{
|
|
if (for_id) {
|
|
NM_CMP_FIELD(obj1, obj2, link.ifindex);
|
|
return 0;
|
|
}
|
|
|
|
NM_CMP_RETURN(nm_platform_link_cmp(&obj1->link, &obj2->link));
|
|
NM_CMP_DIRECT(obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink);
|
|
NM_CMP_RETURN(nmp_object_cmp(obj1->_link.netlink.lnk, obj2->_link.netlink.lnk));
|
|
|
|
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.
|
|
*
|
|
* 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_ip4_route(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id)
|
|
{
|
|
int c;
|
|
guint i;
|
|
|
|
c = nm_platform_ip4_route_cmp(&obj1->ip4_route,
|
|
&obj2->ip4_route,
|
|
for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
|
|
: NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
|
|
NM_CMP_RETURN_DIRECT(c);
|
|
|
|
for (i = 1u; i < obj1->ip4_route.n_nexthops; i++) {
|
|
c = nm_platform_ip4_rt_nexthop_cmp(&obj1->_ip4_route.extra_nexthops[i - 1u],
|
|
&obj2->_ip4_route.extra_nexthops[i - 1u],
|
|
for_id);
|
|
NM_CMP_RETURN_DIRECT(c);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id)
|
|
{
|
|
int c;
|
|
|
|
if (for_id) {
|
|
NM_CMP_DIRECT_PTR(obj1, obj2);
|
|
return nm_assert_unreachable_val(0);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id)
|
|
{
|
|
guint i;
|
|
|
|
if (for_id) {
|
|
NM_CMP_DIRECT_PTR(obj1, obj2);
|
|
return nm_assert_unreachable_val(0);
|
|
}
|
|
|
|
NM_CMP_RETURN(nm_platform_lnk_wireguard_cmp(&obj1->lnk_wireguard, &obj2->lnk_wireguard));
|
|
|
|
NM_CMP_FIELD(obj1, obj2, _lnk_wireguard.peers_len);
|
|
|
|
for (i = 0; i < obj1->_lnk_wireguard.peers_len; i++)
|
|
NM_CMP_RETURN(
|
|
_wireguard_peer_cmp(&obj1->_lnk_wireguard.peers[i], &obj2->_lnk_wireguard.peers[i]));
|
|
|
|
return 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 && 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)
|
|
udev_device_ref(src->_link.udev.device);
|
|
if (dst->_link.udev.device)
|
|
udev_device_unref(dst->_link.udev.device);
|
|
dst->_link.udev.device = src->_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.netlink.lnk = src->_link.netlink.lnk;
|
|
}
|
|
if (dst->_link.ext_data != src->_link.ext_data) {
|
|
if (dst->_link.ext_data)
|
|
g_clear_object(&dst->_link.ext_data);
|
|
if (src->_link.ext_data)
|
|
dst->_link.ext_data = g_object_ref(src->_link.ext_data);
|
|
}
|
|
dst->_link = src->_link;
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_copy_ip4_route(NMPObject *dst, const NMPObject *src)
|
|
{
|
|
nm_assert(dst != src);
|
|
|
|
if (src->ip4_route.n_nexthops <= 1) {
|
|
nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops);
|
|
} else if (src->ip4_route.n_nexthops != dst->ip4_route.n_nexthops
|
|
|| !nm_memeq_n(src->_ip4_route.extra_nexthops,
|
|
src->ip4_route.n_nexthops - 1u,
|
|
dst->_ip4_route.extra_nexthops,
|
|
dst->ip4_route.n_nexthops - 1u,
|
|
sizeof(NMPlatformIP4RtNextHop))) {
|
|
nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops);
|
|
dst->_ip4_route.extra_nexthops =
|
|
nm_memdup(src->_ip4_route.extra_nexthops,
|
|
sizeof(NMPlatformIP4RtNextHop) * (src->ip4_route.n_nexthops - 1u));
|
|
}
|
|
|
|
dst->ip4_route = src->ip4_route;
|
|
}
|
|
|
|
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,
|
|
NM_UNCONST_PPTR(NMVlanQosMapping, &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,
|
|
NM_UNCONST_PPTR(NMVlanQosMapping, &dst->_lnk_vlan.egress_qos_map),
|
|
src->_lnk_vlan.n_egress_qos_map,
|
|
src->_lnk_vlan.egress_qos_map);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_obj_copy_lnk_wireguard(NMPObject *dst, const NMPObject *src)
|
|
{
|
|
guint i;
|
|
|
|
nm_assert(dst != src);
|
|
|
|
_wireguard_clear(&dst->_lnk_wireguard);
|
|
|
|
dst->_lnk_wireguard = src->_lnk_wireguard;
|
|
|
|
dst->_lnk_wireguard.peers = nm_memdup(dst->_lnk_wireguard.peers,
|
|
sizeof(NMPWireGuardPeer) * dst->_lnk_wireguard.peers_len);
|
|
dst->_lnk_wireguard._allowed_ips_buf =
|
|
nm_memdup(dst->_lnk_wireguard._allowed_ips_buf,
|
|
sizeof(NMPWireGuardAllowedIP) * dst->_lnk_wireguard._allowed_ips_buf_len);
|
|
|
|
/* all the peers' pointers point into the buffer. They need to be readjusted. */
|
|
for (i = 0; i < dst->_lnk_wireguard.peers_len; i++) {
|
|
NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &dst->_lnk_wireguard.peers[i];
|
|
|
|
if (peer->allowed_ips_len == 0) {
|
|
nm_assert(!peer->allowed_ips);
|
|
continue;
|
|
}
|
|
nm_assert(dst->_lnk_wireguard._allowed_ips_buf_len > 0);
|
|
nm_assert(src->_lnk_wireguard._allowed_ips_buf);
|
|
nm_assert(peer->allowed_ips >= src->_lnk_wireguard._allowed_ips_buf);
|
|
nm_assert(
|
|
&peer->allowed_ips[peer->allowed_ips_len]
|
|
<= &src->_lnk_wireguard._allowed_ips_buf[src->_lnk_wireguard._allowed_ips_buf_len]);
|
|
|
|
peer->allowed_ips =
|
|
&dst->_lnk_wireguard
|
|
._allowed_ips_buf[peer->allowed_ips - src->_lnk_wireguard._allowed_ips_buf];
|
|
}
|
|
|
|
nm_assert(nmp_object_equal(src, dst));
|
|
}
|
|
|
|
#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 \
|
|
} \
|
|
} \
|
|
_NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
|
|
|
|
_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;
|
|
});
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#define _vt_cmd_plobj_id_cmp(type, plat_type, cmd) \
|
|
static int _vt_cmd_plobj_id_cmp_##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; \
|
|
\
|
|
NM_CMP_SELF(obj1, obj2); \
|
|
{ \
|
|
cmd; \
|
|
} \
|
|
return 0; \
|
|
} \
|
|
_NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
|
|
|
|
static int
|
|
_vt_cmd_plobj_id_cmp_ip4_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_ip4_address_cmp((const NMPlatformIP4Address *) obj1,
|
|
(const NMPlatformIP4Address *) obj2,
|
|
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID);
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_plobj_id_cmp_ip6_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_ip6_address_cmp((const NMPlatformIP6Address *) obj1,
|
|
(const NMPlatformIP6Address *) obj2,
|
|
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID);
|
|
}
|
|
|
|
_vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, {
|
|
NM_CMP_FIELD(obj1, obj2, ifindex);
|
|
NM_CMP_FIELD(obj1, obj2, parent);
|
|
});
|
|
|
|
_vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, {
|
|
NM_CMP_FIELD(obj1, obj2, ifindex);
|
|
NM_CMP_FIELD(obj1, obj2, handle);
|
|
});
|
|
|
|
static int
|
|
_vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1,
|
|
(const NMPlatformIP6Route *) obj2,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID);
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1,
|
|
(const NMPlatformRoutingRule *) obj2,
|
|
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID);
|
|
}
|
|
|
|
_vt_cmd_plobj_id_cmp(mptcp_addr, NMPlatformMptcpAddr, {
|
|
/* The primary key of an MPTCP endpoint is only the address:port@ifindex.
|
|
*
|
|
* Which does not fully correspond to kernel's view. Kernel's view is determined
|
|
* by the question whether you can add two objects that only differ by one
|
|
* attribute. If you can, the attribute is part of the ID otherwise it isn't.
|
|
*
|
|
* Note that for kernel, the "ifindex" is not part of the identity.
|
|
* That is, you cannot two add two endpoints that only differ by
|
|
* ifindex. However, for our purpose, it is very useful to make
|
|
* the "ifindex" part of the identity. For example, NMPGlobalTracker will use
|
|
* this to track MPTCP addresses from independent callers (NML3Cfg).
|
|
* It would be bad, if objects that differ by "ifindex" would be
|
|
* combined.
|
|
*
|
|
* The "id" is intentionally not part of the identity for us. Note however, that kernel
|
|
* does not allow configuring duplicates "id" -- so the "id" could be a primary key
|
|
* as far as kernel is concerned. However, when we track MPTCP endpoints that
|
|
* we want to configure, the "id" is left undefined (and we let kernel choose it).
|
|
* If the "id" would be part of the NMPObject's ID, we could not lookup
|
|
* an object unless we know the "id" -- which we often don't.
|
|
*/
|
|
NM_CMP_FIELD(obj1, obj2, ifindex);
|
|
NM_CMP_FIELD(obj1, obj2, addr_family);
|
|
NM_CMP_FIELD_MEMCMP_LEN(obj1,
|
|
obj2,
|
|
addr,
|
|
nm_utils_addr_family_to_size_untrusted(obj1->addr_family));
|
|
NM_CMP_FIELD(obj1, obj2, port);
|
|
});
|
|
|
|
guint
|
|
nmp_object_id_hash(const NMPObject *obj)
|
|
{
|
|
NMHashState h;
|
|
|
|
if (!obj)
|
|
return nm_hash_static(914932607u);
|
|
|
|
nm_hash_init(&h, 914932607u);
|
|
nmp_object_id_hash_update(obj, &h);
|
|
return nm_hash_complete(&h);
|
|
}
|
|
|
|
#define _vt_cmd_plobj_id_hash_update(type, plat_type, cmd) \
|
|
static void _vt_cmd_plobj_id_hash_update_##type(const NMPlatformObject *_obj, NMHashState *h) \
|
|
{ \
|
|
const plat_type *const obj = (const plat_type *) _obj; \
|
|
{ \
|
|
cmd; \
|
|
} \
|
|
} \
|
|
_NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON
|
|
|
|
_vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, {
|
|
nm_hash_update_vals(
|
|
h,
|
|
obj->ifindex,
|
|
obj->plen,
|
|
obj->address,
|
|
/* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */
|
|
nm_ip4_addr_clear_host_address(obj->peer_address, obj->plen));
|
|
});
|
|
|
|
_vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, {
|
|
nm_hash_update_vals(
|
|
h,
|
|
obj->ifindex,
|
|
/* for IPv6 addresses, the prefix length is not part of the primary identifier. */
|
|
obj->address);
|
|
});
|
|
|
|
_vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, {
|
|
nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h);
|
|
});
|
|
|
|
_vt_cmd_plobj_id_hash_update(routing_rule, NMPlatformRoutingRule, {
|
|
nm_platform_routing_rule_hash_update(obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h);
|
|
});
|
|
|
|
_vt_cmd_plobj_id_hash_update(qdisc, NMPlatformQdisc, {
|
|
nm_hash_update_vals(h, obj->ifindex, obj->parent);
|
|
});
|
|
|
|
_vt_cmd_plobj_id_hash_update(tfilter, NMPlatformTfilter, {
|
|
nm_hash_update_vals(h, obj->ifindex, obj->handle);
|
|
});
|
|
|
|
_vt_cmd_plobj_id_hash_update(mptcp_addr, NMPlatformMptcpAddr, {
|
|
/* See the corresponding ID cmp function for details. */
|
|
nm_hash_update_vals(h, obj->addr_family, obj->port, obj->ifindex);
|
|
nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size_untrusted(obj->addr_family));
|
|
});
|
|
|
|
static void
|
|
_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h)
|
|
{
|
|
return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL,
|
|
h);
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_plobj_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1,
|
|
(const NMPlatformIP6Route *) obj2,
|
|
NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
|
|
}
|
|
|
|
static void
|
|
_vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState *h)
|
|
{
|
|
return nm_platform_routing_rule_hash_update((const NMPlatformRoutingRule *) obj,
|
|
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL,
|
|
h);
|
|
}
|
|
|
|
static inline int
|
|
_vt_cmd_plobj_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1,
|
|
(const NMPlatformRoutingRule *) obj2,
|
|
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL);
|
|
}
|
|
|
|
static int
|
|
_vt_cmd_plobj_cmp_qdisc(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
|
{
|
|
return nm_platform_qdisc_cmp((const NMPlatformQdisc *) obj1,
|
|
(const NMPlatformQdisc *) obj2,
|
|
TRUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
guint
|
|
nmp_object_indirect_id_hash(gconstpointer a)
|
|
{
|
|
const NMPObject *const *p_obj = a;
|
|
|
|
return nmp_object_id_hash(*p_obj);
|
|
}
|
|
|
|
gboolean
|
|
nmp_object_indirect_id_equal(gconstpointer a, gconstpointer b)
|
|
{
|
|
const NMPObject *const *p_obj_a = a;
|
|
const NMPObject *const *p_obj_b = b;
|
|
|
|
return nmp_object_id_equal(*p_obj_a, *p_obj_b);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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 NMP_OBJECT_CAST_LINK(obj)->ifindex > 0
|
|
&& (obj->_link.netlink.is_in_netlink || obj->_link.udev.device);
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_ipx_address(const NMPObject *obj)
|
|
{
|
|
return NMP_OBJECT_CAST_IP_ADDRESS(obj)->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 !NM_FLAGS_HAS(NMP_OBJECT_CAST_IP_ROUTE(obj)->r_rtm_flags, RTM_F_CLONED);
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_routing_rule(const NMPObject *obj)
|
|
{
|
|
return NM_IN_SET(obj->routing_rule.addr_family, AF_INET, AF_INET6);
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_qdisc(const NMPObject *obj)
|
|
{
|
|
return NMP_OBJECT_CAST_QDISC(obj)->ifindex > 0;
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_tfilter(const NMPObject *obj)
|
|
{
|
|
return NMP_OBJECT_CAST_TFILTER(obj)->ifindex > 0;
|
|
}
|
|
|
|
static gboolean
|
|
_vt_cmd_obj_is_alive_mptcp_addr(const NMPObject *obj)
|
|
{
|
|
return NM_IN_SET(obj->mptcp_addr.addr_family, AF_INET, AF_INET6, AF_UNSPEC);
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const guint8 _supported_cache_ids_object[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
|
|
0,
|
|
};
|
|
|
|
static const guint8 _supported_cache_ids_link[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
|
|
0,
|
|
};
|
|
|
|
static const guint8 _supported_cache_ids_ipx_address[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
|
|
0,
|
|
};
|
|
|
|
static const guint8 _supported_cache_ids_ipx_route[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
|
|
NMP_CACHE_ID_TYPE_DEFAULT_ROUTES,
|
|
NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
|
|
0,
|
|
};
|
|
|
|
static const guint8 _supported_cache_ids_routing_rules[] = {
|
|
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
|
|
NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY,
|
|
0,
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_vt_dedup_obj_destroy(NMDedupMultiObj *obj)
|
|
{
|
|
NMPObject *o = (NMPObject *) obj;
|
|
const NMPClass *klass;
|
|
|
|
nm_assert(o->parent._ref_count == 0);
|
|
nm_assert(!o->parent._multi_idx);
|
|
|
|
klass = o->_class;
|
|
if (klass->cmd_obj_dispose)
|
|
klass->cmd_obj_dispose(o);
|
|
g_slice_free1(_NMP_OBJECT_STRUCT_SIZE(klass), o);
|
|
}
|
|
|
|
static const NMDedupMultiObj *
|
|
_vt_dedup_obj_clone(const NMDedupMultiObj *obj)
|
|
{
|
|
return (const NMDedupMultiObj *) nmp_object_clone((const NMPObject *) obj, FALSE);
|
|
}
|
|
|
|
#define DEDUP_MULTI_OBJ_CLASS_INIT() \
|
|
{ \
|
|
.obj_clone = _vt_dedup_obj_clone, \
|
|
.obj_destroy = _vt_dedup_obj_destroy, \
|
|
.obj_full_hash_update = \
|
|
(void (*)(const NMDedupMultiObj *obj, NMHashState *h)) nmp_object_hash_update, \
|
|
.obj_full_equal = (gboolean (*)(const NMDedupMultiObj *obj_a, \
|
|
const NMDedupMultiObj *obj_b)) nmp_object_equal, \
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static NMDedupMultiIdxType *
|
|
_idx_type_get(const NMPCache *cache, NMPCacheIdType cache_id_type)
|
|
{
|
|
nm_assert(cache);
|
|
nm_assert(cache_id_type > NMP_CACHE_ID_TYPE_NONE);
|
|
nm_assert(cache_id_type <= NMP_CACHE_ID_TYPE_MAX);
|
|
nm_assert((int) cache_id_type - 1 >= 0);
|
|
nm_assert((int) cache_id_type - 1 < G_N_ELEMENTS(cache->idx_types));
|
|
|
|
return (NMDedupMultiIdxType *) &cache->idx_types[cache_id_type - 1];
|
|
}
|
|
|
|
gboolean
|
|
nmp_cache_use_udev_get(const NMPCache *cache)
|
|
{
|
|
g_return_val_if_fail(cache, TRUE);
|
|
|
|
return cache->use_udev;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nmp_cache_link_connected_for_port(int ifindex_controller, const NMPObject *port)
|
|
{
|
|
nm_assert(NMP_OBJECT_GET_TYPE(port) == NMP_OBJECT_TYPE_LINK);
|
|
|
|
return ifindex_controller > 0 && port->link.controller == ifindex_controller
|
|
&& port->link.connected && nmp_object_is_visible(port);
|
|
}
|
|
|
|
/**
|
|
* nmp_cache_link_connected_needs_toggle:
|
|
* @cache: the platform cache
|
|
* @controller: the link object, that is checked whether its connected property
|
|
* needs to be toggled.
|
|
* @potential_port: (nullable): 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_port: (nullable): if set, the check will pretend that @ignore_port
|
|
* is not in the cache.
|
|
*
|
|
* NMPlatformLink has two connected flags: (controller->link.flags&IFF_LOWER_UP) (as reported
|
|
* from netlink) and controller->link.connected. For bond and bridge controller, kernel reports
|
|
* those links as IFF_LOWER_UP if they have no ports attached. We want to present instead
|
|
* a combined @connected flag that shows controllers without ports as down.
|
|
*
|
|
* Check if the connected flag of @controller should be toggled according to the content
|
|
* of @cache (including @potential_port).
|
|
*
|
|
* Returns: %TRUE, if @controller->link.connected should be flipped/toggled.
|
|
**/
|
|
gboolean
|
|
nmp_cache_link_connected_needs_toggle(const NMPCache *cache,
|
|
const NMPObject *controller,
|
|
const NMPObject *potential_port,
|
|
const NMPObject *ignore_port)
|
|
{
|
|
gboolean is_lower_up = FALSE;
|
|
|
|
if (!controller || NMP_OBJECT_GET_TYPE(controller) != NMP_OBJECT_TYPE_LINK
|
|
|| controller->link.ifindex <= 0 || !nmp_object_is_visible(controller)
|
|
|| !NM_IN_SET(controller->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 ports. */
|
|
if (!NM_FLAGS_HAS(controller->link.n_ifi_flags, IFF_LOWER_UP))
|
|
return !!controller->link.connected;
|
|
|
|
if (potential_port && NMP_OBJECT_GET_TYPE(potential_port) != NMP_OBJECT_TYPE_LINK)
|
|
potential_port = NULL;
|
|
|
|
if (potential_port
|
|
&& nmp_cache_link_connected_for_port(controller->link.ifindex, potential_port))
|
|
is_lower_up = TRUE;
|
|
else {
|
|
NMPLookup lookup;
|
|
NMDedupMultiIter iter;
|
|
const NMPlatformLink *link = NULL;
|
|
|
|
nmp_cache_iter_for_each_link (
|
|
&iter,
|
|
nmp_cache_lookup(cache, nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK)),
|
|
&link) {
|
|
const NMPObject *obj = NMP_OBJECT_UP_CAST((NMPlatformObject *) link);
|
|
|
|
if ((!potential_port || potential_port->link.ifindex != link->ifindex)
|
|
&& ignore_port != obj
|
|
&& nmp_cache_link_connected_for_port(controller->link.ifindex, obj)) {
|
|
is_lower_up = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return !!controller->link.connected != is_lower_up;
|
|
}
|
|
|
|
/**
|
|
* nmp_cache_link_connected_needs_toggle_by_ifindex:
|
|
* @cache:
|
|
* @controller_ifindex: the ifindex of a potential controller that should be checked
|
|
* whether it needs toggling.
|
|
* @potential_port: (nullable): passed to nmp_cache_link_connected_needs_toggle().
|
|
* It considers @potential_port as being inside the cache, replacing an existing
|
|
* link with the same ifindex.
|
|
* @ignore_port: (nullable): 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 controller, check if the cache contains such a controller link that needs
|
|
* toggling of the connected flag.
|
|
*
|
|
* Returns: NULL if there is no controller link with ifindex @controller_ifindex that should be toggled.
|
|
* Otherwise, return the link object from inside the cache with the given ifindex.
|
|
* The connected flag of that controller should be toggled.
|
|
*/
|
|
const NMPObject *
|
|
nmp_cache_link_connected_needs_toggle_by_ifindex(const NMPCache *cache,
|
|
int controller_ifindex,
|
|
const NMPObject *potential_port,
|
|
const NMPObject *ignore_port)
|
|
{
|
|
const NMPObject *controller;
|
|
|
|
if (controller_ifindex > 0) {
|
|
controller = nmp_cache_lookup_link(cache, controller_ifindex);
|
|
if (nmp_cache_link_connected_needs_toggle(cache, controller, potential_port, ignore_port))
|
|
return controller;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const NMDedupMultiEntry *
|
|
_lookup_entry_with_idx_type(const NMPCache *cache,
|
|
NMPCacheIdType cache_id_type,
|
|
const NMPObject *obj)
|
|
{
|
|
const NMDedupMultiEntry *entry;
|
|
|
|
nm_assert(cache);
|
|
nm_assert(NMP_OBJECT_IS_VALID(obj));
|
|
|
|
entry =
|
|
nm_dedup_multi_index_lookup_obj(cache->multi_idx, _idx_type_get(cache, cache_id_type), obj);
|
|
nm_assert(!entry
|
|
|| (NMP_OBJECT_IS_VALID(entry->obj)
|
|
&& NMP_OBJECT_GET_CLASS(entry->obj) == NMP_OBJECT_GET_CLASS(obj)));
|
|
return entry;
|
|
}
|
|
|
|
static const NMDedupMultiEntry *
|
|
_lookup_entry(const NMPCache *cache, const NMPObject *obj)
|
|
{
|
|
return _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj);
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nmp_cache_lookup_entry_with_idx_type(const NMPCache *cache,
|
|
NMPCacheIdType cache_id_type,
|
|
const NMPObject *obj)
|
|
{
|
|
g_return_val_if_fail(cache, NULL);
|
|
g_return_val_if_fail(obj, NULL);
|
|
g_return_val_if_fail(cache_id_type > NMP_CACHE_ID_TYPE_NONE
|
|
&& cache_id_type <= NMP_CACHE_ID_TYPE_MAX,
|
|
NULL);
|
|
|
|
return _lookup_entry_with_idx_type(cache, cache_id_type, obj);
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nmp_cache_lookup_entry(const NMPCache *cache, const NMPObject *obj)
|
|
{
|
|
g_return_val_if_fail(cache, NULL);
|
|
g_return_val_if_fail(obj, NULL);
|
|
|
|
return _lookup_entry(cache, obj);
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nmp_cache_lookup_entry_link(const NMPCache *cache, int ifindex)
|
|
{
|
|
NMPObject obj_needle;
|
|
|
|
g_return_val_if_fail(cache, NULL);
|
|
g_return_val_if_fail(ifindex > 0, NULL);
|
|
|
|
nmp_object_stackinit_id_link(&obj_needle, ifindex);
|
|
return _lookup_entry(cache, &obj_needle);
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_cache_lookup_obj(const NMPCache *cache, const NMPObject *obj)
|
|
{
|
|
return nm_dedup_multi_entry_get_obj(nmp_cache_lookup_entry(cache, obj));
|
|
}
|
|
|
|
const NMPObject *
|
|
nmp_cache_lookup_link(const NMPCache *cache, int ifindex)
|
|
{
|
|
return nm_dedup_multi_entry_get_obj(nmp_cache_lookup_entry_link(cache, ifindex));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMDedupMultiHeadEntry *
|
|
nmp_cache_lookup_all(const NMPCache *cache,
|
|
NMPCacheIdType cache_id_type,
|
|
const NMPObject *select_obj)
|
|
{
|
|
nm_assert(cache);
|
|
nm_assert(NMP_OBJECT_IS_VALID(select_obj));
|
|
|
|
return nm_dedup_multi_index_lookup_head(cache->multi_idx,
|
|
_idx_type_get(cache, cache_id_type),
|
|
select_obj);
|
|
}
|
|
|
|
static const NMPLookup *
|
|
_L(const NMPLookup *lookup)
|
|
{
|
|
#if NM_MORE_ASSERTS
|
|
DedupMultiIdxType idx_type;
|
|
|
|
nm_assert(lookup);
|
|
_dedup_multi_idx_type_init(&idx_type, lookup->cache_id_type);
|
|
nm_assert(
|
|
idx_type.parent.klass->idx_obj_partitionable((NMDedupMultiIdxType *) &idx_type,
|
|
(NMDedupMultiObj *) &lookup->selector_obj));
|
|
#endif
|
|
return lookup;
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type)
|
|
{
|
|
nm_assert(lookup);
|
|
|
|
switch (obj_type) {
|
|
default:
|
|
nm_assert_not_reached();
|
|
/* fall-through */
|
|
case NMP_OBJECT_TYPE_LINK:
|
|
case NMP_OBJECT_TYPE_IP4_ADDRESS:
|
|
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
|
case NMP_OBJECT_TYPE_QDISC:
|
|
case NMP_OBJECT_TYPE_TFILTER:
|
|
case NMP_OBJECT_TYPE_MPTCP_ADDR:
|
|
_nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE;
|
|
return _L(lookup);
|
|
}
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname)
|
|
{
|
|
NMPObject *o;
|
|
|
|
nm_assert(lookup);
|
|
nm_assert(ifname);
|
|
nm_assert(strlen(ifname) < IFNAMSIZ);
|
|
|
|
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_LINK);
|
|
if (g_strlcpy(o->link.name, ifname, sizeof(o->link.name)) >= sizeof(o->link.name))
|
|
nm_assert_not_reached();
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_LINK_BY_IFNAME;
|
|
return _L(lookup);
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_object_by_ifindex(NMPLookup *lookup, NMPObjectType obj_type, int ifindex)
|
|
{
|
|
NMPObject *o;
|
|
|
|
nm_assert(lookup);
|
|
nm_assert(NM_IN_SET(obj_type,
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE,
|
|
NMP_OBJECT_TYPE_QDISC,
|
|
NMP_OBJECT_TYPE_TFILTER,
|
|
NMP_OBJECT_TYPE_MPTCP_ADDR));
|
|
nm_assert(ifindex > 0
|
|
|| (ifindex == 0
|
|
&& NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)));
|
|
|
|
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
|
|
o->obj_with_ifindex.ifindex = ifindex;
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX;
|
|
return _L(lookup);
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type)
|
|
{
|
|
NMPObject *o;
|
|
|
|
nm_assert(lookup);
|
|
nm_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
|
|
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
|
|
o->ip_route.ifindex = 1;
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES;
|
|
return _L(lookup);
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj)
|
|
{
|
|
const NMPlatformIP4Route *r4;
|
|
const NMPlatformIP6Route *r6;
|
|
|
|
nm_assert(lookup);
|
|
|
|
switch (NMP_OBJECT_GET_TYPE(obj)) {
|
|
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
|
r4 = NMP_OBJECT_CAST_IP4_ROUTE(obj);
|
|
return nmp_lookup_init_ip4_route_by_weak_id(
|
|
lookup,
|
|
nm_platform_route_table_uncoerce(r4->table_coerced, TRUE),
|
|
r4->network,
|
|
r4->plen,
|
|
r4->metric,
|
|
r4->tos);
|
|
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
|
r6 = NMP_OBJECT_CAST_IP6_ROUTE(obj);
|
|
return nmp_lookup_init_ip6_route_by_weak_id(
|
|
lookup,
|
|
nm_platform_route_table_uncoerce(r6->table_coerced, TRUE),
|
|
&r6->network,
|
|
r6->plen,
|
|
r6->metric,
|
|
&r6->src,
|
|
r6->src_plen);
|
|
default:
|
|
nm_assert_not_reached();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
|
|
guint32 route_table,
|
|
in_addr_t network,
|
|
guint plen,
|
|
guint32 metric,
|
|
guint8 tos)
|
|
{
|
|
NMPObject *o;
|
|
|
|
nm_assert(lookup);
|
|
|
|
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE);
|
|
o->ip4_route.ifindex = 1;
|
|
o->ip4_route.plen = plen;
|
|
o->ip4_route.table_coerced = nm_platform_route_table_coerce(route_table);
|
|
o->ip4_route.metric = metric;
|
|
if (network)
|
|
o->ip4_route.network = network;
|
|
o->ip4_route.tos = tos;
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID;
|
|
return _L(lookup);
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_ip6_route_by_weak_id(NMPLookup *lookup,
|
|
guint32 route_table,
|
|
const struct in6_addr *network,
|
|
guint plen,
|
|
guint32 metric,
|
|
const struct in6_addr *src,
|
|
guint8 src_plen)
|
|
{
|
|
NMPObject *o;
|
|
|
|
nm_assert(lookup);
|
|
|
|
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE);
|
|
o->ip6_route.ifindex = 1;
|
|
o->ip6_route.plen = plen;
|
|
o->ip6_route.table_coerced = nm_platform_route_table_coerce(route_table);
|
|
o->ip6_route.metric = metric;
|
|
if (network)
|
|
o->ip6_route.network = *network;
|
|
if (src)
|
|
o->ip6_route.src = *src;
|
|
o->ip6_route.src_plen = src_plen;
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID;
|
|
return _L(lookup);
|
|
}
|
|
|
|
const NMPLookup *
|
|
nmp_lookup_init_object_by_addr_family(NMPLookup *lookup, NMPObjectType obj_type, int addr_family)
|
|
{
|
|
NMPObject *o;
|
|
|
|
nm_assert(lookup);
|
|
nm_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_ROUTING_RULE));
|
|
|
|
if (addr_family == AF_UNSPEC)
|
|
return nmp_lookup_init_obj_type(lookup, obj_type);
|
|
|
|
nm_assert_addr_family(addr_family);
|
|
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type);
|
|
NMP_OBJECT_CAST_ROUTING_RULE(o)->addr_family = addr_family;
|
|
lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY;
|
|
return _L(lookup);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
GArray *
|
|
nmp_cache_lookup_to_array(const NMDedupMultiHeadEntry *head_entry,
|
|
NMPObjectType obj_type,
|
|
gboolean visible_only)
|
|
{
|
|
const NMPClass *klass = nmp_class_from_type(obj_type);
|
|
NMDedupMultiIter iter;
|
|
const NMPObject *o;
|
|
GArray *array;
|
|
|
|
g_return_val_if_fail(klass, NULL);
|
|
|
|
array = g_array_sized_new(FALSE, FALSE, klass->sizeof_public, head_entry ? head_entry->len : 0);
|
|
nmp_cache_iter_for_each (&iter, head_entry, &o) {
|
|
nm_assert(NMP_OBJECT_GET_CLASS(o) == klass);
|
|
if (visible_only && !nmp_object_is_visible(o))
|
|
continue;
|
|
g_array_append_vals(array, &o->object, 1);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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;
|
|
NMDedupMultiIter iter;
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
const NMPlatformLink *link = NULL;
|
|
NMPLookup lookup;
|
|
|
|
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 {
|
|
const NMPObject *obj_best = NULL;
|
|
|
|
if (ifname) {
|
|
if (strlen(ifname) >= IFNAMSIZ)
|
|
return NULL;
|
|
nmp_lookup_init_link_by_ifname(&lookup, ifname);
|
|
} else
|
|
nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK);
|
|
|
|
head_entry = nmp_cache_lookup(cache, &lookup);
|
|
nmp_cache_iter_for_each_link (&iter, head_entry, &link) {
|
|
obj = NMP_OBJECT_UP_CAST(link);
|
|
|
|
if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type)
|
|
continue;
|
|
if (visible_only && !nmp_object_is_visible(obj))
|
|
continue;
|
|
if (match_fn && !match_fn(obj, user_data))
|
|
continue;
|
|
|
|
/* if there are multiple candidates, prefer the visible ones. */
|
|
if (visible_only || nmp_object_is_visible(obj))
|
|
return obj;
|
|
if (!obj_best)
|
|
obj_best = obj;
|
|
}
|
|
return obj_best;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static NMDedupMultiIdxMode
|
|
_obj_get_add_mode(const NMPObject *obj)
|
|
{
|
|
/* new objects are usually appended to the list. Except for
|
|
* addresses, which are prepended during `ip address add`.
|
|
*
|
|
* Actually, for routes it is more complicated, because depending on
|
|
* `ip route append`, `ip route replace`, `ip route prepend`, the object
|
|
* will be added at the tail, at the front, or even replace an element
|
|
* in the list. However, that is handled separately by nmp_cache_update_netlink_route()
|
|
* and of no concern here. */
|
|
if (NM_IN_SET(NMP_OBJECT_GET_TYPE(obj),
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS))
|
|
return NM_DEDUP_MULTI_IDX_MODE_PREPEND;
|
|
return NM_DEDUP_MULTI_IDX_MODE_APPEND;
|
|
}
|
|
|
|
static void
|
|
_idxcache_update_order_for_dump(NMPCache *cache, const NMDedupMultiEntry *entry)
|
|
{
|
|
const NMPClass *klass;
|
|
const guint8 *i_idx_type;
|
|
const NMDedupMultiEntry *entry2;
|
|
|
|
nm_dedup_multi_entry_reorder(entry, NULL, TRUE);
|
|
|
|
klass = NMP_OBJECT_GET_CLASS(entry->obj);
|
|
for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) {
|
|
NMPCacheIdType id_type = *i_idx_type;
|
|
|
|
if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE)
|
|
continue;
|
|
|
|
entry2 = nm_dedup_multi_index_lookup_obj(cache->multi_idx,
|
|
_idx_type_get(cache, id_type),
|
|
entry->obj);
|
|
if (!entry2)
|
|
continue;
|
|
|
|
nm_assert(entry2 != entry);
|
|
nm_assert(entry2->obj == entry->obj);
|
|
|
|
nm_dedup_multi_entry_reorder(entry2, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_idxcache_update_other_cache_ids(NMPCache *cache,
|
|
NMPCacheIdType cache_id_type,
|
|
const NMPObject *obj_old,
|
|
const NMPObject *obj_new,
|
|
gboolean is_dump)
|
|
{
|
|
const NMDedupMultiEntry *entry_new;
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_order;
|
|
NMDedupMultiIdxType *idx_type;
|
|
|
|
nm_assert(obj_new || obj_old);
|
|
nm_assert(!obj_new || NMP_OBJECT_GET_TYPE(obj_new) != NMP_OBJECT_TYPE_UNKNOWN);
|
|
nm_assert(!obj_old || NMP_OBJECT_GET_TYPE(obj_old) != NMP_OBJECT_TYPE_UNKNOWN);
|
|
nm_assert(!obj_old || !obj_new
|
|
|| NMP_OBJECT_GET_CLASS(obj_new) == NMP_OBJECT_GET_CLASS(obj_old));
|
|
nm_assert(!obj_old || !obj_new || !nmp_object_equal(obj_new, obj_old));
|
|
nm_assert(!obj_new || obj_new == nm_dedup_multi_index_obj_find(cache->multi_idx, obj_new));
|
|
nm_assert(!obj_old || obj_old == nm_dedup_multi_index_obj_find(cache->multi_idx, obj_old));
|
|
|
|
idx_type = _idx_type_get(cache, cache_id_type);
|
|
|
|
if (obj_old) {
|
|
entry_old = nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type, obj_old);
|
|
if (!obj_new) {
|
|
if (entry_old)
|
|
nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old);
|
|
return;
|
|
}
|
|
} else
|
|
entry_old = NULL;
|
|
|
|
if (obj_new) {
|
|
if (obj_old && nm_dedup_multi_idx_type_id_equal(idx_type, obj_old, obj_new)
|
|
&& nm_dedup_multi_idx_type_partition_equal(idx_type, obj_old, obj_new)) {
|
|
/* optimize. We just looked up the @obj_old entry and @obj_new compares equal
|
|
* according to idx_obj_id_equal(). entry_new is the same as entry_old. */
|
|
entry_new = entry_old;
|
|
} else {
|
|
entry_new = nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type, obj_new);
|
|
}
|
|
|
|
if (entry_new)
|
|
entry_order = entry_new;
|
|
else if (entry_old
|
|
&& nm_dedup_multi_idx_type_partition_equal(idx_type, entry_old->obj, obj_new))
|
|
entry_order = entry_old;
|
|
else
|
|
entry_order = NULL;
|
|
nm_dedup_multi_index_add_full(
|
|
cache->multi_idx,
|
|
idx_type,
|
|
obj_new,
|
|
is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE : _obj_get_add_mode(obj_new),
|
|
is_dump ? NULL : entry_order,
|
|
entry_new ?: NM_DEDUP_MULTI_ENTRY_MISSING,
|
|
entry_new ? entry_new->head : (entry_order ? entry_order->head : NULL),
|
|
&entry_new,
|
|
NULL);
|
|
|
|
#if NM_MORE_ASSERTS
|
|
if (entry_new) {
|
|
nm_assert(idx_type->klass->idx_obj_partitionable);
|
|
nm_assert(idx_type->klass->idx_obj_partition_equal);
|
|
nm_assert(idx_type->klass->idx_obj_partitionable(idx_type, entry_new->obj));
|
|
nm_assert(idx_type->klass->idx_obj_partition_equal(idx_type,
|
|
(gpointer) obj_new,
|
|
entry_new->obj));
|
|
}
|
|
#endif
|
|
} else
|
|
entry_new = NULL;
|
|
|
|
if (entry_old && entry_old != entry_new)
|
|
nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old);
|
|
}
|
|
|
|
static void
|
|
_idxcache_update(NMPCache *cache,
|
|
const NMDedupMultiEntry *entry_old,
|
|
NMPObject *obj_new,
|
|
gboolean is_dump,
|
|
const NMDedupMultiEntry **out_entry_new)
|
|
{
|
|
const NMPClass *klass;
|
|
const guint8 *i_idx_type;
|
|
NMDedupMultiIdxType *idx_type_o = _idx_type_get(cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE);
|
|
const NMDedupMultiEntry *entry_new = NULL;
|
|
nm_auto_nmpobj const NMPObject *obj_old = NULL;
|
|
|
|
/* we update an object in the cache.
|
|
*
|
|
* Note that @entry_old MUST be what is currently tracked in multi_idx, and it must
|
|
* have the same ID as @obj_new. */
|
|
|
|
nm_assert(cache);
|
|
nm_assert(entry_old || obj_new);
|
|
nm_assert(!obj_new || nmp_object_is_alive(obj_new));
|
|
nm_assert(
|
|
!entry_old
|
|
|| entry_old
|
|
== nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type_o, entry_old->obj));
|
|
nm_assert(!obj_new
|
|
|| entry_old
|
|
== nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type_o, obj_new));
|
|
nm_assert(!entry_old || entry_old->head->idx_type == idx_type_o);
|
|
nm_assert(!entry_old || !obj_new
|
|
|| nm_dedup_multi_idx_type_partition_equal(idx_type_o, entry_old->obj, obj_new));
|
|
nm_assert(!entry_old || !obj_new
|
|
|| nm_dedup_multi_idx_type_id_equal(idx_type_o, entry_old->obj, obj_new));
|
|
nm_assert(!entry_old || !obj_new
|
|
|| (obj_new->parent.klass == ((const NMPObject *) entry_old->obj)->parent.klass
|
|
&& !obj_new->parent.klass->obj_full_equal((NMDedupMultiObj *) obj_new,
|
|
entry_old->obj)));
|
|
|
|
/* keep a reference to the pre-existing entry */
|
|
if (entry_old)
|
|
obj_old = nmp_object_ref(entry_old->obj);
|
|
|
|
/* first update the main index NMP_CACHE_ID_TYPE_OBJECT_TYPE.
|
|
* We already know the pre-existing @entry old, so all that
|
|
* nm_dedup_multi_index_add_full() effectively does, is update the
|
|
* obj reference.
|
|
*
|
|
* We also get the new boxed object, which we need below. */
|
|
if (obj_new) {
|
|
nm_auto_nmpobj NMPObject *obj_old2 = NULL;
|
|
|
|
nm_dedup_multi_index_add_full(cache->multi_idx,
|
|
idx_type_o,
|
|
obj_new,
|
|
is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
|
|
: _obj_get_add_mode(obj_new),
|
|
NULL,
|
|
entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING,
|
|
NULL,
|
|
&entry_new,
|
|
(const NMDedupMultiObj **) &obj_old2);
|
|
nm_assert(entry_new);
|
|
nm_assert(obj_old == obj_old2);
|
|
nm_assert(!entry_old || entry_old == entry_new);
|
|
} else
|
|
nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old);
|
|
|
|
/* now update all other indexes. We know the previously boxed entry, and the
|
|
* newly boxed one. */
|
|
klass = NMP_OBJECT_GET_CLASS(entry_new ? entry_new->obj : obj_old);
|
|
for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) {
|
|
NMPCacheIdType id_type = *i_idx_type;
|
|
|
|
if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE)
|
|
continue;
|
|
_idxcache_update_other_cache_ids(cache,
|
|
id_type,
|
|
obj_old,
|
|
entry_new ? entry_new->obj : NULL,
|
|
is_dump);
|
|
}
|
|
|
|
NM_SET_OUT(out_entry_new, entry_new);
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_remove(NMPCache *cache,
|
|
const NMPObject *obj_needle,
|
|
gboolean equals_by_ptr,
|
|
gboolean only_dirty,
|
|
const NMPObject **out_obj_old)
|
|
{
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMPObject *obj_old;
|
|
|
|
entry_old = _lookup_entry(cache, obj_needle);
|
|
|
|
if (!entry_old) {
|
|
NM_SET_OUT(out_obj_old, NULL);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
obj_old = entry_old->obj;
|
|
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
|
|
if (equals_by_ptr && obj_old != obj_needle) {
|
|
/* We found an identical object, but we only delete it if it's the same pointer as
|
|
* @obj_needle. */
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
if (only_dirty && !entry_old->dirty) {
|
|
/* the entry is not dirty. Skip. */
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_remove_netlink(NMPCache *cache,
|
|
const NMPObject *obj_needle,
|
|
const NMPObject **out_obj_old,
|
|
const NMPObject **out_obj_new)
|
|
{
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_new = NULL;
|
|
const NMPObject *obj_old;
|
|
nm_auto_nmpobj NMPObject *obj_new = NULL;
|
|
|
|
entry_old = _lookup_entry(cache, obj_needle);
|
|
|
|
if (!entry_old) {
|
|
NM_SET_OUT(out_obj_old, NULL);
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
obj_old = entry_old->obj;
|
|
|
|
if (NMP_OBJECT_GET_TYPE(obj_needle) == NMP_OBJECT_TYPE_LINK) {
|
|
/* 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. */
|
|
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
|
|
if (!obj_old->_link.netlink.is_in_netlink) {
|
|
nm_assert(obj_old->_link.udev.device);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
if (!obj_old->_link.udev.device) {
|
|
/* the update would make @obj_old invalid. Remove it. */
|
|
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
obj_new = nmp_object_clone(obj_old, FALSE);
|
|
obj_new->_link.netlink.is_in_netlink = FALSE;
|
|
|
|
_nmp_object_fixup_link_controller_connected(&obj_new, NULL, cache);
|
|
_nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev);
|
|
|
|
_idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
/**
|
|
* nmp_cache_update_netlink:
|
|
* @cache: the platform cache
|
|
* @obj_hand_over: 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.
|
|
* @is_dump: whether this update comes during a dump of object of the same kind.
|
|
* kernel dumps objects in a certain order, which matters especially for routes.
|
|
* Before a dump we mark all objects as dirty, and remove all untouched objects
|
|
* afterwards. Hence, during a dump, every update should move the object to the
|
|
* end of the list, to obtain the correct order. That means, to use NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE,
|
|
* instead of NM_DEDUP_MULTI_IDX_MODE_APPEND.
|
|
* @out_obj_old: (out) (optional) (nullable) (transfer full): return the object
|
|
* with same ID as @obj_hand_over, that was in the cache before update. If an
|
|
* object is returned, the caller must unref it afterwards.
|
|
* @out_obj_new: (out) (optional) (nullable) (transfer full): return the object
|
|
* from the cache after update. The caller must unref this object.
|
|
*
|
|
* Returns: how the cache changed.
|
|
*
|
|
* Even if there was no change in the cache (NMP_CACHE_OPS_UNCHANGED), @out_obj_old
|
|
* and @out_obj_new will be set accordingly.
|
|
**/
|
|
NMPCacheOpsType
|
|
nmp_cache_update_netlink(NMPCache *cache,
|
|
NMPObject *obj_hand_over,
|
|
gboolean is_dump,
|
|
const NMPObject **out_obj_old,
|
|
const NMPObject **out_obj_new)
|
|
{
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_new;
|
|
const NMPObject *obj_old;
|
|
gboolean is_alive;
|
|
|
|
nm_assert(cache);
|
|
nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over));
|
|
nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over));
|
|
/* 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_hand_over) != NMP_OBJECT_TYPE_LINK
|
|
|| (!obj_hand_over->_link.udev.device && !obj_hand_over->link.driver));
|
|
nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over);
|
|
|
|
entry_old = _lookup_entry(cache, obj_hand_over);
|
|
|
|
if (!entry_old) {
|
|
NM_SET_OUT(out_obj_old, NULL);
|
|
|
|
if (!nmp_object_is_alive(obj_hand_over)) {
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_LINK) {
|
|
_nmp_object_fixup_link_controller_connected(&obj_hand_over, NULL, cache);
|
|
_nmp_object_fixup_link_udev_fields(&obj_hand_over, NULL, cache->use_udev);
|
|
}
|
|
|
|
_idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
|
|
return NMP_CACHE_OPS_ADDED;
|
|
}
|
|
|
|
obj_old = entry_old->obj;
|
|
|
|
if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_LINK) {
|
|
if (!obj_hand_over->_link.netlink.is_in_netlink) {
|
|
if (!obj_old->_link.netlink.is_in_netlink) {
|
|
nm_assert(obj_old->_link.udev.device);
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
if (obj_old->_link.udev.device) {
|
|
/* @obj_hand_over 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_hand_over. The usecase is when receiving
|
|
* a valid @obj_hand_over instance from netlink with RTM_DELROUTE.
|
|
*/
|
|
is_alive = TRUE;
|
|
} else
|
|
is_alive = FALSE;
|
|
} else
|
|
is_alive = TRUE;
|
|
|
|
if (is_alive) {
|
|
_nmp_object_fixup_link_controller_connected(&obj_hand_over, NULL, cache);
|
|
|
|
/* Merge the netlink parts with what we have from udev. */
|
|
udev_device_unref(obj_hand_over->_link.udev.device);
|
|
obj_hand_over->_link.udev.device =
|
|
obj_old->_link.udev.device ? udev_device_ref(obj_old->_link.udev.device) : NULL;
|
|
_nmp_object_fixup_link_udev_fields(&obj_hand_over, NULL, cache->use_udev);
|
|
|
|
if (obj_hand_over->_link.netlink.lnk) {
|
|
nm_auto_nmpobj const NMPObject *lnk_old = obj_hand_over->_link.netlink.lnk;
|
|
|
|
/* let's dedup/intern the lnk object. */
|
|
obj_hand_over->_link.netlink.lnk =
|
|
nm_dedup_multi_index_obj_intern(cache->multi_idx, lnk_old);
|
|
}
|
|
}
|
|
} else
|
|
is_alive = nmp_object_is_alive(obj_hand_over);
|
|
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
|
|
if (!is_alive) {
|
|
/* the update would make @obj_old invalid. Remove it. */
|
|
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
if (nmp_object_equal(obj_old, obj_hand_over)) {
|
|
if (is_dump)
|
|
_idxcache_update_order_for_dump(cache, entry_old);
|
|
nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
_idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_update_netlink_route(NMPCache *cache,
|
|
NMPObject *obj_hand_over,
|
|
gboolean is_dump,
|
|
guint16 nlmsgflags,
|
|
gboolean route_is_alive,
|
|
const NMPObject **out_obj_old,
|
|
const NMPObject **out_obj_new,
|
|
const NMPObject **out_obj_replace,
|
|
gboolean *out_resync_required)
|
|
{
|
|
NMDedupMultiIter iter;
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_new;
|
|
const NMDedupMultiEntry *entry_cur;
|
|
const NMDedupMultiEntry *entry_replace;
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
gboolean is_alive;
|
|
NMPCacheOpsType ops_type = NMP_CACHE_OPS_UNCHANGED;
|
|
gboolean resync_required;
|
|
|
|
nm_assert(cache);
|
|
nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over));
|
|
nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over));
|
|
nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_hand_over),
|
|
NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over);
|
|
|
|
if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE)) {
|
|
/* This means, that the message indicates that another route was replaced.
|
|
* Since we don't cache all routes (see "route_is_alive"), we cannot know
|
|
* with certainty which route was replaced.
|
|
*
|
|
* For example, the kernel might have 3 similar routes (same WEAK_ID), one
|
|
* of which is not tracked by us so we don't have it into the cache. If we
|
|
* receive a route replace message, we don't know to what of the 3 routes
|
|
* it affects (one of the 3 we don't even know that exists). Moreover, if
|
|
* we only have one route on cache, we don't know if the replace is for a
|
|
* different one that we don't track.
|
|
*
|
|
* Even if we would cache *all* routes (which we cannot, if kernel adds new
|
|
* routing features that modify the known nmp_object_id_equal()), it would
|
|
* be hard to find the right route that was replaced. Well, probably we
|
|
* would have to keep NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID sorted by order
|
|
* of notifications, which is hard. The code below actually makes an effort
|
|
* to do that, but it's not actually used, because we just resync.
|
|
*
|
|
* The only proper solution for this would be to improve kernel with [1]
|
|
* and [2].
|
|
*
|
|
* [1] https://bugzilla.redhat.com/show_bug.cgi?id=1337855
|
|
* [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860
|
|
*
|
|
* We need to resync.
|
|
*
|
|
* However, a resync is expensive. Think of a routing daemon that updates
|
|
* hundreds of routes per second, the performance penalty is huge. We can
|
|
* optimize it: if we don't have any matching route on cache (by WEAK_ID),
|
|
* we don't have anything to replace and we don't need a full resync, but
|
|
* only to add or discard the new route as usual.
|
|
*/
|
|
if (nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
|
|
entry_replace = NULL;
|
|
resync_required = TRUE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
entry_old = _lookup_entry(cache, obj_hand_over);
|
|
entry_new = NULL;
|
|
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_old)));
|
|
|
|
is_alive = route_is_alive && nmp_object_is_alive(obj_hand_over);
|
|
|
|
if (!entry_old) {
|
|
if (is_alive) {
|
|
_idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new);
|
|
ops_type = NMP_CACHE_OPS_ADDED;
|
|
}
|
|
goto update_done;
|
|
}
|
|
|
|
if (!is_alive) {
|
|
/* the update would make @entry_old invalid. Remove it. */
|
|
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
|
|
ops_type = NMP_CACHE_OPS_REMOVED;
|
|
goto update_done;
|
|
}
|
|
|
|
if (nmp_object_equal(entry_old->obj, obj_hand_over)) {
|
|
if (is_dump)
|
|
_idxcache_update_order_for_dump(cache, entry_old);
|
|
nm_dedup_multi_entry_set_dirty(entry_old, FALSE);
|
|
goto update_done;
|
|
}
|
|
|
|
_idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new);
|
|
ops_type = NMP_CACHE_OPS_UPDATED;
|
|
|
|
update_done:
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_new)));
|
|
|
|
/* a RTM_GETROUTE event may signal that another object was replaced.
|
|
* Find out whether that is the case and return it as @obj_replaced.
|
|
*
|
|
* Also, fixup the order of @entry_new within NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID
|
|
* index. For most parts, we don't care about the order of objects (including routes).
|
|
* But NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID we must keep in the correct order, to
|
|
* properly find @obj_replaced. */
|
|
resync_required = FALSE;
|
|
entry_replace = NULL;
|
|
if (is_dump)
|
|
goto out;
|
|
|
|
if (!entry_new)
|
|
goto out;
|
|
|
|
/* For routes, we only maintain the order correctly for the BY_WEAK_ID
|
|
* index. For all other indexes, their order is not preserved. */
|
|
entry_cur =
|
|
_lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj);
|
|
if (!entry_cur) {
|
|
nm_assert_not_reached();
|
|
goto out;
|
|
}
|
|
nm_assert(entry_cur->obj == entry_new->obj);
|
|
|
|
head_entry = entry_cur->head;
|
|
nm_assert(head_entry
|
|
== nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_cur->obj));
|
|
|
|
if (head_entry->len == 1) {
|
|
/* there is only one object, and we expect it to be @obj_new. */
|
|
nm_assert(nm_dedup_multi_head_entry_get_idx(head_entry, 0) == entry_cur);
|
|
goto out;
|
|
}
|
|
|
|
switch (nlmsgflags & (NLM_F_REPLACE | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND)) {
|
|
case NLM_F_REPLACE:
|
|
/* ip route change */
|
|
|
|
/* get the first element (but skip @obj_new). */
|
|
nm_dedup_multi_iter_init(&iter, head_entry);
|
|
if (!nm_dedup_multi_iter_next(&iter))
|
|
nm_assert_not_reached();
|
|
if (iter.current == entry_cur) {
|
|
if (!nm_dedup_multi_iter_next(&iter))
|
|
nm_assert_not_reached();
|
|
}
|
|
entry_replace = iter.current;
|
|
|
|
nm_assert(entry_replace && entry_cur != entry_replace);
|
|
|
|
nm_dedup_multi_entry_reorder(entry_cur, entry_replace, FALSE);
|
|
break;
|
|
case NLM_F_CREATE | NLM_F_APPEND:
|
|
/* ip route append */
|
|
nm_dedup_multi_entry_reorder(entry_cur, NULL, TRUE);
|
|
break;
|
|
case NLM_F_CREATE:
|
|
/* ip route prepend */
|
|
nm_dedup_multi_entry_reorder(entry_cur, NULL, FALSE);
|
|
break;
|
|
default:
|
|
/* this is an unexpected case, probably a bug that we need to handle better. */
|
|
resync_required = TRUE;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
NM_SET_OUT(out_obj_replace, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_replace)));
|
|
NM_SET_OUT(out_resync_required, resync_required);
|
|
return ops_type;
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_update_link_udev(NMPCache *cache,
|
|
int ifindex,
|
|
struct udev_device *udevice,
|
|
const NMPObject **out_obj_old,
|
|
const NMPObject **out_obj_new)
|
|
{
|
|
const NMPObject *obj_old;
|
|
nm_auto_nmpobj NMPObject *obj_new = NULL;
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_new;
|
|
|
|
entry_old = nmp_cache_lookup_entry_link(cache, ifindex);
|
|
|
|
if (!entry_old) {
|
|
if (!udevice) {
|
|
NM_SET_OUT(out_obj_old, NULL);
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
obj_new = nmp_object_new(NMP_OBJECT_TYPE_LINK, NULL);
|
|
obj_new->link.ifindex = ifindex;
|
|
obj_new->_link.udev.device = udev_device_ref(udevice);
|
|
|
|
_nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev);
|
|
|
|
_idxcache_update(cache, NULL, obj_new, FALSE, &entry_new);
|
|
NM_SET_OUT(out_obj_old, NULL);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
|
|
return NMP_CACHE_OPS_ADDED;
|
|
} else {
|
|
obj_old = entry_old->obj;
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
|
|
if (obj_old->_link.udev.device == udevice) {
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
if (!udevice && !obj_old->_link.netlink.is_in_netlink) {
|
|
/* the update would make @obj_old invalid. Remove it. */
|
|
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_REMOVED;
|
|
}
|
|
|
|
obj_new = nmp_object_clone(obj_old, FALSE);
|
|
|
|
udev_device_unref(obj_new->_link.udev.device);
|
|
obj_new->_link.udev.device = udevice ? udev_device_ref(udevice) : NULL;
|
|
|
|
_nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev);
|
|
|
|
_idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
}
|
|
|
|
NMPCacheOpsType
|
|
nmp_cache_update_link_controller_connected(NMPCache *cache,
|
|
int ifindex,
|
|
const NMPObject **out_obj_old,
|
|
const NMPObject **out_obj_new)
|
|
{
|
|
const NMDedupMultiEntry *entry_old;
|
|
const NMDedupMultiEntry *entry_new = NULL;
|
|
const NMPObject *obj_old;
|
|
nm_auto_nmpobj NMPObject *obj_new = NULL;
|
|
|
|
entry_old = nmp_cache_lookup_entry_link(cache, ifindex);
|
|
|
|
if (!entry_old) {
|
|
NM_SET_OUT(out_obj_old, NULL);
|
|
NM_SET_OUT(out_obj_new, NULL);
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
obj_old = entry_old->obj;
|
|
|
|
if (!nmp_cache_link_connected_needs_toggle(cache, obj_old, NULL, NULL)) {
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old));
|
|
return NMP_CACHE_OPS_UNCHANGED;
|
|
}
|
|
|
|
obj_new = nmp_object_clone(obj_old, FALSE);
|
|
obj_new->link.connected = !obj_old->link.connected;
|
|
|
|
NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old));
|
|
_idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new);
|
|
NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj));
|
|
return NMP_CACHE_OPS_UPDATED;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nmp_cache_dirty_set_all_main(NMPCache *cache, const NMPLookup *lookup)
|
|
{
|
|
const NMDedupMultiHeadEntry *head_entry;
|
|
NMDedupMultiIter iter;
|
|
|
|
nm_assert(cache);
|
|
nm_assert(lookup);
|
|
|
|
head_entry = nmp_cache_lookup(cache, lookup);
|
|
|
|
nm_dedup_multi_iter_init(&iter, head_entry);
|
|
while (nm_dedup_multi_iter_next(&iter)) {
|
|
const NMDedupMultiEntry *main_entry;
|
|
|
|
main_entry = nmp_cache_reresolve_main_entry(cache, iter.current, lookup);
|
|
|
|
nm_dedup_multi_entry_set_dirty(main_entry, TRUE);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMPCache *
|
|
nmp_cache_new(NMDedupMultiIndex *multi_idx, gboolean use_udev)
|
|
{
|
|
NMPCache *cache = g_slice_new0(NMPCache);
|
|
guint i;
|
|
|
|
for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++)
|
|
_dedup_multi_idx_type_init((DedupMultiIdxType *) _idx_type_get(cache, i), i);
|
|
|
|
cache->multi_idx = nm_dedup_multi_index_ref(multi_idx);
|
|
|
|
cache->use_udev = !!use_udev;
|
|
return cache;
|
|
}
|
|
|
|
void
|
|
nmp_cache_free(NMPCache *cache)
|
|
{
|
|
guint i;
|
|
|
|
for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++)
|
|
nm_dedup_multi_index_remove_idx(cache->multi_idx, _idx_type_get(cache, i));
|
|
|
|
nm_dedup_multi_index_unref(cache->multi_idx);
|
|
|
|
g_slice_free(NMPCache, cache);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nmtst_assert_nmp_cache_is_consistent(const NMPCache *cache)
|
|
{}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* below, ensure that addr_family get's automatically initialize to AF_UNSPEC. */
|
|
G_STATIC_ASSERT(AF_UNSPEC == 0);
|
|
|
|
typedef const char *(*CmdPlobjToStringFunc)(const NMPlatformObject *obj, char *buf, gsize len);
|
|
typedef const char *(*CmdPlobjToStringIdFunc)(const NMPlatformObject *obj, char *buf, gsize len);
|
|
typedef void (*CmdPlobjHashUpdateFunc)(const NMPlatformObject *obj, NMHashState *h);
|
|
typedef int (*CmdPlobjCmpFunc)(const NMPlatformObject *obj1, const NMPlatformObject *obj2);
|
|
|
|
const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
|
|
[NMP_OBJECT_TYPE_LINK - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LINK,
|
|
.sizeof_data = sizeof(NMPObjectLink),
|
|
.sizeof_public = sizeof(NMPlatformLink),
|
|
.obj_type_name = "link",
|
|
.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_hash_update = _vt_cmd_obj_hash_update_link,
|
|
.cmd_obj_cmp = _vt_cmd_obj_cmp_link,
|
|
.cmd_obj_copy = _vt_cmd_obj_copy_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,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP4_ADDRESS - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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_is_alive = _vt_cmd_obj_is_alive_ipx_address,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_address,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_address,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_address_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip4_address_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp_full,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP6_ADDRESS - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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_is_alive = _vt_cmd_obj_is_alive_ipx_address,
|
|
.cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp_full,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP4_ROUTE - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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_ipx_route,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route,
|
|
.cmd_obj_hash_update = _vt_cmd_obj_hash_update_ip4_route,
|
|
.cmd_obj_cmp = _vt_cmd_obj_cmp_ip4_route,
|
|
.cmd_obj_copy = _vt_cmd_obj_copy_ip4_route,
|
|
.cmd_obj_dispose = _vt_cmd_obj_dispose_ip4_route,
|
|
.cmd_obj_to_string = _vt_cmd_obj_to_string_ip4_route,
|
|
},
|
|
[NMP_OBJECT_TYPE_IP6_ROUTE - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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_ipx_route,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_route,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_route,
|
|
.cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_route_to_string,
|
|
.cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route,
|
|
.cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip6_route,
|
|
},
|
|
[NMP_OBJECT_TYPE_ROUTING_RULE - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_ROUTING_RULE,
|
|
.sizeof_data = sizeof(NMPObjectRoutingRule),
|
|
.sizeof_public = sizeof(NMPlatformRoutingRule),
|
|
.obj_type_name = "routing-rule",
|
|
.rtm_gettype = RTM_GETRULE,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_ROUTING_RULE,
|
|
.signal_type = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_routing_rules,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_routing_rule,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_routing_rule,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule,
|
|
.cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_routing_rule_to_string,
|
|
.cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule,
|
|
.cmd_plobj_cmp = _vt_cmd_plobj_cmp_routing_rule,
|
|
},
|
|
[NMP_OBJECT_TYPE_QDISC - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_QDISC,
|
|
.sizeof_data = sizeof(NMPObjectQdisc),
|
|
.sizeof_public = sizeof(NMPlatformQdisc),
|
|
.obj_type_name = "qdisc",
|
|
.rtm_gettype = RTM_GETQDISC,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_QDISC,
|
|
.signal_type = NM_PLATFORM_SIGNAL_QDISC_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_object,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_qdisc,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_qdisc,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_qdisc,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_qdisc,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_qdisc_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_qdisc_hash_update,
|
|
.cmd_plobj_cmp = _vt_cmd_plobj_cmp_qdisc,
|
|
},
|
|
[NMP_OBJECT_TYPE_TFILTER - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_TFILTER,
|
|
.sizeof_data = sizeof(NMPObjectTfilter),
|
|
.sizeof_public = sizeof(NMPlatformTfilter),
|
|
.obj_type_name = "tfilter",
|
|
.rtm_gettype = RTM_GETTFILTER,
|
|
.signal_type_id = NM_PLATFORM_SIGNAL_ID_TFILTER,
|
|
.signal_type = NM_PLATFORM_SIGNAL_TFILTER_CHANGED,
|
|
.supported_cache_ids = _supported_cache_ids_object,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_tfilter,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_tfilter,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_tfilter,
|
|
.cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_tfilter,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_tfilter_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_tfilter_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_tfilter_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_BRIDGE - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_BRIDGE,
|
|
.sizeof_data = sizeof(NMPObjectLnkBridge),
|
|
.sizeof_public = sizeof(NMPlatformLnkBridge),
|
|
.obj_type_name = "bridge",
|
|
.lnk_link_type = NM_LINK_TYPE_BRIDGE,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_bridge_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bridge_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bridge_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_GRE - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_gre_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_gre_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_gre_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_GRETAP - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_GRETAP,
|
|
.sizeof_data = sizeof(NMPObjectLnkGre),
|
|
.sizeof_public = sizeof(NMPlatformLnkGre),
|
|
.obj_type_name = "gretap",
|
|
.lnk_link_type = NM_LINK_TYPE_GRETAP,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_gre_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_gre_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_gre_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_HSR - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_HSR,
|
|
.sizeof_data = sizeof(NMPObjectLnkHsr),
|
|
.sizeof_public = sizeof(NMPlatformLnkHsr),
|
|
.obj_type_name = "hsr",
|
|
.lnk_link_type = NM_LINK_TYPE_HSR,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_hsr_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_hsr_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_hsr_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_infiniband_to_string,
|
|
.cmd_plobj_hash_update =
|
|
(CmdPlobjHashUpdateFunc) nm_platform_lnk_infiniband_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_infiniband_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IP6TNL - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IP6GRE - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_IP6GRE,
|
|
.sizeof_data = sizeof(NMPObjectLnkIp6Tnl),
|
|
.sizeof_public = sizeof(NMPlatformLnkIp6Tnl),
|
|
.obj_type_name = "ip6gre",
|
|
.lnk_link_type = NM_LINK_TYPE_IP6GRE,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IP6GRETAP - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_IP6GRETAP,
|
|
.sizeof_data = sizeof(NMPObjectLnkIp6Tnl),
|
|
.sizeof_public = sizeof(NMPlatformLnkIp6Tnl),
|
|
.obj_type_name = "ip6gretap",
|
|
.lnk_link_type = NM_LINK_TYPE_IP6GRETAP,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IPIP - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_ipip_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ipip_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ipip_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_IPVLAN - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_IPVLAN,
|
|
.sizeof_data = sizeof(NMPObjectLnkIpvlan),
|
|
.sizeof_public = sizeof(NMPlatformLnkIpvlan),
|
|
.obj_type_name = "ipvlan",
|
|
.lnk_link_type = NM_LINK_TYPE_IPVLAN,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ipvlan_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ipvlan_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ipvlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_MACSEC - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_MACSEC,
|
|
.sizeof_data = sizeof(NMPObjectLnkMacsec),
|
|
.sizeof_public = sizeof(NMPlatformLnkMacsec),
|
|
.obj_type_name = "macsec",
|
|
.lnk_link_type = NM_LINK_TYPE_MACSEC,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_macsec_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macsec_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_macsec_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_MACVLAN - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_macvlan_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macvlan_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_macvlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_MACVTAP - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP,
|
|
.sizeof_data = sizeof(NMPObjectLnkMacvtap),
|
|
.sizeof_public = sizeof(NMPlatformLnkMacvlan),
|
|
.obj_type_name = "macvtap",
|
|
.lnk_link_type = NM_LINK_TYPE_MACVTAP,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_macvlan_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macvlan_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_macvlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_SIT - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_sit_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_sit_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_sit_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_TUN - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_TUN,
|
|
.sizeof_data = sizeof(NMPObjectLnkTun),
|
|
.sizeof_public = sizeof(NMPlatformLnkTun),
|
|
.obj_type_name = "tun",
|
|
.lnk_link_type = NM_LINK_TYPE_TUN,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_tun_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_tun_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_tun_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VLAN - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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_hash_update = _vt_cmd_obj_hash_update_lnk_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,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VRF - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_VRF,
|
|
.sizeof_data = sizeof(NMPObjectLnkVrf),
|
|
.sizeof_public = sizeof(NMPlatformLnkVrf),
|
|
.obj_type_name = "vrf",
|
|
.lnk_link_type = NM_LINK_TYPE_VRF,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vrf_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vrf_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vrf_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VTI - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_VTI,
|
|
.sizeof_data = sizeof(NMPObjectLnkVti),
|
|
.sizeof_public = sizeof(NMPlatformLnkVti),
|
|
.obj_type_name = "vti",
|
|
.lnk_link_type = NM_LINK_TYPE_VTI,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VTI6 - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_VTI6,
|
|
.sizeof_data = sizeof(NMPObjectLnkVti6),
|
|
.sizeof_public = sizeof(NMPlatformLnkVti6),
|
|
.obj_type_name = "vti6",
|
|
.lnk_link_type = NM_LINK_TYPE_VTI6,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti6_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti6_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti6_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_VXLAN - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.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 = (CmdPlobjToStringFunc) nm_platform_lnk_vxlan_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vxlan_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vxlan_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_WIREGUARD - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_WIREGUARD,
|
|
.sizeof_data = sizeof(NMPObjectLnkWireGuard),
|
|
.sizeof_public = sizeof(NMPlatformLnkWireGuard),
|
|
.obj_type_name = "wireguard",
|
|
.lnk_link_type = NM_LINK_TYPE_WIREGUARD,
|
|
.cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_wireguard,
|
|
.cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_wireguard,
|
|
.cmd_obj_copy = _vt_cmd_obj_copy_lnk_wireguard,
|
|
.cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard,
|
|
.cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard,
|
|
},
|
|
[NMP_OBJECT_TYPE_LNK_BOND - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_LNK_BOND,
|
|
.sizeof_data = sizeof(NMPObjectLnkBond),
|
|
.sizeof_public = sizeof(NMPlatformLnkBond),
|
|
.obj_type_name = "bond",
|
|
.lnk_link_type = NM_LINK_TYPE_BOND,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_bond_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bond_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bond_cmp,
|
|
},
|
|
[NMP_OBJECT_TYPE_MPTCP_ADDR - 1] =
|
|
{
|
|
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
|
.obj_type = NMP_OBJECT_TYPE_MPTCP_ADDR,
|
|
.sizeof_data = sizeof(NMPObjectMptcpAddr),
|
|
.sizeof_public = sizeof(NMPlatformMptcpAddr),
|
|
.obj_type_name = "mptcp-addr",
|
|
.supported_cache_ids = _supported_cache_ids_object,
|
|
.cmd_obj_is_alive = _vt_cmd_obj_is_alive_mptcp_addr,
|
|
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_mptcp_addr,
|
|
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_mptcp_addr,
|
|
.cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_mptcp_addr_to_string,
|
|
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_mptcp_addr_to_string,
|
|
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_mptcp_addr_hash_update,
|
|
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_mptcp_addr_cmp,
|
|
},
|
|
};
|