aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorXin Long <lucien.xin@gmail.com>2017-05-12 02:39:52 -0400
committerDavid S. Miller <davem@davemloft.net>2017-05-12 10:50:32 -0400
commitdbc2b5e9a09e9a6664679a667ff81cff6e5f2641 (patch)
tree7d3e659c02e946e1950b7b6d8d2a78427bfb0068 /net
parentdf0c8d911abf6ba97b2c2fc3c5a12769e0b081a3 (diff)
sctp: fix src address selection if using secondary addresses for ipv6
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>
Diffstat (limited to 'net')
-rw-r--r--net/sctp/ipv6.c46
1 files changed, 29 insertions, 17 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 961ee59f696a..142b70e959af 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -240,12 +240,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
240 struct sctp_bind_addr *bp; 240 struct sctp_bind_addr *bp;
241 struct ipv6_pinfo *np = inet6_sk(sk); 241 struct ipv6_pinfo *np = inet6_sk(sk);
242 struct sctp_sockaddr_entry *laddr; 242 struct sctp_sockaddr_entry *laddr;
243 union sctp_addr *baddr = NULL;
244 union sctp_addr *daddr = &t->ipaddr; 243 union sctp_addr *daddr = &t->ipaddr;
245 union sctp_addr dst_saddr; 244 union sctp_addr dst_saddr;
246 struct in6_addr *final_p, final; 245 struct in6_addr *final_p, final;
247 __u8 matchlen = 0; 246 __u8 matchlen = 0;
248 __u8 bmatchlen;
249 sctp_scope_t scope; 247 sctp_scope_t scope;
250 248
251 memset(fl6, 0, sizeof(struct flowi6)); 249 memset(fl6, 0, sizeof(struct flowi6));
@@ -312,23 +310,37 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
312 */ 310 */
313 rcu_read_lock(); 311 rcu_read_lock();
314 list_for_each_entry_rcu(laddr, &bp->address_list, list) { 312 list_for_each_entry_rcu(laddr, &bp->address_list, list) {
315 if (!laddr->valid) 313 struct dst_entry *bdst;
314 __u8 bmatchlen;
315
316 if (!laddr->valid ||
317 laddr->state != SCTP_ADDR_SRC ||
318 laddr->a.sa.sa_family != AF_INET6 ||
319 scope > sctp_scope(&laddr->a))
316 continue; 320 continue;
317 if ((laddr->state == SCTP_ADDR_SRC) && 321
318 (laddr->a.sa.sa_family == AF_INET6) && 322 fl6->saddr = laddr->a.v6.sin6_addr;
319 (scope <= sctp_scope(&laddr->a))) { 323 fl6->fl6_sport = laddr->a.v6.sin6_port;
320 bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
321 if (!baddr || (matchlen < bmatchlen)) {
322 baddr = &laddr->a;
323 matchlen = bmatchlen;
324 }
325 }
326 }
327 if (baddr) {
328 fl6->saddr = baddr->v6.sin6_addr;
329 fl6->fl6_sport = baddr->v6.sin6_port;
330 final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); 324 final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
331 dst = ip6_dst_lookup_flow(sk, fl6, final_p); 325 bdst = ip6_dst_lookup_flow(sk, fl6, final_p);
326
327 if (!IS_ERR(bdst) &&
328 ipv6_chk_addr(dev_net(bdst->dev),
329 &laddr->a.v6.sin6_addr, bdst->dev, 1)) {
330 if (!IS_ERR_OR_NULL(dst))
331 dst_release(dst);
332 dst = bdst;
333 break;
334 }
335
336 bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
337 if (matchlen > bmatchlen)
338 continue;
339
340 if (!IS_ERR_OR_NULL(dst))
341 dst_release(dst);
342 dst = bdst;
343 matchlen = bmatchlen;
332 } 344 }
333 rcu_read_unlock(); 345 rcu_read_unlock();
334 346