Commit 2dc49d16 authored by Nicolas Dichtel's avatar Nicolas Dichtel Committed by David S. Miller

tcp6: don't move IP6CB before xfrm6_policy_check()

When xfrm6_policy_check() is used, _decode_session6() is called after some
intermediate functions. This function uses IP6CB(), thus TCP_SKB_CB() must be
prepared after the call of xfrm6_policy_check().

Before this patch, scenarii with IPv6 + TCP + IPsec Transport are broken.

Fixes: 971f10ec ("tcp: better TCP_SKB_CB layout to reduce cache line misses")
Reported-by: default avatarHuaibin Wang <huaibin.wang@6wind.com>
Suggested-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarNicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5ad24def
...@@ -1387,6 +1387,28 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -1387,6 +1387,28 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
return 0; return 0;
} }
static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
const struct tcphdr *th)
{
/* This is tricky: we move IP6CB at its correct location into
* TCP_SKB_CB(). It must be done after xfrm6_policy_check(), because
* _decode_session6() uses IP6CB().
* barrier() makes sure compiler won't play aliasing games.
*/
memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb),
sizeof(struct inet6_skb_parm));
barrier();
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
skb->len - th->doff*4);
TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th);
TCP_SKB_CB(skb)->tcp_tw_isn = 0;
TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
TCP_SKB_CB(skb)->sacked = 0;
}
static int tcp_v6_rcv(struct sk_buff *skb) static int tcp_v6_rcv(struct sk_buff *skb)
{ {
const struct tcphdr *th; const struct tcphdr *th;
...@@ -1418,24 +1440,9 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1418,24 +1440,9 @@ static int tcp_v6_rcv(struct sk_buff *skb)
th = tcp_hdr(skb); th = tcp_hdr(skb);
hdr = ipv6_hdr(skb); hdr = ipv6_hdr(skb);
/* This is tricky : We move IPCB at its correct location into TCP_SKB_CB()
* barrier() makes sure compiler wont play fool^Waliasing games.
*/
memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb),
sizeof(struct inet6_skb_parm));
barrier();
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
skb->len - th->doff*4);
TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th);
TCP_SKB_CB(skb)->tcp_tw_isn = 0;
TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
TCP_SKB_CB(skb)->sacked = 0;
sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest, sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest,
tcp_v6_iif(skb)); inet6_iif(skb));
if (!sk) if (!sk)
goto no_tcp_socket; goto no_tcp_socket;
...@@ -1451,6 +1458,8 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1451,6 +1458,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse; goto discard_and_relse;
tcp_v6_fill_cb(skb, hdr, th);
#ifdef CONFIG_TCP_MD5SIG #ifdef CONFIG_TCP_MD5SIG
if (tcp_v6_inbound_md5_hash(sk, skb)) if (tcp_v6_inbound_md5_hash(sk, skb))
goto discard_and_relse; goto discard_and_relse;
...@@ -1482,6 +1491,8 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1482,6 +1491,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_it; goto discard_it;
tcp_v6_fill_cb(skb, hdr, th);
if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
csum_error: csum_error:
TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS); TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
...@@ -1505,6 +1516,8 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1505,6 +1516,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
goto discard_it; goto discard_it;
} }
tcp_v6_fill_cb(skb, hdr, th);
if (skb->len < (th->doff<<2)) { if (skb->len < (th->doff<<2)) {
inet_twsk_put(inet_twsk(sk)); inet_twsk_put(inet_twsk(sk));
goto bad_packet; goto bad_packet;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment