Commit 6c8aa669 authored by Thomas Haller's avatar Thomas Haller

platform: properly handle IPv4 peer-addresses

The peer-address (IFA_ADDRESS) can also be all-zero (0.0.0.0).
That is distinct from an usual address without explicit peer-address,
which implicitly has the same peer and local address.

Previously, we treated an all-zero peer_address as having peer and
local address equal. This is especially grave, because the peer is part
of the primary key for an IPv4 address. So we not only get a property of
the address wrong, but we wrongly consider two different addresses as
one and the same.

To properly handle these addresses, we always must explicitly set the peer.
parent aa5b89a2
......@@ -3274,8 +3274,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla)
g_assert (config);
memset (&address, 0, sizeof (address));
address.address = lla;
address.plen = 16;
nm_platform_ip4_address_set_addr (&address, lla, 16);
address.source = NM_IP_CONFIG_SOURCE_IP4LL;
nm_ip4_config_add_address (config, &address);
......@@ -4016,10 +4015,11 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres
if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) {
/* Use the first user-supplied address */
NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0);
in_addr_t a;
g_assert (user);
nm_ip_address_get_address_binary (user, &address->address);
address->plen = nm_ip_address_get_prefix (user);
nm_ip_address_get_address_binary (user, &a);
nm_platform_ip4_address_set_addr (address, a, nm_ip_address_get_prefix (user));
} else {
/* Find an unused address in the 10.42.x.x range */
guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */
......@@ -4032,8 +4032,7 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres
return FALSE;
}
}
address->address = start + count;
address->plen = 24;
nm_platform_ip4_address_set_addr (address, start + count, 24);
g_hash_table_insert (shared_ips,
GUINT_TO_POINTER (address->address),
......
......@@ -750,6 +750,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
memset (&address, 0, sizeof (address));
address.address = address_network;
address.peer_address = address_network;
address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config);
address.source = NM_IP_CONFIG_SOURCE_WWAN;
nm_ip4_config_add_address (config, &address);
......
......@@ -675,6 +675,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
continue;
if (!inet_pton (AF_INET, value, &address.address))
continue;
address.peer_address = address.address;
/* Gateway */
value = g_hash_table_lookup (hash, "option routers");
......
......@@ -232,6 +232,7 @@ lease_to_ip4_config (const char *iface,
sd_dhcp_lease_get_address (lease, &tmp_addr);
memset (&address, 0, sizeof (address));
address.address = tmp_addr.s_addr;
address.peer_address = tmp_addr.s_addr;
str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
LOG_LEASE (LOGD_DHCP4, " address %s", str);
add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str);
......
......@@ -380,6 +380,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
{
NMIP4Config *ip4_config = NULL;
guint32 tmp_addr;
in_addr_t addr;
NMPlatformIP4Address address;
char *str = NULL;
guint32 gwaddr = 0, plen = 0;
......@@ -391,10 +392,9 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
address.timestamp = nm_utils_get_monotonic_timestamp_s ();
str = g_hash_table_lookup (options, "ip_address");
if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
address.address = tmp_addr;
if (str && (inet_pton (AF_INET, str, &addr) > 0))
nm_log_info (LOGD_DHCP4, " address %s", str);
} else
else
goto error;
str = g_hash_table_lookup (options, "subnet_mask");
......@@ -403,10 +403,10 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
nm_log_info (LOGD_DHCP4, " plen %d (%s)", plen, str);
} else {
/* Get default netmask for the IP according to appropriate class. */
plen = nm_utils_ip4_get_default_prefix (address.address);
plen = nm_utils_ip4_get_default_prefix (addr);
nm_log_info (LOGD_DHCP4, " plen %d (default)", plen);
}
address.plen = plen;
nm_platform_ip4_address_set_addr (&address, addr, plen);
/* Routes: if the server returns classless static routes, we MUST ignore
* the 'static_routes' option.
......
......@@ -666,6 +666,7 @@ test_read_lease_ip4_config_basic (void)
g_assert (inet_aton ("192.168.1.180", (struct in_addr *) &expected_addr));
addr = nm_ip4_config_get_address (config, 0);
g_assert_cmpint (addr->address, ==, expected_addr);
g_assert_cmpint (addr->peer_address, ==, expected_addr);
g_assert_cmpint (addr->plen, ==, 24);
/* Gateway */
......@@ -688,6 +689,7 @@ test_read_lease_ip4_config_basic (void)
g_assert (inet_aton ("10.77.52.141", (struct in_addr *) &expected_addr));
addr = nm_ip4_config_get_address (config, 0);
g_assert_cmpint (addr->address, ==, expected_addr);
g_assert_cmpint (addr->peer_address, ==, expected_addr);
g_assert_cmpint (addr->plen, ==, 8);
/* Gateway */
......
......@@ -95,7 +95,7 @@ test_generic_options (void)
address = nm_ip4_config_get_address (ip4_config, 0);
g_assert (inet_pton (AF_INET, expected_addr, &tmp) > 0);
g_assert (address->address == tmp);
g_assert (address->peer_address == 0);
g_assert (address->peer_address == tmp);
g_assert_cmpint (address->plen, ==, 24);
/* Gateway */
......
......@@ -31,8 +31,7 @@ static NMPlatformIP4Address *
_set_addr (NMPlatformIP4Address *addr, const char *address, int plen)
{
memset (addr, 0, sizeof (*addr));
addr->address = nmtst_inet4_from_string (address);
addr->plen = plen;
nm_platform_ip4_address_set_addr (addr, nmtst_inet4_from_string (address), plen);
return addr;
}
......
......@@ -180,7 +180,7 @@ addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Addre
{
return a->address == b->address
&& a->plen == b->plen
&& nm_platform_ip4_address_equal_peer_net (a, b);
&& ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0;
}
static gboolean
......@@ -309,7 +309,7 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
route.source = NM_IP_CONFIG_SOURCE_KERNEL;
/* The destination network depends on the peer-address. */
route.network = nm_utils_ip4_address_clear_host_address (nm_platform_ip4_address_get_peer (addr), addr->plen);
route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen);
if (_ipv4_is_zeronet (route.network)) {
/* Kernel doesn't add device-routes for destinations that
......@@ -404,6 +404,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
memset (&address, 0, sizeof (address));
nm_ip_address_get_address_binary (s_addr, &address.address);
address.peer_address = address.address;
address.plen = nm_ip_address_get_prefix (s_addr);
address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
......@@ -1005,7 +1006,7 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
dst_addr = nm_ip4_config_get_address (dst, i))) {
are_equal = FALSE;
if ( !addresses_are_duplicate (src_addr, dst_addr)
|| (nm_platform_ip4_address_get_peer (src_addr) != nm_platform_ip4_address_get_peer (dst_addr))) {
|| src_addr->peer_address != dst_addr->peer_address) {
has_relevant_changes = TRUE;
break;
}
......@@ -1273,7 +1274,7 @@ nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 network,
if (item->plen > plen)
continue;
peer_network = nm_utils_ip4_address_clear_host_address (nm_platform_ip4_address_get_peer (item), item->plen);
peer_network = nm_utils_ip4_address_clear_host_address (item->peer_address, item->plen);
if (_ipv4_is_zeronet (peer_network))
continue;
......@@ -2013,7 +2014,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));
hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (address->plen));
}
for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
......@@ -2160,8 +2161,7 @@ get_property (GObject *object, guint prop_id,
g_variant_builder_add (&addr_builder, "{sv}",
"prefix",
g_variant_new_uint32 (address->plen));
if ( address->peer_address
&& address->peer_address != address->address) {
if (address->peer_address != address->address) {
g_variant_builder_add (&addr_builder, "{sv}",
"peer",
g_variant_new_string (nm_utils_inet4_ntop (address->peer_address, NULL)));
......
......@@ -99,6 +99,14 @@ static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in
/******************************************************************/
static gboolean
_ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, int plen)
{
return ((peer1 ^ peer2) & nm_utils_ip4_prefix_to_netmask (plen)) == 0;
}
/******************************************************************/
static gboolean
sysctl_set (NMPlatform *platform, const char *path, const char *value)
{
......@@ -871,7 +879,7 @@ ip4_address_add (NMPlatform *platform,
address.source = NM_IP_CONFIG_SOURCE_KERNEL;
address.ifindex = ifindex;
address.address = addr;
address.peer_address = peer_addr && peer_addr != addr ? peer_addr : 0;
address.peer_address = peer_addr;
address.plen = plen;
address.timestamp = nm_utils_get_monotonic_timestamp_s ();
address.lifetime = lifetime;
......@@ -881,18 +889,19 @@ ip4_address_add (NMPlatform *platform,
for (i = 0; i < priv->ip4_addresses->len; i++) {
NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
gboolean changed;
if (item->ifindex != address.ifindex)
continue;
if (item->address != address.address)
continue;
if (item->plen != address.plen)
continue;
if (!nm_platform_ip4_address_equal_peer_net (item, &address))
if ( item->ifindex != address.ifindex
|| item->address != address.address
|| item->plen != address.plen
|| !_ip4_address_equal_peer_net (item->peer_address, address.peer_address, address.plen))
continue;
changed = !nm_platform_ip4_address_cmp (item, &address);
memcpy (item, &address, sizeof (address));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
if (changed)
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
return TRUE;
}
......@@ -928,18 +937,17 @@ ip6_address_add (NMPlatform *platform,
for (i = 0; i < priv->ip6_addresses->len; i++) {
NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
gboolean changed;
if (item->ifindex != address.ifindex)
continue;
if (!IN6_ARE_ADDR_EQUAL (&item->address, &address.address))
continue;
if (item->plen != address.plen)
if ( item->ifindex != address.ifindex
|| !IN6_ARE_ADDR_EQUAL (&item->address, &address.address))
continue;
if (nm_platform_ip6_address_cmp (item, &address) != 0) {
memcpy (item, &address, sizeof (address));
changed = !nm_platform_ip6_address_cmp (item, &address);
memcpy (item, &address, sizeof (address));
if (changed)
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
}
return TRUE;
}
......@@ -958,8 +966,10 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen,
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 &&
(!peer_address || address->peer_address == peer_address)) {
if ( address->ifindex == ifindex
&& address->plen == plen
&& address->address == addr
&& ((peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (plen)) == 0) {
NMPlatformIP4Address deleted_address;
memcpy (&deleted_address, address, sizeof (deleted_address));
......@@ -981,8 +991,9 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int
for (i = 0; i < priv->ip6_addresses->len; i++) {
NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
if (address->ifindex == ifindex && address->plen == plen
&& IN6_ARE_ADDR_EQUAL (&address->address, &addr)) {
if ( address->ifindex == ifindex
&& address->plen == plen
&& IN6_ARE_ADDR_EQUAL (&address->address, &addr)) {
NMPlatformIP6Address deleted_address;
memcpy (&deleted_address, address, sizeof (deleted_address));
......@@ -1000,12 +1011,6 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in
{
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);
......@@ -1013,7 +1018,7 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in
if ( address->ifindex == ifindex
&& address->plen == plen
&& address->address == addr
&& nm_platform_ip4_address_equal_peer_net (address, &a))
&& _ip4_address_equal_peer_net (address->peer_address, peer_address, plen))
return address;
}
......
......@@ -1442,23 +1442,32 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
obj->ip_address.ifindex = ifa->ifa_index;
obj->ip_address.plen = ifa->ifa_prefixlen;
if (_check_addr_or_errout (tb, IFA_LOCAL, addr_len))
memcpy (obj->ip_address.address_ptr, nla_data (tb[IFA_LOCAL]), addr_len);
if (_check_addr_or_errout (tb, IFA_ADDRESS, addr_len)) {
NMIPAddr *a = nla_data (tb[IFA_ADDRESS]);
/* IPv6 sends the local address as IFA_ADDRESS with
* no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
* with IFA_ADDRESS being the peer address if they differ */
if ( !tb[IFA_LOCAL]
|| !memcmp (a, obj->ip_address.address_ptr, addr_len)) {
memcpy (obj->ip_address.address_ptr, a, addr_len);
} else {
if (is_v4)
obj->ip4_address.peer_address = a->addr4;
else
obj->ip6_address.peer_address = a->addr6;
_check_addr_or_errout (tb, IFA_ADDRESS, addr_len);
_check_addr_or_errout (tb, IFA_LOCAL, addr_len);
if (is_v4) {
/* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they
* are effectively 0.0.0.0 (all-zero). */
if (tb[IFA_LOCAL])
memcpy (&obj->ip4_address.address, nla_data (tb[IFA_LOCAL]), addr_len);
if (tb[IFA_ADDRESS])
memcpy (&obj->ip4_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len);
} else {
/* For IPv6, IFA_ADDRESS is always present.
*
* If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address
* is :: (all-zero).
*
* If unexpectely IFA_ADDRESS is missing, make the best of it -- but it _should_
* actually be there. */
if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) {
if (tb[IFA_LOCAL]) {
memcpy (&obj->ip6_address.address, nla_data (tb[IFA_LOCAL]), addr_len);
if (tb[IFA_ADDRESS])
memcpy (&obj->ip6_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len);
else
obj->ip6_address.peer_address = obj->ip6_address.address;
} else
memcpy (&obj->ip6_address.address, nla_data (tb[IFA_ADDRESS]), addr_len);
}
}
......@@ -4256,7 +4265,14 @@ build_rtnl_addr (NMPlatform *platform,
}
/* Peer/point-to-point address */
if (peer_addr) {
if ( peer_addr
&& family == AF_INET
&& (*((in_addr_t *) peer_addr)) == (*((in_addr_t *) addr))) {
/* For IPv4, a local address being equal the peer address means that
* no explict peer is set.
*
* We don't have to set it explicitly. */
} else if (peer_addr) {
auto_nl_addr struct nl_addr *nlpeer = _nl_addr_build (family, peer_addr, addrlen);
nle = rtnl_addr_set_peer (rtnladdr, nlpeer);
......@@ -4308,7 +4324,7 @@ _nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObjec
AF_INET,
obj->ifindex,
&obj->address,
obj->peer_address ? &obj->peer_address : NULL,
&obj->peer_address,
obj->plen,
lifetime,
preferred,
......@@ -4351,7 +4367,7 @@ ip4_address_add (NMPlatform *platform,
auto_nl_object struct nl_object *nlo = NULL;
nlo = build_rtnl_addr (platform, AF_INET, ifindex, &addr,
peer_addr ? &peer_addr : NULL,
&peer_addr,
plen, lifetime, preferred, 0,
label);
return do_add_addrroute (platform,
......
......@@ -1956,10 +1956,12 @@ _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size)
/******************************************************************/
in_addr_t
nm_platform_ip4_address_get_peer (const NMPlatformIP4Address *addr)
void
nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, int plen)
{
return addr->peer_address ?: addr->address;
addr->address = address;
addr->peer_address = address;
addr->plen = plen;
}
const struct in6_addr *
......@@ -1971,28 +1973,6 @@ nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr)
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)
{
......@@ -2091,7 +2071,8 @@ gboolean
nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address)
{
char str_dev[TO_STRING_DEV_BUF_SIZE];
char str_peer[NM_UTILS_INET_ADDRSTRLEN];
char str_peer2[NM_UTILS_INET_ADDRSTRLEN];
char str_peer[100];
_CHECK_SELF (self, klass, FALSE);
......@@ -2099,11 +2080,10 @@ nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address
g_return_val_if_fail (plen > 0, FALSE);
g_return_val_if_fail (klass->ip4_address_delete, FALSE);
_LOGD ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s",
_LOGD ("address: deleting IPv4 address %s/%d, %sifindex %d%s",
nm_utils_inet4_ntop (address, NULL), plen,
peer_address ? "peer " : "",
peer_address ? nm_utils_inet4_ntop (peer_address, str_peer) : "",
peer_address ? ", " : "",
peer_address != address
? nm_sprintf_buf (str_peer, "peer %s, ", nm_utils_inet4_ntop (peer_address, str_peer2)) : "",
ifindex,
_to_string_dev (self, ifindex, str_dev, sizeof (str_dev)));
return klass->ip4_address_delete (self, ifindex, address, plen, peer_address);
......@@ -2157,7 +2137,7 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address
if ( candidate->address == address->address
&& candidate->plen == address->plen
&& nm_platform_ip4_address_equal_peer_net (candidate, address)) {
&& ((candidate->peer_address & address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) {
guint32 lifetime, preferred;
if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
......@@ -2831,7 +2811,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu
inet_ntop (AF_INET, &address->address, s_address, sizeof (s_address));
if (address->peer_address) {
if (address->peer_address != address->address) {
inet_ntop (AF_INET, &address->peer_address, s_peer, sizeof (s_peer));
str_peer = g_strconcat (" ptp ", s_peer, NULL);
}
......@@ -3237,20 +3217,12 @@ nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan
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, 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, peer_address);
_CMP_FIELD (a, b, timestamp);
_CMP_FIELD (a, b, lifetime);
_CMP_FIELD (a, b, preferred);
......
......@@ -234,8 +234,21 @@ typedef struct {
**/
struct _NMPlatformIP4Address {
__NMPlatformIPAddress_COMMON;
/* The local address IFA_LOCAL. */
in_addr_t address;
/* The IFA_ADDRESS PTP peer address. This field is rather important, because
* it constitutes the identifier for the IPv4 address (e.g. you can add two
* addresses that only differ by their peer's network-part.
*
* Beware that for most cases, NetworkManager doesn't want to set an explicit
* peer-address. Hoever, that corresponds to setting the peer address to @address
* itself. Leaving peer-address unset/zero, means explicitly setting the peer
* address to 0.0.0.0, which you probably don't want.
* */
in_addr_t peer_address; /* PTP peer address */
char label[IFNAMSIZ];
};
......@@ -715,10 +728,8 @@ 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);
in_addr_t nm_platform_ip4_address_get_peer (const NMPlatformIP4Address *addr);
void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, int plen);
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);
......
......@@ -466,8 +466,8 @@ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsiz
}
_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) : "");
obj->peer_address != obj->address ? "," : "",
obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : "");
_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1));
_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric);
_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric);
......@@ -664,7 +664,7 @@ _vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address,
&& 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));
&& ((obj1->peer_address ^ obj2->peer_address) & nm_utils_ip4_prefix_to_netmask (obj1->plen)) == 0);
_vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address,
obj1->ifindex == obj2->ifindex
/* for IPv6 addresses, the prefix length is not part of the primary identifier. */
......@@ -719,7 +719,7 @@ _vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, {
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)));
hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen)));
})
_vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, {
hash = (guint) 2907861637u;
......
......@@ -30,6 +30,7 @@
#include "nm-platform.h"
#include "nm-linux-platform.h"
#include "nm-fake-platform.h"
#include "nm-utils.h"
#define error(...) fprintf (stderr, __VA_ARGS__)
......@@ -490,15 +491,17 @@ do_ip4_address_get_all (char **argv)
int ifindex = parse_ifindex (argv[0]);
GArray *addresses;
NMPlatformIP4Address *address;
char addrstr[INET_ADDRSTRLEN];
int i;
if (ifindex) {
addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
for (i = 0; i < addresses->len; i++) {
address = &g_array_index (addresses, NMPlatformIP4Address, i);
inet_ntop (AF_INET, &address->address, addrstr, sizeof (addrstr));
printf ("%s/%d\n", addrstr, address->plen);
printf ("%s", nm_utils_inet4_ntop (address->address, NULL));
if (address->address != address->peer_address)
printf (" peer %s", nm_utils_inet4_ntop (address->peer_address, NULL));
printf ("/%d\n", address->plen);
}
g_array_unref (addresses);
}
......@@ -574,7 +577,7 @@ do_ip4_address_add (char **argv)
guint32 lifetime = strtol (*argv++, NULL, 10);
guint32 preferred = strtol (*argv++, NULL, 10);
gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, plen, 0, lifetime, preferred, NULL);
gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, plen, address, lifetime, preferred, NULL);
return value;
} else
return FALSE;
......@@ -615,11 +618,11 @@ do_ip6_address_add (char **argv)
} else \
return FALSE; \
}
#define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, 0, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, FALSE)
#define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, address, ##__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, 0)
ADDR_CMD_PRINT (get, address)
static gboolean
do_ip4_route_get_all (char **argv)
......
......@@ -73,13 +73,13 @@ test_ip4_address_general (void)
inet_pton (AF_INET, IP4_ADDRESS, &addr);
/* Add address */
g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0));
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL);
g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0));
g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
accept_signal (address_added);
/* Add address again (aka update) */
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime + 100, preferred + 50, NULL);
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, NULL);
accept_signals (address_changed, 0, 1);
/* Test address listing */
......@@ -89,16 +89,17 @@ test_ip4_address_general (void)
address = &g_array_index (addresses, NMPlatformIP4Address, 0);
g_assert_cmpint (address->ifindex, ==, ifindex);
g_assert_cmphex (address->address, ==, addr);
g_assert_cmphex (address->peer_address, ==, addr);
g_assert_cmpint (address->plen, ==, IP4_PLEN);
g_array_unref (addresses);
/* Remove address */
nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, 0);
g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0));
nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr);
g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
accept_signal (address_removed);
/* Remove address again */
nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, 0);
nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr);
free_signal (address_added);
free_signal (address_changed);
......@@ -173,16 +174,16 @@ test_ip4_address_general_2 (void)
g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, NULL));
/* Add/delete notification */
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL);
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
accept_signal (address_added);
g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0));
nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, 0);
g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr);
accept_signal (address_removed);
g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0));
g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
/* Add/delete conflict */
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL);
g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0));
nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
accept_signal (address_added);
free_signal (address_added);
......@@ -275,6 +276,59 @@ test_ip4_address_peer (void)
/*****************************************************************************/
static void
test_ip4_address_peer_zero (void)
{
const int ifindex = DEVICE_IFINDEX;
in_addr_t addr, addr_peer;
guint32 lifetime = 2000;
guint32 preferred = 1000;
const int plen = 24;
const char *label = NULL;
in_addr_t peers[3], r_peers[3];
int i;
GArray *addrs;