Commit 625034113bd45c71fb9e329f52f25fef9e6993a3

Authored by Weixing Shi
Committed by David S. Miller
1 parent 66e6961c8e

sctp: fix sctp to work with ipv6 source address routing

In the below test case, using the source address routing,
sctp can not work.
Node-A
1)ifconfig eth0 inet6 add 2001:1::1/64
2)ip -6 rule add from 2001:1::1 table 100 pref 100
3)ip -6 route add 2001:2::1 dev eth0 table 100
4)sctp_darn -H 2001:1::1 -P 250 -l &
Node-B
1)ifconfig eth0 inet6 add 2001:2::1/64
2)ip -6 rule add from 2001:2::1 table 100 pref 100
3)ip -6 route add 2001:1::1 dev eth0 table 100
4)sctp_darn -H 2001:2::1 -P 250 -h 2001:1::1 -p 250 -s

root cause:
Node-A and Node-B use the source address routing, and
at begining, source address will be NULL,sctp will
search the  routing table by the destination address,
because using the source address routing table, and
the result dst_entry will be NULL.

solution:
walk through the bind address list to get the source
address and then lookup the routing table again to get
the correct dst_entry.

Signed-off-by: Weixing Shi <Weixing.Shi@windriver.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 43 additions and 1 deletions Side-by-side Diff

... ... @@ -80,6 +80,9 @@
80 80  
81 81 #include <asm/uaccess.h>
82 82  
  83 +static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
  84 + union sctp_addr *s2);
  85 +
83 86 /* Event handler for inet6 address addition/deletion events.
84 87 * The sctp_local_addr_list needs to be protocted by a spin lock since
85 88 * multiple notifiers (say IPv4 and IPv6) may be running at the same
86 89  
... ... @@ -244,8 +247,14 @@
244 247 union sctp_addr *daddr,
245 248 union sctp_addr *saddr)
246 249 {
247   - struct dst_entry *dst;
  250 + struct dst_entry *dst = NULL;
248 251 struct flowi6 fl6;
  252 + struct sctp_bind_addr *bp;
  253 + struct sctp_sockaddr_entry *laddr;
  254 + union sctp_addr *baddr = NULL;
  255 + __u8 matchlen = 0;
  256 + __u8 bmatchlen;
  257 + sctp_scope_t scope;
249 258  
250 259 memset(&fl6, 0, sizeof(fl6));
251 260 ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr);
... ... @@ -261,6 +270,39 @@
261 270 }
262 271  
263 272 dst = ip6_route_output(&init_net, NULL, &fl6);
  273 + if (!asoc || saddr)
  274 + goto out;
  275 +
  276 + if (dst->error) {
  277 + dst_release(dst);
  278 + dst = NULL;
  279 + bp = &asoc->base.bind_addr;
  280 + scope = sctp_scope(daddr);
  281 + /* Walk through the bind address list and try to get a dst that
  282 + * matches a bind address as the source address.
  283 + */
  284 + rcu_read_lock();
  285 + list_for_each_entry_rcu(laddr, &bp->address_list, list) {
  286 + if (!laddr->valid)
  287 + continue;
  288 + if ((laddr->state == SCTP_ADDR_SRC) &&
  289 + (laddr->a.sa.sa_family == AF_INET6) &&
  290 + (scope <= sctp_scope(&laddr->a))) {
  291 + bmatchlen = sctp_v6_addr_match_len(daddr,
  292 + &laddr->a);
  293 + if (!baddr || (matchlen < bmatchlen)) {
  294 + baddr = &laddr->a;
  295 + matchlen = bmatchlen;
  296 + }
  297 + }
  298 + }
  299 + rcu_read_unlock();
  300 + if (baddr) {
  301 + ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr);
  302 + dst = ip6_route_output(&init_net, NULL, &fl6);
  303 + }
  304 + }
  305 +out:
264 306 if (!dst->error) {
265 307 struct rt6_info *rt;
266 308 rt = (struct rt6_info *)dst;