nf_ct_frag6_reasm() removes the fragment header and resets the transport header, but unlike ip6_frag_reasm() it leaves IP6CB(skb)->nhoff set to its pre-reassembly value. Subsequent IPv6 local delivery consumes this stale next-header offset alongside the updated transport header. As a result, the two cursors can point to different protocol boundaries after conntrack defragmentation, causing later processing stages to interpret a different transport protocol than the one evaluated by conntrack and nftables. Restore IP6CB(skb)->nhoff from the saved pre-fragment next-header offset after reassembly, matching ip6_frag_reasm(). Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Cc: stable@vger.kernel.org Signed-off-by: Ryosuke Iwahashi --- v2: - resend with git send-email formatting - use full real name in the author and sign-off - no code change --- net/ipv6/netfilter/nf_conntrack_reasm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index be72c4346..634122055 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -321,6 +321,7 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, int *refs) { void *reasm_data; + unsigned int nhoff; int payload_len; u8 ecn; @@ -345,7 +346,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, /* We have to remove fragment header from datagram and to relocate * header in order to calculate ICV correctly. */ - skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0]; + nhoff = fq->nhoffset; + skb_network_header(skb)[nhoff] = skb_transport_header(skb)[0]; memmove(skb->head + sizeof(struct frag_hdr), skb->head, (skb->data - skb->head) - sizeof(struct frag_hdr)); if (skb_mac_header_was_set(skb)) @@ -360,6 +362,7 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, skb->dev = dev; ipv6_hdr(skb)->payload_len = htons(payload_len); ipv6_change_dsfield(ipv6_hdr(skb), 0xff, ecn); + IP6CB(skb)->nhoff = nhoff; IP6CB(skb)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size; IP6CB(skb)->flags |= IP6SKB_FRAGMENTED; -- 2.56.0