...
 
Commits (12)
......@@ -9,10 +9,10 @@ _Some findings made using DNS Maze were presented at [DNS OARC 31](https://indic
### Requirements
Needed:
* `python3` (used to generated _everything_, at least version 3.6)
* `python3` (used to generate _everything_, at least version 3.6)
* `pyyaml` (YAML parser for Python)
* `python-jinja` (templating)
* Knot DNS (authoritative server of _our_ choice, version 2.9.0 was used while developing this)
* Knot DNS (authoritative server of _our_ choice, version 2.9.0 was used in development)
* _recent_ version of `systemd` (used as process manager)
* Wireshark (`tshark` is used for capturing the traffic)
* `dnsperf` (version 2.3.2 was used during development)
......@@ -21,10 +21,11 @@ Resolvers supported _out-of-the-box_:
_See [this](#resolver-resolver-directory)_
PCAP analysis:
Result analysis:
* `matplotlib`
* `numpy`
* `dpkt`
* `pytest`
Optional (but used in some scenarios)
* `libfaketime` (used for faking time for the resolver -- useful in DNSSEC scenarios)
......@@ -35,7 +36,7 @@ Install all requirements, clone this repository, and try to [run it](#running-dn
## Running DNS Maze
### First run
### Manual run
First, we need to copy the environment to a temporary folder and generate all the services:
......@@ -56,7 +57,7 @@ _Note: Root privileges are necessary since `systemd` does not support using netw
If everything went according to the plan, one could use `systemd status "maze-*"` to see the status of all services generated and run by DNS Maze.
After a while (depending on the complexity of the scenario) a PCAP file `result_kresd.pcap`
After a while (depending on the complexity of the scenario) a PCAP file stored in `pcaps/result_kresd.pcap`
### `process_generator.py` command line options
......@@ -98,7 +99,7 @@ Path of `faketime` differs on various distribution, so if it is present in the s
Scenario is defined by a directory (default path for these is `maze/scenarios/`) and contains a declarative definition of all processes needed.
There are four subdirectories with different purposes to describe the whole environment to be simulated: **auth**, **resolver**, **client**.
There are three subdirectories with different purposes to describe the whole environment to be simulated: **auth**, **resolver**, **client**.
In the following example, I'll use the `simple` scenario found on [here](./scenarios/selected/simple), so it might be beneficial to look at its corresponding directory in order to better understand what is going on.
......
......@@ -12,6 +12,10 @@ sleep 5
PICKLED=$(./pcap_analyzer.py --pcap $PCAP --scenario $SCENARIO)
python3 -m pytest stats_comparator.py -k "sanity" -v -rs --under-test $PICKLED
for ref in $(ls $SCENARIO/references/*.pickle)
do
python3 -m pytest stats_comparator.py -k "reference_is_newer_than_scenario" -v -rs --under-test $PICKLED --reference $ref
done
if [ $? -ne 0 ]; then
echo "Sanity check failed, something is seriously broken."
......@@ -29,6 +33,8 @@ do
python3 -m pytest stats_comparator.py -k "not sanity" -v -rs --junit-xml $SCENARIO/reports/$NAME.xml --under-test $PICKLED --reference $ref --hist-intervals $HIST_INTERVALS --sw-intervals $SW_INTERVALS
done
./graph_plotter.py $GRAPH_ARGS --scenario $SCENARIO
cp $PICKLED $SCENARIO/references/result_previous_stats.pickle
./graph_plotter.py $GRAPH_ARGS --scenario $SCENARIO
# systemctl status maze-resolver
{% if valgrind == True %}
jit.off()
{% endif %}
net = { {%- for ip in address -%} "{{ip}}", {% endfor %} }
net.ipv6 = {{ "false" if ipv6 == false else "true"}}
......@@ -24,5 +28,15 @@ modules = {
hints.root_file("{{rundir}}/{{roothints}}")
{# policy.add(policy.all(policy.DEBUG_ALWAYS))
socket = require "socket"
start = socket.gettime()*1000
function a()
print("Milliseconds: " .. socket.gettime()*1000 - start)
end
event.recurrent(100, a) #}
cache.size = 10*MB
cache.min_ttl(1)
\ No newline at end of file
......@@ -22,4 +22,5 @@ lua-config-file={{(rundir+"/"+tafile)|pdns_ta("config.lua")}}
{% endif %}
threads=1
security-poll-suffix=
qname-minimization={{"no" if qmin == False else "yes"}}
# uncomment when version 4.3 is available
# qname-minimization={{"no" if qmin == False else "yes"}}
stub-addr: 127.0.0.10
stub-addr: 1.0.0.100
CONFIG_END
SCENARIO_BEGIN empty replies
RANGE_BEGIN 0 100
ADDRESS 127.0.0.30
ADDRESS 1.0.0.100
ENTRY_BEGIN
MATCH opcode qtype subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
. IN NS
SECTION ANSWER
. 518400 IN NS j.root-servers.net
SECTION ADDITIONAL
j.root-servers.net 518400 IN A 1.0.0.100
ENTRY_END
ENTRY_BEGIN
MATCH subdomain
ADJUST copy_id copy_query
......
#!/usr/bin/python3
import argparse
from collections import defaultdict
import datetime
import errno
from ipaddress import ip_address as ip
import math
import os.path
import pickle
import statistics
import struct
from typing import Dict, List
import dns.message
from dns.exception import DNSException
import dpkt
import process_generator
......@@ -43,12 +47,22 @@ class PCAPAnalyzer:
self.sliding_window_rtt()
def process_packet(self, ts, packet):
# Unpack Linux cooked capture
ip_packet = dpkt.sll.SLL(packet).data
for member_name in dir(self):
# all methods starting with "_packet" are applied on each packet from PCAP
if member_name.startswith("_packet"):
# Unpack Linux cooked capture
l2_packet = dpkt.sll.SLL(packet)
getattr(self, member_name)(ts, l2_packet.data)
getattr(self, member_name)(ts, ip_packet)
try:
# _dns_packet methods are applied only to DNS packets
dns_packet = dns.message.from_wire(ip_packet.data.data)
if member_name.startswith("_dns_packet"):
getattr(self, member_name)(ts, ip_packet, dns_packet)
except (DNSException, ValueError):
pass
def _packet_count_per_auth_ip(self, ts, ip_packet):
ip_src = ip(ip_packet.src)
......@@ -68,14 +82,13 @@ class PCAPAnalyzer:
self.stats["server_dst"][auth["name"]] += 1
self.stats["auth_config"][auth["name"]] = auth
def _packet_dns_answer_rcode_count(self, ts, ip_packet):
def _dns_packet_dns_answer_rcode_count(self, ts, ip_packet, dns_packet: dns.message.Message):
ip_src = ip(ip_packet.src)
ip_dst = ip(ip_packet.dst)
if ip_src in self.resolver_ips and ip_dst == self.client_ip:
dns_packet = dpkt.dns.DNS(ip_packet.data.data)
self.stats["rcode"][dns_packet.rcode] += 1
self.stats["rcode"][dns_packet.rcode()] += 1
def _packet_dns_query_count(self, ts, ip_packet):
def _packet_query_count(self, ts, ip_packet):
ip_src = ip(ip_packet.src)
ip_dst = ip(ip_packet.dst)
if ip_src in self.resolver_ips:
......@@ -90,23 +103,20 @@ class PCAPAnalyzer:
self.stats["answers"]["from_auths"] += 1
def _packet_get_rtts(self, ts, ip_packet):
def _dns_packet_get_rtts(self, ts, ip_packet, dns_packet: dns.message.Message):
# FIXME: DNS retransmits
ip_src = ip(ip_packet.src)
ip_dst = ip(ip_packet.dst)
if ip_src == self.client_ip:
dns_packet = dpkt.dns.DNS(ip_packet.data.data)
self.stats["rtts"][getattr(dns_packet, "id")] = -ts
if ip_src in self.resolver_ips and ip_dst == self.client_ip:
dns_packet = dpkt.dns.DNS(ip_packet.data.data)
self.stats["rtts"][getattr(dns_packet, "id")] += ts
def _packet_count_queries_per_qname(self, ts, ip_packet):
def _dns_packet_count_queries_per_qname(self, ts, ip_packet, dns_packet: dns.message.Message):
ip_src = ip(ip_packet.src)
ip_dst = ip(ip_packet.dst)
if ip_src in self.resolver_ips and ip_dst != self.client_ip:
if not isinstance(ip_packet.data.data, dpkt.icmp.ICMP.Unreach):
dns_packet = dpkt.dns.DNS(ip_packet.data.data)
self.stats["queries_by_qname"][dns_packet.qd[0].name.lower()] += 1
self.stats["queries_by_qname"][str(dns_packet.question[0].name).lower()] += 1
def sliding_window_rtt(self, window = 100):
......@@ -164,6 +174,8 @@ if __name__ == "__main__":
filename = os.path.basename(os.path.splitext(args.pcap)[0]) + "_stats.pickle"
output = os.path.join(dir_path, filename)
p.stats["created"] = datetime.datetime.now()
p.stats["scenario"] = os.path.realpath(args.scenario)
with open(output, "wb") as f:
pickle.dump(default_to_regular(p.stats), f)
......
#!/bin/sh
set -x
for res in named recursor unbound kresd
do
sed -i "s/config:.*/config: $res.conf/g" $1/resolver/config.yaml
sed -i "s/service:.*/service: $res/g" $1/resolver/config.yaml
EXEC=$(./process_generator.py $1)
cat $1/resolver/config.yaml
PCAP=$($EXEC)
sleep 5
./pcap_analyzer.py --pcap $PCAP --scenario $1 --reference
done
mv $1/references/result_kresd_stats.pickle $1/references/result_kresd-5-0-0_stats.pickle
\ No newline at end of file
#!/bin/sh
set -x
chmod -R o+xw $1
cd $1/services
rm -rf /etc/systemd/system/$2-*
......
......@@ -8,4 +8,5 @@ python-jinja
scipy
systemd
wireshark (tshark)
dpkt
\ No newline at end of file
dpkt
pytest
\ No newline at end of file
name: "resolver"
service: "kresd"
service: kresd
address: ["3.0.0.1"]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
tafile: "root.keys"
dnssec: yes
name: "resolver"
service: "kresd"
service: kresd
address: ["1.0.0.20", "1::20"]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
name: "resolver"
service: "kresd"
service: kresd
address: ["1.0.0.20", "1::20"]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
name: "resolver"
service: "kresd"
service: kresd
address: ["1.0.0.1"]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
\ No newline at end of file
name: "resolver"
service: "kresd"
service: kresd
address: ["1.0.0.20", "1::20"]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
name: "echo-server"
service: "knot"
config: "knotd.conf"
address: ["1.0.0.100"]
netem: delay 100ms
zonefile: "../root.zone"
domain: "."
\ No newline at end of file
. 86400 IN SOA j.root-servers.net. nstld.verisign-grs.com. 2019072500 1800 900 604800 86400
*. 3600 IN A 1.1.1.1
. 86400 IN NS j.root-servers.net.
j.root-servers.net. 86400 IN A 1.0.0.100
\ No newline at end of file
name: "dnsperf"
service: "dnsperf"
address: ["2.0.0.1"]
dnsperf: -d q -Q 100
\ No newline at end of file
dnsperf: -d q
\ No newline at end of file
This diff is collapsed.
name: "resolver"
service: kresd
address: ["1.0.0.1"]
config: kresd.conf
roothints: root.hints
# verbose: yes
. 3600000 NS J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET. 3600000 A 1.0.0.100
......@@ -9,4 +9,4 @@ b.root-servers.net. 518400 IN A 1.0.0.2
c.root-servers.net. 518400 IN A 1.0.0.3
d.root-servers.net. 518400 IN A 1.0.0.4
e.root-servers.net. 518400 IN A 1.0.0.5
*.aaa 3600 IN A 1.1.1.1
\ No newline at end of file
*. 3600 IN A 1.1.1.1
\ No newline at end of file
name: "resolver"
service: "kresd"
service: kresd
address: ["1.0.0.20", "1::20"]
config: "kresd.conf"
roothints: "root.hints"
trespath: /home/sbalazik/cznic/hello-dns/tdns
\ No newline at end of file
config: kresd.conf
roothints: "root.hints"
\ No newline at end of file
......@@ -38,5 +38,4 @@ l.root-server.net. 3600000 IN AAAA 2001:500:9f::42
. 3600000 IN NS m.root-server.net.
m.root-server.net. 3600000 IN A 202.12.27.33
m.root-server.net. 3600000 IN AAAA 2001:dc3::35
*.aaa. 1 IN A 1.1.1.1
aaa. 1 IN A 1.1.1.1
*. 1 IN A 1.1.1.1
name: "resolver"
service: "kresd"
service: kresd
address: [1.0.0.1, 1::1]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
......@@ -38,5 +38,5 @@ l.root-server.net. 3600000 IN AAAA 2001:500:9f::42
. 3600000 IN NS m.root-server.net.
m.root-server.net. 3600000 IN A 202.12.27.33
m.root-server.net. 3600000 IN AAAA 2001:dc3::35
*.aaa. 1 IN A 1.1.1.1
aaa. 1 IN A 1.1.1.1
*. 1 IN A 1.1.1.1
name: "resolver"
service: "kresd"
service: kresd
address: [1.0.0.1, 1::1]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
......@@ -38,5 +38,5 @@ l.root-server.net. 3600000 IN AAAA 2001:500:9f::42
. 3600000 IN NS m.root-server.net.
m.root-server.net. 3600000 IN A 202.12.27.33
m.root-server.net. 3600000 IN AAAA 2001:dc3::35
*.aaa. 1 IN A 1.1.1.1
aaa. 1 IN A 1.1.1.1
*. 1 IN A 1.1.1.1
name: "resolver"
service: "kresd"
service: kresd
address: [1.0.0.1, 1::1]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
......@@ -38,5 +38,4 @@ l.root-server.net. 3600000 IN AAAA 2001:500:9f::42
. 3600000 IN NS m.root-server.net.
m.root-server.net. 3600000 IN A 202.12.27.33
m.root-server.net. 3600000 IN AAAA 2001:dc3::35
*.aaa. 1 IN A 1.1.1.1
aaa. 1 IN A 1.1.1.1
*. 1 IN A 1.1.1.1
name: "resolver"
service: "kresd"
service: kresd
address: [1.0.0.1, 1::1]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
......@@ -38,5 +38,5 @@ l.root-server.net. 3600000 IN AAAA 2001:500:9f::42
. 3600000 IN NS m.root-server.net.
m.root-server.net. 3600000 IN A 202.12.27.33
m.root-server.net. 3600000 IN AAAA 2001:dc3::35
*.aaa. 1 IN A 1.1.1.1
aaa. 1 IN A 1.1.1.1
*. 1 IN A 1.1.1.1
name: "resolver"
service: "kresd"
service: kresd
address: [1.0.0.1, 1::1]
config: "kresd.conf"
config: kresd.conf
roothints: "root.hints"
......@@ -38,5 +38,4 @@ l.root-server.net. 3600000 IN AAAA 2001:500:9f::42
. 3600000 IN NS m.root-server.net.
m.root-server.net. 3600000 IN A 202.12.27.33
m.root-server.net. 3600000 IN AAAA 2001:dc3::35
*.aaa. 1 IN A 1.1.1.1
aaa. 1 IN A 1.1.1.1
*. 1 IN A 1.1.1.1
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.