summaryrefslogtreecommitdiffstats
path: root/examples/qr-multi-pcap-state.lua
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-13 07:54:12 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-13 07:54:12 +0000
commit4754ed45b607e82450a5e31fea1da3ba61433b04 (patch)
tree3554490bdc003e6004f605abe41929cdf98b0651 /examples/qr-multi-pcap-state.lua
parentInitial commit. (diff)
downloaddnsjit-4754ed45b607e82450a5e31fea1da3ba61433b04.tar.xz
dnsjit-4754ed45b607e82450a5e31fea1da3ba61433b04.zip
Adding upstream version 1.1.0+debian.upstream/1.1.0+debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples/qr-multi-pcap-state.lua')
-rwxr-xr-xexamples/qr-multi-pcap-state.lua270
1 files changed, 270 insertions, 0 deletions
diff --git a/examples/qr-multi-pcap-state.lua b/examples/qr-multi-pcap-state.lua
new file mode 100755
index 0000000..cf2cf0d
--- /dev/null
+++ b/examples/qr-multi-pcap-state.lua
@@ -0,0 +1,270 @@
+#!/usr/bin/env dnsjit
+local clock = require("dnsjit.lib.clock")
+local log = require("dnsjit.core.log")
+local getopt = require("dnsjit.lib.getopt").new({
+ { "v", "verbose", 0, "Enable and increase verbosity for each time given", "?+" },
+ { "r", "read-state", "", "File to read state from before processing", "?" },
+ { "w", "write-state", "", "File to write state to after processing", "?" },
+})
+local pcap = unpack(getopt:parse())
+if getopt:val("help") then
+ getopt:usage()
+ return
+end
+local v = getopt:val("v")
+if v > 0 then
+ log.enable("warning")
+end
+if v > 1 then
+ log.enable("notice")
+end
+if v > 2 then
+ log.enable("info")
+end
+if v > 3 then
+ log.enable("debug")
+end
+
+if pcap == nil then
+ print("usage: "..arg[1].." <pcap>")
+ return
+end
+
+local object = require("dnsjit.core.objects")
+local input = require("dnsjit.input.mmpcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local dns = require("dnsjit.core.object.dns").new()
+local label = require("dnsjit.core.object.dns.label")
+local ffi = require("ffi")
+local labels = require("dnsjit.core.object.dns.label").new(16)
+local q = require("dnsjit.core.object.dns.q").new()
+local bit = require("bit")
+
+hash_u32 = ffi.new("uint32_t[2]")
+hash_u32p = ffi.cast("uint32_t*", hash_u32)
+function hashkey(dns, transport, protocol)
+ if transport.obj_type == object.IP then
+ ffi.copy(hash_u32p, transport.src, 4)
+ hash_u32[1] = hash_u32[0]
+ ffi.copy(hash_u32p, transport.dst, 4)
+ hash_u32[1] = bit.bxor(hash_u32[0], hash_u32[1])
+ else
+ local srcp = ffi.cast("uint8_t*", transport.src)
+ ffi.copy(hash_u32p, srcp, 4)
+ hash_u32[1] = hash_u32[0]
+ ffi.copy(hash_u32p, srcp+4, 4)
+ hash_u32[1] = bit.bxor(hash_u32[0], hash_u32[1])
+ srcp = ffi.cast("uint8_t*", transport.dst)
+ ffi.copy(hash_u32p, srcp, 4)
+ hash_u32[1] = bit.bxor(hash_u32[0], hash_u32[1])
+ ffi.copy(hash_u32p, srcp+4, 4)
+ hash_u32[1] = bit.bxor(hash_u32[0], hash_u32[1])
+ end
+ if dns.qr == 1 then
+ hash_u32[0] = protocol.dport + bit.lshift(protocol.sport, 16)
+ else
+ hash_u32[0] = protocol.sport + bit.lshift(protocol.dport, 16)
+ end
+ hash_u32[1] = bit.bxor(hash_u32[0], hash_u32[1])
+ hash_u32[0] = dns.id + bit.lshift(dns.id, 16)
+ return bit.bxor(hash_u32[0], hash_u32[1])
+end
+
+function dump_inflight(hkey, obj)
+ return string.format("return %d, { qsec = %d, qnsec = %d, rsec = %d, rnsec = %d, src = %q, sport = %d, dst = %q, dport = %d, id = %d, qname = %q, qtype = %q, rcode = %d }",
+ hkey,
+ tonumber(obj.qsec), tonumber(obj.qnsec),
+ tonumber(obj.rsec), tonumber(obj.rnsec),
+ obj.src, obj.sport,
+ obj.dst, obj.dport,
+ obj.id,
+ obj.qname, obj.qtype,
+ obj.rcode
+ )
+end
+
+function qrout(res)
+ local tsq = tonumber(res.qsec) + (tonumber(res.qnsec)/1000000000)
+ local tsr = tonumber(res.rsec) + (tonumber(res.rnsec)/1000000000)
+ print(tsq, tsr, math.floor(((tsr-tsq)*1000000)+0.5),
+ res.src, res.dst, res.id, res.rcode, res.qname, res.qtype)
+end
+
+input:open(pcap)
+layer:producer(input)
+local producer, ctx = layer:produce()
+
+local inflight = {}
+
+if getopt:val("read-state") > "" then
+ local f, _ = io.open(getopt:val("read-state"))
+ local inflights = 0
+ if f ~= nil then
+ for chunk in f:lines() do
+ local hkey, query = assert(loadstring(chunk))()
+ if hkey and query then
+ if not inflight[hkey] then
+ inflight[hkey] = {
+ queries = {},
+ size = 0,
+ }
+ end
+
+ table.insert(inflight[hkey].queries, query)
+ inflight[hkey].size = inflight[hkey].size + 1
+ inflights = inflights + 1
+ end
+ end
+ f:close()
+ print(string.format("== read %d inflight states from %q", inflights, getopt:val("read-state")))
+ end
+end
+
+local stat = {
+ packets = 0,
+ queries = 0,
+ responses = 0,
+ dropped = 0,
+}
+local start_sec, start_nsec = clock:monotonic()
+while true do
+ local obj = producer(ctx)
+ if obj == nil then break end
+ stat.packets = stat.packets + 1
+ local pl = obj:cast()
+ if obj:type() == "payload" and pl.len > 0 then
+ local protocol = obj.obj_prev
+ while protocol ~= nil do
+ if protocol.obj_type == object.UDP or protocol.obj_type == object.TCP then
+ break
+ end
+ protocol = protocol.obj_prev
+ end
+ local transport = protocol.obj_prev
+ while transport ~= nil do
+ if transport.obj_type == object.IP or transport.obj_type == object.IP6 then
+ break
+ end
+ transport = transport.obj_prev
+ end
+ local pcap = transport.obj_prev
+ while pcap ~= nil do
+ if pcap.obj_type == object.PCAP then
+ break
+ end
+ pcap = pcap.obj_prev
+ end
+
+ dns.obj_prev = obj
+ if pcap ~= nil and transport ~= nil and protocol ~= nil and dns:parse_header() == 0 then
+ transport = transport:cast()
+ protocol = protocol:cast()
+ pcap = pcap:cast()
+
+ local hkey = hashkey(dns, transport, protocol)
+
+ if dns.qr == 1 then
+ stat.responses = stat.responses + 1
+ if inflight[hkey] then
+ for k, n in pairs(inflight[hkey].queries) do
+ if n.id == dns.id
+ and n.sport == protocol.dport
+ and n.dport == protocol.sport
+ and n.src == transport:destination()
+ and n.dst == transport:source()
+ then
+ n.rsec = pcap.ts.sec
+ n.rnsec = pcap.ts.nsec
+ n.rcode = dns.rcode
+ qrout(n)
+ inflight[hkey].queries[k] = nil
+ inflight[hkey].size = inflight[hkey].size - 1
+ if inflight[hkey].size < 1 then
+ inflight[hkey] = nil
+ end
+ break
+ end
+ end
+ else
+ print("== dropped",
+ tonumber(pcap.ts.sec) + (tonumber(pcap.ts.nsec) / 1000000000),
+ transport:source(),
+ transport:destination(),
+ dns.id,
+ label.tooffstr(dns, labels, 16),
+ dns.type_tostring(q.type)
+ )
+ stat.dropped = stat.dropped + 1
+ end
+ else
+ stat.queries = stat.queries + 1
+ if dns.qdcount > 0 and dns:parse_q(q, labels, 16) == 0 then
+ if not inflight[hkey] then
+ inflight[hkey] = {
+ queries = {},
+ size = 0,
+ }
+ end
+
+ table.insert(inflight[hkey].queries, {
+ qsec = pcap.ts.sec,
+ qnsec = pcap.ts.nsec,
+ rsec = -1,
+ rnsec = -1,
+ src = transport:source(),
+ sport = protocol.sport,
+ dst = transport:destination(),
+ dport = protocol.dport,
+ id = dns.id,
+ qname = label.tooffstr(dns, labels, 16),
+ qtype = dns.type_tostring(q.type),
+ rcode = -1,
+ })
+ inflight[hkey].size = inflight[hkey].size + 1
+ end
+ end
+ end
+ end
+end
+local end_sec, end_nsec = clock:monotonic()
+
+local runtime = 0
+if end_sec > start_sec then
+ runtime = ((end_sec - start_sec) - 1) + ((1000000000 - start_nsec + end_nsec)/1000000000)
+elseif end_sec == start_sec and end_nsec > start_nsec then
+ runtime = (end_nsec - start_nsec) / 1000000000
+end
+
+print("== runtime", runtime)
+print("== packets", stat.packets, stat.packets/runtime)
+print("== queries", stat.queries, stat.queries/runtime)
+print("== responses", stat.responses, stat.responses/runtime)
+print("== dropped", stat.dropped, stat.dropped/runtime)
+
+if getopt:val("write-state") > "" then
+ local f, _ = io.open(getopt:val("write-state"), "w+")
+ local inflights = 0
+ if f ~= nil then
+ for hkey, unanswered in pairs(inflight) do
+ for _, query in pairs(unanswered.queries) do
+ f:write(dump_inflight(hkey, query), "\n")
+ inflights = inflights + 1
+ end
+ end
+ f:close()
+ print(string.format("== wrote %d inflight states to %q", inflights, getopt:val("write-state")))
+ end
+else
+ inflights = 0
+ for hkey, unanswered in pairs(inflight) do
+ inflights = inflights + unanswered.size
+ end
+ if inflights > 0 then
+ print("== inflight queries (tsq, src, dst, id, qname, qtype)")
+ for hkey, unanswered in pairs(inflight) do
+ for _, query in pairs(unanswered.queries) do
+ print(tonumber(query.qsec) + (tonumber(query.qnsec)/1000000000), query.src, query.dst, query.id, query.qname, query.qtype)
+ end
+ end
+ end
+end