Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-14 06:13:09 +00:00

zapret-obfs: synhide

This commit is contained in:
bol-van
2026-01-30 11:52:22 +03:00
parent 6eb4970c9b
commit 48fbd39ada
2 changed files with 100 additions and 5 deletions

View File

@@ -208,6 +208,6 @@ v0.8.1
* nfqws2: VERDICT_PRESERVE_NEXT * nfqws2: VERDICT_PRESERVE_NEXT
* nfqws2: keepsum reconstruct option * nfqws2: keepsum reconstruct option
* nfqws2: more helpers * nfqws2: more helpers
* zapret-obfs: ippxor, udp2icmp * zapret-obfs: ippxor, udp2icmp, synhide
* nfqws2: LUA_COMPAT_VER=5 * nfqws2: LUA_COMPAT_VER=5
* winws2: --wf-raw-filter * winws2: --wf-raw-filter

View File

@@ -158,12 +158,12 @@ end
-- test case: -- test case:
-- endpoint1: -- endpoint1:
-- --in-range=a --lua-desync=udp2icmp -- --in-range=a --lua-desync=udp2icmp
-- nft add rule inet ztest2 post meta mark and 0x40000000 == 0 udp dport 12345 queue num 200 bypass -- nft add rule inet ztest post meta mark and 0x40000000 == 0 udp dport 12345 queue num 200 bypass
-- nft add rule inet ztest2 pre meta mark and 0x40000000 == 0 meta l4proto "{icmp,icmpv6}" queue num 200 bypass -- nft add rule inet ztest pre meta mark and 0x40000000 == 0 meta l4proto "{icmp,icmpv6}" queue num 200 bypass
-- endpoint2: -- endpoint2:
-- --in-range=a --lua-desync=udp2icmp --server -- --in-range=a --lua-desync=udp2icmp --server
-- nft add rule inet ztest2 post meta mark and 0x40000000 == 0 udp sport 12345 queue num 200 bypass -- nft add rule inet ztest post meta mark and 0x40000000 == 0 udp sport 12345 queue num 200 bypass
-- nft add rule inet ztest2 pre meta mark and 0x40000000 == 0 meta l4proto "{icmp,icmpv6}" queue num 200 bypass -- nft add rule inet ztest pre meta mark and 0x40000000 == 0 meta l4proto "{icmp,icmpv6}" queue num 200 bypass
-- packs udp datagram to icmp message without changing packet size -- packs udp datagram to icmp message without changing packet size
-- function keeps icmp identifier as (sport xor dport) to help traverse NAT (it won't help if NAT changes id) -- function keeps icmp identifier as (sport xor dport) to help traverse NAT (it won't help if NAT changes id)
-- one end must be in server mode, another - in client mode -- one end must be in server mode, another - in client mode
@@ -256,3 +256,98 @@ function udp2icmp(ctx, desync)
return VERDICT_MODIFY return VERDICT_MODIFY
end end
end end
-- test case :
-- client:
-- --in-range="<d1" --out-range="<d1" --lua-desync=desync=synhide:synack:ghost=2
-- nft add rule inet ztest post meta mark & 0x40000000 == 0x00000000 tcp dport 80 tcp flags & (fin | syn | rst | ack | urg) == syn queue flags bypass to 200
-- nft add rule inet ztest pre meta mark & 0x40000000 == 0x00000000 tcp sport 80 tcp flags & (fin | syn | rst | ack | urg) == (rst | ack) tcp urgptr != 0 queue flags bypass to 200
-- server:
-- --in-range=a --lua-desync=synhide:synack
-- nft add rule inet ztest post meta mark & 0x40000000 == 0x00000000 tcp sport 80 tcp flags & (fin | syn | rst | ack | urg) == (syn | ack) queue flags bypass to 200
-- nft add rule inet ztest pre meta mark & 0x40000000 == 0x00000000 tcp dport 80 tcp flags & (fin | syn | rst | ack | urg) == ack tcp urgptr != 0 queue flags bypass to 200
-- arg : ghost - ghost syn ttl for ipv4. must be hop_to_last_nat+1. syn is not ghosted if not supplied
-- arg : ghost6 - ghost syn hl for ipv6. must be hop_to_last_nat+1. syn is not ghosted if not supplied
-- arg : synack - also fake synack
function synhide(ctx, desync)
if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync)
return
end
local fl = bitand(desync.dis.tcp.th_flags, TH_SYN+TH_ACK+TH_FIN+TH_RST+TH_URG)
local function make_magic(client)
local m
-- use client seq0
m = client and desync.dis.tcp.th_seq or desync.dis.tcp.th_ack-1
m = bitxor(bitrshift(m,16),bitand(m,0xFFFF))
if m==0 then
-- 0 is not acceptable
m = client and desync.dis.tcp.th_dport or desync.dis.tcp.th_sport
end
return m
end
local function set_magic(client)
desync.dis.tcp.th_urp = make_magic(client)
end
local function ver_magic(client)
return desync.dis.tcp.th_urp == make_magic(client)
end
local function clear_magic()
return desync.dis.tcp.th_urp == 0
end
if fl==TH_SYN then
-- client sent
local ttl = tonumber(desync.dis.ip and desync.arg.ghost or desync.arg.ghost6)
if ttl then
DLOG("synhide: punch NAT hole with ttl="..ttl)
local dis = deepcopy(desync.dis)
if dis.ip then
dis.ip.ip_ttl = ttl
elseif dis.ip6 then
dis.ip6.ip6_hlim = ttl
end
if not rawsend_dissect(dis, rawsend_opts_base(desync)) then
instance_cutoff_shim(ctx, desync) -- failed
return
end
end
DLOG("synhide: client sends SYN. remove SYN")
set_magic(true)
-- remove SYN, set ACK
desync.dis.tcp.th_flags = bitor(bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN)), TH_ACK)
if not desync.arg.synack then
DLOG("synhide: mission complete")
instance_cutoff_shim(ctx, desync)
end
return VERDICT_MODIFY
elseif fl==(TH_SYN+TH_ACK) then
-- server sent
if desync.arg.synack then
DLOG("synhide: server sends SYN+ACK. remove SYN, set RST")
set_magic(false)
desync.dis.tcp.th_flags = bitor(bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN)), TH_RST)
return VERDICT_MODIFY
else
DLOG("synhide: server sends SYN+ACK. do not remove SYN because 'synack' arg is not set.")
return -- do nothing
end
elseif fl==TH_ACK and ver_magic(true) then
DLOG("synhide: server received magic. restore SYN")
desync.dis.tcp.th_flags = bitor(bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK)), TH_SYN)
clear_magic()
return VERDICT_MODIFY
elseif fl==(TH_ACK+TH_RST) and ver_magic(false) then
-- client received
DLOG("synhide: client received magic. restore SYN, remove RST")
desync.dis.tcp.th_flags = bitor(bitand(desync.dis.tcp.th_flags, bitnot(TH_RST)), TH_SYN)
DLOG("synhide: mission complete")
instance_cutoff_shim(ctx, desync)
return VERDICT_MODIFY
end
DLOG("synhide: sequence failed")
instance_cutoff_shim(ctx, desync)
end