platform: add semantic comparison for IP addresses and add "nm_platform_vtable_address"

We already have a comparison of NMPlatformIPXAddress with the modes
"full" and "id". The former is needed to fully compare two addresses,
the latter as identity for tracking addresses in the cache.

In NetworkManager we also use the NMPlatformIP[46]Address structure to
track the addresses we want to configure. When we add them in kernel,
we will later see them in the platform cache. However, some fields
will be slightly different. For example, "addr_source" address will
always be "kernel", because that one is not a field we configure in
kernel. Also, the "n_ifa_flags" probably differ (getting "permanent"
and "secondary" flags).

Add a compare function that can ignore such differences.

Also add nm_platform_vtable_address for accessing the IPv4 and IPv6
methods generically (based on an "IS_IPv4" variable).
This commit is contained in:
Thomas Haller 2022-03-29 18:20:31 +02:00
parent 7c92663f8d
commit ef1b60c061
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
3 changed files with 224 additions and 72 deletions

View file

@ -7937,6 +7937,25 @@ _address_pretty_sort_get_prio_6(const struct in6_addr *addr)
return 6;
}
static int
_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b)
{
guint32 lifetime_a;
guint32 lifetime_b;
guint32 preferred_a;
guint32 preferred_b;
gint32 now = 0;
lifetime_a =
nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a);
lifetime_b =
nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b);
NM_CMP_DIRECT(lifetime_a, lifetime_b);
NM_CMP_DIRECT(preferred_a, preferred_b);
return 0;
}
int
nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1,
const NMPlatformIP6Address *a2,
@ -8016,26 +8035,55 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState
}
int
nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a,
const NMPlatformIP4Address *b,
NMPlatformIPAddressCmpType cmp_type)
{
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, ifindex);
NM_CMP_FIELD(a, b, address);
NM_CMP_FIELD(a, b, plen);
NM_CMP_FIELD(a, b, peer_address);
NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address);
if (a->use_ip4_broadcast_address)
NM_CMP_FIELD(a, b, broadcast_address);
NM_CMP_FIELD(a, b, addr_source);
NM_CMP_FIELD(a, b, timestamp);
NM_CMP_FIELD(a, b, lifetime);
NM_CMP_FIELD(a, b, preferred);
NM_CMP_FIELD(a, b, n_ifa_flags);
NM_CMP_FIELD_STR(a, b, label);
NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready);
NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once);
NM_CMP_FIELD_UNSAFE(a, b, a_force_commit);
return 0;
NM_CMP_FIELD(a, b, address);
switch (cmp_type) {
case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID:
/* for IPv4 addresses, you can add the same local address with differing peer-address
* (IFA_ADDRESS), provided that their net-part differs. */
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen);
return 0;
case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY:
case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL:
NM_CMP_FIELD(a, b, peer_address);
NM_CMP_FIELD_STR(a, b, label);
if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) {
NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a,
(const NMPlatformIPAddress *) b));
/* Most flags are set by kernel. We only compare the ones that
* NetworkManager actively sets.
*
* NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6),
* where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE.
* There are thus no flags to compare for IPv4. */
NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a),
nm_platform_ip4_broadcast_address_from_addr(b));
} else {
NM_CMP_FIELD(a, b, timestamp);
NM_CMP_FIELD(a, b, lifetime);
NM_CMP_FIELD(a, b, preferred);
NM_CMP_FIELD(a, b, n_ifa_flags);
NM_CMP_FIELD(a, b, addr_source);
NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address);
if (a->use_ip4_broadcast_address)
NM_CMP_FIELD(a, b, broadcast_address);
NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready);
NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once);
NM_CMP_FIELD_UNSAFE(a, b, a_force_commit);
}
return 0;
}
return nm_assert_unreachable_val(0);
}
void
@ -8056,25 +8104,51 @@ nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState
}
int
nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a,
const NMPlatformIP6Address *b,
NMPlatformIPAddressCmpType cmp_type)
{
const struct in6_addr *p_a, *p_b;
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, ifindex);
NM_CMP_FIELD_MEMCMP(a, b, address);
NM_CMP_FIELD(a, b, plen);
p_a = nm_platform_ip6_address_get_peer(a);
p_b = nm_platform_ip6_address_get_peer(b);
NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a));
NM_CMP_FIELD(a, b, addr_source);
NM_CMP_FIELD(a, b, timestamp);
NM_CMP_FIELD(a, b, lifetime);
NM_CMP_FIELD(a, b, preferred);
NM_CMP_FIELD(a, b, n_ifa_flags);
NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once);
NM_CMP_FIELD_UNSAFE(a, b, a_force_commit);
return 0;
NM_CMP_FIELD_IN6ADDR(a, b, address);
switch (cmp_type) {
case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID:
/* for IPv6 addresses, the prefix length is not part of the primary identifier. */
return 0;
case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY:
case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL:
NM_CMP_FIELD(a, b, plen);
p_a = nm_platform_ip6_address_get_peer(a);
p_b = nm_platform_ip6_address_get_peer(b);
NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a));
if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) {
NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a,
(const NMPlatformIPAddress *) b));
/* Most flags are set by kernel. We only compare the ones that
* NetworkManager actively sets.
*
* NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR,
* where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE.
* We thus only care about IFA_F_MANAGETEMPADDR. */
NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR,
b->n_ifa_flags & IFA_F_MANAGETEMPADDR);
} else {
NM_CMP_FIELD(a, b, timestamp);
NM_CMP_FIELD(a, b, lifetime);
NM_CMP_FIELD(a, b, preferred);
NM_CMP_FIELD(a, b, n_ifa_flags);
NM_CMP_FIELD(a, b, addr_source);
NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once);
NM_CMP_FIELD_UNSAFE(a, b, a_force_commit);
}
return 0;
}
return nm_assert_unreachable_val(0);
}
void
@ -9030,6 +9104,37 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns)
/*****************************************************************************/
const _NMPlatformVTableAddressUnion nm_platform_vtable_address = {
.v4 =
{
.is_ip4 = TRUE,
.obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS,
.addr_family = AF_INET,
.sizeof_address = sizeof(NMPlatformIP4Address),
.address_cmp =
(int (*)(const NMPlatformIPXAddress *a,
const NMPlatformIPXAddress *b,
NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp,
.address_to_string = (const char *(*) (const NMPlatformIPXAddress *address,
char *buf,
gsize len)) nm_platform_ip4_address_to_string,
},
.v6 =
{
.is_ip4 = FALSE,
.obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS,
.addr_family = AF_INET6,
.sizeof_address = sizeof(NMPlatformIP6Address),
.address_cmp =
(int (*)(const NMPlatformIPXAddress *a,
const NMPlatformIPXAddress *b,
NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp,
.address_to_string = (const char *(*) (const NMPlatformIPXAddress *address,
char *buf,
gsize len)) nm_platform_ip6_address_to_string,
},
};
const _NMPlatformVTableRouteUnion nm_platform_vtable_route = {
.v4 =
{

View file

@ -94,6 +94,14 @@ typedef enum {
NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL,
} NMPNlmFlags;
typedef enum {
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID,
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY,
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL,
} NMPlatformIPAddressCmpType;
typedef enum {
/* compare fields which kernel considers as similar routes.
* It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
@ -775,6 +783,27 @@ typedef struct {
#undef __NMPlatformObjWithIfindex_COMMON
typedef struct {
bool is_ip4;
NMPObjectType obj_type;
gint8 addr_family;
guint8 sizeof_address;
int (*address_cmp)(const NMPlatformIPXAddress *a,
const NMPlatformIPXAddress *b,
NMPlatformIPAddressCmpType cmp_type);
const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len);
} NMPlatformVTableAddress;
typedef union {
struct {
NMPlatformVTableAddress v6;
NMPlatformVTableAddress v4;
};
NMPlatformVTableAddress vx[2];
} _NMPlatformVTableAddressUnion;
extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address;
typedef struct {
bool is_ip4;
gint8 addr_family;
@ -2343,8 +2372,24 @@ int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan
int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b);
int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b);
int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b);
int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b);
int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b);
int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a,
const NMPlatformIP4Address *b,
NMPlatformIPAddressCmpType cmp_type);
int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a,
const NMPlatformIP6Address *b,
NMPlatformIPAddressCmpType cmp_type);
static inline int
nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
{
return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL);
}
static inline int
nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
{
return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL);
}
int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1,
const NMPlatformIP4Address *a2);

View file

@ -1511,20 +1511,21 @@ nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2)
_vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); });
_vt_cmd_plobj_id_cmp(ip4_address, NMPlatformIP4Address, {
NM_CMP_FIELD(obj1, obj2, ifindex);
NM_CMP_FIELD(obj1, obj2, plen);
NM_CMP_FIELD(obj1, obj2, address);
/* for IPv4 addresses, you can add the same local address with differing peer-address
* (IFA_ADDRESS), provided that their net-part differs. */
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(obj1->peer_address, obj2->peer_address, obj1->plen);
});
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);
}
_vt_cmd_plobj_id_cmp(ip6_address, NMPlatformIP6Address, {
NM_CMP_FIELD(obj1, obj2, ifindex);
/* for IPv6 addresses, the prefix length is not part of the primary identifier. */
NM_CMP_FIELD_IN6ADDR(obj1, obj2, address);
});
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);
@ -1539,24 +1540,24 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, {
static int
_vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
{
return nm_platform_ip4_route_cmp((NMPlatformIP4Route *) obj1,
(NMPlatformIP4Route *) obj2,
return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1,
(const NMPlatformIP4Route *) obj2,
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID);
}
static int
_vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
{
return nm_platform_ip6_route_cmp((NMPlatformIP6Route *) obj1,
(NMPlatformIP6Route *) 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((NMPlatformRoutingRule *) obj1,
(NMPlatformRoutingRule *) obj2,
return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1,
(const NMPlatformRoutingRule *) obj2,
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID);
}
@ -3146,28 +3147,29 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.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,
.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_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},
[NMP_OBJECT_TYPE_IP4_ROUTE - 1] =
{
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),