diff --git a/docs/changes.txt b/docs/changes.txt index b454f2e..30f3016 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -54,3 +54,5 @@ v0.4 v0.5 * nfqws2: u8add,u16add,u24add,u32add luacalls * nfqws2: abandon any arithmetics beyond 32bit (because lua 5.1 does not support 64 bit integers, store everything as double) +* zapret-lib: http_reply, url and nld dissectors +* zapret-auto: circular orchestrator diff --git a/init.d/openwrt/zapret2 b/init.d/openwrt/zapret2 index 2a14949..03bddc8 100755 --- a/init.d/openwrt/zapret2 +++ b/init.d/openwrt/zapret2 @@ -41,7 +41,7 @@ PIDDIR=/var/run USEROPT="--user=$WS_USER" NFQWS2="${NFQWS2:-$ZAPRET_BASE/nfq2/nfqws2}" -LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua" +LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua --lua-init=@$ZAPRET_BASE/lua/zapret-auto.lua" NFQWS2_OPT_BASE="$USEROPT --fwmark=$DESYNC_MARK $LUAOPT" run_daemon() diff --git a/init.d/sysv/functions b/init.d/sysv/functions index a748974..a08d2b5 100644 --- a/init.d/sysv/functions +++ b/init.d/sysv/functions @@ -72,7 +72,7 @@ DESYNC_MARK_POSTNAT=${DESYNC_MARK_POSTNAT:-0x20000000} QNUM=${QNUM:-300} NFQWS2="${NFQWS2:-$ZAPRET_BASE/nfq2/nfqws2}" -LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua" +LUAOPT="--lua-init=@$ZAPRET_BASE/lua/zapret-lib.lua --lua-init=@$ZAPRET_BASE/lua/zapret-antidpi.lua --lua-init=@$ZAPRET_BASE/lua/zapret-auto.lua" NFQWS2_OPT_BASE="$USEROPT --fwmark=$DESYNC_MARK $LUAOPT" diff --git a/lua/zapret-antidpi.lua b/lua/zapret-antidpi.lua index 1e63496..e3788e8 100644 --- a/lua/zapret-antidpi.lua +++ b/lua/zapret-antidpi.lua @@ -113,7 +113,7 @@ end -- standard args : direction function http_domcase(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -139,7 +139,7 @@ end -- arg : spell= . spelling of the "Host" header. must be exactly 4 chars long function http_hostcase(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -164,7 +164,7 @@ end -- standard args : direction function http_methodeol(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -221,10 +221,10 @@ function synack_split(ctx, desync) error("synack_split: bad mode '"..mode.."'") end else - instance_cutoff(ctx) -- mission complete + instance_cutoff_shim(ctx, desync) -- mission complete end else - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) end end @@ -238,10 +238,10 @@ function synack(ctx, desync) DLOG("synack: sending") rawsend_dissect_ipfrag(dis, desync_opts(desync)) else - instance_cutoff(ctx) -- mission complete + instance_cutoff_shim(ctx, desync) -- mission complete end else - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) end end @@ -256,10 +256,10 @@ function wsize(ctx, desync) return VERDICT_MODIFY end else - instance_cutoff(ctx) -- mission complete + instance_cutoff_shim(ctx, desync) -- mission complete end else - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) end end @@ -270,7 +270,7 @@ end -- arg : forced_cutoff= - comma separated list of payloads that trigger forced wssize cutoff. by default - any non-empty payload function wssize(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end local verdict = VERDICT_PASS @@ -281,7 +281,7 @@ function wssize(ctx, desync) end if #desync.dis.payload>0 and (not desync.arg.forced_cutoff or in_list(desync.arg.forced_cutoff, desync.l7payload)) then DLOG("wssize: forced cutoff") - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) end end return verdict @@ -305,10 +305,10 @@ function syndata(ctx, desync) return VERDICT_DROP end else - instance_cutoff(ctx) -- mission complete + instance_cutoff_shim(ctx, desync) -- mission complete end else - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) end end @@ -317,7 +317,7 @@ end -- arg : rstack - send RST,ACK instead of RST function rst(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -371,7 +371,7 @@ end -- arg : nodrop - do not drop current dissect function multisplit(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -425,7 +425,7 @@ end -- arg : nodrop - do not drop current dissect function multidisorder(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -491,7 +491,7 @@ end -- arg : nodrop - do not drop current dissect function hostfakesplit(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -604,7 +604,7 @@ end -- arg : nodrop - do not drop current dissect function fakedsplit(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -697,7 +697,7 @@ end -- arg : nodrop - do not drop current dissect function fakeddisorder(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -797,7 +797,7 @@ end -- arg : blob= - use this data instead of desync.dis.payload function tcpseg(ctx, desync) if not desync.dis.tcp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -841,7 +841,7 @@ end -- arg : pattern_offset=N . offset in the pattern. 0 by default function udplen(ctx, desync) if not desync.dis.udp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) @@ -877,7 +877,7 @@ end -- arg : dn=N - message starts from "dN". 2 by default function dht_dn(ctx, desync) if not desync.dis.udp then - instance_cutoff(ctx) + instance_cutoff_shim(ctx, desync) return end direction_cutoff_opposite(ctx, desync) diff --git a/lua/zapret-auto.lua b/lua/zapret-auto.lua new file mode 100644 index 0000000..ac5a9d6 --- /dev/null +++ b/lua/zapret-auto.lua @@ -0,0 +1,216 @@ +function automate_host_record(desync) + if not autostate then + autostate = {} + end + local key = host_or_ip(desync) + if not autostate[key] then + autostate[key] = {} + end + return autostate[key] +end +function automate_conn_record(desync) + if not desync.track.lua_state.automate then + desync.track.lua_state.automate = {} + end + return desync.track.lua_state.automate +end + +-- counts failure, optionally (if crec is given) prevents dup failure counts in a single connection +-- if 'maxtime' between failures is exceeded then failure count is reset +-- return true if threshold ('fails') is reached +-- hres is host record. host or ip bound table +-- cres is connection record. connection bound table +function automate_failure_counter(hrec, crec, fails, maxtime) + if crec and crec.failure then + DLOG("automate: duplicate failure in the same connection. not counted") + else + if crec then crec.failure = true end + local tnow=os.time() + if not hrec.failure_time_last then + hrec.failure_time_last = tnow + end + if not hrec.failure_counter then + hrec.failure_counter = 0 + elseif tnow>(hrec.failure_time_last + maxtime) then + DLOG("automate: failure counter reset because last failure was "..(tnow - hrec.failure_time_last).." seconds ago") + hrec.failure_counter = 0 + end + hrec.failure_counter = hrec.failure_counter + 1 + hrec.failure_time_last = tnow + if b_debug then DLOG("automate: failure counter "..hrec.failure_counter..(fails and ('/'..fails) or '')) end + if fails and hrec.failure_counter>=fails then + hrec.failure_counter = nil -- reset counter + return true + end + end + return false +end + +-- location is url compatible with Location: header +-- hostname is original hostname +function is_dpi_redirect(hostname, location) + local ds = dissect_url(location) + if ds.domain then + local sld1 = dissect_nld(hostname,2) + local sld2 = dissect_nld(ds.domain,2) + return sld2 and sld1~=sld2 + end + return false +end + +-- circularily change strategy numbers when failure count reaches threshold ('fails') +-- detected failures: incoming RST, incoming http redirection, outgoing retransmissions +-- this orchestrator requires redirection of incoming traffic to cache RST and http replies ! +-- each orchestrated instance must have strategy=N arg, where N starts from 1 and increment without gaps +-- if 'final' arg is present in an orchestrated instance it stops rotation +-- arg: fails=N - failture count threshold. default is 3 +-- arg: retrans=N - retrans count threshold. default is 3 +-- arg: seq= - if packet is beyond this relative sequence number treat this connection as successful. default is 64K +-- arg: rst= - maximum relative sequence number to treat incoming RST as DPI reset. default is 1 +-- arg: time= - if last failure happened earlier than `maxtime` seconds ago - reset failure counter. default is 60. +-- test case: nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s1 --lua-desync=circular --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2 +function circular(ctx, desync) + local function count_strategies(hrec, plan) + if not hrec.ctstrategy then + local uniq={} + local n=0 + for i,instance in pairs(plan) do + if instance.arg.strategy then + n = tonumber(instance.arg.strategy) + if not n or n<1 then + error("circular: strategy number '"..tostring(instance.arg.strategy).."' is invalid") + end + uniq[tonumber(instance.arg.strategy)] = true + if instance.arg.final then + hrec.final = n + end + end + end + n=0 + for i,v in pairs(uniq) do + n=n+1 + end + if n~=#uniq then + error("circular: strategies numbers must start from 1 and increment. gaps are not allowed.") + end + hrec.ctstrategy = n + end + end + + -- take over orchestration. prevent further instance execution in case of error + execution_plan_cancel(ctx) + + if not desync.dis.tcp then + DLOG("circular: this orchestrator is tcp only") + instance_cutoff(ctx) + return + end + + if not desync.track then + DLOG_ERR("circular: conntrack is missing but required") + return + end + + local rstseq = tonumber(desync.arg.rst) or 1 + local maxseq = tonumber(desync.arg.seq) or 0x10000 + local fails = tonumber(desync.arg.fails) or 3 + local retrans = tonumber(desync.arg.retrans) or 3 + local maxtime = tonumber(desync.arg.time) or 60 + local hrec = automate_host_record(desync) + local crec = automate_conn_record(desync) + local pos = bitand(desync.track.tcp.seq - desync.track.tcp.seq0,0xFFFFFFFF) + local trigger = false + local once = true + local plan = execution_plan(ctx) + + if #plan==0 then + DLOG("circular: need some desync instances or useless") + return + end + + count_strategies(hrec, plan) + if hrec.ctstrategy==0 then + error("circular: add strategy=N tag argument to each following instance ! N must start from 1 and increment") + end + + if not hrec.nstrategy then + DLOG("circular: start from strategy 1") + hrec.nstrategy = 1 + end + + if not crec.nocheck then + local seq = pos_get(desync,'s') + if seq>maxseq then + DLOG("circular: s"..seq.." is beyond s"..maxseq..". treating connection as successful") + crec.nocheck = true + end + end + + local verdict = VERDICT_PASS + + if not crec.nocheck and hrec.final~=hrec.nstrategy then + if desync.outgoing then + if #desync.dis.payload>0 and (crec.retrans or 0)=retrans + end + if desync.track.tcp.pos_orig>crec.uppos then + crec.uppos=desync.track.tcp.pos_orig + end + end + else + if bitand(desync.dis.tcp.th_flags, TH_RST)~=0 then + local seq=u32add(desync.track.tcp.ack, -desync.track.tcp.ack0) + trigger = seq<=rstseq + if b_debug then + if trigger then + DLOG("circular: incoming RST s"..seq.." in range s"..rstseq) + else + DLOG("circular: not counting incoming RST s"..seq.." beyond s"..rstseq) + end + end + elseif desync.l7payload=="http_reply" and desync.track.hostname then + local hdis = http_dissect_reply(desync.dis.payload) + if hdis and (hdis.code==302 or hdis.code==307) and hdis.headers.location and hdis.headers.location then + trigger = is_dpi_redirect(desync.track.hostname, hdis.headers.location.value) + if trigger and b_debug then + DLOG("circular: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'") + end + end + end + end + if trigger then + if automate_failure_counter(hrec, crec, fails, maxtime) then + -- circular strategy change + hrec.nstrategy = (hrec.nstrategy % hrec.ctstrategy) + 1 + DLOG("circular: rotate strategy to "..hrec.nstrategy) + if hrec.nstrategy == hrec.final then + DLOG("circular: final strategy "..hrec.final.." reached. will rotate no more.") + end + end + end + end + + DLOG("circular: current strategy "..hrec.nstrategy) + local dcopy = desync_copy(desync) + for i=1,#plan do + if plan[i].arg.strategy and tonumber(plan[i].arg.strategy)==hrec.nstrategy then + apply_execution_plan(dcopy, plan[i]) + if cutoff_shim_check(dcopy) then + DLOG("circular: not calling '"..dcopy.func_instance.."' because of voluntary cutoff") + elseif not payload_match_filter(dcopy.l7payload, plan[i].payload_filter) then + DLOG("circular: not calling '"..dcopy.func_instance.."' because payload '"..dcopy.l7payload.."' does not match filter '"..plan[i].payload_filter.."'") + elseif not pos_check_range(dcopy, plan[i].range) then + DLOG("circular: not calling '"..dcopy.func_instance.."' because pos "..pos_str(dcopy,plan[i].range.from).." "..pos_str(dcopy,plan[i].range.to).." is out of range '"..pos_range_str(plan[i].range).."'") + else + DLOG("circular: executing '"..dcopy.func_instance.."'") + verdict = verdict_aggregate(verdict,_G[plan[i].func](nil, dcopy)) + end + end + end + + return verdict +end diff --git a/lua/zapret-lib.lua b/lua/zapret-lib.lua index cf4e5cb..336e858 100644 --- a/lua/zapret-lib.lua +++ b/lua/zapret-lib.lua @@ -61,6 +61,46 @@ function posdebug(ctx,desync) DLOG(s) end +-- this shim is needed then function is orchestrated. ctx services not available +-- have to emulate cutoff in LUA using connection persistent table track.lua_state +function instance_cutoff_shim(ctx, desync, dir) + if ctx then + instance_cutoff(ctx, dir) + elseif not desync.track then + DLOG("instance_cutoff_shim: cannot cutoff '"..desync.func_instance.."' because conntrack is absent") + else + if not desync.track.lua_state.cutoff_shim then + desync.track.lua_state.cutoff_shim = {} + end + if not desync.track.lua_state.cutoff_shim[desync.func_instance] then + desync.track.lua_state.cutoff_shim[desync.func_instance] = {} + end + if type(dir)=="nil" then + -- cutoff both directions by default + desync.track.lua_state.cutoff_shim[desync.func_instance][true] = true + desync.track.lua_state.cutoff_shim[desync.func_instance][false] = true + else + desync.track.lua_state.cutoff_shim[desync.func_instance][dir] = true + end + if b_debug then DLOG("instance_cutoff_shim: cutoff '"..desync.func_instance.."' in="..tostring(type(dir)=="nil" and true or not dir).." out="..tostring(type(dir)=="nil" or dir)) end + end +end +function cutoff_shim_check(desync) + if not desync.track then + DLOG("cutoff_shim_check: cannot check '"..desync.func_instance.."' cutoff because conntrack is absent") + return false + else + local b=desync.track.lua_state.cutoff_shim and + desync.track.lua_state.cutoff_shim[desync.func_instance] and + desync.track.lua_state.cutoff_shim[desync.func_instance][desync.outgoing] + if b and b_debug then + DLOG("cutoff_shim_check: '"..desync.func_instance.."' "..(desync.outgoing and "out" or "in").." cutoff") + end + return b + end +end + + -- applies # and $ prefixes. #var means var length, %var means var value function apply_arg_prefix(arg) for a,v in pairs(arg) do @@ -100,21 +140,34 @@ function verdict_aggregate(v1, v2) return v end -- redo what whould be done without orchestration -function replay_execution_plan(ctx, desync, plan) +function replay_execution_plan(desync, plan) local verdict = VERDICT_PASS for i=1,#plan do - if not payload_match_filter(desync.l7payload, plan[i].payload_filter) then + if cutoff_shim_check(desync) then + DLOG("orchestrator: not calling '"..desync.func_instance.."' because of voluntary cutoff") + elseif not payload_match_filter(desync.l7payload, plan[i].payload_filter) then DLOG("orchestrator: not calling '"..desync.func_instance.."' because payload '"..desync.l7payload.."' does not match filter '"..plan[i].payload_filter.."'") elseif not pos_check_range(desync, plan[i].range) then DLOG("orchestrator: not calling '"..desync.func_instance.."' because pos "..pos_str(desync,plan[i].range.from).." "..pos_str(desync,plan[i].range.to).." is out of range '"..pos_range_str(plan[i].range).."'") else apply_execution_plan(desync, plan[i]) DLOG("orchestrator: executing '"..desync.func_instance.."'") - verdict = verdict_aggregate(verdict,_G[plan[i].func](ctx, desync)) + verdict = verdict_aggregate(verdict,_G[plan[i].func](nil, desync)) end end return verdict end + +-- copy desync preserving lua_state +function desync_copy(desync) + local dcopy = deepcopy(desync) + if desync.track then + -- preserve lua state + dcopy.track.lua_state = desync.track.lua_state + end + return dcopy +end + -- this function demonstrates how to stop execution of upcoming desync instances and take over their job -- this can be used, for example, for orchestrating conditional processing without modifying of desync functions code -- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-desync=desync_orchestrator_example --lua-desync=pass --lua-desync=pass @@ -122,9 +175,9 @@ function desync_orchestrator_example(ctx, desync) local plan = execution_plan(ctx) if #plan>0 then DLOG("orchestrator: taking over upcoming desync instances") - local desync_copy = deepcopy(desync) + local dcopy = desync_copy(desync) execution_plan_cancel(ctx) - return replay_execution_plan(ctx, desync_copy, plan) + return replay_execution_plan(dcopy, plan) end end @@ -257,7 +310,7 @@ function var_debug(v) local function dbg(v,level) if type(v)=="table" then for key, value in pairs(v) do - DLOG(string.rep(" ",2*level).."."..key) + DLOG(string.rep(" ",2*level).."."..tostring(key)) dbg(v[key],level+1) end elseif type(v)=="string" then @@ -1010,10 +1063,10 @@ function direction_cutoff_opposite(ctx, desync, def) local dir = desync.arg.dir or def or "out" if dir=="out" then -- cutoff in - instance_cutoff(ctx, false) + instance_cutoff_shim(ctx, desync, false) elseif dir=="in" then -- cutoff out - instance_cutoff(ctx, true) + instance_cutoff_shim(ctx, desync, true) end end @@ -1103,7 +1156,10 @@ end -- return hostname if present or ip address in text form otherwise function host_or_ip(desync) - return desync.hostname or (desync.dis.ip and ntop(desync.dis.ip) or desync.dis.ip6 and ntop(desync.dis.ip6)) + if desync.track and desync.track.hostname then + return desync.track.hostname + end + return desync.target.ip and ntop(desync.target.ip) or desync.target.ip6 and ntop(desync.target.ip6) end function is_absolute_path(path) @@ -1254,3 +1310,16 @@ function ipfrag2(dis, ipfrag_options) return {dis1,dis2} end + + +-- location is url compatible with Location: header +-- hostname is original hostname +function is_dpi_redirect(hostname, location) + local ds = dissect_url(location) + if ds.domain then + local sld1 = dissect_nld(hostname,2) + local sld2 = dissect_nld(ds.domain,2) + return sld2 and sld1~=sld2 + end + return false +end diff --git a/nfq2/desync.c b/nfq2/desync.c index a703272..e5a28d3 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -648,6 +648,7 @@ static uint8_t desync( const t_ctrack_position *pos, t_l7payload l7payload, const struct dissect *dis, + const struct in_addr *sdp4, const struct in6_addr *sdp6, uint16_t sdport, uint8_t *mod_pkt, size_t *len_mod_pkt, unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, const uint8_t *rdata_payload, size_t rlen_payload, const uint8_t *data_decrypt, size_t len_decrypt) @@ -677,6 +678,9 @@ static uint8_t desync( } if (!pos) pos = &ctrack->pos; } + + LUA_STACK_GUARD_ENTER(params.L) + if (LIST_FIRST(&dp->lua_desync)) { b_cutoff_all = b_unwanted_payload = true; @@ -733,6 +737,12 @@ static uint8_t desync( lua_pushf_str("ifin", (ifin && *ifin) ? ifin : NULL); lua_pushf_str("ifout", (ifout && *ifout) ? ifout : NULL); lua_pushf_int("fwmark", fwmark); + lua_pushf_table("target"); + lua_getfield(params.L,-1,"target"); + if (sdport) lua_pushf_int("port",sdport); + if (sdp4) lua_pushf_lstr("ip",(const char*)sdp4,sizeof(*sdp4)); + if (sdp6) lua_pushf_lstr("ip6",(const char*)sdp6,sizeof(*sdp6)); + lua_pop(params.L,1); lua_pushf_bool("replay", !!replay_piece_count); if (replay_piece_count) { @@ -864,6 +874,7 @@ static uint8_t desync( DLOG("no lua functions in this profile\n"); ex: luaL_unref(params.L, LUA_REGISTRYINDEX, ref_arg); + LUA_STACK_GUARD_LEAVE(params.L, 0) return verdict; err: DLOG_ERR("desync ERROR. passing packet unmodified.\n"); @@ -1354,7 +1365,7 @@ static uint8_t dpi_desync_tcp_packet_play( ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2)); DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload)); } - verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0); + verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, sdip4, sdip6, sdport, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0); pass: return (!bReverseFixed && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict; @@ -1810,7 +1821,7 @@ static uint8_t dpi_desync_udp_packet_play( ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2)); DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload)); } - verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt); + verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, dis, sdip4, sdip6, sdport, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt); pass: return (!bReverse && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict; diff --git a/nfq2/lua.c b/nfq2/lua.c index 0667e95..fb7d66c 100644 --- a/nfq2/lua.c +++ b/nfq2/lua.c @@ -692,8 +692,8 @@ static int luacall_clock_gettime(lua_State *L) } static int luacall_instance_cutoff(lua_State *L) { - // out : func_name.profile_number[0] - // in : func_name.profile_number[1] + // out : instance_name.profile_number[0] + // in : instance_name.profile_number[1] lua_check_argc_range(L,"instance_cutoff",1,2); @@ -701,45 +701,51 @@ static int luacall_instance_cutoff(lua_State *L) const t_lua_desync_context *ctx; - if (!lua_islightuserdata(L,1)) - luaL_error(L, "instance_cutoff expect desync context in the first argument"); - ctx = lua_touserdata(L,1); - - int argc=lua_gettop(L); - bool bIn,bOut; - if (argc>=2 && lua_type(L,2)!=LUA_TNIL) - { - luaL_checktype(L,2,LUA_TBOOLEAN); - bOut = lua_toboolean(L,2); - bIn = !bOut; - } + if (lua_isnil(L,1)) + // this can happen in orchestrated function. they do not have their own ctx and they cant cutoff + DLOG("instance cutoff not possible because missing ctx\n"); else - bIn = bOut = true; - - if (ctx->ctrack) { - DLOG("instance cutoff for '%s' in=%u out=%u\n",ctx->instance,bIn,bOut); - lua_rawgeti(L,LUA_REGISTRYINDEX,ctx->ctrack->lua_instance_cutoff); - lua_getfield(L,-1,ctx->instance); - if (!lua_istable(L,-1)) + if (!lua_islightuserdata(L,1)) + luaL_error(L, "instance_cutoff expect desync context in the first argument"); + ctx = lua_touserdata(L,1); + + int argc=lua_gettop(L); + bool bIn,bOut; + if (argc>=2 && lua_type(L,2)!=LUA_TNIL) { - lua_pop(L,1); - lua_pushf_table(ctx->instance); + luaL_checktype(L,2,LUA_TBOOLEAN); + bOut = lua_toboolean(L,2); + bIn = !bOut; + } + else + bIn = bOut = true; + + if (ctx->ctrack) + { + DLOG("instance cutoff for '%s' in=%u out=%u\n",ctx->instance,bIn,bOut); + lua_rawgeti(L,LUA_REGISTRYINDEX,ctx->ctrack->lua_instance_cutoff); lua_getfield(L,-1,ctx->instance); - } - lua_rawgeti(L,-1,ctx->dp->n); - if (!lua_istable(L,-1)) - { - lua_pop(L,1); - lua_pushi_table(ctx->dp->n); + if (!lua_istable(L,-1)) + { + lua_pop(L,1); + lua_pushf_table(ctx->instance); + lua_getfield(L,-1,ctx->instance); + } lua_rawgeti(L,-1,ctx->dp->n); + if (!lua_istable(L,-1)) + { + lua_pop(L,1); + lua_pushi_table(ctx->dp->n); + lua_rawgeti(L,-1,ctx->dp->n); + } + if (bOut) lua_pushi_bool(0,true); + if (bIn) lua_pushi_bool(1,true); + lua_pop(L,3); } - if (bOut) lua_pushi_bool(0,true); - if (bIn) lua_pushi_bool(1,true); - lua_pop(L,3); + else + DLOG("instance cutoff requested for '%s' in=%u out=%u but not possible without conntrack\n",ctx->instance,bIn,bOut); } - else - DLOG("instance cutoff requested for '%s' in=%u out=%u but not possible without conntrack\n",ctx->instance,bIn,bOut); LUA_STACK_GUARD_RETURN(L,0) } @@ -935,6 +941,18 @@ void lua_pushi_str(lua_Integer idx, const char *str) lua_pushstring(params.L, str); // pushes nil if str==NULL lua_rawset(params.L,-3); } +void lua_pushf_lstr(const char *field, const char *str, size_t size) +{ + lua_pushstring(params.L, field); + lua_pushlstring(params.L, str, size); + lua_rawset(params.L,-3); +} +void lua_pushi_lstr(lua_Integer idx, const char *str, size_t size) +{ + lua_pushinteger(params.L, idx); + lua_pushlstring(params.L, str, size); + lua_rawset(params.L,-3); +} void lua_push_raw(const void *v, size_t l) { if (v) diff --git a/nfq2/lua.h b/nfq2/lua.h index 64c74ad..0bfb206 100644 --- a/nfq2/lua.h +++ b/nfq2/lua.h @@ -51,6 +51,8 @@ void lua_pushf_bool(const char *field, bool b); void lua_pushi_bool(lua_Integer idx, bool b); void lua_pushf_str(const char *field, const char *str); void lua_pushi_str(lua_Integer idx, const char *str); +void lua_pushf_lstr(const char *field, const char *str, size_t len); +void lua_pushi_lstr(lua_Integer idx, const char *str, size_t len); void lua_pushf_int(const char *field, lua_Integer v); void lua_pushi_int(lua_Integer idx, lua_Integer v); void lua_push_raw(const void *v, size_t l);