diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 160c24ea1..bcef61dbb 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -179,7 +179,8 @@ static gboolean addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { return a->address == b->address - && a->plen == b->plen; + && a->plen == b->plen + && nm_platform_ip4_address_equal_peer_net (a, b); } static gboolean @@ -2014,6 +2015,7 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); hash_u32 (sum, address->address); hash_u32 (sum, address->plen); + hash_u32 (sum, nm_platform_ip4_address_get_peer_net (address)); } for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 800425ace..0732802ba 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1031,15 +1031,24 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int } static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; + NMPlatformIP4Address a = { + .ifindex = ifindex, + .address = addr, + .plen = plen, + .peer_address = peer_address, + }; for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address->ifindex == ifindex && address->plen == plen && address->address == addr) + if ( address->ifindex == ifindex + && address->plen == plen + && address->address == addr + && nm_platform_ip4_address_equal_peer_net (address, &a)) return address; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1f6b1e22f..8b7a617e3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -4252,7 +4252,7 @@ ip4_address_add (NMPlatform *platform, plen, lifetime, preferred, 0, label); return do_add_addrroute (platform, - nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen), + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen, peer_addr), nlo); } @@ -4283,8 +4283,7 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, { NMPObject obj_needle; - nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); - obj_needle.ip4_address.peer_address = peer_address; + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen, peer_address); return do_delete_object (platform, &obj_needle, NULL); } @@ -4298,12 +4297,12 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int } static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { NMPObject obj_needle; const NMPObject *obj; - nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen, peer_address); obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); if (nmp_object_is_visible (obj)) return &obj->ip4_address; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 4cb57bed0..1469b5fee 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1823,6 +1823,43 @@ _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) /******************************************************************/ +in_addr_t +nm_platform_ip4_address_get_peer (const NMPlatformIP4Address *addr) +{ + return addr->peer_address ?: addr->address; +} + +const struct in6_addr * +nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr) +{ + if ( IN6_IS_ADDR_UNSPECIFIED (&addr->peer_address) + || IN6_ARE_ADDR_EQUAL (&addr->peer_address, &addr->address)) + return &addr->address; + return &addr->peer_address; +} + +in_addr_t +nm_platform_ip4_address_get_peer_net (const NMPlatformIP4Address *addr) +{ + return (addr->peer_address ?: addr->address) & nm_utils_ip4_prefix_to_netmask (addr->plen); +} + +gboolean +nm_platform_ip4_address_equal_peer_net (const NMPlatformIP4Address *addr1, const NMPlatformIP4Address *addr2) +{ + guint32 a1, a2; + + if (addr1->plen != addr2->plen) + return FALSE; + + /* For kernel, if the peer address is unset, that effectively means that + * the peer address equals the local address. */ + a1 = addr1->peer_address ? addr1->peer_address : addr1->address; + a2 = addr2->peer_address ? addr2->peer_address : addr2->address; + + return ((a1 ^ a2) & nm_utils_ip4_prefix_to_netmask (addr1->plen)) == 0; +} + GArray * nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { @@ -1957,13 +1994,13 @@ nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr a } const NMPlatformIP4Address * -nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen) +nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen, guint32 peer_address) { _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (plen > 0, NULL); - return klass->ip4_address_get (self, ifindex, address, plen); + return klass->ip4_address_get (self, ifindex, address, plen, peer_address); } const NMPlatformIP6Address * @@ -1985,7 +2022,9 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address for (i = 0; i < len; i++) { NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i); - if (candidate->address == address->address && candidate->plen == address->plen) { + if ( candidate->address == address->address + && candidate->plen == address->plen + && nm_platform_ip4_address_equal_peer_net (candidate, address)) { guint32 lifetime, preferred; if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, @@ -2702,6 +2741,12 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return 1; \ } G_STMT_END +#define _CMP_DIRECT(a, b) \ + G_STMT_START { \ + if ((a) != (b)) \ + return ((a) < (b)) ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD(a, b, field) \ G_STMT_START { \ if (((a)->field) != ((b)->field)) \ @@ -2784,12 +2829,20 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) { + in_addr_t p_a, p_b; + _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, source); _CMP_FIELD (a, b, address); - _CMP_FIELD (a, b, peer_address); _CMP_FIELD (a, b, plen); + + /* a peer-address of zero is the same as setting it to address. + * Here we consider the full address, including the host-part. */ + p_a = nm_platform_ip4_address_get_peer (a); + p_b = nm_platform_ip4_address_get_peer (b); + _CMP_DIRECT (p_a, p_b); + _CMP_FIELD (a, b, timestamp); _CMP_FIELD (a, b, lifetime); _CMP_FIELD (a, b, preferred); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index d68be070d..33fc480c9 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -531,7 +531,7 @@ typedef struct { guint flags); gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, int plen); - const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, int plen); + const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address, int plen); GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); @@ -705,7 +705,12 @@ guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel); gboolean nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); -const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen); +in_addr_t nm_platform_ip4_address_get_peer (const NMPlatformIP4Address *addr); +const struct in6_addr *nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr); +in_addr_t nm_platform_ip4_address_get_peer_net (const NMPlatformIP4Address *addr); +gboolean nm_platform_ip4_address_equal_peer_net (const NMPlatformIP4Address *addr1, const NMPlatformIP4Address *addr2); + +const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen); GArray *nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex); GArray *nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 162b61fea..1a35a352a 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -296,19 +296,20 @@ _vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src) } const NMPObject * -nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen) +nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen, guint32 peer_address) { nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL); obj->ip4_address.ifindex = ifindex; obj->ip4_address.address = address; obj->ip4_address.plen = plen; + obj->ip4_address.peer_address = peer_address; return obj; } static void _vt_cmd_obj_stackinit_id_ip4_address (NMPObject *obj, const NMPObject *src) { - nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen); + nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen, src->ip4_address.peer_address); } const NMPObject * @@ -425,17 +426,21 @@ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsiz { \ plat_type *const obj = (plat_type *) _obj; \ char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ + char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ \ (void) buf1; \ + (void) buf2; \ g_snprintf (buf, buf_len, \ __VA_ARGS__); \ return buf; \ } -_vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); -_vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen); -_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s/%d", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1), obj->plen); -_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); -_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); +_vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); +_vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen, + obj->peer_address && obj->peer_address != obj->address ? "," : "", + obj->peer_address && obj->peer_address != obj->address ? nm_utils_inet4_ntop (nm_platform_ip4_address_get_peer_net (obj), buf2) : ""); +_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s/%d", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1), obj->plen); +_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); +_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) @@ -533,6 +538,7 @@ _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; @@ -619,7 +625,10 @@ _vt_cmd_plobj_id_equal (link, NMPlatformLink, _vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address, obj1->ifindex == obj2->ifindex && obj1->plen == obj2->plen - && obj1->address == obj2->address); + && obj1->address == obj2->address + /* for IPv4 addresses, you can add the same local address with differing peer-adddress + * (IFA_ADDRESS), provided that their net-part differs. */ + && nm_platform_ip4_address_equal_peer_net (obj1, obj2)); _vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, obj1->ifindex == obj2->ifindex && obj1->plen == obj2->plen @@ -655,21 +664,17 @@ _vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ return hash; \ } _vt_cmd_plobj_id_hash (link, NMPlatformLink, { - /* libnl considers: - * .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY, - */ hash = (guint) 3982791431u; hash = hash + ((guint) obj->ifindex); }) _vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { - /* libnl considers: - * .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | - * ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN), - */ hash = (guint) 3591309853u; hash = hash + ((guint) obj->ifindex); hash = hash * 33 + ((guint) obj->plen); hash = hash * 33 + ((guint) obj->address); + + /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ + hash = hash * 33 + ((guint) (nm_platform_ip4_address_get_peer_net (obj))); }) _vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { hash = (guint) 2907861637u; @@ -678,11 +683,6 @@ _vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { hash = hash * 33 + _id_hash_ip6_addr (&obj->address); }) _vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { - /* libnl considers: - * .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | - * ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | - * ROUTE_ATTR_PRIO), - */ hash = (guint) 2569857221u; hash = hash + ((guint) obj->ifindex); hash = hash * 33 + ((guint) obj->plen); diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 5b1887b82..1367e8e67 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -269,7 +269,7 @@ NMPObject *nmp_object_new_link (int ifindex); const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj); const NMPObject *nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src); const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex); -const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen); +const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen, guint32 peer_address); const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, int plen); const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, int plen, guint32 metric); const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, int plen, guint32 metric); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index ec671eaa2..08e89007f 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -624,11 +624,11 @@ do_ip6_address_add (char **argv) } else \ return FALSE; \ } -#define ADDR_CMD(cmdname) ADDR_CMD_FULL (ip4, cmdname, FALSE, 0) ADDR_CMD_FULL (ip6, cmdname, FALSE) -#define ADDR_CMD_PRINT(cmdname) ADDR_CMD_FULL (ip4, cmdname, TRUE) ADDR_CMD_FULL (ip6, cmdname, TRUE) +#define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, 0, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, FALSE) +#define ADDR_CMD_PRINT(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, TRUE, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, TRUE) ADDR_CMD (delete) -ADDR_CMD_PRINT (get) +ADDR_CMD_PRINT (get, 0) static gboolean do_ip4_route_get_all (char **argv) diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 14cd9efdf..902bc43df 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -64,9 +64,9 @@ test_ip4_address (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL)); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); accept_signal (address_added); /* Add address again (aka update) */ @@ -85,7 +85,7 @@ test_ip4_address (void) /* Remove address */ g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); accept_signal (address_removed); /* Remove address again */ @@ -167,20 +167,20 @@ test_ip4_address_external (void) run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); wait_signal (address_added); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); wait_signal (address_removed); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); /* Add/delete conflict */ run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", - IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); + IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL)); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); accept_signal (address_added); /*run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); accept_signal (address_removed);*/ free_signal (address_added);