diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index 569524566c..6df082a74b 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -726,6 +726,7 @@ export CDS export CHECKZONE export DESCRIPTION export DIG +export DNSTAPREAD export FEATURETEST export FSTRM_CAPTURE export GENCHECK diff --git a/bin/tests/system/dnstap/clean.sh b/bin/tests/system/dnstap/clean.sh index ff13e8d3e2..0a0081fd94 100644 --- a/bin/tests/system/dnstap/clean.sh +++ b/bin/tests/system/dnstap/clean.sh @@ -17,8 +17,7 @@ rm -f */named.run rm -f */named.run.prev rm -f */named.stats rm -f dig.out* -rm -f dnstap.out dnstap.hex -rm -f dnstap.out.save +rm -f dnstap.* rm -f fstrm_capture.out rm -f ns*/dnstap.out rm -f ns*/dnstap.out.save diff --git a/bin/tests/system/dnstap/tests_dnstap.py b/bin/tests/system/dnstap/tests_dnstap.py new file mode 100644 index 0000000000..87533f4214 --- /dev/null +++ b/bin/tests/system/dnstap/tests_dnstap.py @@ -0,0 +1,83 @@ +#!/usr/bin/python3 + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import os +import re +import subprocess + +import pytest + +pytest.importorskip("dns") +import dns.resolver + + +def run_rndc(server, rndc_command): + """ + Send the specified 'rndc_command' to 'server' with a timeout of 10 seconds + """ + rndc = os.getenv("RNDC") + port = os.getenv("CONTROLPORT") + + cmdline = [rndc, "-c", "../common/rndc.conf", "-p", port, "-s", server] + cmdline.extend(rndc_command) + + subprocess.check_output(cmdline, stderr=subprocess.STDOUT, timeout=10) + + +def test_dnstap_dispatch_socket_addresses(named_port): + # Prepare for querying ns3. + resolver = dns.resolver.Resolver() + resolver.nameservers = ["10.53.0.3"] + resolver.port = named_port + + # Send some query to ns3 so that it records something in its dnstap file. + ans = resolver.query("mail.example.", "A") + assert ans[0].address == "10.0.0.2" + + # Before continuing, roll dnstap file to ensure it is flushed to disk. + run_rndc("10.53.0.3", ["dnstap", "-roll", "1"]) + + # Move the dnstap file aside so that it is retained for troubleshooting. + os.rename(os.path.join("ns3", "dnstap.out.0"), "dnstap.out.resolver_addresses") + + # Read the contents of the dnstap file using dnstap-read. + output = subprocess.check_output( + [os.getenv("DNSTAPREAD"), "dnstap.out.resolver_addresses"], encoding="utf-8" + ) + + # Check whether all frames contain the expected addresses. + # + # Expected dnstap-read output format: + # + # 22-Jun-2022 12:09:06.168 RR 10.53.0.3:0 -> 10.53.0.1:7523 TCP ... + # 22-Jun-2022 12:09:06.168 RR 10.53.0.3:0 <- 10.53.0.1:7523 TCP ... + # 22-Jun-2022 12:09:06.168 RQ 10.53.0.3:56306 -> 10.53.0.2:7523 UDP ... + # 22-Jun-2022 12:09:06.168 RQ 10.53.0.3:56306 <- 10.53.0.2:7523 UDP ... + # + bad_frames = [] + inspected_frames = 0 + addr_regex = r"^10\.53\.0\.[0-9]+:[0-9]{1,5}$" + for line in output.splitlines(): + _, _, frame_type, addr1, _, addr2, _ = line.split(" ", 6) + # Only inspect RESOLVER_QUERY and RESOLVER_RESPONSE frames. + if frame_type not in ("RQ", "RR"): + continue + inspected_frames += 1 + if not re.match(addr_regex, addr1) or not re.match(addr_regex, addr2): + bad_frames.append(line) + + assert ( + len(bad_frames) == 0 + ), "{} out of {} inspected frames contain unexpected addresses:\n\n{}".format( + len(bad_frames), inspected_frames, "\n".join(bad_frames) + ) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 2b1f46b65f..82d6647680 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1041,6 +1041,14 @@ default is used. resolver query; }; + .. note:: In the default configuration, the dnstap output for + recursive resolver traffic does not include the IP addresses used + by server-side sockets. This is caused by the fact that unless the + :ref:`query source address ` is explicitly set, + these sockets are bound to wildcard IP addresses and determining + the specific IP address used by each of them requires issuing a + system call (i.e. incurring a performance penalty). + Logged ``dnstap`` messages can be parsed using the :iscman:`dnstap-read` utility (see :ref:`man_dnstap-read` for details).