diff options
Diffstat (limited to 'src/output/dnssim.lua')
-rw-r--r-- | src/output/dnssim.lua | 433 |
1 files changed, 0 insertions, 433 deletions
diff --git a/src/output/dnssim.lua b/src/output/dnssim.lua deleted file mode 100644 index 25193c4..0000000 --- a/src/output/dnssim.lua +++ /dev/null @@ -1,433 +0,0 @@ --- Copyright (c) 2018-2021, CZ.NIC, z.s.p.o. --- All rights reserved. --- --- This file is part of dnsjit. --- --- dnsjit is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- dnsjit is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with dnsjit. If not, see <http://www.gnu.org/licenses/>. - --- dnsjit.output.dnssim --- Simulate independent DNS clients over various transports --- output = require("dnsjit.output.dnssim").new() --- .SS Usage --- output:udp() --- output:target("::1", 53) --- recv, rctx = output:receive() --- -- pass in objects using recv(rctx, obj) --- -- repeatedly call output:run_nowait() until it returns 0 --- .SS DNS-over-TLS example configuration --- output:tls("NORMAL:-VERS-ALL:+VERS-TLS1.3") -- enforce TLS 1.3 --- .SS DNS-over-HTTPS/2 example configuration --- output:https2({ method = "POST", uri_path = "/doh" }) --- --- Output module for simulating traffic from huge number of independent, --- individual DNS clients. --- Uses libuv for asynchronous communication. --- There may only be a single DnsSim in a thread. --- Use --- .I dnsjit.core.thread --- to have multiple DnsSim instances. --- .P --- With proper use of this component, it is possible to simulate hundreds of --- thousands of clients when using a high-performance server. --- This also applies for state-full transports. --- The complete set-up is quite complex and requires other components. --- See DNS Shotgun --- .RI ( https://gitlab.nic.cz/knot/shotgun ) --- for dnsjit scripts ready for use for high-performance --- benchmarking. -module(...,package.seeall) - -require("dnsjit.output.dnssim_h") -local bit = require("bit") -local object = require("dnsjit.core.objects") -local ffi = require("ffi") -local C = ffi.C - -local DnsSim = {} - -local _DNSSIM_VERSION = 20210129 -local _DNSSIM_JSON_VERSION = 20200527 - --- Create a new DnsSim output for up to max_clients. -function DnsSim.new(max_clients) - local self = { - obj = C.output_dnssim_new(max_clients), - max_clients = max_clients, - } - ffi.gc(self.obj, C.output_dnssim_free) - return setmetatable(self, { __index = DnsSim }) -end - -local function _check_version(version, req_version) - if req_version == nil then - return version - end - local min_version = tonumber(req_version) - if min_version == nil then - C.output_dnssim_log():fatal("invalid version number: "..req_version) - return nil - end - if version >= min_version then - return version - end - return nil -end - --- Check that version of dnssim is at minimum the one passed as --- .B req_version --- and return the actual version number. --- Return nil if the condition is not met. --- --- If no --- .B req_version --- is specified no check is done and only the version number is returned. -function DnsSim.check_version(req_version) - return _check_version(_DNSSIM_VERSION, req_version) -end - --- Check that version of dnssim's JSON data format is at minimum the one passed as --- .B req_version --- and return the actual version number. --- Return nil if the condition is not met. --- --- If no --- .B req_version --- is specified no check is done and only the version number is returned. -function DnsSim.check_json_version(req_version) - return _check_version(_DNSSIM_JSON_VERSION, req_version) -end - --- Return the Log object to control logging of this instance or module. --- Optionally, set the instance's log name. --- Unique name should be used for each instance. -function DnsSim:log(name) - if self == nil then - return C.output_dnssim_log() - end - if name ~= nil then - C.output_dnssim_log_name(self.obj, name) - end - return self.obj._log -end - --- Set the target IPv4/IPv6 address where queries will be sent to. -function DnsSim:target(ip, port) - local nport = tonumber(port) - if nport == nil then - self.obj._log:fatal("invalid port: "..port) - return -1 - end - if nport <= 0 or nport > 65535 then - self.obj._log:fatal("invalid port number: "..nport) - return -1 - end - return C.output_dnssim_target(self.obj, ip, nport) -end - --- Specify source IPv4/IPv6 address for sending queries. --- Can be set multiple times. --- Addresses are selected round-robin when sending. -function DnsSim:bind(ip) - return C.output_dnssim_bind(self.obj, ip) -end - --- Set the preferred transport to UDP. --- --- When the optional argument --- .B tcp_fallback --- is set to true, individual queries are re-tried over TCP when TC bit is set in the answer. --- Defaults to --- .B false --- (aka only UDP is used). -function DnsSim:udp(tcp_fallback) - if tcp_fallback == true then - C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP) - else - C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY) - end -end - --- Set the transport to TCP. -function DnsSim:tcp() - C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_TCP) -end - --- Set the transport to TLS. --- --- The optional argument --- .B tls_priority --- is a GnuTLS priority string, which can be used to select TLS versions, cipher suites etc. --- For example: --- --- .RB "- """ NORMAL:%NO_TICKETS """" --- will use defaults without TLS session resumption. --- --- .RB "- """ SECURE128:-VERS-ALL:+VERS-TLS1.3 """" --- will use only TLS 1.3 with 128-bit secure ciphers. --- --- Refer to: --- .I https://gnutls.org/manual/html_node/Priority-Strings.html -function DnsSim:tls(tls_priority) - if tls_priority ~= nil then - C.output_dnssim_tls_priority(self.obj, tls_priority) - end - C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_TLS) -end - --- Set the transport to HTTP/2 over TLS. --- --- .B http2_options --- is a lua table which supports the following keys: --- --- .B method: --- .B GET --- (default) --- or --- .B POST --- --- .B uri_path: --- where queries will be sent. --- Defaults to --- .B /dns-query --- --- .B zero_out_msgid: --- when --- .B true --- (default), query ID is always set to 0 --- --- See tls() method for --- .B tls_priority --- documentation. -function DnsSim:https2(http2_options, tls_priority) - if tls_priority ~= nil then - C.output_dnssim_tls_priority(self.obj, tls_priority) - end - - uri_path = "/dns-query" - zero_out_msgid = true - method = "GET" - - if http2_options ~= nil then - if type(http2_options) ~= "table" then - self.obj._log:fatal("http2_options must be a table") - else - if http2_options["uri_path"] ~= nil then - uri_path = http2_options["uri_path"] - end - if http2_options["zero_out_msgid"] ~= nil and http2_options["zero_out_msgid"] ~= true then - zero_out_msgid = false - end - if http2_options["method"] ~= nil then - method = http2_options["method"] - end - end - end - - C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_HTTPS2) - C.output_dnssim_h2_uri_path(self.obj, uri_path) - C.output_dnssim_h2_method(self.obj, method) - C.output_dnssim_h2_zero_out_msgid(self.obj, zero_out_msgid) -end - --- Set timeout for the individual requests in seconds (default 2s). --- --- .BR Beware : --- increasing this value while the target resolver isn't very responsive --- (cold cache, heavy load) may degrade DnsSim's performance and skew --- the results. -function DnsSim:timeout(seconds) - if seconds == nil then - seconds = 2 - end - timeout_ms = math.floor(seconds * 1000) - C.output_dnssim_timeout_ms(self.obj, timeout_ms) -end - --- Set TCP connection idle timeout for connection reuse according to RFC7766, --- Section 6.2.3 (defaults to 10s). --- When set to zero, connections are closed immediately after there are no --- more pending queries. -function DnsSim:idle_timeout(seconds) - if seconds == nil then - seconds = 10 - end - self.obj.idle_timeout_ms = math.floor(seconds * 1000) -end - --- Set TCP connection handshake timeout (defaults to 5s). --- During heavy load, the server may no longer accept new connections. --- This parameter ensures such connection attempts are aborted after the --- timeout expires. -function DnsSim:handshake_timeout(seconds) - if seconds == nil then - seconds = 5 - end - self.obj.handshake_timeout_ms = math.floor(seconds * 1000) -end - --- Run the libuv loop once without blocking when there is no I/O. --- This should be called repeatedly until 0 is returned and no more data --- is expected to be received by DnsSim. -function DnsSim:run_nowait() - return C.output_dnssim_run_nowait(self.obj) -end - --- Set this to true if DnsSim should free the memory of passed-in objects --- (useful when using --- .I dnsjit.filter.copy --- to pass objects from different thread). -function DnsSim:free_after_use(free_after_use) - self.obj.free_after_use = free_after_use -end - --- Number of input packets discarded due to various reasons. --- To investigate causes, run with increased logging level. -function DnsSim:discarded() - return tonumber(self.obj.discarded) -end - --- Number of valid requests (input packets) processed. -function DnsSim:requests() - return tonumber(self.obj.stats_sum.requests) -end - --- Number of requests that received an answer -function DnsSim:answers() - return tonumber(self.obj.stats_sum.answers) -end - --- Number of requests that received a NOERROR response -function DnsSim:noerror() - return tonumber(self.obj.stats_sum.rcode_noerror) -end - --- Configure statistics to be collected every N seconds. -function DnsSim:stats_collect(seconds) - if seconds == nil then - self.obj._log:fatal("number of seconds must be set for stats_collect()") - end - interval_ms = math.floor(seconds * 1000) - C.output_dnssim_stats_collect(self.obj, interval_ms) -end - --- Stop the collection of statistics. -function DnsSim:stats_finish() - C.output_dnssim_stats_finish(self.obj) -end - --- Export the results to a JSON file. -function DnsSim:export(filename) - local file = io.open(filename, "w") - if file == nil then - self.obj._log:fatal("export failed: no filename") - return - end - - local function write_stats(file, stats) - file:write( - "{ ", - '"since_ms":', tonumber(stats.since_ms), ',', - '"until_ms":', tonumber(stats.until_ms), ',', - '"requests":', tonumber(stats.requests), ',', - '"ongoing":', tonumber(stats.ongoing), ',', - '"answers":', tonumber(stats.answers), ',', - '"conn_active":', tonumber(stats.conn_active), ',', - '"conn_handshakes":', tonumber(stats.conn_handshakes), ',', - '"conn_resumed":', tonumber(stats.conn_resumed), ',', - '"conn_handshakes_failed":', tonumber(stats.conn_handshakes_failed), ',', - '"rcode_noerror":', tonumber(stats.rcode_noerror), ',', - '"rcode_formerr":', tonumber(stats.rcode_formerr), ',', - '"rcode_servfail":', tonumber(stats.rcode_servfail), ',', - '"rcode_nxdomain":', tonumber(stats.rcode_nxdomain), ',', - '"rcode_notimp":', tonumber(stats.rcode_notimp), ',', - '"rcode_refused":', tonumber(stats.rcode_refused), ',', - '"rcode_yxdomain":', tonumber(stats.rcode_yxdomain), ',', - '"rcode_yxrrset":', tonumber(stats.rcode_yxrrset), ',', - '"rcode_nxrrset":', tonumber(stats.rcode_nxrrset), ',', - '"rcode_notauth":', tonumber(stats.rcode_notauth), ',', - '"rcode_notzone":', tonumber(stats.rcode_notzone), ',', - '"rcode_badvers":', tonumber(stats.rcode_badvers), ',', - '"rcode_badkey":', tonumber(stats.rcode_badkey), ',', - '"rcode_badtime":', tonumber(stats.rcode_badtime), ',', - '"rcode_badmode":', tonumber(stats.rcode_badmode), ',', - '"rcode_badname":', tonumber(stats.rcode_badname), ',', - '"rcode_badalg":', tonumber(stats.rcode_badalg), ',', - '"rcode_badtrunc":', tonumber(stats.rcode_badtrunc), ',', - '"rcode_badcookie":', tonumber(stats.rcode_badcookie), ',', - '"rcode_other":', tonumber(stats.rcode_other), ',', - '"latency":[') - file:write(tonumber(stats.latency[0])) - for i=1,tonumber(self.obj.timeout_ms) do - file:write(',', tonumber(stats.latency[i])) - end - file:write("]}") - end - - file:write( - "{ ", - '"version":', _DNSSIM_JSON_VERSION, ',', - '"merged":false,', - '"stats_interval_ms":', tonumber(self.obj.stats_interval_ms), ',', - '"timeout_ms":', tonumber(self.obj.timeout_ms), ',', - '"idle_timeout_ms":', tonumber(self.obj.idle_timeout_ms), ',', - '"handshake_timeout_ms":', tonumber(self.obj.handshake_timeout_ms), ',', - '"discarded":', self:discarded(), ',', - '"stats_sum":') - write_stats(file, self.obj.stats_sum) - file:write( - ',', - '"stats_periodic":[') - - local stats = self.obj.stats_first - write_stats(file, stats) - - while (stats.next ~= nil) do - stats = stats.next - file:write(',') - write_stats(file, stats) - end - - file:write(']}') - file:close() - self.obj._log:notice("results exported to "..filename) -end - --- Return the C function and context for receiving objects. --- Only --- .I dnsjit.filter.core.object.ip --- or --- .I dnsjit.filter.core.object.ip6 --- objects are supported. --- The component expects a 32bit integer (in host order) ranging from 0 --- to max_clients written to first 4 bytes of destination IP. --- See --- .IR dnsjit.filter.ipsplit . -function DnsSim:receive() - local receive = C.output_dnssim_receiver() - return receive, self.obj -end - --- Deprecated: use udp() instead. --- --- Set the transport to UDP (without any TCP fallback). -function DnsSim:udp_only() - C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY) -end - --- dnsjit.filter.copy (3), --- dnsjit.filter.ipsplit (3), --- dnsjit.filter.core.object.ip (3), --- dnsjit.filter.core.object.ip6 (3), --- https://gitlab.nic.cz/knot/shotgun -return DnsSim |