Skip to content

Commit 704e6c6

Browse files
lxingregkh
authored andcommitted
sctp: fix src address selection if using secondary addresses for ipv6
[ Upstream commit dbc2b5e9a09e9a6664679a667ff81cff6e5f2641 ] Commit 0ca50d1 ("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>
1 parent 90e3f8a commit 704e6c6

1 file changed

Lines changed: 29 additions & 17 deletions

File tree

net/sctp/ipv6.c

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
239239
struct sctp_bind_addr *bp;
240240
struct ipv6_pinfo *np = inet6_sk(sk);
241241
struct sctp_sockaddr_entry *laddr;
242-
union sctp_addr *baddr = NULL;
243242
union sctp_addr *daddr = &t->ipaddr;
244243
union sctp_addr dst_saddr;
245244
struct in6_addr *final_p, final;
246245
__u8 matchlen = 0;
247-
__u8 bmatchlen;
248246
sctp_scope_t scope;
249247

250248
memset(fl6, 0, sizeof(struct flowi6));
@@ -311,23 +309,37 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
311309
*/
312310
rcu_read_lock();
313311
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
314-
if (!laddr->valid)
312+
struct dst_entry *bdst;
313+
__u8 bmatchlen;
314+
315+
if (!laddr->valid ||
316+
laddr->state != SCTP_ADDR_SRC ||
317+
laddr->a.sa.sa_family != AF_INET6 ||
318+
scope > sctp_scope(&laddr->a))
315319
continue;
316-
if ((laddr->state == SCTP_ADDR_SRC) &&
317-
(laddr->a.sa.sa_family == AF_INET6) &&
318-
(scope <= sctp_scope(&laddr->a))) {
319-
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
320-
if (!baddr || (matchlen < bmatchlen)) {
321-
baddr = &laddr->a;
322-
matchlen = bmatchlen;
323-
}
324-
}
325-
}
326-
if (baddr) {
327-
fl6->saddr = baddr->v6.sin6_addr;
328-
fl6->fl6_sport = baddr->v6.sin6_port;
320+
321+
fl6->saddr = laddr->a.v6.sin6_addr;
322+
fl6->fl6_sport = laddr->a.v6.sin6_port;
329323
final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
330-
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
324+
bdst = ip6_dst_lookup_flow(sk, fl6, final_p);
325+
326+
if (!IS_ERR(bdst) &&
327+
ipv6_chk_addr(dev_net(bdst->dev),
328+
&laddr->a.v6.sin6_addr, bdst->dev, 1)) {
329+
if (!IS_ERR_OR_NULL(dst))
330+
dst_release(dst);
331+
dst = bdst;
332+
break;
333+
}
334+
335+
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
336+
if (matchlen > bmatchlen)
337+
continue;
338+
339+
if (!IS_ERR_OR_NULL(dst))
340+
dst_release(dst);
341+
dst = bdst;
342+
matchlen = bmatchlen;
331343
}
332344
rcu_read_unlock();
333345

0 commit comments

Comments
 (0)