Skip to content

Commit

Permalink
route/addr: fix handling peer addresses for IPv4 addresses
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
thom311 committed Jun 29, 2016
1 parent 83e851c commit 62f0fdb
Showing 1 changed file with 54 additions and 19 deletions.
73 changes: 54 additions & 19 deletions lib/route/addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 62f0fdb

Please sign in to comment.