From: sridhargaddam Date: Mon, 19 Oct 2015 05:32:16 +0000 (+0000) Subject: Support for IPv6 RDNSS Option in Router Advts X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=a3e102934cee01c14d5327ae7520555bc12affdd;p=openstack-build%2Fneutron-build.git Support for IPv6 RDNSS Option in Router Advts RFC6106 standardizes IPv6 Router Advertisements to support Recursive DNS server information. RDNSS info allows an IPv6 host to configure the DNS information via RA messages without needing DHCPv6 for the DNS configuration. This patch configures RADVD daemon to include RDNSS entries in the Router Advertisements when the IPv6 subnet has dns_nameservers. Closes-Bug: #1495465 Change-Id: Ia516d40b1c7a83cd7046b2b7f42d1204f44288a9 --- diff --git a/neutron/agent/linux/ra.py b/neutron/agent/linux/ra.py index d9eca8d64..67fdb31b2 100644 --- a/neutron/agent/linux/ra.py +++ b/neutron/agent/linux/ra.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from itertools import chain as iter_chain import jinja2 import netaddr from oslo_config import cfg @@ -26,6 +27,8 @@ from neutron.common import constants RADVD_SERVICE_NAME = 'radvd' RADVD_SERVICE_CMD = 'radvd' +# We can configure max of 3 DNS servers in radvd RDNSS section. +MAX_RDNSS_ENTRIES = 3 LOG = logging.getLogger(__name__) @@ -51,6 +54,10 @@ CONFIG_TEMPLATE = jinja2.Template("""interface {{ interface_name }} AdvManagedFlag on; {% endif %} + {% if dns_servers %} + RDNSS {% for dns in dns_servers %} {{ dns }} {% endfor %} {}; + {% endif %} + {% for prefix in prefixes %} prefix {{ prefix }} { @@ -88,10 +95,15 @@ class DaemonMonitor(object): subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC or subnet['ipv6_ra_mode'] == constants.DHCPV6_STATELESS] interface_name = self._dev_name_helper(p['id']) + slaac_subnets = [subnet for subnet in v6_subnets if + subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC] + dns_servers = list(iter_chain(*[subnet['dns_nameservers'] for + subnet in slaac_subnets if subnet.get('dns_nameservers')])) buf.write('%s' % CONFIG_TEMPLATE.render( ra_modes=list(ra_modes), interface_name=interface_name, prefixes=auto_config_prefixes, + dns_servers=dns_servers[0:MAX_RDNSS_ENTRIES], constants=constants)) utils.replace_file(radvd_conf, buf.getvalue()) diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py index 0f01040f5..d20bb4b17 100644 --- a/neutron/db/l3_db.py +++ b/neutron/db/l3_db.py @@ -1207,7 +1207,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase): return {} filters = {'network_id': [id for id in network_ids]} - fields = ['id', 'cidr', 'gateway_ip', + fields = ['id', 'cidr', 'gateway_ip', 'dns_nameservers', 'network_id', 'ipv6_ra_mode', 'subnetpool_id'] subnets_by_network = dict((id, []) for id in network_ids) @@ -1238,6 +1238,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase): subnet_info = {'id': subnet['id'], 'cidr': subnet['cidr'], 'gateway_ip': subnet['gateway_ip'], + 'dns_nameservers': subnet['dns_nameservers'], 'ipv6_ra_mode': subnet['ipv6_ra_mode'], 'subnetpool_id': subnet['subnetpool_id']} for fixed_ip in port['fixed_ips']: diff --git a/neutron/tests/common/l3_test_common.py b/neutron/tests/common/l3_test_common.py index 1c3a9f36d..18b9b8960 100644 --- a/neutron/tests/common/l3_test_common.py +++ b/neutron/tests/common/l3_test_common.py @@ -175,7 +175,8 @@ def router_append_interface(router, count=1, ip_version=4, ra_mode=None, def router_append_subnet(router, count=1, ip_version=4, - ipv6_subnet_modes=None, interface_id=None): + ipv6_subnet_modes=None, interface_id=None, + dns_nameservers=None): if ip_version == 6: subnet_mode_none = {'ra_mode': None, 'address_mode': None} if not ipv6_subnet_modes: @@ -222,6 +223,7 @@ def router_append_subnet(router, count=1, ip_version=4, {'id': subnet_id, 'cidr': cidr_pool % (i + num_existing_subnets), 'gateway_ip': gw_pool % (i + num_existing_subnets), + 'dns_nameservers': dns_nameservers, 'ipv6_ra_mode': ipv6_subnet_modes[i]['ra_mode'], 'ipv6_address_mode': ipv6_subnet_modes[i]['address_mode']}) diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index c205e5d3e..e899feca7 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -1190,7 +1190,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): return expected_calls def _process_router_ipv6_subnet_added( - self, router, ipv6_subnet_modes=None): + self, router, ipv6_subnet_modes=None, dns_nameservers=None): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() @@ -1201,7 +1201,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): router, count=len(ipv6_subnet_modes), ip_version=6, - ipv6_subnet_modes=ipv6_subnet_modes) + ipv6_subnet_modes=ipv6_subnet_modes, + dns_nameservers=dns_nameservers) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) return ri @@ -2169,6 +2170,21 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): assertFlag(managed_flag)('AdvManagedFlag on;', self.utils_replace_file.call_args[0][1]) + def test_generate_radvd_rdnss_conf(self): + router = l3_test_common.prepare_router_data() + ipv6_subnet_modes = [{'ra_mode': l3_constants.IPV6_SLAAC, + 'address_mode': l3_constants.IPV6_SLAAC}] + dns_list = ['fd01:1::100', 'fd01:1::200', 'fd01::300', 'fd01::400'] + ri = self._process_router_ipv6_subnet_added(router, + ipv6_subnet_modes, + dns_nameservers=dns_list) + ri.radvd._generate_radvd_conf(router[l3_constants.INTERFACE_KEY]) + # Verify that radvd configuration file includes RDNSS entries + expected = "RDNSS " + for dns in dns_list[0:ra.MAX_RDNSS_ENTRIES]: + expected += "%s " % dns + self.assertIn(expected, self.utils_replace_file.call_args[0][1]) + def _pd_expected_call_external_process(self, requestor, ri, enable=True): expected_calls = [] if enable: diff --git a/neutron/tests/unit/db/test_l3_db.py b/neutron/tests/unit/db/test_l3_db.py index 19171c81c..e97842240 100644 --- a/neutron/tests/unit/db/test_l3_db.py +++ b/neutron/tests/unit/db/test_l3_db.py @@ -68,6 +68,7 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase): subnet = {'id': mock.sentinel.subnet_id, 'cidr': cidr, 'gateway_ip': mock.sentinel.gateway_ip, + 'dns_nameservers': mock.sentinel.dns_nameservers, 'ipv6_ra_mode': mock.sentinel.ipv6_ra_mode, 'subnetpool_id': mock.sentinel.subnetpool_id} get_subnets_by_network.return_value = {'net_id': [subnet]}