]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add index generation for IPv6 rules for DVR
authorrajeev <rajeev.grover@hp.com>
Wed, 12 Nov 2014 16:25:55 +0000 (11:25 -0500)
committerrajeev <rajeev.grover@hp.com>
Wed, 7 Jan 2015 21:57:57 +0000 (16:57 -0500)
For IPv6 support with DVR the index used for rule priority and
route table needs to be generated such that the index is 32 bits
or less but greater than the system generated rule entries.

For IPv4 the numeric value of the network is used as the index.
For IPv6 the 30 bit xor-folded crc32 of the cidr is used.
Values smaller than system generated entries are extended into
the range freed up because of xor-folding.
For code modularity, index generation is wrapped into a helper
method.

Partial-bug: #1376325

Change-Id: I4bcde80257ef5fe7c744086019841cdef5e3c60c

neutron/agent/l3/dvr.py
neutron/tests/unit/test_l3_agent.py

index db88b5a84faa54f1ac2978b46282d82d3009caa9..2e1f49e3676bb2c74d783513307e436fc739b228 100644 (file)
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import binascii
 import netaddr
 import os
 
@@ -36,6 +37,8 @@ FIP_PR_START = 32768
 FIP_PR_END = FIP_PR_START + 40000
 # Route Table index for FIPs
 FIP_RT_TBL = 16
+# xor-folding mask used for IPv6 rule index
+MASK_30 = 0x3fffffff
 
 
 class RouterMixin(object):
@@ -163,6 +166,27 @@ class AgentMixin(object):
                       router['id'])
         return host
 
+    def _get_snat_idx(self, ip_cidr):
+        """Generate index for DVR snat rules and route tables.
+
+        The index value has to be 32 bits or less but more than the system
+        generated entries i.e. 32768. For IPv4 use the numeric value of the
+        cidr. For IPv6 generate a crc32 bit hash and xor-fold to 30 bits.
+        Use the freed range to extend smaller values so that they become
+        greater than system generated entries.
+        """
+        net = netaddr.IPNetwork(ip_cidr)
+        if net.version == 6:
+            # the crc32 & 0xffffffff is for Python 2.6 and 3.0 compatibility
+            snat_idx = binascii.crc32(ip_cidr) & 0xffffffff
+            # xor-fold the hash to reserve upper range to extend smaller values
+            snat_idx = (snat_idx >> 30) ^ (snat_idx & MASK_30)
+            if snat_idx < 32768:
+                snat_idx = snat_idx + MASK_30
+        else:
+            snat_idx = net.value
+        return snat_idx
+
     def _map_internal_interfaces(self, ri, int_port, snat_ports):
         """Return the SNAT port for the given internal interface port."""
         fixed_ip = int_port['fixed_ips'][0]
@@ -378,7 +402,7 @@ class AgentMixin(object):
     def _snat_redirect_add(self, ri, gateway, sn_port, sn_int):
         """Adds rules and routes for SNAT redirection."""
         try:
-            snat_idx = netaddr.IPNetwork(sn_port['ip_cidr']).value
+            snat_idx = self._get_snat_idx(sn_port['ip_cidr'])
             ns_ipr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name)
             ns_ipd = ip_lib.IPDevice(sn_int, self.root_helper,
                                      namespace=ri.ns_name)
@@ -392,7 +416,7 @@ class AgentMixin(object):
     def _snat_redirect_remove(self, ri, sn_port, sn_int):
         """Removes rules and routes for SNAT redirection."""
         try:
-            snat_idx = netaddr.IPNetwork(sn_port['ip_cidr']).value
+            snat_idx = self._get_snat_idx(sn_port['ip_cidr'])
             ns_ipr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name)
             ns_ipd = ip_lib.IPDevice(sn_int, self.root_helper,
                                      namespace=ri.ns_name)
index c64a303912385d5d2a9c8ef1b68039e3d1fca6b4..9e97c5011d4d73eda951426fcd4a0c4edbc99160 100644 (file)
@@ -665,6 +665,28 @@ class TestBasicRouterOperations(base.BaseTestCase):
             else:
                 self.assertIn(r.rule, expected_rules)
 
+    def test__get_snat_idx_ipv4(self):
+        ip_cidr = '101.12.13.00/24'
+        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+        snat_idx = agent._get_snat_idx(ip_cidr)
+        # 0x650C0D00 is numerical value of 101.12.13.00
+        self.assertEqual(0x650C0D00, snat_idx)
+
+    def test__get_snat_idx_ipv6(self):
+        ip_cidr = '2620:0:a03:e100::/64'
+        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+        snat_idx = agent._get_snat_idx(ip_cidr)
+        # 0x3D345705 is 30 bit xor folded crc32 of the ip_cidr
+        self.assertEqual(0x3D345705, snat_idx)
+
+    def test__get_snat_idx_ipv6_below_32768(self):
+        ip_cidr = 'd488::/30'
+        # crc32 of this ip_cidr is 0x1BD7
+        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+        snat_idx = agent._get_snat_idx(ip_cidr)
+        # 0x1BD7 + 0x3FFFFFFF = 0x40001BD6
+        self.assertEqual(0x40001BD6, snat_idx)
+
     def test__map_internal_interfaces(self):
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
         router = prepare_router_data(num_internal_ports=4)