Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-13 22:03: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
* 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: file open test before destroying in-memory content of ipset/hostlist
* 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_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. ipb6: starting from fragmentable part. must be multiple of 8. default 32
* 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_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.
]]
@@ -114,7 +116,8 @@ end
-- standard args : direction
function http_domcase(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -140,7 +143,8 @@ end
-- arg : spell=<str> . spelling of the "Host" header. must be exactly 4 chars long
function http_hostcase(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -171,7 +175,8 @@ end
-- NOTE : if using with other http tampering methodeol should be the last !
function http_methodeol(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -202,7 +207,8 @@ end
-- standard args : direction
function http_unixeol(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -271,7 +277,8 @@ function synack_split(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete
end
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
@@ -288,7 +295,8 @@ function synack(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete
end
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
@@ -306,7 +314,8 @@ function wsize(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete
end
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
@@ -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
function wssize(ctx, desync)
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
end
local verdict = VERDICT_PASS
@@ -346,7 +356,8 @@ end
-- arg: sni_last - add name to the end
function tls_client_hello_clone(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -388,7 +399,8 @@ function syndata(ctx, desync)
instance_cutoff_shim(ctx, desync) -- mission complete
end
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
@@ -397,7 +409,8 @@ end
-- arg : rstack - send RST,ACK instead of RST
function rst(ctx, desync)
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
end
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
function fake(ctx, desync)
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
if direction_check(desync) and payload_check(desync) then
-- by default process only outgoing known payloads. works only for tcp and udp
if (desync.dis.tcp or desync.dis.udp) and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
if not desync.arg.blob then
error("fake: 'blob' arg required")
@@ -457,7 +470,8 @@ end
-- arg : nodrop - do not drop current dissect
function multisplit(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -570,7 +584,8 @@ end
-- arg : nodrop - do not drop current dissect
function multidisorder(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -621,7 +636,8 @@ end
-- arg : optional - use zero pattern if seqovl_pattern blob is absent
function multidisorder_legacy(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -678,7 +694,8 @@ end
-- arg : nodrop - do not drop current dissect
function hostfakesplit(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -795,7 +812,8 @@ end
-- arg : nodrop - do not drop current dissect
function fakedsplit(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -899,7 +917,8 @@ end
-- arg : nodrop - do not drop current dissect
function fakeddisorder(ctx, desync)
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
end
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
function tcpseg(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -1064,7 +1084,8 @@ end
function oob(ctx, desync)
if not desync.track then return end
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
end
local key = desync.func_instance.."_syn"
@@ -1163,7 +1184,8 @@ end
-- arg : pattern_offset=N . offset in the pattern. 0 by default
function udplen(ctx, desync)
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
end
direction_cutoff_opposite(ctx, desync)
@@ -1199,7 +1221,8 @@ end
-- arg : dn=N - message starts from "dN". 3 by default
function dht_dn(ctx, desync)
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
end
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)
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)
if hdis and (hdis.code==302 or hdis.code==307) then
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
error("cond_payload_str: missing 'pattern'")
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
-- check iff function available. error if not
function require_iff(desync, name)
@@ -458,13 +458,17 @@ end
function repeater(ctx, desync)
local repeats = tonumber(desync.arg.repeats)
if not repeats then
error("repeat: missing 'repeats'")
error("repeater: missing 'repeats'")
end
local iff = desync.arg.iff or "cond_true"
if type(_G[iff])~="function" then
error(name..": invalid 'iff' function '"..iff.."'")
error("repeater: invalid 'iff' function '"..iff.."'")
end
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 stop = desync.arg.stop
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
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
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
function find_tcp_option(options, kind)
@@ -980,7 +1015,7 @@ function l3_extra_len(dis, ip6_exthdr_last_idx)
end
elseif dis.ip6 and dis.ip6.exthdr then
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
else
ct = #dis.ip6.exthdr
@@ -1007,6 +1042,8 @@ function l4_base_len(dis)
return TCP_BASE_LEN
elseif dis.udp then
return UDP_BASE_LEN
elseif dis.icmp then
return ICMP_BASE_LEN
else
return 0
end
@@ -1253,6 +1290,31 @@ function host_or_ip(desync)
return host_ip(desync)
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)
if string.sub(path,1,1)=='/' then return true end
local un = uname()
@@ -1298,8 +1360,10 @@ end
-- standard fragmentation to 2 ip 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_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
-- 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. 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.
function ipfrag2(dis, ipfrag_options)
local function frag_idx(exthdr)
@@ -1333,6 +1397,8 @@ function ipfrag2(dis, ipfrag_options)
pos = ipfrag_options.ipfrag_pos_tcp or 32
elseif dis.udp then
pos = ipfrag_options.ipfrag_pos_udp or 8
elseif dis.icmp then
pos = ipfrag_options.ipfrag_pos_icmp or 8
else
pos = ipfrag_options.ipfrag_pos or 32
end
@@ -1349,12 +1415,8 @@ function ipfrag2(dis, ipfrag_options)
if (pos+l3)>0xFFFF then
error("ipfrag2: too high frag offset")
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
-- 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
@@ -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
-- 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
local ip_id = dis.ip.ip_id==0 and math.random(1,0xFFFF) or dis.ip.ip_id
dis1 = deepcopy(dis)
@@ -1382,7 +1449,15 @@ function ipfrag2(dis, ipfrag_options)
-- C code constructs unfragmented packet then moves fragmentable part as needed
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)
dis1 = deepcopy(dis)
@@ -1398,7 +1473,6 @@ function ipfrag2(dis, ipfrag_options)
end
dis2.ip6.ip6_plen = plen - IP6_BASE_LEN + 8 - pos -- packet len without frag + 8 byte frag header - ipv6 base header
end
return {dis1,dis2}
end

View File

@@ -13,12 +13,14 @@ end
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
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
function test_random()
@@ -31,6 +33,30 @@ function test_random()
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()
local hashes={}
for i=1,5 do
@@ -455,13 +481,56 @@ function test_dissect()
}
raw1 = reconstruct_dissect(ip_tcp)
print("IP+TCP : "..string2hex(raw1))
dis1 = dissect(raw1);
dis1 = dissect(raw1)
raw2 = reconstruct_dissect(dis1)
dis2 = dissect(raw2);
dis2 = dissect(raw2)
print("IP+TCP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
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 = {
ip6 = {
ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF),
@@ -482,18 +551,37 @@ function test_dissect()
raw1 = reconstruct_dissect(ip6_udp)
print("IP6+UDP : "..string2hex(raw1))
dis1 = dissect(raw1);
dis1 = dissect(raw1)
raw2 = reconstruct_dissect(dis1)
dis2 = dissect(raw2);
dis2 = dissect(raw2)
print("IP6+UDP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
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
function test_csum()
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 = {
ip_tos = math.random(0,255),
ip_id = math.random(0,0xFFFF),
@@ -626,6 +714,37 @@ function test_csum()
dis2 = dissect(ip6b..udpb..payload)
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)
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
function test_resolve()
@@ -677,6 +796,26 @@ function test_resolve()
print("resolve_pos http non-existent : "..m.." : "..tostring(pos))
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)
local ifout = (opts and opts.ifout) and opts.ifout
local function rawsend_fail_warning()
@@ -718,14 +857,15 @@ function test_rawsend(opts)
local payload = brandom(math.random(100,1200))
local b
local target = pton("192.168.1.2")
ip = {
ip_tos = 0,
ip_id = math.random(0,0xFFFF),
ip_off = 0,
ip_ttl = 1,
ip_p = IPPROTO_UDP,
ip_src = pton("192.168.1.1"),
ip_dst = pton("192.168.1.2")
ip_src = get_source_ip(target),
ip_dst = target
}
udp = {
uh_sport = math.random(0,0xFFFF),
@@ -749,11 +889,12 @@ function test_rawsend(opts)
print("send ipv4 udp using pure rawsend without dissect")
test_assert(rawsend_print(raw, {repeats=5}))
target = pton("fdce:3124:164a:5318::2")
ip6 = {
ip6_flow = 0x60000000,
ip6_hlim = 1,
ip6_src = pton("fdce:3124:164a:5318::1"),
ip6_dst = pton("fdce:3124:164a:5318::2")
ip6_src = get_source_ip(target),
ip6_dst = target
}
dis = {ip6 = ip6, udp = udp, payload = payload}
print("send ipv6 udp")
@@ -784,8 +925,8 @@ function test_rawsend(opts)
test_assert(rawsend_dissect_print(d))
end
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\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")
insert_ip6_exthdr(ip6, nil, IPPROTO_DSTOPTS, "\x00\x00\x00\x00\x00\x00")
ip6.ip6_flow = 0x60001234;
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in ipairs(ddis) do
@@ -793,13 +934,30 @@ function test_rawsend(opts)
test_assert(rawsend_dissect_print(d, {fwmark = 0x50EA}))
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;
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
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.
-- by default reconstruct fixes next proto chain
test_assert(rawsend_dissect_print(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true}))
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

View File

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

View File

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

View File

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