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@xxxxxxxxxxxxx> --- net/sctp/ipv6.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 62 insertions(+), 3 deletions(-) diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 9fb5d37..5c55f98 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -77,6 +77,10 @@ #include <net/sctp/sctp.h> #include <asm/uaccess.h> +static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, + __be16 port); +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2); /* Event handler for inet6 address addition/deletion events. * The sctp_local_addr_list needs to be protocted by a spin lock since @@ -242,8 +246,12 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, union sctp_addr *daddr, union sctp_addr *saddr) { - struct dst_entry *dst; - struct flowi fl; + struct dst_entry *dst = NULL; + struct flowi fl; + struct sctp_bind_addr *bp; + struct sctp_sockaddr_entry *laddr; + union sctp_addr dst_saddr; + sctp_scope_t scope; memset(&fl, 0, sizeof(fl)); ipv6_addr_copy(&fl.fl6_dst, &daddr->v6.sin6_addr); @@ -259,7 +267,58 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, } dst = ip6_route_output(&init_net, NULL, &fl); - if (!dst->error) { + if (!asoc || saddr) + goto out; + + bp = &asoc->base.bind_addr; + scope = sctp_scope(daddr); + + if (dst) { + /* Walk through the bind address list and look for a bind + * address that matches the source address of the returned dst. + */ + sctp_v6_dst_saddr(&dst_saddr, dst, htons(bp->port)); + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid) + continue; + if ((laddr->state == SCTP_ADDR_SRC) && + (laddr->a.sa.sa_family == AF_INET6) && + (scope <= sctp_scope(&laddr->a)) && + (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))){ + goto out_unlock; + } + rcu_read_unlock(); + + /* None of the bound addresses match the source address of the + * dst. So release it. + */ + dst_release(dst); + dst = NULL; + } + } + + /* Walk through the bind address list and try to get a dst that + * matches a bind address as the source address. + */ + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid) + continue; + if ((laddr->state == SCTP_ADDR_SRC) && + (laddr->a.sa.sa_family == AF_INET6) && + (scope <= sctp_scope(&laddr->a))){ + ipv6_addr_copy(&fl.fl6_src, &laddr->a.v6.sin6_addr); + dst = ip6_route_output(&init_net, NULL, &fl); + goto out_unlock; + } + } + +out_unlock: + rcu_read_unlock(); + +out: + if (dst) { struct rt6_info *rt; rt = (struct rt6_info *)dst; SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", -- 1.5.5.1 -- To unsubscribe from this list: send the line "unsubscribe linux-sctp" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html