[PATCH 2/2] route/addr: fix handling peer addresses for IPv4 addresses
Thomas Haller
thaller at redhat.com
Tue Jun 28 07:56:22 PDT 2016
For IPv4, a "normal" route has IFA_LOCAL and IFA_ADDRESS set
to the same destination. An address with a explicit peer, has
them differing. A peer of 0.0.0.0 is also valid and must
be treated different from a normal address.
unshare -n
ip link add T type dummy
ip link set T up
ip addr add 192.168.5.10 peer 192.168.5.10/24 dev T
ip addr add 192.168.5.10/24 dev T
#RTNETLINK answers: File exists
ip addr add 192.168.5.10 peer 192.168.6.10/24 dev T
ip addr add 192.168.5.10 peer 0.0.0.0/24 dev T
Previously, that would give:
nl-addr-list
#192.168.5.10/24 inet dev T scope global <permanent>
#192.168.5.10 peer 192.168.6.10/24 inet dev T scope global <permanent>
#192.168.5.10/24 inet dev T scope global <permanent>
With this change, we properly get:
nl-addr-list
#192.168.5.10/24 inet dev T scope global <permanent>
#192.168.5.10/24 peer 192.168.6.10 inet dev T scope global <permanent>
#192.168.5.10/24 peer 0.0.0.0 inet dev T scope global <permanent>
Signed-off-by: Thomas Haller <thaller at redhat.com>
---
lib/route/addr.c | 73 +++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 54 insertions(+), 19 deletions(-)
diff --git a/lib/route/addr.c b/lib/route/addr.c
index 8dad736..b699c64 100644
--- a/lib/route/addr.c
+++ b/lib/route/addr.c
@@ -241,34 +241,69 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
addr->ce_mask |= ADDR_ATTR_CACHEINFO;
}
- if (tb[IFA_LOCAL]) {
- addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
+ if (family == AF_INET) {
+ uint32_t null = 0;
+
+ /* for IPv4/AF_INET, kernel always sets IFA_LOCAL and IFA_ADDRESS, unless it
+ * is effectively 0.0.0.0. */
+ if (tb[IFA_LOCAL])
+ addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
+ else
+ addr->a_local = nl_addr_build(family, &null, sizeof (null));
if (!addr->a_local)
goto errout_nomem;
addr->ce_mask |= ADDR_ATTR_LOCAL;
- plen_addr = addr->a_local;
- }
- if (tb[IFA_ADDRESS]) {
- struct nl_addr *a;
-
- a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
- if (!a)
+ if (tb[IFA_ADDRESS])
+ addr->a_peer = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
+ else
+ addr->a_peer = nl_addr_build(family, &null, sizeof (null));
+ if (!addr->a_peer)
goto errout_nomem;
- /* 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] || !nl_addr_cmp(a, addr->a_local)) {
- nl_addr_put(addr->a_local);
- addr->a_local = a;
- addr->ce_mask |= ADDR_ATTR_LOCAL;
- } else {
- addr->a_peer = a;
+ if (!nl_addr_cmp (addr->a_local, addr->a_peer)) {
+ /* having IFA_ADDRESS equal to IFA_LOCAL does not really mean
+ * there is no peer. It means the peer is equal to the local address,
+ * which is the case for "normal" addresses.
+ *
+ * Still, clear the peer and pretend it is unset for backward
+ * compatibility. */
+ nl_addr_put(addr->a_peer);
+ addr->a_peer = NULL;
+ } else
addr->ce_mask |= ADDR_ATTR_PEER;
+
+ plen_addr = addr->a_local;
+ } else {
+ if (tb[IFA_LOCAL]) {
+ addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
+ if (!addr->a_local)
+ goto errout_nomem;
+ addr->ce_mask |= ADDR_ATTR_LOCAL;
+ plen_addr = addr->a_local;
}
- plen_addr = a;
+ if (tb[IFA_ADDRESS]) {
+ struct nl_addr *a;
+
+ a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
+ if (!a)
+ goto errout_nomem;
+
+ /* 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] || !nl_addr_cmp(a, addr->a_local)) {
+ nl_addr_put(addr->a_local);
+ addr->a_local = a;
+ addr->ce_mask |= ADDR_ATTR_LOCAL;
+ } else {
+ addr->a_peer = a;
+ addr->ce_mask |= ADDR_ATTR_PEER;
+ }
+
+ plen_addr = a;
+ }
}
if (plen_addr)
--
2.7.4
More information about the libnl
mailing list