From nobody Mon Jun 8 22:52:41 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 87D7D3939BD for ; Mon, 25 May 2026 20:11:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779739903; cv=none; b=oHMcaKX3srK1RHBty6c+NauqP3dIHU8DN3u2hY8y++YZHuZdpMAi2HvykVFEde2MythK8e/7vyDk06WUDTASsiNR5wvcSlO77QwnfMufZnM6Bp8t85v2XNBBOvxkxefIr/WwzOpfdi/1njEkb6zhB7fhGPNAEFdsd0Bmv+7zCsE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779739903; c=relaxed/simple; bh=iPocNdP11IWJ84RYgMwB6ikmsitLUIqeZWoQumVliOg=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=pZ2bn4ZPj29Sbr/jqJ2+CC0oj6o7pXB3uffnGpZmNilVFwzSqyk6IUCIVRnxnbkB+ncgnjC+2ZsS/HjUaDBugXZRHxQaz/g1nr7HC4uit0CEM9JgRs+GBpH2MvQmbVBMgZk3P7uiDAP0Dd68UzX1D1ZdoAkMuuwVguiVp3kVbHA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=R+O0xYY/; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="R+O0xYY/" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-49041fb8c23so27730145e9.0 for ; Mon, 25 May 2026 13:11:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779739900; x=1780344700; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=9LV2RF28IqA2a7V3wy5JIzlW5lVMgYSXDFk9zTWJsOk=; b=R+O0xYY/juqjTUygtm8Yinnir6cnO/VKNSlOMRnlzX3BtHcCzZNcMLtvNjEp4pC7dq Cjg4zBbHgyPln8AQO8zs6/9pPSO+dTieFG5OghW/10wImDswnKuzJODhx3Y+SCCtJREQ T0wzWyOqjK9iedmENI088h9JVGEzXI6NkKyJ3Q1KWCQ/W+yHkbuOCqexJIhoXoRiLAqr HGiEIwJyYe8veI6tUpXm6M7Rz7Uw5MAFKTXdQ1qjYtRarROy0PtdtlO+8qW9g8KCxOWv LAZTkDrPi3etxAvU50eE2L4Vy3+e73EOsXH+VHB75oaFtmR0P1xnO/RGHQ65Z0iMyTjA K7Mg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779739900; x=1780344700; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=9LV2RF28IqA2a7V3wy5JIzlW5lVMgYSXDFk9zTWJsOk=; b=ra6TUhlP40g8VCwpMRlarx63n/LFd1l4vZwksT3cQRT/8IxYw3AynAY4CwEKwsmGvY HJGEm8XVbHed3iY4WD5qdq5tuJWJ3yGcXjnz5p1lIxUZYTVqMdn+7z5vyfWCZl5YzJXs +YuqI7Jnorg4yHDRont1cBBeLKjeCOhwvJmqKY60v7GyjegC+OQIGR9Zr939LnXovlt7 9U+FvrMUyEioVkBLXwGuzakJgs+u38FjjsHYhWawli7kO3lD3O19TIQaQoxOImke4ytj bBBtv5NP62cD19UjbPNrcCmjlJeAdAcRnaPxG7CsyN5JCUd3uGOI8m3f/aI6pTEkNvjY BADw== X-Forwarded-Encrypted: i=1; AFNElJ8kB2CwFd1AqL7YPKxAdbbLGHf16CDoEPgbrxZhIMszSMos+B0Zd/VxHPfEgbZ1kErEZPt123ZnOMBb8Sc=@vger.kernel.org X-Gm-Message-State: AOJu0YxM32u5mj1rz7yEO5+vroC4cQp9c+zlkQI80zP8vOm+ahaMfhfO oPXKKkc3H2Vw1qVbkyAGrBvpzMZNdQp9GR9lkR8YT4CwtllaQTBda+Ew X-Gm-Gg: Acq92OGES6dL7MbvqKdxtJwfASdPvHDfVuBY3keOAdvhx4WU9j19SP/Gt9acR/z6v5U GM+CzimJ65u8IBmOhT04v1Oa3+ibAz8UUctssvE9qgeyOB8TgUsBBvU69aBO1rP61ypfCzfJKGN Ym8xZTuNto+DCyfColmBIz1LVqtMJPjoMwyTg2A/qt6D0Yw0Y3Ws/uMbjL9zDHnI99cMRA0Ws54 eidtxjOadtsQDCodavyHTZuhlTHt/cGiUWqB9nQ1Z7Bu+qQBEQPadMiSiNQZH2uy1cNImH78sXQ 4W70l951DcE+kFhhMNau7I+QV406lbGwtEDzElF37OdY2da/qmnm0xCabIgPgMPlSr7Ht7hRgVW 5yxVPduhJH7bNbVFDFiEaUM2+gRrtFpcjQIPUuu7m2RUTwDksttti5a27CTsyzh5OYXhzq1bbWF VTJDKkYXg/IAoFSPdxlJxm9+JfyrHRoNE51tzBhIl8pG2DNumWPAQtSXIOJMKEJjM3wD4I0WQNg BplB4F1Cdm2h0jP4+x28qJugxE++eTYvPpH8Y9Z69c= X-Received: by 2002:a05:600c:4688:b0:48f:e230:80a3 with SMTP id 5b1f17b1804b1-49042ae99afmr256122915e9.33.1779739899702; Mon, 25 May 2026 13:11:39 -0700 (PDT) Received: from britney-pc.tail2180da.ts.net (host217-46-89-96.range217-46.btcentralplus.com. [217.46.89.96]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49059cc9a91sm52673055e9.1.2026.05.25.13.11.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 May 2026 13:11:39 -0700 (PDT) From: Kacper Kokot To: Pablo Neira Ayuso , Florian Westphal , Phil Sutter , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , netfilter-devel@vger.kernel.org, coreteam@netfilter.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org, Kacper Kokot Subject: [PATCH] netfilter: TCPMSS: fix dropped packets when MSS option is unaligned Date: Mon, 25 May 2026 21:11:16 +0100 Message-ID: <20260525201116.407338-2-kacper.kokot.44@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Padding TCP options with NOPs is optional, so it is legal to send an MSS option that is not aligned to a word boundary and therefore not aligned for checksum calculation. The current TCPMSS target is not robust to this: when the MSS option is unaligned it produces an invalid checksum, and the packet is dropped. When the changed word is not aligned, the modified bytes straddle two checksum words, and using the standard incremental update helper (which assumes alignment) produces an invalid checksum: | w1 | w2 | OLD | a b | c d | NEW | a b' | c' d | Since b' and c' sit across w1 and w2, we could compute the incremental checksum in two operations by recalculating w1 and then w2: C' =3D C - w1 + w1' - w2 + w2' But working it out: C' =3D C - w1 - w2 + w1' + w2' =3D C - (a * 2^8 + b) - (c * 2^8 + d) + (a * 2^8 + b') + (c' * 2^8 + d) =3D C + 2^8 * (a - a + c' - c) + (b' - b + d - d) =3D C + 2^8 * (c' - c) + (b' - b) =3D C - (2^8 * c + b) + (2^8 * c' + b') So an unaligned incremental checksum can be done in a single operation by byteswapping the changed bytes before passing them to the helper. This patch implements that trick for unaligned MSS option updates. Signed-off-by: Kacper Kokot --- Reproduction script #!/usr/bin/env python3 import argparse from scapy.all import * =20 parser =3D argparse.ArgumentParser() parser.add_argument("target_ip") parser.add_argument("target_port", type=3Dint) args =3D parser.parse_args() =20 def try_handshake(options): sport =3D RandShort() ip =3D IP(dst=3Dargs.target_ip) =20 syn =3D TCP( sport=3Dsport, dport=3Dargs.target_port, flags=3D"S", seq=3D1000, options=3Doptions ) =20 synack =3D sr1(ip/syn, timeout=3D2) =20 print("SYNACK", synack) if synack and synack.haslayer(TCP) and synack[TCP].flags =3D=3D 0x12: ack =3D TCP( sport=3Dsport, dport=3Dargs.target_port, flags=3D"R", seq=3Dsyn.seq + 1, ack=3Dsynack.seq + 1, ) =20 send(ip/ack) print("SYN-ACK received") else: print("No SYN-ACK received") =20 print("\n>>> MSS Aligned") try_handshake([ ('MSS', 1460), ("NOP", None), ("NOP", None), ('SAckOK', b''), ('Timestamp', (12345, 0)), ('WScale', 7) ]) =20 print("\n>>> MSS Misaligned") try_handshake([ ("NOP", None), ('MSS', 1460), ("NOP", None), ('SAckOK', b''), ('Timestamp', (12345, 0)), ('WScale', 7) ]) A script to reproduce: #!/usr/bin/env python3 import argparse from scapy.all import * parser =3D argparse.ArgumentParser() parser.add_argument("target_ip") parser.add_argument("target_port", type=3Dint) args =3D parser.parse_args() mss_aligned_tcp_options =3D [ ('MSS', 1460), ("NOP", None), ("NOP", None), ('SAckOK', b''), ('Timestamp', (12345, 0)), ('WScale', 7) ] mss_misaligned_tcp_options =3D [ ("NOP", None), ('MSS', 1460), ("NOP", None), ('SAckOK', b''), ('Timestamp', (12345, 0)), ('WScale', 7) ] def try_handshake(options): sport =3D RandShort() ip =3D IP(dst=3Dargs.target_ip) syn =3D TCP( sport=3Dsport, dport=3Dargs.target_port, flags=3D"S", seq=3D1000, options=3Doptions ) synack =3D sr1(ip/syn, timeout=3D2) print("SYNACK", synack) if synack and synack.haslayer(TCP) and synack[TCP].flags =3D=3D 0x12: ack =3D TCP( sport=3Dsport, dport=3Dargs.target_port, flags=3D"R", seq=3Dsyn.seq + 1, ack=3Dsynack.seq + 1, ) send(ip/ack) print("SYN-ACK response") else: print("No SYN-ACK received") print("\n>>> MSS Aligned") try_handshake(mss_aligned_tcp_options) print("\n>>> MSS Misaligned") try_handshake(mss_misaligned_tcp_options) net/netfilter/xt_TCPMSS.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 80e1634bc51f..8e409858505d 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -117,6 +117,7 @@ tcpmss_mangle_packet(struct sk_buff *skb, for (i =3D sizeof(struct tcphdr); i <=3D tcp_hdrlen - TCPOLEN_MSS; i +=3D= optlen(opt, i)) { if (opt[i] =3D=3D TCPOPT_MSS && opt[i+1] =3D=3D TCPOLEN_MSS) { u_int16_t oldmss; + u16 csum_oldmss, csum_newmss; =20 oldmss =3D (opt[i+2] << 8) | opt[i+3]; =20 @@ -130,8 +131,19 @@ tcpmss_mangle_packet(struct sk_buff *skb, opt[i+2] =3D (newmss & 0xff00) >> 8; opt[i+3] =3D newmss & 0x00ff; =20 + csum_oldmss =3D htons(oldmss); + csum_newmss =3D htons(newmss); + + /* MSS may be unaligned; fix up the incremental checksum + * to avoid an invalid checksum and a dropped packet. + */ + if (((char *)&opt[i + 2] - (char *)tcph) & 0x1 !=3D 0) { + csum_oldmss =3D swab16(csum_oldmss); + csum_newmss =3D swab16(csum_newmss); + } + inet_proto_csum_replace2(&tcph->check, skb, - htons(oldmss), htons(newmss), + csum_oldmss, csum_newmss, false); return 0; } --=20 2.43.0