]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Provide work around for 0.0.0.0/0 ::/0 for ipset
authorAaron Rosen <aaronorosen@gmail.com>
Wed, 3 Jun 2015 23:19:39 +0000 (16:19 -0700)
committerAaron Rosen <aaronorosen@gmail.com>
Wed, 24 Jun 2015 17:25:24 +0000 (10:25 -0700)
Previously, the ipset_manager would pass in 0.0.0.0/0 or ::/0 if
these addresses were inputted as allowed address pairs. This causes
ipset to raise an error as it does not work with zero prefix sizes.
To solve this problem we use two ipset rules to represent this:

Ipv4: 0.0.0.0/1 and 128.0.0.1/1
IPv6: ::/1' and '8000::/1

All of this logic is handled via _sanitize_addresses() in the ipset_manager
which is called to convert the input.

Change-Id: I8c6a08e0cf3b5b5386fe03af9f2174c666b8ac75
Closes-bug: 1461054

neutron/agent/linux/ipset_manager.py
neutron/tests/unit/agent/linux/test_ipset_manager.py

index f0dc7de4ad0d69a7af42418a477ed2a06f3cec01..d76304f2f503175fbef4667f0a42f0166e336f19 100644 (file)
@@ -13,6 +13,8 @@
 
 import copy
 
+import netaddr
+
 from neutron.agent.linux import utils as linux_utils
 from neutron.common import utils
 
@@ -34,6 +36,26 @@ class IpsetManager(object):
         self.namespace = namespace
         self.ipset_sets = {}
 
+    def _sanitize_addresses(self, addresses):
+        """This method converts any address to ipset format.
+
+        If an address has a mask of /0 we need to cover to it to a mask of
+        /1 as ipset does not support /0 length addresses. Instead we use two
+        /1's to represent the /0.
+        """
+        sanitized_addresses = []
+        for ip in addresses:
+            if (netaddr.IPNetwork(ip).prefixlen == 0):
+                if(netaddr.IPNetwork(ip).version == 4):
+                    sanitized_addresses.append('0.0.0.0/1')
+                    sanitized_addresses.append('128.0.0.0/1')
+                elif (netaddr.IPNetwork(ip).version == 6):
+                    sanitized_addresses.append('::/1')
+                    sanitized_addresses.append('8000::/1')
+            else:
+                sanitized_addresses.append(ip)
+        return sanitized_addresses
+
     @staticmethod
     def get_name(id, ethertype):
         """Returns the given ipset name for an id+ethertype pair.
@@ -54,6 +76,7 @@ class IpsetManager(object):
         add / remove new members, or swapped atomically if
         that's faster.
         """
+        member_ips = self._sanitize_addresses(member_ips)
         set_name = self.get_name(id, ethertype)
         if not self.set_exists(id, ethertype):
             # The initial creation is handled with create/refresh to
index cedbbceeeafaf7fa66992f6875eb0a13e1a2a8a1..2b5a58a6dd70e55498ea15b46b5c746bd0d51727 100644 (file)
@@ -62,7 +62,7 @@ class BaseIpsetManagerTest(base.BaseTestCase):
     def expect_set(self, addresses):
         temp_input = ['create %s hash:net family inet' % TEST_SET_NAME_NEW]
         temp_input.extend('add %s %s' % (TEST_SET_NAME_NEW, ip)
-                          for ip in addresses)
+                          for ip in self.ipset._sanitize_addresses(addresses))
         input = '\n'.join(temp_input)
         self.expected_calls.extend([
             mock.call(['ipset', 'restore', '-exist'],
@@ -79,13 +79,16 @@ class BaseIpsetManagerTest(base.BaseTestCase):
         self.expected_calls.extend(
             mock.call(['ipset', 'add', '-exist', TEST_SET_NAME, ip],
                       process_input=None,
-                      run_as_root=True) for ip in addresses)
+                      run_as_root=True)
+            for ip in self.ipset._sanitize_addresses(addresses))
 
     def expect_del(self, addresses):
+
         self.expected_calls.extend(
             mock.call(['ipset', 'del', TEST_SET_NAME, ip],
                       process_input=None,
-                      run_as_root=True) for ip in addresses)
+                      run_as_root=True)
+            for ip in self.ipset._sanitize_addresses(addresses))
 
     def expect_create(self):
         self.expected_calls.append(
@@ -137,6 +140,16 @@ class IpsetManagerTestCase(BaseIpsetManagerTest):
         self.ipset.set_members(TEST_SET_ID, ETHERTYPE, FAKE_IPS)
         self.verify_mock_calls()
 
+    def test_set_members_adding_all_zero_ipv4(self):
+        self.expect_set(['0.0.0.0/0'])
+        self.ipset.set_members(TEST_SET_ID, ETHERTYPE, ['0.0.0.0/0'])
+        self.verify_mock_calls()
+
+    def test_set_members_adding_all_zero_ipv6(self):
+        self.expect_set(['::/0'])
+        self.ipset.set_members(TEST_SET_ID, ETHERTYPE, ['::/0'])
+        self.verify_mock_calls()
+
     def test_destroy(self):
         self.add_first_ip()
         self.expect_destroy()