]> granicus.if.org Git - libnl/commitdiff
route/addr: fix handling peer addresses for IPv4 addresses
authorThomas Haller <thaller@redhat.com>
Tue, 28 Jun 2016 14:56:22 +0000 (16:56 +0200)
committerThomas Haller <thaller@redhat.com>
Wed, 29 Jun 2016 08:26:18 +0000 (10:26 +0200)
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>

http://lists.infradead.org/pipermail/libnl/2016-June/002157.html

Signed-off-by: Thomas Haller <thaller@redhat.com>
lib/route/addr.c

index 8dad7367fa70ff7150fa66978e786aaa0542337a..b699c6413d56305d9cf86f138097d77b03084cb3 100644 (file)
@@ -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)