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
*/
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;
}
* 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. */
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.
.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,
*/
static struct dst_entry *sctp_v4_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 rtable *rt;
- struct flowi4 fl4;
+ struct flowi4 *fl4 = &fl->u.ip4;
struct sctp_bind_addr *bp;
struct sctp_sockaddr_entry *laddr;
struct dst_entry *dst = NULL;
union sctp_addr dst_saddr;
- memset(&fl4, 0x0, sizeof(struct flowi4));
- fl4.daddr = daddr->v4.sin_addr.s_addr;
- fl4.fl4_dport = daddr->v4.sin_port;
- fl4.flowi4_proto = IPPROTO_SCTP;
+ memset(fl4, 0x0, sizeof(struct flowi4));
+ fl4->daddr = daddr->v4.sin_addr.s_addr;
+ fl4->fl4_dport = daddr->v4.sin_port;
+ fl4->flowi4_proto = IPPROTO_SCTP;
if (asoc) {
- fl4.flowi4_tos = RT_CONN_FLAGS(asoc->base.sk);
- fl4.flowi4_oif = asoc->base.sk->sk_bound_dev_if;
- fl4.fl4_sport = htons(asoc->base.bind_addr.port);
+ fl4->flowi4_tos = RT_CONN_FLAGS(asoc->base.sk);
+ fl4->flowi4_oif = asoc->base.sk->sk_bound_dev_if;
+ fl4->fl4_sport = htons(asoc->base.bind_addr.port);
}
if (saddr) {
- fl4.saddr = saddr->v4.sin_addr.s_addr;
- fl4.fl4_sport = saddr->v4.sin_port;
+ fl4->saddr = saddr->v4.sin_addr.s_addr;
+ fl4->fl4_sport = saddr->v4.sin_port;
}
SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
- __func__, &fl4.daddr, &fl4.saddr);
+ __func__, &fl4->daddr, &fl4->saddr);
- rt = ip_route_output_key(&init_net, &fl4);
+ rt = ip_route_output_key(&init_net, fl4);
if (!IS_ERR(rt))
dst = &rt->dst;
continue;
if ((laddr->state == SCTP_ADDR_SRC) &&
(AF_INET == laddr->a.sa.sa_family)) {
- fl4.saddr = laddr->a.v4.sin_addr.s_addr;
- fl4.fl4_sport = laddr->a.v4.sin_port;
- rt = ip_route_output_key(&init_net, &fl4);
+ fl4->saddr = laddr->a.v4.sin_addr.s_addr;
+ fl4->fl4_sport = laddr->a.v4.sin_port;
+ rt = ip_route_output_key(&init_net, fl4);
if (!IS_ERR(rt)) {
dst = &rt->dst;
goto out_unlock;
* to cache it separately and hence this is an empty routine.
*/
static void sctp_v4_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 rtable *rt = (struct rtable *)dst;
-
- if (!asoc)
- return;
+ union sctp_addr *saddr = &t->saddr;
+ struct rtable *rt = (struct rtable *)t->dst;
if (rt) {
saddr->v4.sin_family = AF_INET;
- saddr->v4.sin_port = htons(asoc->base.bind_addr.port);
saddr->v4.sin_addr.s_addr = rt->rt_src;
}
}
.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,