@@ -244,6 +244,30 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
return ip6_xmit(sk, skb, &fl6, np->opt);
}
+/* Small helper function that combines route and XFRM lookups. This is
+ * done since we might be looping through route lookups.
+ */
+static int sctp_v6_dst_lookup(struct sock *sk, struct dst_entry **dst,
+ struct flowi6 *fl6)
+{
+ int err;
+
+ err = ip6_dst_lookup(sk, dst, fl6);
+ if (err)
+ goto done;
+
+ err = xfrm_lookup(sock_net(sk), *dst, flowi6_to_flowi(fl6), sk, 0);
+ if (err)
+ goto done;
+
+ return 0;
+
+done:
+ dst_release(*dst);
+ *dst = NULL;
+ return err;
+}
+
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
@@ -266,18 +290,23 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
memset(fl6, 0, sizeof(struct flowi6));
ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr);
+ fl6->fl6_dport = daddr->v6.sin6_port;
if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
fl6->flowi6_oif = daddr->v6.sin6_scope_id;
SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
+ if (asoc)
+ fl6->fl6_sport = htons(asoc->base.bind_addr.port);
+
if (saddr) {
ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr);
+ fl6->fl6_sport = saddr->v6.sin6_port;
SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- err = ip6_dst_lookup(sk, &dst, fl6);
+ err = sctp_v6_dst_lookup(sk, &dst, fl6);
if (!asoc || saddr)
goto out;
@@ -331,7 +360,8 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
rcu_read_unlock();
if (baddr) {
ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
- err = ip6_dst_lookup(sk, &dst, fl6);
+ fl6->fl6_sport = baddr->v6.sin6_port;
+ err = sctp_v6_dst_lookup(sk, &dst, fl6);
}
out: