As the IPv6 route lookup now also returns the selected source address
there is no need for a separate source address lookup. In fact, the
source address selection needs to be moved to get_dst() because the
selected IPv6 source address isn't always stored in the route.
Sometimes this makes it impossible to guess the correct address later on.

Signed-off-by: Ville Nuorvala <[EMAIL PROTECTED]>
---
 include/net/sctp/structs.h |    7 -
 net/sctp/ipv6.c            |  235 +++++++++++++++++++++++---------------------
 net/sctp/protocol.c        |   56 ++++------
 net/sctp/transport.c       |    8 +
 4 files changed, 148 insertions(+), 158 deletions(-)

diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index c6d93bb..e0973a3 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -529,15 +529,8 @@ struct sctp_af {
        struct dst_entry *(*get_dst)    (struct sctp_association *asoc,
                                         union sctp_addr *daddr,
                                         union sctp_addr *saddr);
-       void            (*get_saddr)    (struct sctp_association *asoc,
-                                        struct dst_entry *dst,
-                                        union sctp_addr *daddr,
-                                        union sctp_addr *saddr);
        void            (*copy_addrlist) (struct list_head *,
                                          struct net_device *);
-       void            (*dst_saddr)    (union sctp_addr *saddr,
-                                        struct dst_entry *dst,
-                                        unsigned short port);
        int             (*cmp_addr)     (const union sctp_addr *addr1,
                                         const union sctp_addr *addr2);
        void            (*addr_copy)    (union sctp_addr *dst,
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 78071c6..68ead54 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -188,46 +188,6 @@ static int sctp_v6_xmit(struct sk_buff *
        return ip6_xmit(sk, skb, &fl, np->opt, ipfragok);
 }

-/* Returns the dst cache entry for the given source and destination ip
- * addresses.
- */
-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;
-
-       memset(&fl, 0, sizeof(fl));
-       ipv6_addr_copy(&fl.fl6_dst, &daddr->v6.sin6_addr);
-       if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
-               fl.oif = daddr->v6.sin6_scope_id;
-       
-
-       SCTP_DEBUG_PRINTK("%s: DST=" NIP6_FMT " ",
-                         __FUNCTION__, NIP6(fl.fl6_dst));
-
-       if (saddr) {
-               ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr);
-               SCTP_DEBUG_PRINTK(
-                       "SRC=" NIP6_FMT " - ",
-                       NIP6(fl.fl6_src));
-       }
-
-       dst = ip6_route_output(NULL, &fl);
-       if (!dst->error) {
-               struct rt6_info *rt;
-               rt = (struct rt6_info *)dst;
-               SCTP_DEBUG_PRINTK(
-                       "rt6_dst:" NIP6_FMT " rt6_src:" NIP6_FMT "\n",
-                       NIP6(rt->rt6i_dst.addr), NIP6(rt->rt6i_src.addr));
-               return dst;
-       }
-       SCTP_DEBUG_PRINTK("NO ROUTE\n");
-       dst_release(dst);
-       return NULL;
-}
-
 /* Returns the number of consecutive initial bits that match in the 2 ipv6
  * addresses.
  */
@@ -250,69 +210,6 @@ static inline int sctp_v6_addr_match_len
        return (i*32);
 }

-/* Fills in the source address(saddr) based on the destination address(daddr)
- * and asoc's bind address list.
- */
-static void sctp_v6_get_saddr(struct sctp_association *asoc,
-                             struct dst_entry *dst,
-                             union sctp_addr *daddr,
-                             union sctp_addr *saddr)
-{
-       struct sctp_bind_addr *bp;
-       rwlock_t *addr_lock;
-       struct sctp_sockaddr_entry *laddr;
-       struct list_head *pos;
-       sctp_scope_t scope;
-       union sctp_addr *baddr = NULL;
-       __u8 matchlen = 0;
-       __u8 bmatchlen;
-
-       SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p "
-                         "daddr:" NIP6_FMT " ",
-                         __FUNCTION__, asoc, dst, NIP6(daddr->v6.sin6_addr));
-
-       if (!asoc) {
-               ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr);
-               SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n",
-                                 NIP6(saddr->v6.sin6_addr));
-               return;
-       }
-
-       scope = sctp_scope(daddr);
-
-       bp = &asoc->base.bind_addr;
-       addr_lock = &asoc->base.addr_lock;
-
-       /* Go through the bind address list and find the best source address
-        * that matches the scope of the destination address.
-        */
-       sctp_read_lock(addr_lock);
-       list_for_each(pos, &bp->address_list) {
-               laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
-               if ((laddr->use_as_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: " NIP6_FMT "\n",
-                                 NIP6(saddr->v6.sin6_addr));
-       } else {
-               printk(KERN_ERR "%s: asoc:%p Could not find a valid source "
-                      "address for the dest:" NIP6_FMT "\n",
-                      __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr));
-       }
-
-       sctp_read_unlock(addr_lock);
-}
-
 /* Make a copy of all potential local addresses. */
 static void sctp_v6_copy_addrlist(struct list_head *addrlist,
                                  struct net_device *dev)
