]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Install SNAT rules for ipv4 only
authorBaodong Li <baoli@cisco.com>
Thu, 24 Apr 2014 01:47:13 +0000 (01:47 +0000)
committerThomas Goirand <thomas@goirand.fr>
Mon, 9 Jun 2014 15:06:55 +0000 (23:06 +0800)
Change-Id: I37bd770aa0d54a985ac2bec708c571785084e0ec
Closes-Bug: #1309195
(cherry picked from commit d23bc8fa6e2d8a735a2aa75224b1bc96a3b992f5)

neutron/agent/l3_agent.py
neutron/tests/unit/test_l3_agent.py

index 59e84e2a7de84bc6ce4f3d5959fece8409277953..94700aa360b94902ae774d1f36f1f1aea7c8e2e1 100644 (file)
@@ -451,7 +451,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
                                namespace=ri.ns_name,
                                prefix=INTERNAL_DEV_PREFIX)
 
-        internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports]
+        # Get IPv4 only internal CIDRs
+        internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports
+                          if netaddr.IPNetwork(p['ip_cidr']).version == 4]
         # TODO(salv-orlando): RouterInfo would be a better place for
         # this logic too
         ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or
@@ -530,11 +532,16 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
         # And add them back if the action if add_rules
         if action == 'add_rules' and ex_gw_port:
             # ex_gw_port should not be None in this case
-            ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address']
-            for rule in self.external_gateway_nat_rules(ex_gw_ip,
-                                                        internal_cidrs,
-                                                        interface_name):
-                ri.iptables_manager.ipv4['nat'].add_rule(*rule)
+            # NAT rules are added only if ex_gw_port has an IPv4 address
+            for ip_addr in ex_gw_port['fixed_ips']:
+                ex_gw_ip = ip_addr['ip_address']
+                if netaddr.IPAddress(ex_gw_ip).version == 4:
+                    rules = self.external_gateway_nat_rules(ex_gw_ip,
+                                                            internal_cidrs,
+                                                            interface_name)
+                    for rule in rules:
+                        ri.iptables_manager.ipv4['nat'].add_rule(*rule)
+                    break
         ri.iptables_manager.apply()
 
     def process_router_floating_ip_nat_rules(self, ri):
index d1dffffb4cb187ea2c3be502387eef6ba5f1fca2..ec272ef6a939df480672b6773efb19e895591ae6 100644 (file)
@@ -17,6 +17,7 @@ import contextlib
 import copy
 
 import mock
+import netaddr
 from oslo.config import cfg
 from testtools import matchers
 
@@ -331,24 +332,37 @@ class TestBasicRouterOperations(base.BaseTestCase):
             '! -i %s ! -o %s -m conntrack ! --ctstate DNAT -j ACCEPT' %
             (interface_name, interface_name)]
         for source_cidr in source_cidrs:
-            value_dict = {'source_cidr': source_cidr,
-                          'source_nat_ip': source_nat_ip}
-            expected_rules.append('-s %(source_cidr)s -j SNAT --to-source '
-                                  '%(source_nat_ip)s' % value_dict)
+            # Create SNAT rules for IPv4 only
+            if (netaddr.IPNetwork(source_cidr).version == 4 and
+                netaddr.IPNetwork(source_nat_ip).version == 4):
+                value_dict = {'source_cidr': source_cidr,
+                              'source_nat_ip': source_nat_ip}
+                expected_rules.append('-s %(source_cidr)s -j SNAT --to-source '
+                                      '%(source_nat_ip)s' % value_dict)
         for r in rules:
             if negate:
                 self.assertNotIn(r.rule, expected_rules)
             else:
                 self.assertIn(r.rule, expected_rules)
 
