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

icmp and ipp support

This commit is contained in:
bol-van
2026-01-26 14:55:09 +03:00
parent 78b3baa03f
commit 6adb789314
8 changed files with 322 additions and 60 deletions

View File

@@ -196,8 +196,11 @@ v0.8.1
* nfqws2: fix critical bug - wrong ipv6 dissection * nfqws2: fix critical bug - wrong ipv6 dissection
* zapret-auto: fix standard_failure_detector http redirect regression * zapret-auto: fix standard_failure_detector http redirect regression
0.8.7 0.9.0
* nfqws2: removed hard check for host: presence in http_req * nfqws2: removed hard check for host: presence in http_req
* nfqws2: file open test before destroying in-memory content of ipset/hostlist * nfqws2: file open test before destroying in-memory content of ipset/hostlist
* github actions: lua 5.5 * github actions: lua 5.5
* nfqws2: enable dead reasm protection in wsize=0 case
* nfqws2: --intercept
* winws2: changed icon to multi-res png up to 256px

View File

@@ -65,8 +65,10 @@ standard ipfrag :
* ipfrag[=frag_function] - ipfrag function name. "ipfrag2" by default if empty * ipfrag[=frag_function] - ipfrag function name. "ipfrag2" by default if empty
* ipfrag_disorder - send fragments from last to first * ipfrag_disorder - send fragments from last to first
* ipfrag2 : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8 * ipfrag2 : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
* ipfrag2 : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32 * ipfrag2 : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 8
* ipfrag2 : ipfrag_pos_icmp - icmp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 8
* ipfrag2 : ipfrag_pos - frag position for other L4. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
* ipfrag2 : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default. * ipfrag2 : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
]] ]]
@@ -114,7 +116,8 @@ end
-- standard args : direction -- standard args : direction
function http_domcase(ctx, desync) function http_domcase(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -140,7 +143,8 @@ end
-- arg : spell=<str> . spelling of the "Host" header. must be exactly 4 chars long -- arg : spell=<str> . spelling of the "Host" header. must be exactly 4 chars long
function http_hostcase(ctx, desync) function http_hostcase(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -171,7 +175,8 @@ end
-- NOTE : if using with other http tampering methodeol should be the last ! -- NOTE : if using with other http tampering methodeol should be the last !
function http_methodeol(ctx, desync) function http_methodeol(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -202,7 +207,8 @@ end
-- standard args : direction -- standard args : direction
function http_unixeol(ctx, desync) function http_unixeol(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -271,7 +277,8 @@ function synack_split(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete instance_cutoff_shim(ctx, desync) -- mission complete
end end
else else
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
end end
end end
@@ -288,7 +295,8 @@ function synack(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete instance_cutoff_shim(ctx, desync) -- mission complete
end end
else else
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
end end
end end
@@ -306,7 +314,8 @@ function wsize(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete instance_cutoff_shim(ctx, desync) -- mission complete
end end
else else
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
end end
end end
@@ -317,7 +326,8 @@ end
-- arg : forced_cutoff=<list> - comma separated list of payloads that trigger forced wssize cutoff. by default - any non-empty payload -- arg : forced_cutoff=<list> - comma separated list of payloads that trigger forced wssize cutoff. by default - any non-empty payload
function wssize(ctx, desync) function wssize(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
local verdict = VERDICT_PASS local verdict = VERDICT_PASS
@@ -346,7 +356,8 @@ end
-- arg: sni_last - add name to the end -- arg: sni_last - add name to the end
function tls_client_hello_clone(ctx, desync) function tls_client_hello_clone(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -388,7 +399,8 @@ function syndata(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete instance_cutoff_shim(ctx, desync) -- mission complete
end end
else else
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
end end
end end
@@ -397,7 +409,8 @@ end
-- arg : rstack - send RST,ACK instead of RST -- arg : rstack - send RST,ACK instead of RST
function rst(ctx, desync) function rst(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -424,8 +437,8 @@ end
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap . sni=%var is supported -- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap . sni=%var is supported
function fake(ctx, desync) function fake(ctx, desync)
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads -- by default process only outgoing known payloads. works only for tcp and udp
if direction_check(desync) and payload_check(desync) then if (desync.dis.tcp or desync.dis.udp) and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then if replay_first(desync) then
if not desync.arg.blob then if not desync.arg.blob then
error("fake: 'blob' arg required") error("fake: 'blob' arg required")
@@ -457,7 +470,8 @@ end
-- arg : nodrop - do not drop current dissect -- arg : nodrop - do not drop current dissect
function multisplit(ctx, desync) function multisplit(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -570,7 +584,8 @@ end
-- arg : nodrop - do not drop current dissect -- arg : nodrop - do not drop current dissect
function multidisorder(ctx, desync) function multidisorder(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -621,7 +636,8 @@ end
-- arg : optional - use zero pattern if seqovl_pattern blob is absent -- arg : optional - use zero pattern if seqovl_pattern blob is absent
function multidisorder_legacy(ctx, desync) function multidisorder_legacy(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -678,7 +694,8 @@ end
-- arg : nodrop - do not drop current dissect -- arg : nodrop - do not drop current dissect
function hostfakesplit(ctx, desync) function hostfakesplit(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -795,7 +812,8 @@ end
-- arg : nodrop - do not drop current dissect -- arg : nodrop - do not drop current dissect
function fakedsplit(ctx, desync) function fakedsplit(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -899,7 +917,8 @@ end
-- arg : nodrop - do not drop current dissect -- arg : nodrop - do not drop current dissect
function fakeddisorder(ctx, desync) function fakeddisorder(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -1010,7 +1029,8 @@ end
-- arg : optional - skip if blob is absent. use zero pattern if seqovl_pattern blob is absent -- arg : optional - skip if blob is absent. use zero pattern if seqovl_pattern blob is absent
function tcpseg(ctx, desync) function tcpseg(ctx, desync)
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -1064,7 +1084,8 @@ end
function oob(ctx, desync) function oob(ctx, desync)
if not desync.track then return end if not desync.track then return end
if not desync.dis.tcp then if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
local key = desync.func_instance.."_syn" local key = desync.func_instance.."_syn"
@@ -1163,7 +1184,8 @@ end
-- arg : pattern_offset=N . offset in the pattern. 0 by default -- arg : pattern_offset=N . offset in the pattern. 0 by default
function udplen(ctx, desync) function udplen(ctx, desync)
if not desync.dis.udp then if not desync.dis.udp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)
@@ -1199,7 +1221,8 @@ end
-- arg : dn=N - message starts from "dN". 3 by default -- arg : dn=N - message starts from "dN". 3 by default
function dht_dn(ctx, desync) function dht_dn(ctx, desync)
if not desync.dis.udp then if not desync.dis.udp then
instance_cutoff_shim(ctx, desync) -- do not cutoff on related icmp
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return return
end end
direction_cutoff_opposite(ctx, desync) direction_cutoff_opposite(ctx, desync)

View File

@@ -179,7 +179,7 @@ function standard_failure_detector(desync, crec)
DLOG("standard_failure_detector: not counting incoming RST s"..seq.." beyond s"..arg.inseq) DLOG("standard_failure_detector: not counting incoming RST s"..seq.." beyond s"..arg.inseq)
end end
end end
elseif not arg.no_http_redirect and desync.l7payload=="http_reply" and desync.track and desync.track.hostname then elseif not arg.no_http_redirect and desync.l7payload=="http_reply" and desync.track.hostname then
local hdis = http_dissect_reply(desync.dis.payload) local hdis = http_dissect_reply(desync.dis.payload)
if hdis and (hdis.code==302 or hdis.code==307) then if hdis and (hdis.code==302 or hdis.code==307) then
local idx_loc = array_field_search(hdis.headers, "header_low", "location") local idx_loc = array_field_search(hdis.headers, "header_low", "location")
@@ -403,7 +403,7 @@ function cond_payload_str(desync)
if not desync.arg.pattern then if not desync.arg.pattern then
error("cond_payload_str: missing 'pattern'") error("cond_payload_str: missing 'pattern'")
end end
return string.find(desync.dis.payload,desync.arg.pattern,1,true) return desync.dis.payload and string.find(desync.dis.payload,desync.arg.pattern,1,true)
end end
-- check iff function available. error if not -- check iff function available. error if not
function require_iff(desync, name) function require_iff(desync, name)
@@ -458,13 +458,17 @@ end
function repeater(ctx, desync) function repeater(ctx, desync)
local repeats = tonumber(desync.arg.repeats) local repeats = tonumber(desync.arg.repeats)
if not repeats then if not repeats then
error("repeat: missing 'repeats'") error("repeater: missing 'repeats'")
end end
local iff = desync.arg.iff or "cond_true" local iff = desync.arg.iff or "cond_true"
if type(_G[iff])~="function" then if type(_G[iff])~="function" then
error(name..": invalid 'iff' function '"..iff.."'") error("repeater: invalid 'iff' function '"..iff.."'")
end end
orchestrate(ctx, desync) orchestrate(ctx, desync)
if #desync.plan==0 then
DLOG("repeater: execution plan is empty - nothing to repeat")
return
end
local neg = desync.arg.neg local neg = desync.arg.neg
local stop = desync.arg.stop local stop = desync.arg.stop
local clear = desync.arg.clear local clear = desync.arg.clear

View File

@@ -1,4 +1,4 @@
NFQWS2_COMPAT_VER_REQUIRED=4 NFQWS2_COMPAT_VER_REQUIRED=5
if NFQWS2_COMPAT_VER~=NFQWS2_COMPAT_VER_REQUIRED then if NFQWS2_COMPAT_VER~=NFQWS2_COMPAT_VER_REQUIRED then
error("Incompatible NFQWS2_COMPAT_VER. Use pktws and lua scripts from the same release !") error("Incompatible NFQWS2_COMPAT_VER. Use pktws and lua scripts from the same release !")
@@ -629,7 +629,42 @@ function parse_tcp_flags(s)
end end
end end
return f return f
end end
-- get ip protocol from l3 headers
function ip_proto_l3(dis)
if dis.ip then
return dis.ip.ip_p
elseif dis.ip6 then
return #dis.ip6.exthdr==0 and dis.ip6.ip6_nxt or dis.ip6.exthdr[#dis.ip6.exthdr].next
end
end
-- get ip protocol from l4 headers
function ip_proto_l4(dis)
if dis.tcp then
return IPPROTO_TCP
elseif dis.udp then
return IPPROTO_UDP
elseif dis.ip then
return dis.icmp and IPPROTO_ICMP or nil
elseif dis.ip6 then
return dis.icmp and IPPROTO_ICMPV6 or nil
end
end
function ip_proto(dis)
return ip_proto_l4(dis) or ip_proto_l3(dis)
end
-- discover ip protocol and fix "next" fields
function fix_ip_proto(dis, proto)
local pr = proto or ip_proto(dis)
if pr then
if dis.ip then
dis.ip.ip_p = pr
elseif dis.ip6 then
fix_ip6_next(dis.ip6, pr)
end
end
end
-- find first tcp options of specified kind in dissect.tcp.options -- find first tcp options of specified kind in dissect.tcp.options
function find_tcp_option(options, kind) function find_tcp_option(options, kind)
@@ -980,7 +1015,7 @@ function l3_extra_len(dis, ip6_exthdr_last_idx)
end end
elseif dis.ip6 and dis.ip6.exthdr then elseif dis.ip6 and dis.ip6.exthdr then
local ct local ct
if ip6_exthdr_last_idx and ip6_exthdr_last_idx<=#dis.ip6.exthdr then if ip6_exthdr_last_idx and ip6_exthdr_last_idx>=0 and ip6_exthdr_last_idx<=#dis.ip6.exthdr then
ct = ip6_exthdr_last_idx ct = ip6_exthdr_last_idx
else else
ct = #dis.ip6.exthdr ct = #dis.ip6.exthdr
@@ -1007,6 +1042,8 @@ function l4_base_len(dis)
return TCP_BASE_LEN return TCP_BASE_LEN
elseif dis.udp then elseif dis.udp then
return UDP_BASE_LEN return UDP_BASE_LEN
elseif dis.icmp then
return ICMP_BASE_LEN
else else
return 0 return 0
end end
@@ -1253,6 +1290,31 @@ function host_or_ip(desync)
return host_ip(desync) return host_ip(desync)
end end
-- rate limited update of global ifaddrs
function update_ifaddrs()
if ifaddrs then
local now = os.time()
if not ifaddrs_last then ifaddrs_last = now end
if ifaddrs_last~=now then
ifaddrs = get_ifaddrs()
ifaddrs_last = now
end
else
ifaddrs = get_ifaddrs()
end
end
-- search ifaddrs for ip and return interface name or nil if not found
-- do not call get_ifaddrs too often to avoid overhead
function ip2ifname(ip)
update_ifaddrs()
if not ifaddrs then return nil end
for ifname,ifinfo in pairs(ifaddrs) do
if array_field_search(ifinfo.addr, "addr", ip) then
return ifname
end
end
end
function is_absolute_path(path) function is_absolute_path(path)
if string.sub(path,1,1)=='/' then return true end if string.sub(path,1,1)=='/' then return true end
local un = uname() local un = uname()
@@ -1298,8 +1360,10 @@ end
-- standard fragmentation to 2 ip fragments -- standard fragmentation to 2 ip fragments
-- function returns 2 dissects with fragments -- function returns 2 dissects with fragments
-- option : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8 -- option : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 8
-- option : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32 -- option : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_pos_icmp - icmp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 8
-- option : ipfrag_pos - icmp frag position for other L4. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default. -- option : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
function ipfrag2(dis, ipfrag_options) function ipfrag2(dis, ipfrag_options)
local function frag_idx(exthdr) local function frag_idx(exthdr)
@@ -1333,6 +1397,8 @@ function ipfrag2(dis, ipfrag_options)
pos = ipfrag_options.ipfrag_pos_tcp or 32 pos = ipfrag_options.ipfrag_pos_tcp or 32
elseif dis.udp then elseif dis.udp then
pos = ipfrag_options.ipfrag_pos_udp or 8 pos = ipfrag_options.ipfrag_pos_udp or 8
elseif dis.icmp then
pos = ipfrag_options.ipfrag_pos_icmp or 8
else else
pos = ipfrag_options.ipfrag_pos or 32 pos = ipfrag_options.ipfrag_pos or 32
end end
@@ -1349,12 +1415,8 @@ function ipfrag2(dis, ipfrag_options)
if (pos+l3)>0xFFFF then if (pos+l3)>0xFFFF then
error("ipfrag2: too high frag offset") error("ipfrag2: too high frag offset")
end end
local plen = l3 + l4_len(dis) + #dis.payload
if (pos+l3)>=plen then
DLOG("ipfrag2: ip frag pos exceeds packet length. ipfrag cancelled.")
return nil
end
local plen = l3 + l4_len(dis) + #dis.payload
if dis.ip then if dis.ip then
-- ipv4 frag is done by both lua and C part -- ipv4 frag is done by both lua and C part
-- lua code must correctly set ip_len, IP_MF and ip_off and provide full unfragmented payload -- lua code must correctly set ip_len, IP_MF and ip_off and provide full unfragmented payload
@@ -1362,6 +1424,11 @@ function ipfrag2(dis, ipfrag_options)
-- ip_off must be set to fragment offset and IP_MF bit must be set if it's not the last fragment -- ip_off must be set to fragment offset and IP_MF bit must be set if it's not the last fragment
-- C code constructs unfragmented packet then moves everything after ip header according to ip_off and ip_len -- C code constructs unfragmented packet then moves everything after ip header according to ip_off and ip_len
if (pos+l3)>=plen then
DLOG("ipfrag2: ip frag pos "..pos.." exceeds packet length. ipfrag cancelled.")
return nil
end
-- ip_id must not be zero or fragment will be dropped -- ip_id must not be zero or fragment will be dropped
local ip_id = dis.ip.ip_id==0 and math.random(1,0xFFFF) or dis.ip.ip_id local ip_id = dis.ip.ip_id==0 and math.random(1,0xFFFF) or dis.ip.ip_id
dis1 = deepcopy(dis) dis1 = deepcopy(dis)
@@ -1382,7 +1449,15 @@ function ipfrag2(dis, ipfrag_options)
-- C code constructs unfragmented packet then moves fragmentable part as needed -- C code constructs unfragmented packet then moves fragmentable part as needed
local idxfrag = frag_idx(dis.ip6.exthdr) local idxfrag = frag_idx(dis.ip6.exthdr)
local l3extra = l3_extra_len(dis, idxfrag-1) + 8 -- all ext headers before frag + 8 bytes for frag header local l3extra = l3_extra_len(dis, idxfrag-1) -- all ext headers before frag
l3 = l3_base_len(dis) + l3extra
if (pos+l3)>=plen then
DLOG("ipfrag2: ip frag pos "..pos.." exceeds packet length. ipfrag cancelled.")
return nil
end
l3extra = l3extra + 8 -- + 8 bytes for frag header
local ident = math.random(1,0xFFFFFFFF) local ident = math.random(1,0xFFFFFFFF)
dis1 = deepcopy(dis) dis1 = deepcopy(dis)
@@ -1398,7 +1473,6 @@ function ipfrag2(dis, ipfrag_options)
end end
dis2.ip6.ip6_plen = plen - IP6_BASE_LEN + 8 - pos -- packet len without frag + 8 byte frag header - ipv6 base header dis2.ip6.ip6_plen = plen - IP6_BASE_LEN + 8 - pos -- packet len without frag + 8 byte frag header - ipv6 base header
end end
return {dis1,dis2} return {dis1,dis2}
end end

View File

@@ -13,12 +13,14 @@ end
function test_all(...) function test_all(...)
test_run({test_crypto, test_bin, test_gzip, test_ipstr, test_dissect, test_csum, test_resolve, test_rawsend},...) test_run({
test_crypto, test_bin, test_gzip, test_ipstr, test_dissect, test_csum, test_resolve,
test_get_source_ip, test_ifaddrs, test_rawsend},...)
end end
function test_crypto(...) function test_crypto(...)
test_run({test_random, test_aes, test_aes_gcm, test_aes_ctr, test_hkdf, test_hash},...) test_run({test_random, test_bop, test_aes, test_aes_gcm, test_aes_ctr, test_hkdf, test_hash},...)
end end
function test_random() function test_random()
@@ -31,6 +33,30 @@ function test_random()
end end
end end
function test_bop()
for n,test in ipairs(
{
{ fb = bxor, fbit = bitxor, nb = "bxor", nbit="bitxor" },
{ fb = bor, fbit = bitor, nb = "bor", nbit="bitor" },
{ fb = band, fbit = bitand, nb = "band", nbit="bitand" }
}) do
for k=1,5 do
local r = {}
for i=1,6 do r[i] = math.random(0,0xFFFFFFFFFFFF) end
local v1 = bu48(r[1])..bu48(r[2])..bu48(r[3])
local v2 = bu48(r[4])..bu48(r[5])..bu48(r[6])
print("x1 : "..string2hex(v1))
print("x2 : "..string2hex(v2))
local v3 = test.fb(v1,v2)
local v4 = bu48(test.fbit(r[1],r[4]))..bu48(test.fbit(r[2],r[5]))..bu48(test.fbit(r[3],r[6]))
print(test.nb.." : "..string2hex(v3))
print(test.nbit.." : "..string2hex(v4))
print("result : "..(v3==v4 and "OK" or "FAIL"))
test_assert(v3==v4)
end
end
end
function test_hash() function test_hash()
local hashes={} local hashes={}
for i=1,5 do for i=1,5 do
@@ -455,13 +481,56 @@ function test_dissect()
} }
raw1 = reconstruct_dissect(ip_tcp) raw1 = reconstruct_dissect(ip_tcp)
print("IP+TCP : "..string2hex(raw1)) print("IP+TCP : "..string2hex(raw1))
dis1 = dissect(raw1); dis1 = dissect(raw1)
raw2 = reconstruct_dissect(dis1) raw2 = reconstruct_dissect(dis1)
dis2 = dissect(raw2); dis2 = dissect(raw2)
print("IP+TCP2: "..string2hex(raw2)) print("IP+TCP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" ) print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2) test_assert(raw1==raw2)
print("IP standalone")
raw1 = reconstruct_iphdr(ip_tcp.ip)
print("IP1: "..string2hex(raw1))
dis1 = dissect_iphdr(raw1)
raw2 = reconstruct_iphdr(dis1)
print("IP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
print("TCP standalone")
raw1 = reconstruct_tcphdr(ip_tcp.tcp)
print("TCP1: "..string2hex(raw1))
dis1 = dissect_tcphdr(raw1)
raw2 = reconstruct_tcphdr(dis1)
print("TCP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
local ip_icmp = {
ip = {
ip_tos = math.random(0,255),
ip_id = math.random(0,0xFFFF),
ip_off = 0,
ip_ttl = math.random(0,255),
ip_p = IPPROTO_ICMP,
ip_src = brandom(4),
ip_dst = brandom(4),
options = brandom(math.random(0,40))
},
icmp = {
icmp_type = ICMP_DEST_UNREACH, icmp_code=ICMP_UNREACH_PORT,
icmp_data = math.random(1,0xFFFFFFFF)
}
}
print("ICMP standalone")
raw1 = reconstruct_icmphdr(ip_icmp.icmp)
print("ICMP1: "..string2hex(raw1))
dis1 = dissect_icmphdr(raw1)
raw2 = reconstruct_icmphdr(dis1)
print("ICMP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
local ip6_udp = { local ip6_udp = {
ip6 = { ip6 = {
ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF), ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF),
@@ -482,18 +551,37 @@ function test_dissect()
raw1 = reconstruct_dissect(ip6_udp) raw1 = reconstruct_dissect(ip6_udp)
print("IP6+UDP : "..string2hex(raw1)) print("IP6+UDP : "..string2hex(raw1))
dis1 = dissect(raw1); dis1 = dissect(raw1)
raw2 = reconstruct_dissect(dis1) raw2 = reconstruct_dissect(dis1)
dis2 = dissect(raw2); dis2 = dissect(raw2)
print("IP6+UDP2: "..string2hex(raw2)) print("IP6+UDP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" ) print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2) test_assert(raw1==raw2)
print("UDP standalone")
raw1 = reconstruct_udphdr(ip6_udp.udp)
print("UDP1: "..string2hex(raw1))
dis1 = dissect_udphdr(raw1)
raw2 = reconstruct_udphdr(dis1)
print("UDP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
print("IP6 standalone")
ip6_udp.ip6.ip6_plen = nil;
raw1 = reconstruct_ip6hdr(ip6_udp.ip6,{ip6_last_proto=IPPROTO_UDP})
print("IP1: "..string2hex(raw1))
dis1 = dissect_ip6hdr(raw1)
raw2 = reconstruct_ip6hdr(dis1,{ip6_last_proto=IPPROTO_UDP})
print("IP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
end end
end end
function test_csum() function test_csum()
local payload = brandom(math.random(10,20)) local payload = brandom(math.random(10,20))
local ip4b, ip6b, raw, tcpb, udpb, dis1, dis2 local ip4b, ip6b, raw, tcpb, udpb, icmpb, dis1, dis2
local ip = { local ip = {
ip_tos = math.random(0,255), ip_tos = math.random(0,255),
ip_id = math.random(0,0xFFFF), ip_id = math.random(0,0xFFFF),
@@ -626,6 +714,37 @@ function test_csum()
dis2 = dissect(ip6b..udpb..payload) dis2 = dissect(ip6b..udpb..payload)
print( dis1.udp.uh_sum==dis2.udp.uh_sum and "UDP+IP6 CSUM OK" or "UDP+IP6 CSUM FAILED" ) print( dis1.udp.uh_sum==dis2.udp.uh_sum and "UDP+IP6 CSUM OK" or "UDP+IP6 CSUM FAILED" )
test_assert(dis1.udp.uh_sum==dis2.udp.uh_sum) test_assert(dis1.udp.uh_sum==dis2.udp.uh_sum)
local icmp = {
icmp_type = math.random(0,0xFF), icmp_code=math.random(0,0xFF),
icmp_data = math.random(0,0xFFFFFFFF)
}
ip.ip_p = IPPROTO_ICMP
ip4b = reconstruct_iphdr(ip)
ip6.ip6_plen = packet_len({ip6=ip6,icmp=icmp,payload=payload}) - IP6_BASE_LEN
ip6b = reconstruct_ip6hdr(ip6, {ip6_last_proto=IPPROTO_ICMPV6})
icmpb = reconstruct_icmphdr(icmp)
raw = bu8(icmp.icmp_type) ..
bu8(icmp.icmp_code) ..
bu16(0) ..
bu32(icmp.icmp_data)
print( raw==icmpb and "ICMP RECONSTRUCT OK" or "ICMP RECONSTRUCT FAILED" )
test_assert(raw==icmpb)
raw = reconstruct_dissect({ip=ip, icmp=icmp, payload=payload})
dis1 = dissect(raw)
icmpb = csum_icmp_fix(ip4b,icmpb,payload)
dis2 = dissect(ip4b..icmpb..payload)
print( dis1.icmp.icmp_cksum==dis2.icmp.icmp_cksum and "ICMP+IP4 CSUM OK" or "ICMP+IP4 CSUM FAILED" )
test_assert(dis1.icmp.icmp_cksum==dis2.icmp.icmp_cksum)
raw = reconstruct_dissect({ip6=ip6, icmp=icmp, payload=payload})
dis1 = dissect(raw)
icmpb = csum_icmp_fix(ip6b,icmpb,payload)
dis2 = dissect(ip6b..icmpb..payload)
print( dis1.icmp.icmp_cksum==dis2.icmp.icmp_cksum and "ICMP+IP6 CSUM OK" or "ICMP+IP6 CSUM FAILED" )
test_assert(dis1.icmp.icmp_cksum==dis2.icmp.icmp_cksum)
end end
function test_resolve() function test_resolve()
@@ -677,6 +796,26 @@ function test_resolve()
print("resolve_pos http non-existent : "..m.." : "..tostring(pos)) print("resolve_pos http non-existent : "..m.." : "..tostring(pos))
end end
function test_get_source_ip(opts)
for k,d in ipairs({
'127.0.0.1','192.168.1.1','10.1.1.1','1.1.1.1','255.255.255.255',
'::1','fc81::4','2a06::1','2001:470::1','2002:0101:0101::1','::1.1.1.1'})
do
local src = get_source_ip(pton(d))
print((src and ntop(src) or "?").." => "..d)
end
end
function test_ifaddrs(opts)
local ifa = get_ifaddrs()
test_assert(ifa)
for ifname,ifinfo in pairs(ifa) do
print(ifname.." index="..tostring(ifinfo.index).." mtu="..tostring(ifinfo.mtu))
for i,addr in ipairs(ifinfo.addr) do
print(" "..ntop(addr.addr)..(addr.netmask and " mask "..tostring(ntop(addr.netmask)) or ""))
end
end
end
function test_rawsend(opts) function test_rawsend(opts)
local ifout = (opts and opts.ifout) and opts.ifout local ifout = (opts and opts.ifout) and opts.ifout
local function rawsend_fail_warning() local function rawsend_fail_warning()
@@ -718,14 +857,15 @@ function test_rawsend(opts)
local payload = brandom(math.random(100,1200)) local payload = brandom(math.random(100,1200))
local b local b
local target = pton("192.168.1.2")
ip = { ip = {
ip_tos = 0, ip_tos = 0,
ip_id = math.random(0,0xFFFF), ip_id = math.random(0,0xFFFF),
ip_off = 0, ip_off = 0,
ip_ttl = 1, ip_ttl = 1,
ip_p = IPPROTO_UDP, ip_p = IPPROTO_UDP,
ip_src = pton("192.168.1.1"), ip_src = get_source_ip(target),
ip_dst = pton("192.168.1.2") ip_dst = target
} }
udp = { udp = {
uh_sport = math.random(0,0xFFFF), uh_sport = math.random(0,0xFFFF),
@@ -749,11 +889,12 @@ function test_rawsend(opts)
print("send ipv4 udp using pure rawsend without dissect") print("send ipv4 udp using pure rawsend without dissect")
test_assert(rawsend_print(raw, {repeats=5})) test_assert(rawsend_print(raw, {repeats=5}))
target = pton("fdce:3124:164a:5318::2")
ip6 = { ip6 = {
ip6_flow = 0x60000000, ip6_flow = 0x60000000,
ip6_hlim = 1, ip6_hlim = 1,
ip6_src = pton("fdce:3124:164a:5318::1"), ip6_src = get_source_ip(target),
ip6_dst = pton("fdce:3124:164a:5318::2") ip6_dst = target
} }
dis = {ip6 = ip6, udp = udp, payload = payload} dis = {ip6 = ip6, udp = udp, payload = payload}
print("send ipv6 udp") print("send ipv6 udp")
@@ -784,8 +925,8 @@ function test_rawsend(opts)
test_assert(rawsend_dissect_print(d)) test_assert(rawsend_dissect_print(d))
end end
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" }) insert_ip6_exthdr(ip6, nil, IPPROTO_DSTOPTS, "\x00\x00\x00\x00\x00\x00")
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" }) insert_ip6_exthdr(ip6, nil, IPPROTO_DSTOPTS, "\x00\x00\x00\x00\x00\x00")
ip6.ip6_flow = 0x60001234; ip6.ip6_flow = 0x60001234;
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80}) ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in ipairs(ddis) do for k,d in ipairs(ddis) do
@@ -793,13 +934,30 @@ function test_rawsend(opts)
test_assert(rawsend_dissect_print(d, {fwmark = 0x50EA})) test_assert(rawsend_dissect_print(d, {fwmark = 0x50EA}))
end end
fix_ip6_next(ip6) -- required to forge next proto in the second fragment fix_ip_proto(dis) -- ip6_preserve_next requires next fields in ip6.exthdr
ip6.ip6_flow = 0x6000AE38; ip6.ip6_flow = 0x6000AE38;
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80, ipfrag_next = IPPROTO_TCP}) ddis = ipfrag2(dis, {ipfrag_pos_udp = 72, ipfrag_next = IPPROTO_TCP})
for k,d in ipairs(ddis) do for k,d in ipairs(ddis) do
print("send ipv6 udp frag "..k.." with hopbyhop, destopt ext headers in unfragmentable part and another destopt ext header in fragmentable part. forge next proto in fragment header of the second fragment to TCP") print("send ipv6 udp frag "..k.." with hopbyhop, destopt ext headers in unfragmentable part and another destopt ext header in fragmentable part. forge next proto in fragment header of the second fragment to TCP")
-- reconstruct dissect using next proto fields in the dissect. do not auto fix next proto chain. -- reconstruct dissect using next proto fields in the dissect. do not auto fix next proto chain.
-- by default reconstruct fixes next proto chain -- by default reconstruct fixes next proto chain
test_assert(rawsend_dissect_print(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true})) test_assert(rawsend_dissect_print(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true}))
end end
local icmp = {
icmp_type = ICMP_ECHO, icmp_code=0,
icmp_data = u32(bu16(math.random(1,0xFFFF))..bu16(1))
}
ip.ip_p = IPPROTO_ICMP
payload=brandom_az09(math.random(10,1100))
dis = {ip = ip, icmp = icmp, payload = payload}
print("send ipv4 icmp")
test_assert(rawsend_dissect_print(dis, {fwmark = 0xD133, repeats=3}))
ip6.exthdr={{ type = IPPROTO_HOPOPTS, data = "\x00\x00\x00\x00\x00\x00" }}
ip6.ip6_flow=0x60009E3B;
icmp.icmp_type = ICMP6_ECHO_REQUEST;
dis = {ip6 = ip6, icmp = icmp, payload = payload}
print("send ipv6 icmp")
test_assert(rawsend_dissect_print(dis, {fwmark = 0x8E10, repeats=3}))
end end

View File

@@ -14,7 +14,7 @@ function wgobfs(ctx, desync)
local function genkey() local function genkey()
-- cache key in a global var bound to instance name -- cache key in a global var bound to instance name
local key_cache_name = desync.func_instance.."_key" local key_cache_name = desync.func_instance.."_key"
key = _G[key_cache_name] local key = _G[key_cache_name]
if not key then if not key then
key = hkdf("sha256", "wgobfs_salt", desync.arg.secret, nil, 16) key = hkdf("sha256", "wgobfs_salt", desync.arg.secret, nil, 16)
_G[key_cache_name] = key _G[key_cache_name] = key

View File

@@ -232,7 +232,7 @@ static bool dp_match(
default: default:
if (!ipp_filters_match(&dp->ipf, l3proto)) return false; if (!ipp_filters_match(&dp->ipf, l3proto)) return false;
} }
if (l3proto == IPPROTO_ICMP && !icmp_filters_match(&dp->icf, icmp_type, icmp_code)) if (l3proto == IPPROTO_ICMP && !icmp_filters_match(&dp->icf, icmp_type, icmp_code))
// icmp filter does not match // icmp filter does not match
return false; return false;

View File

@@ -498,7 +498,7 @@ static int dvt_main(void)
memset(&bp6, 0, sizeof(bp6)); memset(&bp6, 0, sizeof(bp6));
bp6.sin6_family = AF_INET6; bp6.sin6_family = AF_INET6;
bp6.sin6_port = htons(params.port); bp6.sin6_port = htons(params.port);
DLOG_CONDUP("creating divert6 socket\n"); DLOG_CONDUP("creating divert6 socket\n");
fd[1] = socket_divert(AF_INET6); fd[1] = socket_divert(AF_INET6);
if (fd[1] == -1) { if (fd[1] == -1) {