From 0affed48f595f5328a418a093f22fae41de2a824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 6 Jul 2025 22:52:47 +0200 Subject: [PATCH 1/2] tests: extract and deduplicate run_netvm_cmd function --- qubes/tests/integ/network.py | 38 +++++++++++++++---------------- qubes/tests/integ/network_ipv6.py | 18 ++++----------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/qubes/tests/integ/network.py b/qubes/tests/integ/network.py index b7e22b4f9..6e7c57ec8 100644 --- a/qubes/tests/integ/network.py +++ b/qubes/tests/integ/network.py @@ -134,22 +134,22 @@ def tearDown(self): super(VmNetworkingMixin, self).tearDown() + def run_netvm_cmd(self, cmd): + try: + self.loop.run_until_complete( + self.testnetvm.run_for_stdio(cmd, user="root") + ) + except subprocess.CalledProcessError as e: + self.fail( + "Command '%s' failed: %s%s" + % (cmd, e.stdout.decode(), e.stderr.decode()) + ) + def configure_netvm(self): """ :type self: qubes.tests.SystemTestCase | VMNetworkingMixin """ - def run_netvm_cmd(cmd): - try: - self.loop.run_until_complete( - self.testnetvm.run_for_stdio(cmd, user="root") - ) - except subprocess.CalledProcessError as e: - self.fail( - "Command '%s' failed: %s%s" - % (cmd, e.stdout.decode(), e.stderr.decode()) - ) - if not self.testnetvm.is_running(): self.loop.run_until_complete(self.testnetvm.start()) # Ensure that dnsmasq is installed: @@ -160,28 +160,28 @@ def run_netvm_cmd(cmd): except subprocess.CalledProcessError: self.skipTest("dnsmasq not installed") - run_netvm_cmd("ip link add test0 type dummy") - run_netvm_cmd("ip link set test0 up") - run_netvm_cmd("ip addr add {}/24 dev test0".format(self.test_ip)) - run_netvm_cmd( + self.run_netvm_cmd("ip link add test0 type dummy") + self.run_netvm_cmd("ip link set test0 up") + self.run_netvm_cmd("ip addr add {}/24 dev test0".format(self.test_ip)) + self.run_netvm_cmd( "nft add ip qubes custom-input ip daddr {} accept".format( self.test_ip ) ) # ignore failure self.run_cmd(self.testnetvm, "while pkill dnsmasq; do sleep 1; done") - run_netvm_cmd( + self.run_netvm_cmd( "dnsmasq -a {ip} -A /{name}/{ip} -i test0 -z".format( ip=self.test_ip, name=self.test_name ) ) - run_netvm_cmd( + self.run_netvm_cmd( "rm -f /etc/resolv.conf && echo nameserver {} > /etc/resolv.conf".format( self.test_ip ) ) - run_netvm_cmd("systemctl try-restart systemd-resolved || :") - run_netvm_cmd("/usr/lib/qubes/qubes-setup-dnat-to-ns") + self.run_netvm_cmd("systemctl try-restart systemd-resolved || :") + self.run_netvm_cmd("/usr/lib/qubes/qubes-setup-dnat-to-ns") def test_000_simple_networking(self): """ diff --git a/qubes/tests/integ/network_ipv6.py b/qubes/tests/integ/network_ipv6.py index 9ce462595..0e137c3f6 100644 --- a/qubes/tests/integ/network_ipv6.py +++ b/qubes/tests/integ/network_ipv6.py @@ -69,26 +69,16 @@ def configure_netvm(self): self.testnetvm.features["ipv6"] = True super(VmIPv6NetworkingMixin, self).configure_netvm() - def run_netvm_cmd(cmd): - try: - self.loop.run_until_complete( - self.testnetvm.run_for_stdio(cmd, user="root") - ) - except subprocess.CalledProcessError as e: - self.fail( - "Command '%s' failed: %s%s" - % (cmd, e.stdout.decode(), e.stderr.decode()) - ) - - run_netvm_cmd("ip addr add {}/128 dev test0".format(self.test_ip6)) - run_netvm_cmd( + self.run_netvm_cmd("ip addr add {}/128 dev test0".format( + self.test_ip6)) + self.run_netvm_cmd( "nft add ip6 qubes custom-input ip6 daddr {} accept".format( self.test_ip6 ) ) # ignore failure self.run_cmd(self.testnetvm, "while pkill dnsmasq; do sleep 1; done") - run_netvm_cmd( + self.run_netvm_cmd( "dnsmasq -a {ip} -A /{name}/{ip} -A /{name}/{ip6} -i test0 -z".format( ip=self.test_ip, ip6=self.test_ip6, name=self.test_name ) From d510b59adc6e716aa000a3df9f9ce2d1d8930f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 7 Jul 2025 02:03:37 +0200 Subject: [PATCH 2/2] tests: DNS over IPv6 Test if IPv6 DNS works, both if it's the only DNS and in dual-stack setup. QubesOS/qubes-issues#10038 --- qubes/tests/integ/network_ipv6.py | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/qubes/tests/integ/network_ipv6.py b/qubes/tests/integ/network_ipv6.py index 0e137c3f6..4ba885736 100644 --- a/qubes/tests/integ/network_ipv6.py +++ b/qubes/tests/integ/network_ipv6.py @@ -84,6 +84,22 @@ def configure_netvm(self): ) ) + def setup_ipv6_dns(self, ipv6_only=False): + """ + Enable IPv6 DNS, needs configure_netvm to be called already + :return: + """ + self.run_netvm_cmd( + "echo nameserver {} {} /etc/resolv.conf".format( + self.test_ip6, + ">" if ipv6_only else ">>" + ) + ) + + self.run_netvm_cmd("systemctl try-restart systemd-resolved || :") + self.run_netvm_cmd("/usr/lib/qubes/qubes-setup-dnat-to-ns; " + "[ $? -eq 0 -o $? -eq 100 ]") + def test_500_ipv6_simple_networking(self): """ :type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin @@ -513,6 +529,30 @@ def test_550_ipv6_spoof_ip(self): packets = output[line].lstrip().split()[index] self.assertEqual(packets, "0", "Some packet hit the INPUT rule") + def test_560_ipv6_dns(self): + """DNS over IPv6/IPv4 + + :return: + """ + self.setup_ipv6_dns() + self.loop.run_until_complete(self.start_vm(self.testvm1)) + self.assertEqual(self.run_cmd(self.testvm1, self.ping6_ip), 0) + self.assertEqual(self.run_cmd(self.testvm1, self.ping6_name), 0) + self.assertEqual(self.run_cmd(self.testvm1, self.ping_ip), 0) + self.assertEqual(self.run_cmd(self.testvm1, self.ping_name), 0) + + def test_560_ipv6_dns_only(self): + """DNS over IPv6 + + :return: + """ + self.setup_ipv6_dns(ipv6_only=True) + self.loop.run_until_complete(self.start_vm(self.testvm1)) + self.assertEqual(self.run_cmd(self.testvm1, self.ping6_ip), 0) + self.assertEqual(self.run_cmd(self.testvm1, self.ping6_name), 0) + self.assertEqual(self.run_cmd(self.testvm1, self.ping_ip), 0) + self.assertEqual(self.run_cmd(self.testvm1, self.ping_name), 0) + def test_710_ipv6_custom_ip_simple(self): """Custom AppVM IP