-    def _prepare_router_data(self, enable_snat=None, num_internal_ports=1):
+    def _prepare_router_data(self, ip_version=4,
+                             enable_snat=None, num_internal_ports=1):
+        if ip_version == 4:
+            ip_addr = '19.4.4.4'
+            cidr = '19.4.4.0/24'
+            gateway_ip = '19.4.4.1'
+        elif ip_version == 6:
+            ip_addr = 'fd00::4'
+            cidr = 'fd00::/64'
+            gateway_ip = 'fd00::1'
+
         router_id = _uuid()
         ex_gw_port = {'id': _uuid(),
                       'network_id': _uuid(),
-                      'fixed_ips': [{'ip_address': '19.4.4.4',
+                      'fixed_ips': [{'ip_address': ip_addr,
                                      'subnet_id': _uuid()}],
-                      'subnet': {'cidr': '19.4.4.0/24',
-                                 'gateway_ip': '19.4.4.1'}}
+                      'subnet': {'cidr': cidr,
+                                 'gateway_ip': gateway_ip}}
         int_ports = []
         for i in range(num_internal_ports):
             int_ports.append({'id': _uuid(),
@@ -635,6 +649,99 @@ class TestBasicRouterOperations(base.BaseTestCase):
         self._verify_snat_rules(nat_rules_delta, router)
         self.send_arp.assert_called_once()
 
+    def test_process_ipv6_only_gw(self):
+        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+        router = self._prepare_router_data(ip_version=6)
+        # Get NAT rules without the gw_port
+        gw_port = router['gw_port']
+        router['gw_port'] = None
+        ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
+                                 self.conf.use_namespaces, router=router)
+        agent.external_gateway_added = mock.Mock()
+        agent.process_router(ri)
+        orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:]
+
+        # Get NAT rules with the gw_port
+        router['gw_port'] = gw_port
+        ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
+                                 self.conf.use_namespaces, router=router)
+        with mock.patch.object(
+                agent,
+                'external_gateway_nat_rules') as external_gateway_nat_rules:
+            agent.process_router(ri)
+            new_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:]
+
+            # There should be no change with the NAT rules
+            self.assertFalse(external_gateway_nat_rules.called)
+            self.assertEqual(orig_nat_rules, new_nat_rules)
+
+    def test_process_router_ipv6_interface_added(self):
+        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+        router = self._prepare_router_data()
+        ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
+                                 self.conf.use_namespaces, router=router)
+        agent.external_gateway_added = mock.Mock()
+        # Process with NAT
+        agent.process_router(ri)
+        orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:]
+        # Add an IPv6 interface and reprocess
+        router[l3_constants.INTERFACE_KEY].append(
+            {'id': _uuid(),
+             'network_id': _uuid(),
+             'admin_state_up': True,
+             'fixed_ips': [{'ip_address': 'fd00::2',
+                            'subnet_id': _uuid()}],
+             'mac_address': 'ca:fe:de:ad:be:ef',
+             'subnet': {'cidr': 'fd00::/64',
+                        'gateway_ip': 'fd00::1'}})
+        # Reassign the router object to RouterInfo
+        ri.router = router
+        agent.process_router(ri)
+        # For some reason set logic does not work well with
+        # IpTablesRule instances
+        nat_rules_delta = [r for r in ri.iptables_manager.ipv4['nat'].rules
+                           if r not in orig_nat_rules]
+        self.assertFalse(nat_rules_delta)
+
+    def test_process_router_ipv6v4_interface_added(self):
+        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+        router = self._prepare_router_data()
+        ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
+                                 self.conf.use_namespaces, router=router)
+        agent.external_gateway_added = mock.Mock()
+        # Process with NAT
+        agent.process_router(ri)
+        orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:]
+        # Add an IPv4 and IPv6 interface and reprocess
+        router[l3_constants.INTERFACE_KEY].append(
+            {'id': _uuid(),
+             'network_id': _uuid(),
+             'admin_state_up': True,
+             'fixed_ips': [{'ip_address': '35.4.1.4',
+                            'subnet_id': _uuid()}],
+             'mac_address': 'ca:fe:de:ad:be:ef',
+             'subnet': {'cidr': '35.4.1.0/24',
+                        'gateway_ip': '35.4.1.1'}})
+
+        router[l3_constants.INTERFACE_KEY].append(
+            {'id': _uuid(),
+             'network_id': _uuid(),
+             'admin_state_up': True,
+             'fixed_ips': [{'ip_address': 'fd00::2',
+                            'subnet_id': _uuid()}],
+             'mac_address': 'ca:fe:de:ad:be:ef',
+             'subnet': {'cidr': 'fd00::/64',
+                        'gateway_ip': 'fd00::1'}})
+        # Reassign the router object to RouterInfo
+        ri.router = router
+        agent.process_router(ri)
+        # For some reason set logic does not work well with
+        # IpTablesRule instances
+        nat_rules_delta = [r for r in ri.iptables_manager.ipv4['nat'].rules
+                           if r not in orig_nat_rules]
+        self.assertEqual(1, len(nat_rules_delta))
+        self._verify_snat_rules(nat_rules_delta, router)
+
     def test_process_router_interface_removed(self):
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
         router = self._prepare_router_data(num_internal_ports=2)