]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ipv6: discard fragment queue earlier if there is malformed datagram
authorFernando Fernandez Mancera <fmancera@suse.de>
Wed, 25 Feb 2026 13:37:58 +0000 (14:37 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sat, 28 Feb 2026 03:08:54 +0000 (19:08 -0800)
Currently the kernel IPv6 implementation is not dicarding the fragment
queue upon receiving a IPv6 fragment that is not 8 bytes aligned. It
relies on queue expiration to free the queue.

While RFC 8200 section 4.5 does not explicitly mention that the rest of
fragments must be discarded, it does not make sense to keep them. The
parameter problem message is sent regardless that. In addition, if the
sender is able to re-compose the datagram so it is 8 bytes aligned it
would qualify as a new whole datagram not fitting into the same fragment
queue.

The same situation happens if segment end is exceeding the IPv6 maximum
packet length. The sooner we can free resources the better during
reassembly, the better.

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Link: https://patch.msgid.link/20260225133758.4553-1-fmancera@suse.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/reassembly.c

index 25ec8001898df76eba08233c182e0c7154c1c1b7..11f9144bebbe2cf0152992c526fb6728f83f462d 100644 (file)
@@ -132,6 +132,9 @@ static int ip6_frag_queue(struct net *net,
                /* note that if prob_offset is set, the skb is freed elsewhere,
                 * we do not free it here.
                 */
+               inet_frag_kill(&fq->q, refs);
+               __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+                               IPSTATS_MIB_REASMFAILS);
                return -1;
        }
 
@@ -163,6 +166,9 @@ static int ip6_frag_queue(struct net *net,
                         * this case. -DaveM
                         */
                        *prob_offset = offsetof(struct ipv6hdr, payload_len);
+                       inet_frag_kill(&fq->q, refs);
+                       __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+                                       IPSTATS_MIB_REASMFAILS);
                        return -1;
                }
                if (end > fq->q.len) {