[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