mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-27 12:13:20 -04:00
Add SRTT-based server selection system test
Verify that the resolver selects authoritative servers in increasing SRTT order. Four servers are configured with increasing response delays. 100 queries are sent, expecting most to go to the fastest server (ns2). Then ns2 stops responding, another 100 queries are sent and should go to ns3 (the next fastest), and so on through ns4 and ns5. Each query uses a unique name to avoid cache hits. (cherry picked from commit a8d11e14f5b4e4d53219ba751d1b741162b0b84b)
This commit is contained in:
parent
4340b3537d
commit
2be8bdb3f4
11 changed files with 417 additions and 0 deletions
18
bin/tests/system/srtt/README
Normal file
18
bin/tests/system/srtt/README
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
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.
|
||||
|
||||
ns1 is root
|
||||
|
||||
ans{2-5} simulates four NS servers making authority on the same domain
|
||||
`example.`. ans2 is the quickest to answer, followed by ans3, then ans4, with
|
||||
ans5 being the slowest.
|
||||
|
||||
ns6 is a resolver
|
||||
36
bin/tests/system/srtt/ans2/ans.py
Normal file
36
bin/tests/system/srtt/ans2/ans.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
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 dns.rcode
|
||||
|
||||
from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries
|
||||
|
||||
from ..srtt_ans import DelayedQnameRangeHandler
|
||||
|
||||
|
||||
class Foo1ToFoo99Handler(DelayedQnameRangeHandler):
|
||||
max_qname = 99
|
||||
delay = 0.0
|
||||
|
||||
|
||||
def main() -> None:
|
||||
server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR)
|
||||
server.install_response_handlers(
|
||||
Foo1ToFoo99Handler(),
|
||||
IgnoreAllQueries(),
|
||||
)
|
||||
server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
36
bin/tests/system/srtt/ans3/ans.py
Normal file
36
bin/tests/system/srtt/ans3/ans.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
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 dns.rcode
|
||||
|
||||
from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries
|
||||
|
||||
from ..srtt_ans import DelayedQnameRangeHandler
|
||||
|
||||
|
||||
class Foo1ToFoo199Handler(DelayedQnameRangeHandler):
|
||||
max_qname = 199
|
||||
delay = 0.03
|
||||
|
||||
|
||||
def main() -> None:
|
||||
server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR)
|
||||
server.install_response_handlers(
|
||||
Foo1ToFoo199Handler(),
|
||||
IgnoreAllQueries(),
|
||||
)
|
||||
server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
36
bin/tests/system/srtt/ans4/ans.py
Normal file
36
bin/tests/system/srtt/ans4/ans.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
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 dns.rcode
|
||||
|
||||
from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries
|
||||
|
||||
from ..srtt_ans import DelayedQnameRangeHandler
|
||||
|
||||
|
||||
class Foo1ToFoo299Handler(DelayedQnameRangeHandler):
|
||||
max_qname = 299
|
||||
delay = 0.08
|
||||
|
||||
|
||||
def main() -> None:
|
||||
server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR)
|
||||
server.install_response_handlers(
|
||||
Foo1ToFoo299Handler(),
|
||||
IgnoreAllQueries(),
|
||||
)
|
||||
server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
36
bin/tests/system/srtt/ans5/ans.py
Normal file
36
bin/tests/system/srtt/ans5/ans.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
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 dns.rcode
|
||||
|
||||
from isctest.asyncserver import AsyncDnsServer, IgnoreAllQueries
|
||||
|
||||
from ..srtt_ans import DelayedQnameRangeHandler
|
||||
|
||||
|
||||
class Foo1ToFoo399Handler(DelayedQnameRangeHandler):
|
||||
max_qname = 399
|
||||
delay = 0.15
|
||||
|
||||
|
||||
def main() -> None:
|
||||
server = AsyncDnsServer(default_aa=True, default_rcode=dns.rcode.NOERROR)
|
||||
server.install_response_handlers(
|
||||
Foo1ToFoo399Handler(),
|
||||
IgnoreAllQueries(),
|
||||
)
|
||||
server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
29
bin/tests/system/srtt/ns1/named.conf.j2
Normal file
29
bin/tests/system/srtt/ns1/named.conf.j2
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
options {
|
||||
query-source address 10.53.0.1;
|
||||
notify-source 10.53.0.1;
|
||||
transfer-source 10.53.0.1;
|
||||
port @PORT@;
|
||||
pid-file "named.pid";
|
||||
listen-on { 10.53.0.1; };
|
||||
listen-on-v6 { none; };
|
||||
recursion no;
|
||||
notify yes;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type primary;
|
||||
file "root.db";
|
||||
};
|
||||
36
bin/tests/system/srtt/ns1/root.db
Normal file
36
bin/tests/system/srtt/ns1/root.db
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
; 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.
|
||||
|
||||
$TTL 300
|
||||
. IN SOA owner.root-servers.nil. a.root-servers.nil. (
|
||||
2000042100 ; serial
|
||||
600 ; refresh
|
||||
600 ; retry
|
||||
1200 ; expire
|
||||
600 ; minimum
|
||||
)
|
||||
. NS a.root-servers.nil.
|
||||
a.root-servers.nil. A 10.53.0.1
|
||||
|
||||
; The idea is that the resolver would do 2 ADB lookups, so there would be 2
|
||||
; find list, both with 2 IPs in it. ns1 (which is actually ans2 and ans5) would
|
||||
; have both the slowest and fastest addresses. ns2 (which is actually ans3 and
|
||||
; ans4) would have two addresses in the middle.
|
||||
|
||||
example. NS ns1.example.
|
||||
example. NS ns1.example.
|
||||
example. NS ns2.example.
|
||||
example. NS ns2.example.
|
||||
|
||||
ns1.example. A 10.53.0.2 ; delay is 0
|
||||
ns1.example. A 10.53.0.5 ; delay is 0.15
|
||||
ns2.example. A 10.53.0.4 ; delay is 0.08
|
||||
ns2.example. A 10.53.0.3 ; delay is 0.03
|
||||
1
bin/tests/system/srtt/ns6/named.args
Normal file
1
bin/tests/system/srtt/ns6/named.args
Normal file
|
|
@ -0,0 +1 @@
|
|||
-D srtt-ns6 -m record -c named.conf -d 99 -g -T maxcachesize=2097152 -4
|
||||
41
bin/tests/system/srtt/ns6/named.conf.j2
Normal file
41
bin/tests/system/srtt/ns6/named.conf.j2
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
options {
|
||||
query-source address 10.53.0.6;
|
||||
notify-source 10.53.0.6;
|
||||
transfer-source 10.53.0.6;
|
||||
port @PORT@;
|
||||
pid-file "named.pid";
|
||||
listen-on { 10.53.0.6; };
|
||||
listen-on-v6 { none; };
|
||||
recursion yes;
|
||||
dnssec-validation no;
|
||||
dnstap { resolver query; };
|
||||
dnstap-output file "dnstap.out";
|
||||
};
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm @DEFAULT_HMAC@;
|
||||
};
|
||||
|
||||
controls {
|
||||
inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "../../_common/root.hint";
|
||||
};
|
||||
59
bin/tests/system/srtt/srtt_ans.py
Normal file
59
bin/tests/system/srtt/srtt_ans.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
import abc
|
||||
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
|
||||
from isctest.asyncserver import DnsResponseSend, QnameQtypeHandler, QueryContext
|
||||
|
||||
|
||||
class DelayedQnameRangeHandler(QnameQtypeHandler):
|
||||
"""
|
||||
Respond to queries for QNAMEs "foo1.example." through "foo<N>.example."
|
||||
with QTYPE=A, where <N> must be defined by the subclass. Every response is
|
||||
delayed by a fixed amount of time, which must also be defined (in seconds)
|
||||
by the subclass.
|
||||
"""
|
||||
|
||||
@property
|
||||
def qnames(self) -> list[str]:
|
||||
return [f"foo{x}.example." for x in range(1, self.max_qname + 1)]
|
||||
|
||||
qtypes = [dns.rdatatype.A]
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def max_qname(self) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def delay(self) -> float:
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.__class__.__name__}(foo[1-{self.max_qname}].example/A)"
|
||||
|
||||
async def get_responses(
|
||||
self, qctx: QueryContext
|
||||
) -> AsyncGenerator[DnsResponseSend, None]:
|
||||
a_rrset = dns.rrset.from_text(
|
||||
qctx.qname, 300, dns.rdataclass.IN, dns.rdatatype.A, "10.53.9.9"
|
||||
)
|
||||
qctx.response.answer.append(a_rrset)
|
||||
yield DnsResponseSend(qctx.response, delay=self.delay)
|
||||
89
bin/tests/system/srtt/tests_srtt.py
Normal file
89
bin/tests/system/srtt/tests_srtt.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# 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 isctest
|
||||
import isctest.mark
|
||||
|
||||
pytestmark = [isctest.mark.with_dnstap]
|
||||
|
||||
|
||||
def line_to_dst_ips(line):
|
||||
# dnstap-read output line example
|
||||
# 05-Feb-2026 11:00:57.853 RQ 10.53.0.6:38507 -> 10.53.0.3:22047 TCP 56b fooXXX.example./IN/NS
|
||||
_, _, _, _, _, dst, _, _, _ = line.split(" ", 9)
|
||||
ip, _ = dst.split(":", 1)
|
||||
return ip
|
||||
|
||||
|
||||
def extract_dnstap(ns):
|
||||
ns.rndc("dnstap -roll 1")
|
||||
path = os.path.join(ns.identifier, "dnstap.out.0")
|
||||
dnstapread = isctest.run.cmd(
|
||||
[isctest.vars.ALL["DNSTAPREAD"], path],
|
||||
)
|
||||
|
||||
lines = dnstapread.out.splitlines()
|
||||
return map(line_to_dst_ips, lines)
|
||||
|
||||
|
||||
def assert_used_auth(ns, authip):
|
||||
ips = extract_dnstap(ns)
|
||||
queries = 0
|
||||
matches = 0
|
||||
for ip in ips:
|
||||
queries += 1
|
||||
if ip == authip:
|
||||
matches += 1
|
||||
assert matches > 85
|
||||
assert queries <= 115
|
||||
|
||||
|
||||
def test_srtt(ns6):
|
||||
for i in range(1, 100):
|
||||
msg = isctest.query.create(f"foo{i}.example.", "A")
|
||||
res = isctest.query.udp(msg, ns6.ip)
|
||||
isctest.check.noerror(res)
|
||||
assert len(res.answer[0]) == 1
|
||||
res.answer[0].ttl = 300
|
||||
assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9"
|
||||
|
||||
assert_used_auth(ns6, "10.53.0.2")
|
||||
|
||||
for i in range(100, 200):
|
||||
msg = isctest.query.create(f"foo{i}.example.", "A")
|
||||
res = isctest.query.udp(msg, ns6.ip)
|
||||
isctest.check.noerror(res)
|
||||
assert len(res.answer[0]) == 1
|
||||
res.answer[0].ttl = 300
|
||||
assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9"
|
||||
|
||||
assert_used_auth(ns6, "10.53.0.3")
|
||||
|
||||
for i in range(200, 300):
|
||||
msg = isctest.query.create(f"foo{i}.example.", "A")
|
||||
res = isctest.query.udp(msg, ns6.ip)
|
||||
isctest.check.noerror(res)
|
||||
assert len(res.answer[0]) == 1
|
||||
res.answer[0].ttl = 300
|
||||
assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9"
|
||||
|
||||
assert_used_auth(ns6, "10.53.0.4")
|
||||
|
||||
for i in range(300, 400):
|
||||
msg = isctest.query.create(f"foo{i}.example.", "A")
|
||||
res = isctest.query.udp(msg, ns6.ip)
|
||||
isctest.check.noerror(res)
|
||||
assert len(res.answer[0]) == 1
|
||||
res.answer[0].ttl = 300
|
||||
assert str(res.answer[0]) == f"foo{i}.example. 300 IN A 10.53.9.9"
|
||||
assert_used_auth(ns6, "10.53.0.5")
|
||||
Loading…
Reference in a new issue