@@ -431,14 +328,14 @@ static int sctp_v6_to_addr_param(const u
        return length;
 }

-/* Initialize a sctp_addr from a dst_entry. */
-static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
-                             unsigned short port)
+/* Initialize a sctp_addr from a flowi. */
+static void sctp_v6_fl_saddr(union sctp_addr *addr, struct flowi *fl,
+                            unsigned short 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, &fl->fl6_src);
+       addr->v6.sin6_scope_id = fl->oif;
 }

 /* Compare addresses exactly.
@@ -479,6 +376,126 @@ static int sctp_v6_cmp_addr(const union
        return 1;
 }

+/* Returns the dst cache entry for the given source and destination ip
+ * addresses.
+ */
+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 sctp_bind_addr *bp;
+       rwlock_t *addr_lock;
+       struct sctp_sockaddr_entry *laddr;
+       struct list_head *pos;
+       struct rt6_info *rt;
+       union sctp_addr baddr;
+       sctp_scope_t scope;
+       __u8 matchlen = 0;
+       __u8 bmatchlen;
+
+       memset(&fl, 0, sizeof(fl));
+       ipv6_addr_copy(&fl.fl6_dst, &daddr->v6.sin6_addr);
+       if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
+               fl.oif = daddr->v6.sin6_scope_id;
+
+       ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr);
+       SCTP_DEBUG_PRINTK("%s: DST=" NIP6_FMT " SRC=" NIP6_FMT " ",
+                         __FUNCTION__, NIP6(fl.fl6_dst), NIP6(fl.fl6_src));
+
+       dst = ip6_route_output(NULL, &fl);
+       if (dst->error) {
+               dst_release(dst);
+               dst = NULL;
+       }
+       if (!ipv6_addr_any(&saddr->v6.sin6_addr))
+               goto out;
+       if (!asoc) {
+               if (dst)
+                       ipv6_addr_copy(&saddr->v6.sin6_addr, &fl.fl6_src);
+               goto out;
+       }
+       bp = &asoc->base.bind_addr;
+       addr_lock = &asoc->base.addr_lock;
+
+       if (dst) {
+               /* Walk through the bind address list and look for a bind
+                * address that matches the source address of the returned rt.
+                */
+               sctp_v6_fl_saddr(&baddr, &fl, bp->port);
+               sctp_read_lock(addr_lock);
+               list_for_each(pos, &bp->address_list) {
+                       laddr = list_entry(pos, struct sctp_sockaddr_entry,
+                                          list);
+                       if (!laddr->use_as_src)
+                               continue;
+                       if (sctp_v6_cmp_addr(&baddr, &laddr->a))
+                               goto init_saddr;
+               }
+               sctp_read_unlock(addr_lock);
+
+               /* Invalid rt or none of the bound addresses match the source
+                * address. So release it.
+                */
+               dst_release(dst);
+               dst = NULL;
+       }
+
+       /* Go through the bind address list and find the best source address
+        * that matches the scope of the destination address.
+        */
+       memset(&baddr, 0, sizeof(union sctp_addr));
+       scope = sctp_scope(daddr);
+       sctp_read_lock(addr_lock);
+       list_for_each(pos, &bp->address_list) {
+               laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
+               
+               if (!laddr->use_as_src ||
+                   laddr->a.sa.sa_family != AF_INET6 ||
+                   scope > sctp_scope(&laddr->a) ||
+                   (ipv6_addr_type(&laddr->a.v6.sin6_addr) &
+                    IPV6_ADDR_LINKLOCAL &&
+                    laddr->a.v6.sin6_scope_id != fl.oif))
+                       continue;
+
+               bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+               if (!dst || (matchlen < bmatchlen)) {
+                       struct dst_entry *dst2;
+                       ipv6_addr_copy(&fl.fl6_src, &laddr->a.v6.sin6_addr);
+                       dst2 = ip6_route_output(NULL, &fl);
+                       if (dst2->error) {
+                               dst_release(dst2);
+                               dst2 = NULL;
+                               continue;
+                       }
+                       dst_release(dst);
+                       dst = dst2;
+                       memcpy(&baddr, &laddr->a, sizeof(union sctp_addr));
+                       matchlen = bmatchlen;
+               }
+       }
+       if (dst)
+               goto init_saddr;
+out_unlock:
+       sctp_read_unlock(addr_lock);
+out:
+       if (dst) {
+               rt = (struct rt6_info *) dst;
+               SCTP_DEBUG_PRINTK("SRC=" NIP6_FMT
+                                 " rt6_dst=" NIP6_FMT
+                                 " rt6_src=" NIP6_FMT "\n",
+                                 NIP6(saddr->v6.sin6_addr),
+                                 NIP6(rt->rt6i_dst.addr),
+                                 NIP6(rt->rt6i_src.addr));
+       } else
+               SCTP_DEBUG_PRINTK("NO ROUTE\n");
+       return dst;
+init_saddr:
+       memcpy(saddr, &baddr, sizeof(union sctp_addr));
+       goto out_unlock;
+}
+
 /* Initialize addr struct to INADDR_ANY. */
 static void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port)
 {
@@ -922,7 +939,6 @@ static struct sctp_af sctp_ipv6_specific
        .setsockopt        = ipv6_setsockopt,
        .getsockopt        = ipv6_getsockopt,
        .get_dst           = sctp_v6_get_dst,
-       .get_saddr         = sctp_v6_get_saddr,
        .copy_addrlist     = sctp_v6_copy_addrlist,
        .from_skb          = sctp_v6_from_skb,
        .from_sk           = sctp_v6_from_sk,
@@ -930,7 +946,6 @@ static struct sctp_af sctp_ipv6_specific
        .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,
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index fac7674..d412ec0 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -313,11 +313,10 @@ static int sctp_v4_to_addr_param(const u
        return length;
 }

-/* Initialize a sctp_addr from a dst_entry. */
-static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst,
+/* Initialize a sctp_addr from a rtable. */
+static void sctp_v4_rt_saddr(union sctp_addr *saddr, struct rtable *rt,
                              unsigned short port)
 {
-       struct rtable *rt = (struct rtable *)dst;
        saddr->v4.sin_family = AF_INET;
        saddr->v4.sin_port = port;
        saddr->v4.sin_addr.s_addr = rt->rt_src;
@@ -451,39 +450,40 @@ static struct dst_entry *sctp_v4_get_dst
                fl.fl4_tos = RT_CONN_FLAGS(asoc->base.sk);
                fl.oif = asoc->base.sk->sk_bound_dev_if;
        }
-       if (saddr)
-               fl.fl4_src = saddr->v4.sin_addr.s_addr;
-
+       fl.fl4_src = saddr->v4.sin_addr.s_addr;
        SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
                          __FUNCTION__, NIPQUAD(fl.fl4_dst),
                          NIPQUAD(fl.fl4_src));

