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
This commit is contained in:
parent
3ed7371332
commit
a3e102934c
|
@ -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())
|
||||
|
|
|
@ -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']:
|
||||
|
|
|
@ -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']})
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]}
|
||||
|
|
Loading…
Reference in New Issue