Do not announce any DNS resolver if "0.0.0.0" or "::" provided
The DHCP server should not announce any DNS resolver at all on the subnet if "0.0.0.0" (IPv4) or "::" (IPv6) are configured as DNS name servers in any subnet. https://docs.openstack.org/neutron/latest/admin/config-dns-res.html Closes-Bug: #1950686 Change-Id: I78dd012764c7bd7a29aeb8d97c00b627d7723aeb
This commit is contained in:
parent
c788f09f1b
commit
a416f8b0ab
@ -1153,10 +1153,8 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
addr_mode == constants.IPV6_SLAAC)):
|
addr_mode == constants.IPV6_SLAAC)):
|
||||||
continue
|
continue
|
||||||
if subnet.dns_nameservers:
|
if subnet.dns_nameservers:
|
||||||
if ((subnet.ip_version == 4 and
|
if common_utils.is_dns_servers_any_address(
|
||||||
subnet.dns_nameservers == ['0.0.0.0']) or
|
subnet.dns_nameservers, subnet.ip_version):
|
||||||
(subnet.ip_version == 6 and
|
|
||||||
subnet.dns_nameservers == ['::'])):
|
|
||||||
# Special case: Do not announce DNS servers
|
# Special case: Do not announce DNS servers
|
||||||
options.append(
|
options.append(
|
||||||
self._format_option(
|
self._format_option(
|
||||||
|
@ -37,6 +37,8 @@ from ovsdbapp import constants as ovsdbapp_const
|
|||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.common.ovn import constants
|
from neutron.common.ovn import constants
|
||||||
from neutron.common.ovn import exceptions as ovn_exc
|
from neutron.common.ovn import exceptions as ovn_exc
|
||||||
|
from neutron.common import utils as common_utils
|
||||||
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.objects import ports as ports_obj
|
from neutron.objects import ports as ports_obj
|
||||||
|
|
||||||
@ -414,6 +416,26 @@ def get_system_dns_resolvers(resolver_file=DNS_RESOLVER_FILE):
|
|||||||
return resolvers
|
return resolvers
|
||||||
|
|
||||||
|
|
||||||
|
def get_dhcp_dns_servers(subnet, ip_version=const.IP_VERSION_4):
|
||||||
|
"""Retrieve the DHCP option DNS servers
|
||||||
|
|
||||||
|
The DHCP should not announce any DNS resolver at all on the subnet if any
|
||||||
|
configured DNS server is "0.0.0.0" (IPv4) or "::" (IPv6).
|
||||||
|
https://docs.openstack.org/neutron/latest/admin/config-dns-res.html
|
||||||
|
"""
|
||||||
|
if ip_version == const.IP_VERSION_4:
|
||||||
|
dns_servers = (subnet.get('dns_nameservers') or
|
||||||
|
ovn_conf.get_dns_servers() or
|
||||||
|
get_system_dns_resolvers())
|
||||||
|
else:
|
||||||
|
dns_servers = subnet['dns_nameservers']
|
||||||
|
|
||||||
|
if common_utils.is_dns_servers_any_address(dns_servers, ip_version):
|
||||||
|
return []
|
||||||
|
|
||||||
|
return dns_servers
|
||||||
|
|
||||||
|
|
||||||
def get_port_subnet_ids(port):
|
def get_port_subnet_ids(port):
|
||||||
fixed_ips = list(port['fixed_ips'])
|
fixed_ips = list(port['fixed_ips'])
|
||||||
return [f['subnet_id'] for f in fixed_ips]
|
return [f['subnet_id'] for f in fixed_ips]
|
||||||
|
@ -145,6 +145,13 @@ def get_dhcp_agent_device_id(network_id, host):
|
|||||||
return 'dhcp%s-%s' % (host_uuid, network_id)
|
return 'dhcp%s-%s' % (host_uuid, network_id)
|
||||||
|
|
||||||
|
|
||||||
|
def is_dns_servers_any_address(dns_servers, ip_version):
|
||||||
|
"""Checks if DNS server list matches the IP any address '0.0.0.0'/'::'"""
|
||||||
|
ip_any = netaddr.IPNetwork(n_const.IP_ANY[ip_version]).ip
|
||||||
|
return (len(dns_servers) == 1 and
|
||||||
|
netaddr.IPNetwork(dns_servers[0]).ip == ip_any)
|
||||||
|
|
||||||
|
|
||||||
class exception_logger(object):
|
class exception_logger(object):
|
||||||
"""Wrap a function and log raised exception
|
"""Wrap a function and log raised exception
|
||||||
|
|
||||||
|
@ -1925,9 +1925,7 @@ class OVNClient(object):
|
|||||||
options['server_mac'] = n_net.get_random_mac(
|
options['server_mac'] = n_net.get_random_mac(
|
||||||
cfg.CONF.base_mac.split(':'))
|
cfg.CONF.base_mac.split(':'))
|
||||||
|
|
||||||
dns_servers = (subnet.get('dns_nameservers') or
|
dns_servers = utils.get_dhcp_dns_servers(subnet)
|
||||||
ovn_conf.get_dns_servers() or
|
|
||||||
utils.get_system_dns_resolvers())
|
|
||||||
if dns_servers:
|
if dns_servers:
|
||||||
options['dns_server'] = '{%s}' % ', '.join(dns_servers)
|
options['dns_server'] = '{%s}' % ', '.join(dns_servers)
|
||||||
else:
|
else:
|
||||||
@ -1966,9 +1964,10 @@ class OVNClient(object):
|
|||||||
cfg.CONF.base_mac.split(':'))
|
cfg.CONF.base_mac.split(':'))
|
||||||
}
|
}
|
||||||
|
|
||||||
if subnet['dns_nameservers']:
|
dns_servers = utils.get_dhcp_dns_servers(subnet,
|
||||||
dns_servers = '{%s}' % ', '.join(subnet['dns_nameservers'])
|
ip_version=const.IP_VERSION_6)
|
||||||
dhcpv6_opts['dns_server'] = dns_servers
|
if dns_servers:
|
||||||
|
dhcpv6_opts['dns_server'] = '{%s}' % ', '.join(dns_servers)
|
||||||
|
|
||||||
if subnet.get('ipv6_address_mode') == const.DHCPV6_STATELESS:
|
if subnet.get('ipv6_address_mode') == const.DHCPV6_STATELESS:
|
||||||
dhcpv6_opts[ovn_const.DHCPV6_STATELESS_OPT] = 'true'
|
dhcpv6_opts[ovn_const.DHCPV6_STATELESS_OPT] = 'true'
|
||||||
|
@ -14,10 +14,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from os import path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext
|
from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext
|
||||||
|
from neutron_lib import constants as n_const
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from neutron.common.ovn import constants
|
from neutron.common.ovn import constants
|
||||||
from neutron.common.ovn import utils
|
from neutron.common.ovn import utils
|
||||||
@ -33,6 +36,7 @@ nameserver foo 10.0.0.4
|
|||||||
nameserver aef0::4
|
nameserver aef0::4
|
||||||
foo 10.0.0.5
|
foo 10.0.0.5
|
||||||
"""
|
"""
|
||||||
|
RESOLV_DNS_SERVERS = ['10.0.0.1', '10.0.0.3']
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(base.BaseTestCase):
|
class TestUtils(base.BaseTestCase):
|
||||||
@ -43,7 +47,7 @@ class TestUtils(base.BaseTestCase):
|
|||||||
tmp_resolv_file = open(resolver_file_name, 'w')
|
tmp_resolv_file = open(resolver_file_name, 'w')
|
||||||
tmp_resolv_file.writelines(RESOLV_CONF_TEMPLATE)
|
tmp_resolv_file.writelines(RESOLV_CONF_TEMPLATE)
|
||||||
tmp_resolv_file.close()
|
tmp_resolv_file.close()
|
||||||
expected_dns_resolvers = ['10.0.0.1', '10.0.0.3']
|
expected_dns_resolvers = RESOLV_DNS_SERVERS
|
||||||
observed_dns_resolvers = utils.get_system_dns_resolvers(
|
observed_dns_resolvers = utils.get_system_dns_resolvers(
|
||||||
resolver_file=resolver_file_name)
|
resolver_file=resolver_file_name)
|
||||||
self.assertEqual(expected_dns_resolvers, observed_dns_resolvers)
|
self.assertEqual(expected_dns_resolvers, observed_dns_resolvers)
|
||||||
@ -379,3 +383,53 @@ class TestConnectionConfigToTargetString(base.BaseTestCase):
|
|||||||
for config, target in config_target:
|
for config, target in config_target:
|
||||||
output = utils.connection_config_to_target_string(config)
|
output = utils.connection_config_to_target_string(config)
|
||||||
self.assertEqual(target, output)
|
self.assertEqual(target, output)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetDhcpDnsServers(base.BaseTestCase):
|
||||||
|
|
||||||
|
def test_ipv4(self):
|
||||||
|
# DNS servers from subnet.
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers(
|
||||||
|
{'dns_nameservers': ['1.2.3.4', '5.6.7.8']})
|
||||||
|
self.assertEqual(['1.2.3.4', '5.6.7.8'], dns_servers)
|
||||||
|
|
||||||
|
# DNS servers from config parameter.
|
||||||
|
cfg.CONF.set_override('dns_servers',
|
||||||
|
'1.1.2.2,3.3.4.4', group='ovn')
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers({})
|
||||||
|
self.assertEqual(['1.1.2.2', '3.3.4.4'], dns_servers)
|
||||||
|
|
||||||
|
# DNS servers from local DNS resolver.
|
||||||
|
cfg.CONF.set_override('dns_servers', '', group='ovn')
|
||||||
|
with mock.patch('builtins.open',
|
||||||
|
mock.mock_open(read_data=RESOLV_CONF_TEMPLATE)), \
|
||||||
|
mock.patch.object(path, 'exists', return_value=True):
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers({})
|
||||||
|
self.assertEqual(RESOLV_DNS_SERVERS, dns_servers)
|
||||||
|
|
||||||
|
# No DNS servers if only '0.0.0.0' configured.
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers(
|
||||||
|
{'dns_nameservers': ['0.0.0.0', '5.6.7.8']})
|
||||||
|
self.assertEqual(['0.0.0.0', '5.6.7.8'], dns_servers)
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers(
|
||||||
|
{'dns_nameservers': ['0.0.0.0']})
|
||||||
|
self.assertEqual([], dns_servers)
|
||||||
|
|
||||||
|
def test_ipv6(self):
|
||||||
|
# DNS servers from subnet.
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers(
|
||||||
|
{'dns_nameservers': ['2001:4860:4860::8888',
|
||||||
|
'2001:4860:4860::8844']},
|
||||||
|
ip_version=n_const.IP_VERSION_6)
|
||||||
|
self.assertEqual(['2001:4860:4860::8888',
|
||||||
|
'2001:4860:4860::8844'], dns_servers)
|
||||||
|
|
||||||
|
# No DNS servers if only '::' configured.
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers(
|
||||||
|
{'dns_nameservers': ['2001:4860:4860::8888', '::']},
|
||||||
|
ip_version=n_const.IP_VERSION_6)
|
||||||
|
self.assertEqual(['2001:4860:4860::8888', '::'], dns_servers)
|
||||||
|
dns_servers = utils.get_dhcp_dns_servers(
|
||||||
|
{'dns_nameservers': ['::']},
|
||||||
|
ip_version=n_const.IP_VERSION_6)
|
||||||
|
self.assertEqual([], dns_servers)
|
||||||
|
Loading…
Reference in New Issue
Block a user