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