Remember when all fragments describe a locally constructed IPv6
representation. Restore that state after conntrack reassembly so
IPv6 output can fragment the datagram for its actual route without
treating the old fragment size as a PMTU constraint.
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
---
New patch in RFC v2.
include/net/ipv6_frag.h | 2 ++
net/ipv6/netfilter/nf_conntrack_reasm.c | 11 ++++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
index 41d9fc6965f9..df0a6309b342 100644
--- a/include/net/ipv6_frag.h
+++ b/include/net/ipv6_frag.h
@@ -26,6 +26,7 @@ struct frag_queue {
int iif;
__u16 nhoffset;
u8 ecn;
+ bool frag_source;
};
#if IS_ENABLED(CONFIG_IPV6)@@ -36,6 +37,7 @@ static inline void ip6frag_init(struct inet_frag_queue *q, const void *a)
q->key.v6 = *key;
fq->ecn = 0;
+ fq->frag_source = true;
}
static inline u32 ip6frag_key_hashfn(const void *data, u32 len, u32 seed)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index be72c4346f8b..dee89aaf56ee 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -272,6 +272,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
fq->q.tstamp_type = skb->tstamp_type;
fq->q.meat += skb->len;
fq->ecn |= ecn;
+ fq->frag_source &= !!(IP6CB(skb)->flags & IP6SKB_FRAG_SOURCE);
if (payload_len > fq->q.max_size)
fq->q.max_size = payload_len;
add_frag_mem_limit(fq->q.fqdir, skb->truesize);
@@ -360,8 +361,16 @@ 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)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
+ if (fq->frag_source) {
+ /* use the output route MTU instead of the old fragment size */
+ IP6CB(skb)->frag_max_size = 0;
+ IP6CB(skb)->flags |= IP6SKB_FRAG_SOURCE;
+ } else {
+ IP6CB(skb)->frag_max_size = sizeof(struct ipv6hdr) +
+ fq->q.max_size;
+ IP6CB(skb)->flags &= ~IP6SKB_FRAG_SOURCE;
+ }
/* Yes, and fold redundant checksum back. 8) */
if (skb->ip_summed == CHECKSUM_COMPLETE)--
2.55.0