platform: properly handle peer-address for IPv4 addresses

Kernel allows to add the same IPv4 address that only differs by
peer-address (IFL_ADDRESS):

    $ ip link add dummy type dummy
    $ ip address add 1.1.1.1 peer 1.1.1.3/24 dev dummy
    $ ip address add 1.1.1.1 peer 1.1.1.4/24 dev dummy
    RTNETLINK answers: File exists
    $ ip address add 1.1.1.1 peer 1.1.2.3/24 dev dummy
    $ ip address show dev dummy
    2: dummy@NONE: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
        link/ether 52:58:a7:1e:e8:93 brd ff:ff:ff:ff:ff:ff
        inet 1.1.1.1 peer 1.1.1.3/24 scope global dummy
           valid_lft forever preferred_lft forever
        inet 1.1.1.1 peer 1.1.2.3/24 scope global dummy
           valid_lft forever preferred_lft forever

We must also consider peer-address, otherwise platform will treat
two different addresses as one and the same.

https://bugzilla.gnome.org/show_bug.cgi?id=756356
This commit is contained in:
Thomas Haller
2015-10-10 17:48:32 +02:00
parent df8e5da3c0
commit 8968e15eb7
9 changed files with 114 additions and 46 deletions

View File

@@ -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++) {

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,14 +426,18 @@ _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 (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);
@@ -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);

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);
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);