diff options
author | Vlad Yasevich <vladislav.yasevich@hp.com> | 2011-04-26 23:51:31 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-27 22:14:04 +0200 |
commit | 9914ae3ca770389a3bec3114d0a07532a7f235dd (patch) | |
tree | 269120740aa1afdb0dd792284341aaee93024f28 /net/sctp/ipv6.c | |
parent | sctp: fix sctp to work with ipv6 source address routing (diff) | |
download | linux-9914ae3ca770389a3bec3114d0a07532a7f235dd.tar.xz linux-9914ae3ca770389a3bec3114d0a07532a7f235dd.zip |
sctp: cache the ipv6 source after route lookup
The ipv6 routing lookup does give us a source address,
but instead of filling it into the dst, it's stored in
the flowi. We can use that instead of going through the
entire source address selection again.
Also the useless ->dst_saddr member of sctp_pf is removed.
And sctp_v6_dst_saddr() is removed, instead by introduce
sctp_v6_to_addr(), which can be reused to cleanup some dup
code.
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>
Diffstat (limited to 'net/sctp/ipv6.c')
-rw-r--r-- | net/sctp/ipv6.c | 161 |
1 files changed, 73 insertions, 88 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 3a571d6614f9..51c048d256f5 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -82,6 +82,10 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, union sctp_addr *s2); +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, + __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 @@ -245,73 +249,99 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) */ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, union sctp_addr *daddr, - union sctp_addr *saddr) + union sctp_addr *saddr, + struct flowi *fl, + struct sock *sk) { struct dst_entry *dst = NULL; - struct flowi6 fl6; + struct flowi6 *fl6 = &fl->u.ip6; struct sctp_bind_addr *bp; struct sctp_sockaddr_entry *laddr; union sctp_addr *baddr = NULL; + union sctp_addr dst_saddr; __u8 matchlen = 0; __u8 bmatchlen; sctp_scope_t scope; + int err = 0; - memset(&fl6, 0, sizeof(fl6)); - ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr); + memset(fl6, 0, sizeof(struct flowi6)); + ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr); if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) - fl6.flowi6_oif = daddr->v6.sin6_scope_id; + fl6->flowi6_oif = daddr->v6.sin6_scope_id; - SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6.daddr); + SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr); if (saddr) { - ipv6_addr_copy(&fl6.saddr, &saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6.saddr); + ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr); + SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr); } - dst = ip6_route_output(&init_net, NULL, &fl6); + err = ip6_dst_lookup(sk, &dst, fl6); if (!asoc || saddr) goto out; - if (dst->error) { - dst_release(dst); - dst = NULL; - bp = &asoc->base.bind_addr; - scope = sctp_scope(daddr); - /* Walk through the bind address list and try to get a dst that - * matches a bind address as the source address. + bp = &asoc->base.bind_addr; + scope = sctp_scope(daddr); + /* ip6_dst_lookup has filled in the fl6->saddr for us. Check + * to see if we can use it. + */ + if (!err) { + /* Walk through the bind address list and look for a bind + * address that matches the source address of the returned dst. */ + sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port)); rcu_read_lock(); list_for_each_entry_rcu(laddr, &bp->address_list, list) { - if (!laddr->valid) + if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) 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; - } + + /* Do not compare against v4 addrs */ + if ((laddr->a.sa.sa_family == AF_INET6) && + (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) { + rcu_read_unlock(); + goto out; } } rcu_read_unlock(); - if (baddr) { - ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr); - dst = ip6_route_output(&init_net, NULL, &fl6); + /* 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 the + * best source address for a given destination. + */ + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid && laddr->state != SCTP_ADDR_SRC) + continue; + if ((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; + } } } + rcu_read_unlock(); + if (baddr) { + ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr); + err = ip6_dst_lookup(sk, &dst, fl6); + } + out: - if (!dst->error) { + if (!err) { struct rt6_info *rt; rt = (struct rt6_info *)dst; SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", - &rt->rt6i_dst.addr, &rt->rt6i_src.addr); + &rt->rt6i_dst.addr, &fl6->saddr); return dst; } SCTP_DEBUG_PRINTK("NO ROUTE\n"); - dst_release(dst); return NULL; } @@ -328,64 +358,21 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, * and asoc's bind address list. */ static void sctp_v6_get_saddr(struct sctp_sock *sk, - struct sctp_association *asoc, - struct dst_entry *dst, + struct sctp_transport *t, union sctp_addr *daddr, - union sctp_addr *saddr) + struct flowi *fl) { - struct sctp_bind_addr *bp; - struct sctp_sockaddr_entry *laddr; - sctp_scope_t scope; - union sctp_addr *baddr = NULL; - __u8 matchlen = 0; - __u8 bmatchlen; + struct flowi6 *fl6 = &fl->u.ip6; + union sctp_addr *saddr = &t->saddr; SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ", - __func__, asoc, dst, &daddr->v6.sin6_addr); - - if (!asoc) { - ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)), - dst ? ip6_dst_idev(dst)->dev : NULL, - &daddr->v6.sin6_addr, - inet6_sk(&sk->inet.sk)->srcprefs, - &saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n", - &saddr->v6.sin6_addr); - return; - } - - scope = sctp_scope(daddr); + __func__, t->asoc, t->dst, &daddr->v6.sin6_addr); - bp = &asoc->base.bind_addr; - - /* Go through the bind address list and find the best source address - * that matches the scope of the destination 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))) { - bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); - if (!baddr || (matchlen < bmatchlen)) { - baddr = &laddr->a; - matchlen = bmatchlen; - } - } - } - if (baddr) { - memcpy(saddr, baddr, sizeof(union sctp_addr)); - SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr); - } else { - pr_err("%s: asoc:%p Could not find a valid source " - "address for the dest:%pI6\n", - __func__, asoc, &daddr->v6.sin6_addr); + if (t->dst) { + saddr->v6.sin6_family = AF_INET6; + ipv6_addr_copy(&saddr->v6.sin6_addr, &fl6->saddr); } - - rcu_read_unlock(); } /* Make a copy of all potential local addresses. */ @@ -507,14 +494,13 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr, return length; } -/* Initialize a sctp_addr from a dst_entry. */ -static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, +/* Initialize a sctp_addr from struct in6_addr. */ +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, __be16 port) { - struct rt6_info *rt = (struct rt6_info *)dst; addr->sa.sa_family = AF_INET6; addr->v6.sin6_port = port; - ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); + ipv6_addr_copy(&addr->v6.sin6_addr, saddr); } /* Compare addresses exactly. @@ -1001,7 +987,6 @@ static struct sctp_af sctp_af_inet6 = { .to_sk_daddr = sctp_v6_to_sk_daddr, .from_addr_param = sctp_v6_from_addr_param, .to_addr_param = sctp_v6_to_addr_param, - .dst_saddr = sctp_v6_dst_saddr, .cmp_addr = sctp_v6_cmp_addr, .scope = sctp_v6_scope, .addr_valid = sctp_v6_addr_valid, |