|
@@ -150,14 +150,16 @@ int ip6_output(struct sock *sk, struct sk_buff *skb)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * xmit an sk_buff (used by TCP, SCTP and DCCP)
|
|
|
+ * xmit an sk_buff (used by TCP, SCTP and DCCP)
|
|
|
+ * Note : socket lock is not held for SYNACK packets, but might be modified
|
|
|
+ * by calls to skb_set_owner_w() and ipv6_local_error(),
|
|
|
+ * which are using proper atomic operations or spinlocks.
|
|
|
*/
|
|
|
-
|
|
|
-int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
|
|
|
+int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
|
|
|
struct ipv6_txoptions *opt, int tclass)
|
|
|
{
|
|
|
struct net *net = sock_net(sk);
|
|
|
- struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
+ const struct ipv6_pinfo *np = inet6_sk(sk);
|
|
|
struct in6_addr *first_hop = &fl6->daddr;
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
|
|
struct ipv6hdr *hdr;
|
|
@@ -186,7 +188,10 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
|
|
|
}
|
|
|
consume_skb(skb);
|
|
|
skb = skb2;
|
|
|
- skb_set_owner_w(skb, sk);
|
|
|
+ /* skb_set_owner_w() changes sk->sk_wmem_alloc atomically,
|
|
|
+ * it is safe to call in our context (socket lock not held)
|
|
|
+ */
|
|
|
+ skb_set_owner_w(skb, (struct sock *)sk);
|
|
|
}
|
|
|
if (opt->opt_flen)
|
|
|
ipv6_push_frag_opts(skb, opt, &proto);
|
|
@@ -224,13 +229,20 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
|
|
|
if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
|
|
|
IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
|
IPSTATS_MIB_OUT, skb->len);
|
|
|
+ /* hooks should never assume socket lock is held.
|
|
|
+ * we promote our socket to non const
|
|
|
+ */
|
|
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
|
|
|
- net, sk, skb, NULL, dst->dev,
|
|
|
+ net, (struct sock *)sk, skb, NULL, dst->dev,
|
|
|
dst_output_okfn);
|
|
|
}
|
|
|
|
|
|
skb->dev = dst->dev;
|
|
|
- ipv6_local_error(sk, EMSGSIZE, fl6, mtu);
|
|
|
+ /* ipv6_local_error() does not require socket lock,
|
|
|
+ * we promote our socket to non const
|
|
|
+ */
|
|
|
+ ipv6_local_error((struct sock *)sk, EMSGSIZE, fl6, mtu);
|
|
|
+
|
|
|
IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS);
|
|
|
kfree_skb(skb);
|
|
|
return -EMSGSIZE;
|