-       if (!ip_route_output_key(&rt, &fl)) {
+       if (!ip_route_output_key(&rt, &fl))
                dst = &rt->u.dst;
-       }

        /* If there is no association or if a source address is passed, no
         * more validation is required.
         */
-       if (!asoc || saddr)
+       if (saddr->v4.sin_addr.s_addr != INADDR_ANY)
                goto out;
-
+       if (!asoc) {
+               if (dst)
+                       saddr->v4.sin_addr.s_addr = rt->rt_src;
+               goto out;
+       }
        bp = &asoc->base.bind_addr;
        addr_lock = &asoc->base.addr_lock;

        if (dst) {
                /* Walk through the bind address list and look for a bind
-                * address that matches the source address of the returned dst.
+                * address that matches the source address of the returned rt.
                 */
+               sctp_v4_rt_saddr(&dst_saddr, rt, bp->port);
                sctp_read_lock(addr_lock);
                list_for_each(pos, &bp->address_list) {
                        laddr = list_entry(pos, struct sctp_sockaddr_entry,
                                           list);
                        if (!laddr->use_as_src)
                                continue;
-                       sctp_v4_dst_saddr(&dst_saddr, dst, bp->port);
                        if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
-                               goto out_unlock;
+                               goto init_saddr;
                }
                sctp_read_unlock(addr_lock);

@@ -506,11 +506,10 @@ static struct dst_entry *sctp_v4_get_dst
                        fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
                        if (!ip_route_output_key(&rt, &fl)) {
                                dst = &rt->u.dst;
-                               goto out_unlock;
+                               goto init_saddr;
                        }
                }
        }
-
 out_unlock:
        sctp_read_unlock(addr_lock);
 out:
@@ -521,26 +520,11 @@ out:
                SCTP_DEBUG_PRINTK("NO ROUTE\n");

        return dst;
-}
-
-/* For v4, the source address is cached in the route entry(dst). So no need
- * to cache it separately and hence this is an empty routine.
- */
-static void sctp_v4_get_saddr(struct sctp_association *asoc,
-                             struct dst_entry *dst,
-                             union sctp_addr *daddr,
-                             union sctp_addr *saddr)
-{
-       struct rtable *rt = (struct rtable *)dst;
-
-       if (!asoc)
-               return;
-
-       if (rt) {
-               saddr->v4.sin_family = AF_INET;
-               saddr->v4.sin_port = asoc->base.bind_addr.port;
-               saddr->v4.sin_addr.s_addr = rt->rt_src;
-       }
+init_saddr:
+       saddr->v4.sin_family = AF_INET;
+       saddr->v4.sin_port = dst_saddr.v4.sin_port;
+       saddr->v4.sin_addr.s_addr = dst_saddr.v4.sin_addr.s_addr;
+       goto out_unlock;
 }

 /* What interface did this skb arrive on? */
@@ -891,7 +875,6 @@ static struct sctp_af sctp_ipv4_specific
        .setsockopt        = ip_setsockopt,
        .getsockopt        = ip_getsockopt,
        .get_dst           = sctp_v4_get_dst,
-       .get_saddr         = sctp_v4_get_saddr,
        .copy_addrlist     = sctp_v4_copy_addrlist,
        .from_skb          = sctp_v4_from_skb,
        .from_sk           = sctp_v4_from_sk,
@@ -899,7 +882,6 @@ static struct sctp_af sctp_ipv4_specific
        .to_sk_daddr       = sctp_v4_to_sk_daddr,
        .from_addr_param   = sctp_v4_from_addr_param,
        .to_addr_param     = sctp_v4_to_addr_param,
-       .dst_saddr         = sctp_v4_dst_saddr,
        .cmp_addr          = sctp_v4_cmp_addr,
        .addr_valid        = sctp_v4_addr_valid,
        .inaddr_any        = sctp_v4_inaddr_any,
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 3e5936a..e365e9c 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -232,7 +232,8 @@ void sctp_transport_pmtu(struct sctp_tra
 {
        struct dst_entry *dst;

-       dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
+       dst = transport->af_specific->get_dst(NULL, &transport->ipaddr,
+                                             &transport->saddr);

        if (dst) {
                transport->pathmtu = dst_mtu(dst);
@@ -252,12 +253,11 @@ void sctp_transport_route(struct sctp_tr
        union sctp_addr *daddr = &transport->ipaddr;
        struct dst_entry *dst;

-       dst = af->get_dst(asoc, daddr, saddr);
-
        if (saddr)
                memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
        else
-               af->get_saddr(asoc, dst, daddr, &transport->saddr);
+               af->inaddr_any(&transport->saddr, transport->saddr.v4.sin_port);
+       dst = af->get_dst(asoc, daddr, &transport->saddr);

        transport->dst = dst;
        if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) 
{
-- 
1.4.2.3

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to