diff --git a/.gitattributes b/.gitattributes index 5350ea1..466935a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,7 @@ * text=auto eol=lf *.cmd eol=crlf *.bat eol=crlf +*.manifest eol=crlf +*.rc eol=crlf init.d/windivert.filter.examples/** eol=crlf files/** binary diff --git a/lua/zapret-lib.lua b/lua/zapret-lib.lua index 4f810a7..a8bfba0 100644 --- a/lua/zapret-lib.lua +++ b/lua/zapret-lib.lua @@ -175,6 +175,9 @@ end -- produce resulting verdict from 2 verdicts function verdict_aggregate(v1, v2) local v + local vn = bitor(bitand(v1,VERDICT_PRESERVE_NEXT),bitand(v2,VERDICT_PRESERVE_NEXT)) + v1 = bitand(v1, VERDICT_MASK) + v2 = bitand(v2, VERDICT_MASK) v1 = v1 or VERDICT_PASS v2 = v2 or VERDICT_PASS if v1==VERDICT_DROP or v2==VERDICT_DROP then @@ -184,7 +187,7 @@ function verdict_aggregate(v1, v2) else v=VERDICT_PASS end - return v + return bitor(v,vn) end function plan_instance_execute(desync, verdict, instance) apply_execution_plan(desync, instance) @@ -751,6 +754,14 @@ function dis_reverse(dis) end end +function dis_reconstruct_l3(dis, options) + if dis.ip then + return csum_ip4_fix(reconstruct_iphdr(dis.ip)) + elseif dis.ip6 then + return reconstruct_ip6hdr(dis.ip6, options) + end +end + -- parse autottl : delta,min-max function parse_autottl(s) if s then diff --git a/lua/zapret-wgobfs.lua b/lua/zapret-obfs.lua similarity index 57% rename from lua/zapret-wgobfs.lua rename to lua/zapret-obfs.lua index 5be95d5..a3a7832 100644 --- a/lua/zapret-wgobfs.lua +++ b/lua/zapret-obfs.lua @@ -42,7 +42,7 @@ function wgobfs(ctx, desync) return end if not desync.arg.secret or #desync.arg.secret==0 then - error("wgobfs requires secret") + error("wgobfs: secret required") end if padmin>padmax then error("wgobfs: padmin>padmax") @@ -78,3 +78,71 @@ function wgobfs(ctx, desync) end end end + +-- test case : +-- endpoint1: +-- --filter-icmp=0,8,128,129 --filter-ipp=193,198,209,250 --filter-tcp=* --filter-udp=* --in-range=a --lua-desync=ippxor:xor=192:dataxor=0xABCD +-- nft add rule inet ztest pre meta mark and 0x40000000 == 0 meta l4proto {193, 198, 209, 250} queue num 200 bypass +-- nft add rule inet ztest post meta mark and 0x40000000 == 0 tcp dport "{5001}" queue num 200 bypass +-- nft add rule inet ztest post meta mark and 0x40000000 == 0 udp dport "{5001}" queue num 200 bypass +-- iperf -i 1 -c endpoint2 +-- endpoint2: +-- --filter-icmp=0,8,128,129 --filter-ipp=193,198,209,250 --filter-tcp=* --filter-udp=* --in-range=a --lua-desync=ippxor:xor=192:dataxor=0xABCD --server +-- nft add rule inet ztest pre meta mark and 0x40000000 == 0 meta l4proto {193, 198, 209, 250} queue num 200 bypass +-- nft add rule inet ztest post meta mark and 0x40000000 == 0 tcp sport "{5001}" queue num 200 bypass +-- nft add rule inet ztest post meta mark and 0x40000000 == 0 udp sport "{5001}" queue num 200 bypass +-- iperf -s +-- xor ip protocol number and optionally xor tcp,udp,icmp payload with supplied blob pattern +-- arg : ippxor - value to xor ip protocol number +-- arg : dataxor - blob to xor tcp, udp or icmp payload +function ippxor(ctx, desync) + local dataxor + local function dxor(dis) + if dataxor and dis.payload and #dis.payload>0 and (dis.tcp or dis.udp or dis.icmp) then + dis.payload = bxor(dis.payload, pattern(dataxor,1,#dis.payload)) + return true + end + return false + end + + if not desync.arg.ippxor then + error("ippxor: ippxor value required") + end + local ippxor = tonumber(desync.arg.ippxor) + if ippxor<0 or ippxor>0xFF then + error("ippxor: invalid ippxor value. should be 0..255") + end + if desync.arg.dataxor then + dataxor = blob(desync,desync.arg.dataxor) + if #dataxor==0 then + error("ippxor: empty dataxor value") + end + end + + local l3_from = ip_proto_l3(desync.dis) + local l3_to = bitxor(l3_from, ippxor) + + local bdxor = dxor(desync.dis) + if bdxor then + DLOG("ippxor: dataxor out") + end + fix_ip_proto(desync.dis, l3_to) + + local raw_ip = reconstruct_dissect(desync.dis, {ip6_preserve_next=true}) + + local dis = dissect(raw_ip) + if not dis.ip and not dis.ip6 then + DLOG_ERR("ippxor: could not rebuild packet") + return + end + + if not bdxor then + if dxor(dis) then + DLOG("ippxor: dataxor in") + end + end + + desync.dis = dis + DLOG("ippxor: "..l3_from.." => "..l3_to) + return VERDICT_MODIFY + VERDICT_PRESERVE_NEXT +end diff --git a/nfq2/darkmagic.h b/nfq2/darkmagic.h index cba8604..c5ea4c3 100644 --- a/nfq2/darkmagic.h +++ b/nfq2/darkmagic.h @@ -99,8 +99,10 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment); #define VERDICT_MODIFY 1 #define VERDICT_DROP 2 #define VERDICT_MASK 3 -#define VERDICT_NOCSUM 4 -#define VERDICT_MASK_VALID 7 +#define VERDICT_PRESERVE_NEXT 4 +#define VERDICT_MASK_VALID_LUA (VERDICT_MASK|VERDICT_PRESERVE_NEXT) +#define VERDICT_NOCSUM 8 +#define VERDICT_MASK_VALID 15 #define IP4_TOS(ip_header) (ip_header ? ip_header->ip_tos : 0) #define IP4_IP_ID(ip_header) (ip_header ? ip_header->ip_id : 0) diff --git a/nfq2/desync.c b/nfq2/desync.c index 12868e6..b5a00e2 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -227,6 +227,7 @@ static bool dp_match( if (!port_filters_match(&dp->pf_udp, port)) return false; break; case IPPROTO_ICMP: + case IPPROTO_ICMPV6: if (!icmp_filters_match(&dp->icf, icmp_type, icmp_code)) return false; break; default: @@ -821,7 +822,7 @@ static bool desync_get_result(uint8_t *verdict) goto err; } lua_Integer lv = lua_tointeger(params.L, -1); - if (lv & ~VERDICT_MASK) + if (lv & ~VERDICT_MASK_VALID_LUA) { DLOG_ERR("desync function returned bad int result\n"); goto err; @@ -836,6 +837,20 @@ err: lua_pop(params.L, rescount); return false; } +static uint8_t verdict_aggregate(uint8_t v1,uint8_t v2) +{ + uint8_t verdict_action = v1 & VERDICT_MASK; + switch (v2 & VERDICT_MASK) + { + case VERDICT_MODIFY: + if (verdict_action == VERDICT_PASS) verdict_action = VERDICT_MODIFY; + break; + case VERDICT_DROP: + verdict_action = VERDICT_DROP; + break; + } + return v1 & ~VERDICT_MASK | verdict_action | v2 & VERDICT_PRESERVE_NEXT; +} static uint8_t desync( struct desync_profile *dp, uint32_t fwmark, @@ -1031,14 +1046,8 @@ static uint8_t desync( } if (!desync_get_result(&verdict_func)) goto err; - switch (verdict_func & VERDICT_MASK) - { - case VERDICT_MODIFY: - if (verdict == VERDICT_PASS) verdict = VERDICT_MODIFY; - break; - case VERDICT_DROP: - verdict = VERDICT_DROP; - } + + verdict = verdict_aggregate(verdict, verdict_func); } else DLOG("* lua '%s' : payload_type '%s' does not satisfy filter\n", instance, l7payload_str(l7payload)); @@ -1057,7 +1066,7 @@ static uint8_t desync( } } - if (verdict == VERDICT_MODIFY) + if ((verdict & VERDICT_MASK)==VERDICT_MODIFY) { // use same memory buffer to reduce memory copying // packet size cannot grow @@ -1073,16 +1082,12 @@ static uint8_t desync( } else { - b = lua_reconstruct_dissect(params.L, -1, mod_pkt, len_mod_pkt, false, false, IPPROTO_NONE, false); + b = lua_reconstruct_dissect(params.L, -1, mod_pkt, len_mod_pkt, false, false, IPPROTO_NONE, !!(verdict & VERDICT_PRESERVE_NEXT)); lua_pop(params.L, 2); if (!b) { DLOG_ERR("failed to reconstruct packet after VERDICT_MODIFY\n"); - // to reduce memory copying we used original packet buffer for reconstruction. - // it may have been modified. windows and BSD will send modified data despite of VERDICT_PASS. - // force same behavior on all OS - // it's LUA script error, they passed bad data - verdict = VERDICT_DROP; + verdict = VERDICT_PASS; goto ex; } DLOG("reconstructed packet due to VERDICT_MODIFY. size %zu => %zu\n", dis->len_pkt, *len_mod_pkt); diff --git a/nfq2/lua.c b/nfq2/lua.c index ba5e979..6c9a393 100644 --- a/nfq2/lua.c +++ b/nfq2/lua.c @@ -3827,6 +3827,8 @@ static void lua_init_const(void) {"VERDICT_PASS",VERDICT_PASS}, {"VERDICT_MODIFY",VERDICT_MODIFY}, {"VERDICT_DROP",VERDICT_DROP}, + {"VERDICT_MASK",VERDICT_MASK}, + {"VERDICT_PRESERVE_NEXT",VERDICT_PRESERVE_NEXT}, {"DEFAULT_MSS",DEFAULT_MSS},