From d6bcae54c89bf3555687723bd8a537e3f888559a Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Mon, 24 Aug 2026 13:06:10 +0200 Subject: [PATCH] fix(xdp): two RST-challenge bugs found by live kernel testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found on real kernel 5.15 (netns+veth, challenge enabled): 1. truncated IP: after bpf_xdp_adjust_tail the IPv4 tot_len / IPv6 payload_len still claimed the original SYN size — peer silently dropped the malformed SYN-ACK, challenge never seen 2. endianness: marker/bad_ack written as raw host-order halfwords into network-order tcpw[] — client echoed swap16halves(bad_ack) in its RST.seq, verification always failed (CHAL_FAILED == CHAL_SENT) Post-fix e2e: SYN -> bad-ACK SYN-ACK (XDP_TX) -> client RST -> verified -> retry connects transparently; counters SENT=1 VERIFIED=1 FAILED=0 --- xdp/core/syn_challenge.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/xdp/core/syn_challenge.h b/xdp/core/syn_challenge.h index d150608..17e015e 100644 --- a/xdp/core/syn_challenge.h +++ b/xdp/core/syn_challenge.h @@ -96,8 +96,14 @@ static __always_inline void chal_build_synack(struct xdp_md *ctx, ipw[8] = s0; ipw[9] = s1; ((__u8 *)ipw)[8] = 64; // TTL + // tot_len must match the trimmed frame: 20 (IP) + 20 (TCP). + // Without this the peer sees "truncated IP" and drops the challenge. + ipw[1] = bpf_htons(sizeof(struct iphdr) + sizeof(struct tcphdr)); ipw[5] = 0; // checksum placeholder ipw[5] = chal_csum_fold(chal_sum10(ipw)); + } else { + ((struct ipv6hdr *)l3)->payload_len = + bpf_htons(sizeof(struct tcphdr)); } // TCP header: swap ports, inject secret-derived seq/ack, SYN|ACK @@ -105,10 +111,12 @@ static __always_inline void chal_build_synack(struct xdp_md *ctx, __u16 ndport = tcpw[0]; // new dest = old source tcpw[0] = nsport; tcpw[1] = ndport; - tcpw[2] = (__u16)(marker >> 16); - tcpw[3] = (__u16)marker; - tcpw[4] = (__u16)(bad_ack >> 16); - tcpw[5] = (__u16)bad_ack; + // seq/ack are written per-halfword with htons: tcpw is a view over + // network-order memory, raw host-order stores would swap halfword bytes. + tcpw[2] = bpf_htons((__u16)(marker >> 16)); + tcpw[3] = bpf_htons((__u16)(marker & 0xFFFF)); + tcpw[4] = bpf_htons((__u16)(bad_ack >> 16)); + tcpw[5] = bpf_htons((__u16)(bad_ack & 0xFFFF)); tcpw[6] = bpf_htons(0x5012); // doff=5, flags SYN|ACK tcpw[7] = bpf_htons(0xFFFF); // window tcpw[8] = 0; // checksum placeholder