From 584b7561c162ec461e5c6a8dff5c012402cac5e0 Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Mon, 21 Aug 2017 09:13:33 +0000 Subject: [PATCH] Allow to disable DNS server announcement per subnet Currently there is no way to have DHCP agents not announce DNS servers for a subnet. The current behaviour when the dns_nameservers option is set to '0.0.0.0' is that each agent will only announce itself instead of announcing the list of all dhcp agents for that subnet, which seems not too useful. So we redefine the meaning of this option to instruct the DHCP agent to not announce any DNS server in that case. Actually, going back to square one, it would be more natural to swap the meaning of "option unset" and "option 0.0.0.0", but that would change the default behaviour for all existing installation and thus does not seem feasible. Change-Id: I32d943360162c483ac1364100a21ab56b13517fb Closes-Bug: 1311040 --- neutron/agent/linux/dhcp.py | 22 +++++-- neutron/tests/unit/agent/linux/test_dhcp.py | 61 +++++++++++++++++++ ...-1311040-dhcp-no-dns-09291c23e2ce800a.yaml | 16 +++++ 3 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/bug-1311040-dhcp-no-dns-09291c23e2ce800a.yaml diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 0229a8eb6ff..a0b81fce45d 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -882,12 +882,22 @@ class Dnsmasq(DhcpLocalProcess): addr_mode == constants.IPV6_SLAAC)): continue if subnet.dns_nameservers: - options.append( - self._format_option( - subnet.ip_version, i, 'dns-server', - ','.join( - Dnsmasq._convert_to_literal_addrs( - subnet.ip_version, subnet.dns_nameservers)))) + if ((subnet.ip_version == 4 and + subnet.dns_nameservers == ['0.0.0.0']) or + (subnet.ip_version == 6 and + subnet.dns_nameservers == ['::'])): + # Special case: Do not announce DNS servers + options.append( + self._format_option( + subnet.ip_version, i, 'dns-server')) + else: + options.append( + self._format_option( + subnet.ip_version, i, 'dns-server', + ','.join( + Dnsmasq._convert_to_literal_addrs( + subnet.ip_version, + subnet.dns_nameservers)))) else: # use the dnsmasq ip as nameservers only if there is no # dns-server submitted by the server diff --git a/neutron/tests/unit/agent/linux/test_dhcp.py b/neutron/tests/unit/agent/linux/test_dhcp.py index c9cb450f404..ac63ebbfeae 100644 --- a/neutron/tests/unit/agent/linux/test_dhcp.py +++ b/neutron/tests/unit/agent/linux/test_dhcp.py @@ -442,6 +442,13 @@ class FakeV4SubnetAgentWithManyDnsProvided(FakeV4Subnet): self.host_routes = [] +class FakeV4SubnetAgentWithNoDnsProvided(FakeV4Subnet): + def __init__(self): + super(FakeV4SubnetAgentWithNoDnsProvided, self).__init__() + self.dns_nameservers = ['0.0.0.0'] + self.host_routes = [] + + class FakeV4MultipleAgentsWithoutDnsProvided(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' @@ -469,6 +476,15 @@ class FakeV4AgentWithManyDnsProvided(object): self.namespace = 'qdhcp-ns' +class FakeV4AgentWithNoDnsProvided(object): + def __init__(self): + self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' + self.subnets = [FakeV4SubnetAgentWithNoDnsProvided()] + self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), + FakePortMultipleAgents1()] + self.namespace = 'qdhcp-ns' + + class FakeV4SubnetMultipleAgentsWithDnsProvided(FakeV4Subnet): def __init__(self): super(FakeV4SubnetMultipleAgentsWithDnsProvided, self).__init__() @@ -546,6 +562,19 @@ class FakeV6SubnetStateless(object): self.ipv6_ra_mode = None +class FakeV6SubnetStatelessNoDnsProvided(object): + def __init__(self): + self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' + self.ip_version = 6 + self.cidr = 'ffea:3ba5:a17a:4ba3::/64' + self.gateway_ip = 'ffea:3ba5:a17a:4ba3::1' + self.enable_dhcp = True + self.dns_nameservers = ['::'] + self.host_routes = [] + self.ipv6_address_mode = constants.DHCPV6_STATELESS + self.ipv6_ra_mode = None + + class FakeV6SubnetStatelessBadPrefixLength(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' @@ -898,6 +927,14 @@ class FakeV6NetworkStatelessDHCP(object): self.namespace = 'qdhcp-ns' +class FakeV6NetworkStatelessDHCPNoDnsProvided(object): + def __init__(self): + self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' + self.subnets = [FakeV6SubnetStatelessNoDnsProvided()] + self.ports = [FakeV6Port()] + self.namespace = 'qdhcp-ns' + + class FakeV6NetworkStatelessDHCPBadPrefixLength(object): def __init__(self): self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' @@ -1429,6 +1466,18 @@ class TestDnsmasq(TestBase): self._test_output_opts_file(expected, FakeV4AgentWithManyDnsProvided()) + def test_output_opts_file_agent_with_no_dns_provided(self): + expected = ('tag:tag0,' + 'option:dns-server\n' + 'tag:tag0,option:classless-static-route,' + '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' + 'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,' + '192.168.0.1\n' + 'tag:tag0,option:router,192.168.0.1').lstrip() + + self._test_output_opts_file(expected, + FakeV4AgentWithNoDnsProvided()) + def test_output_opts_file_multiple_agents_with_dns_provided(self): expected = ('tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,' @@ -2239,6 +2288,18 @@ class TestDnsmasq(TestBase): self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), mock.call(exp_opt_name, exp_opt_data)]) + def test_host_and_opts_file_on_stateless_dhcpv6_network_no_dns(self): + exp_host_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/host' + exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts' + exp_opt_data = ('tag:tag0,option6:dns-server\n' + 'tag:tag0,' + 'option6:domain-search,openstacklocal').lstrip() + dm = self._get_dnsmasq(FakeV6NetworkStatelessDHCPNoDnsProvided()) + dm._output_hosts_file() + dm._output_opts_file() + self.safe.assert_has_calls([mock.call(exp_host_name, ''), + mock.call(exp_opt_name, exp_opt_data)]) + def test_host_file_on_net_with_v6_slaac_and_v4(self): exp_host_name = '/dhcp/eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee/host' exp_host_data = ( diff --git a/releasenotes/notes/bug-1311040-dhcp-no-dns-09291c23e2ce800a.yaml b/releasenotes/notes/bug-1311040-dhcp-no-dns-09291c23e2ce800a.yaml new file mode 100644 index 00000000000..546d6b72b9a --- /dev/null +++ b/releasenotes/notes/bug-1311040-dhcp-no-dns-09291c23e2ce800a.yaml @@ -0,0 +1,16 @@ +--- +prelude: > + DNS server assignment can now be disabled in replies sent from the DHCP agent. +features: + - | + It is now possible to instruct the DHCP agent not to supply any DNS server + address to their clients by setting the ``dns_nameservers`` attribute for + the corresponding subnet to ``0.0.0.0`` or ``::``, for IPv4 or IPv6 subnets + (respectively). +upgrade: + - | + The functionality when a subnet has its DNS server set to ``0.0.0.0`` or + ``::`` has been changed with this release. The old behaviour was that each + DHCP agent would supply only its own IP address as the DNS server to its + clients. The new behaviour is that the DHCP agent will not supply any DNS + server IP address at all.