| From foo@baz Wed May 31 09:13:34 JST 2017 |
| From: Xin Long <lucien.xin@gmail.com> |
| Date: Fri, 12 May 2017 14:39:52 +0800 |
| Subject: sctp: fix src address selection if using secondary addresses for ipv6 |
| |
| From: Xin Long <lucien.xin@gmail.com> |
| |
| |
| [ Upstream commit dbc2b5e9a09e9a6664679a667ff81cff6e5f2641 ] |
| |
| Commit 0ca50d12fe46 ("sctp: fix src address selection if using secondary |
| addresses") has fixed a src address selection issue when using secondary |
| addresses for ipv4. |
| |
| Now sctp ipv6 also has the similar issue. When using a secondary address, |
| sctp_v6_get_dst tries to choose the saddr which has the most same bits |
| with the daddr by sctp_v6_addr_match_len. It may make some cases not work |
| as expected. |
| |
| hostA: |
| [1] fd21:356b:459a:cf10::11 (eth1) |
| [2] fd21:356b:459a:cf20::11 (eth2) |
| |
| hostB: |
| [a] fd21:356b:459a:cf30::2 (eth1) |
| [b] fd21:356b:459a:cf40::2 (eth2) |
| |
| route from hostA to hostB: |
| fd21:356b:459a:cf30::/64 dev eth1 metric 1024 mtu 1500 |
| |
| The expected path should be: |
| fd21:356b:459a:cf10::11 <-> fd21:356b:459a:cf30::2 |
| But addr[2] matches addr[a] more bits than addr[1] does, according to |
| sctp_v6_addr_match_len. It causes the path to be: |
| fd21:356b:459a:cf20::11 <-> fd21:356b:459a:cf30::2 |
| |
| This patch is to fix it with the same way as Marcelo's fix for sctp ipv4. |
| As no ip_dev_find for ipv6, this patch is to use ipv6_chk_addr to check |
| if the saddr is in a dev instead. |
| |
| Note that for backwards compatibility, it will still do the addr_match_len |
| check here when no optimal is found. |
| |
| Reported-by: Patrick Talbert <ptalbert@redhat.com> |
| Signed-off-by: Xin Long <lucien.xin@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/sctp/ipv6.c | 46 +++++++++++++++++++++++++++++----------------- |
| 1 file changed, 29 insertions(+), 17 deletions(-) |
| |
| --- a/net/sctp/ipv6.c |
| +++ b/net/sctp/ipv6.c |
| @@ -240,12 +240,10 @@ static void sctp_v6_get_dst(struct sctp_ |
| struct sctp_bind_addr *bp; |
| struct ipv6_pinfo *np = inet6_sk(sk); |
| struct sctp_sockaddr_entry *laddr; |
| - union sctp_addr *baddr = NULL; |
| union sctp_addr *daddr = &t->ipaddr; |
| union sctp_addr dst_saddr; |
| struct in6_addr *final_p, final; |
| __u8 matchlen = 0; |
| - __u8 bmatchlen; |
| sctp_scope_t scope; |
| |
| memset(fl6, 0, sizeof(struct flowi6)); |
| @@ -312,23 +310,37 @@ static void sctp_v6_get_dst(struct sctp_ |
| */ |
| rcu_read_lock(); |
| list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| - if (!laddr->valid) |
| + struct dst_entry *bdst; |
| + __u8 bmatchlen; |
| + |
| + if (!laddr->valid || |
| + laddr->state != SCTP_ADDR_SRC || |
| + laddr->a.sa.sa_family != AF_INET6 || |
| + scope > sctp_scope(&laddr->a)) |
| continue; |
| - if ((laddr->state == SCTP_ADDR_SRC) && |
| - (laddr->a.sa.sa_family == AF_INET6) && |
| - (scope <= sctp_scope(&laddr->a))) { |
| - bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); |
| - if (!baddr || (matchlen < bmatchlen)) { |
| - baddr = &laddr->a; |
| - matchlen = bmatchlen; |
| - } |
| - } |
| - } |
| - if (baddr) { |
| - fl6->saddr = baddr->v6.sin6_addr; |
| - fl6->fl6_sport = baddr->v6.sin6_port; |
| + |
| + fl6->saddr = laddr->a.v6.sin6_addr; |
| + fl6->fl6_sport = laddr->a.v6.sin6_port; |
| final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); |
| - dst = ip6_dst_lookup_flow(sk, fl6, final_p); |
| + bdst = ip6_dst_lookup_flow(sk, fl6, final_p); |
| + |
| + if (!IS_ERR(bdst) && |
| + ipv6_chk_addr(dev_net(bdst->dev), |
| + &laddr->a.v6.sin6_addr, bdst->dev, 1)) { |
| + if (!IS_ERR_OR_NULL(dst)) |
| + dst_release(dst); |
| + dst = bdst; |
| + break; |
| + } |
| + |
| + bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); |
| + if (matchlen > bmatchlen) |
| + continue; |
| + |
| + if (!IS_ERR_OR_NULL(dst)) |
| + dst_release(dst); |
| + dst = bdst; |
| + matchlen = bmatchlen; |
| } |
| rcu_read_unlock(); |
| |