From d06e4f4c828e424a819c72692461d2bb366a2d4b Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 15 Dec 2025 10:59:29 +0300 Subject: [PATCH] nfqws2,zapret-lib: check tcp seq overflow --- docs/changes.txt | 4 ++++ lua/zapret-lib.lua | 10 ++++++++-- nfq2/conntrack.c | 19 ++++++------------- nfq2/conntrack_base.h | 5 +++-- nfq2/desync.c | 13 +++++++++---- nfq2/desync.h | 4 +--- nfq2/lua.c | 3 ++- 7 files changed, 33 insertions(+), 25 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 0f6ddb1..f52139a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -106,3 +106,7 @@ v0.7.2 * zapret-auto: add success detector logic * nfqws2: clean lua cutoff on profile change * zapret-auto: separate hostkey function + +v0.7.2 + +* nfqws2, zapret-lib : check tcp sequence range overflow diff --git a/lua/zapret-lib.lua b/lua/zapret-lib.lua index 2b4a578..33f3d1f 100644 --- a/lua/zapret-lib.lua +++ b/lua/zapret-lib.lua @@ -234,6 +234,12 @@ function desync_orchestrator_example(ctx, desync) return replay_execution_plan(desync) end +-- if seq is over 2G s and p position comparision can be wrong +function pos_counter_overflow(desync, mode, reverse) + if not desync.track or not desync.track.tcp or (mode~='s' and mode~='p') then return false end + local track_pos = reverse and desync.track.pos.reverse or desync.track.pos.direct + return track_pos.tcp.seq_over_2G +end -- these functions duplicate range check logic from C code -- mode must be n,d,b,s,x,a -- pos is {mode,pos} @@ -265,7 +271,7 @@ function pos_get(desync, mode, reverse) return 0 end function pos_check_from(desync, range) - if range.from.mode == 'x' then return false end + if range.from.mode == 'x' or pos_counter_overflow(desync, range.from.mode) then return false end if range.from.mode ~= 'a' then if desync.track then return pos_get(desync, range.from.mode) >= range.from.pos @@ -277,7 +283,7 @@ function pos_check_from(desync, range) end function pos_check_to(desync, range) local ps - if range.to.mode == 'x' then return false end + if range.to.mode == 'x' or pos_counter_overflow(desync, range.to.mode) then return false end if range.to.mode ~= 'a' then if desync.track then ps = pos_get(desync, range.to.mode) diff --git a/nfq2/conntrack.c b/nfq2/conntrack.c index 6f0fa16..879672b 100644 --- a/nfq2/conntrack.c +++ b/nfq2/conntrack.c @@ -155,6 +155,11 @@ static void ConntrackApplyPos(const struct tcphdr *tcp, t_ctrack *t, bool bRever if (direct->scale != SCALE_NONE) direct->winsize_calc <<= direct->scale; if (mss && !direct->mss) direct->mss = mss; if (scale != SCALE_NONE) direct->scale = scale; + + if (!direct->seq_over_2G && ((direct->seq_last - direct->seq0) & 0x80000000)) + direct->seq_over_2G = true; + if (!reverse->seq_over_2G && ((reverse->seq_last - reverse->seq0) & 0x80000000)) + reverse->seq_over_2G = true; } // non-tcp packets are passed with tcphdr=NULL but len_payload filled @@ -208,19 +213,7 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr ConntrackApplyPos(tcphdr, t, bReverse, len_payload); } - else - { - if (bReverse) - { - t->pos.server.seq_last = t->pos.server.pos; - t->pos.server.pos += len_payload; - } - else - { - t->pos.client.seq_last = t->pos.client.pos; - t->pos.client.pos += len_payload; - } - } + clock_gettime(CLOCK_REALTIME, &t->pos.t_last); // make sure t_start gets exactly the same value as first t_last if (!t->t_start.tv_sec) t->t_start = t->pos.t_last; diff --git a/nfq2/conntrack_base.h b/nfq2/conntrack_base.h index 31c0c20..9c93cd9 100644 --- a/nfq2/conntrack_base.h +++ b/nfq2/conntrack_base.h @@ -17,17 +17,18 @@ typedef struct uint64_t pcounter; // packet counter uint64_t pdcounter; // data packet counter (with payload) uint64_t pbcounter; // transferred byte counter. includes retransmissions. it's not the same as relative seq. + + // tcp only state, not used in udp uint32_t pos; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current uint32_t uppos; // max seen position. useful to detect retransmissions uint32_t uppos_prev; // previous max seen position. useful to detect retransmissions uint32_t seq_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current - - // tcp only state, not used in udp uint32_t seq0; // starting seq and ack uint16_t winsize; // last seen window size uint16_t mss; uint32_t winsize_calc; // calculated window size uint8_t scale; // last seen window scale factor. SCALE_NONE if none + bool seq_over_2G; } t_ctrack_position; typedef struct diff --git a/nfq2/desync.c b/nfq2/desync.c index c158f41..22b5808 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -505,7 +505,10 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect * return VERDICT_DROP; } - +static bool pos_overflow(const t_ctrack_position *pos, char mode) +{ + return (mode=='s' || mode=='p') && pos && pos->seq_over_2G; +} static uint64_t pos_get(const t_ctrack_position *pos, char mode) { if (pos) @@ -524,7 +527,7 @@ static uint64_t pos_get(const t_ctrack_position *pos, char mode) static bool check_pos_from(const t_ctrack_position *pos, const struct packet_range *range) { uint64_t ps; - if (range->from.mode == 'x') return false; + if ((range->from.mode == 'x') || pos_overflow(pos,range->from.mode)) return false; if (range->from.mode != 'a') { if (pos) @@ -540,7 +543,7 @@ static bool check_pos_from(const t_ctrack_position *pos, const struct packet_ran static bool check_pos_to(const t_ctrack_position *pos, const struct packet_range *range) { uint64_t ps; - if (range->to.mode == 'x') return false; + if (range->to.mode == 'x' || pos_overflow(pos,range->to.mode)) return false; if (range->to.mode != 'a') { if (pos) @@ -754,10 +757,12 @@ static uint8_t desync( DLOG("* lua '%s' : voluntary cutoff\n", instance); else if (check_pos_cutoff(pos, range)) { - DLOG("* lua '%s' : %s pos %c%llu %c%llu is beyond range %c%u%c%c%u (ctrack %s)\n", + DLOG("* lua '%s' : %s pos %c%llu %c%llu overflow %u %u is beyond range %c%u%c%c%u (ctrack %s)\n", instance, sDirection, range->from.mode, pos_get(pos, range->from.mode), range->to.mode, pos_get(pos, range->to.mode), + pos_overflow(pos, range->from.mode), + pos_overflow(pos, range->to.mode), range->from.mode, range->from.pos, range->upper_cutoff ? '<' : '-', range->to.mode, range->to.pos, diff --git a/nfq2/desync.h b/nfq2/desync.h index 2ac9ec8..e9ff11f 100644 --- a/nfq2/desync.h +++ b/nfq2/desync.h @@ -13,10 +13,8 @@ #ifdef __linux__ #define DPI_DESYNC_FWMARK_DEFAULT 0x40000000 -#elif defined(SO_USER_COOKIE) -#define DPI_DESYNC_FWMARK_DEFAULT 512 #else -#define DPI_DESYNC_FWMARK_DEFAULT 0 +#define DPI_DESYNC_FWMARK_DEFAULT 512 #endif uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, const uint8_t *data_pkt, size_t len_pkt, uint8_t *mod_pkt, size_t *len_mod_pkt); diff --git a/nfq2/lua.c b/nfq2/lua.c index ac6f139..a43c5b0 100644 --- a/nfq2/lua.c +++ b/nfq2/lua.c @@ -1282,7 +1282,7 @@ void lua_pushf_ctrack_pos(const t_ctrack *ctrack, const t_ctrack_position *pos) if (ctrack->ipproto == IPPROTO_TCP) { lua_pushliteral(params.L, "tcp"); - lua_createtable(params.L, 0, 10); + lua_createtable(params.L, 0, 11); lua_pushf_lint("seq0", pos->seq0); lua_pushf_lint("seq", pos->seq_last); lua_pushf_lint("rseq", pos->seq_last - pos->seq0); @@ -1293,6 +1293,7 @@ void lua_pushf_ctrack_pos(const t_ctrack *ctrack, const t_ctrack_position *pos) lua_pushf_int("winsize_calc", pos->winsize_calc); lua_pushf_int("scale", pos->scale); lua_pushf_int("mss", pos->mss); + lua_pushf_bool("seq_over_2G", pos->seq_over_2G); lua_rawset(params.L,-3); }