summaryrefslogtreecommitdiff
blob: c6c2230c9bd39147f079d8916bb0463897cac3a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
From e0c8cbc5dd610b4c580935ea56436495a6d4eb26 Mon Sep 17 00:00:00 2001
From: Aaron Rosen <aaronorosen@gmail.com>
Date: Wed, 3 Jun 2015 16:19:39 -0700
Subject: [PATCH] Provide work around for 0.0.0.0/0 ::/0 for ipset

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.

Closes-bug: 1461054

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

(cherry picked from commit 80a0fc3ba063e036b76e05e89b0cc54fc2d47c81)
---
 neutron/agent/linux/ipset_manager.py               | 23 ++++++++++++++++++++++
 .../tests/unit/agent/linux/test_ipset_manager.py   | 19 +++++++++++++++---
 2 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/neutron/agent/linux/ipset_manager.py b/neutron/agent/linux/ipset_manager.py
index 0f76418..af59f1f 100644
--- a/neutron/agent/linux/ipset_manager.py
+++ b/neutron/agent/linux/ipset_manager.py
@@ -11,6 +11,8 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
+import netaddr
+
 from neutron.agent.linux import utils as linux_utils
 from neutron.common import utils
 
@@ -31,6 +33,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.
@@ -51,6 +73,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
diff --git a/neutron/tests/unit/agent/linux/test_ipset_manager.py b/neutron/tests/unit/agent/linux/test_ipset_manager.py
index 4484008..a1c6dc5 100644
--- a/neutron/tests/unit/agent/linux/test_ipset_manager.py
+++ b/neutron/tests/unit/agent/linux/test_ipset_manager.py
@@ -38,7 +38,7 @@ class BaseIpsetManagerTest(base.BaseTestCase):
     def expect_set(self, addresses):
         temp_input = ['create NETIPv4fake_sgid-new hash:net family inet']
         temp_input.extend('add NETIPv4fake_sgid-new %s' % 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'],
@@ -55,13 +55,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(
@@ -113,6 +116,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()
-- 
1.9.1