diff options
author | Thomas Haller <thaller@redhat.com> | 2016-06-28 14:56:22 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2016-06-29 08:26:18 (GMT) |
commit | 62f0fdb9c150779d04e2d83d2916b74f8789e076 (patch) | |
tree | 808508d136451a8d33ba6b15abe214687274c102 | |
parent | 83e851ca9c842ccb6dae411d3fff9c7e9561269a (diff) | |
download | libnl-62f0fdb9c150779d04e2d83d2916b74f8789e076.zip libnl-62f0fdb9c150779d04e2d83d2916b74f8789e076.tar.gz libnl-62f0fdb9c150779d04e2d83d2916b74f8789e076.tar.bz2 |
route/addr: fix handling peer addresses for IPv4 addresses
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>
-rw-r--r-- | lib/route/addr.c | 73 |
1 files 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) |