]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Do not prohibit VXLAN over IPv6
authorDustin Lundquist <dustin@null-ptr.net>
Wed, 30 Dec 2015 23:59:36 +0000 (15:59 -0800)
committerDoug Wiegley <dougwig@parkside.io>
Thu, 14 Jan 2016 15:36:34 +0000 (15:36 +0000)
9fc45cee in introduced a regression prohibiting using VXLAN over
IPv6. Relax restriction on local_ip, but validate that both
local_ip and vxlan_group of the same address family. Move existing
validation of vxlan_group into validate_vxlan_group_with_local_ip()
method and refactor existing tests for that validation.

Change-Id: I3d67732d2e1f3e079fee336b403744596fb7db77
Depends-On: I6440445b80637a5a9f4de052cf5ea1fbd8dcf7d1
Closes-Bug: #1531660

neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py
neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py
neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py

index df0ef7559c9be195c34e710e79ab0d08e94dc603..b2d192e1f2213445df149ae8199b802241f80c1e 100644 (file)
@@ -40,8 +40,7 @@ vxlan_opts = [
                       "To reserve a unique group for each possible "
                       "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This "
                       "setting must be the same on all the agents.")),
-    cfg.IPOpt('local_ip', version=4,
-              help=_("Local IP address of the VXLAN endpoints.")),
+    cfg.IPOpt('local_ip', help=_("Local IP address of the VXLAN endpoints.")),
     cfg.BoolOpt('l2_population', default=False,
                 help=_("Extension to use alongside ml2 plugin's l2population "
                        "mechanism driver. It enables the plugin to populate "
index de8f51c38edc825ad1c3858b0e863acb8e4cea1a..ee95c2e3e98628e6f297f9500e87ed54e0947a85 100644 (file)
@@ -79,6 +79,7 @@ class LinuxBridgeManager(object):
         self.vxlan_mode = lconst.VXLAN_NONE
         if cfg.CONF.VXLAN.enable_vxlan:
             device = self.get_local_ip_device(self.local_ip)
+            self.validate_vxlan_group_with_local_ip()
             self.local_int = device.name
             self.check_vxlan_support()
         # Store network mapping to segments
@@ -100,6 +101,26 @@ class LinuxBridgeManager(object):
                           {'brq': bridge, 'net': physnet})
                 sys.exit(1)
 
+    def validate_vxlan_group_with_local_ip(self):
+        if not cfg.CONF.VXLAN.vxlan_group:
+            return
+        try:
+            ip_addr = netaddr.IPAddress(self.local_ip)
+            # Ensure the configured group address/range is valid and multicast
+            group_net = netaddr.IPNetwork(cfg.CONF.VXLAN.vxlan_group)
+            if not group_net.is_multicast():
+                raise ValueError()
+            if not ip_addr.version == group_net.version:
+                raise ValueError()
+        except (netaddr.core.AddrFormatError, ValueError):
+            LOG.error(_LE("Invalid VXLAN Group: %(group)s, must be an address "
+                          "or network (in CIDR notation) in a multicast "
+                          "range of the same address family as local_ip: "
+                          "%(ip)s"),
+                      {'group': cfg.CONF.VXLAN.vxlan_group,
+                       'ip': self.local_ip})
+            sys.exit(1)
+
     def get_local_ip_device(self, local_ip):
         """Return the device with local_ip on the host."""
         device = self.ip.get_device_by_ip(local_ip)
@@ -146,19 +167,10 @@ class LinuxBridgeManager(object):
                             "incorrect vxlan device name"), segmentation_id)
 
     def get_vxlan_group(self, segmentation_id):
-        try:
-            # Ensure the configured group address/range is valid and multicast
-            net = netaddr.IPNetwork(cfg.CONF.VXLAN.vxlan_group)
-            if not net.is_multicast():
-                raise ValueError()
-            # Map the segmentation ID to (one of) the group address(es)
-            return str(net.network +
-                       (int(segmentation_id) & int(net.hostmask)))
-        except (netaddr.core.AddrFormatError, ValueError):
-            LOG.warning(_LW("Invalid VXLAN Group: %s, must be an address "
-                            "or network (in CIDR notation) in a multicast "
-                            "range"),
-                        cfg.CONF.VXLAN.vxlan_group)
+        net = netaddr.IPNetwork(cfg.CONF.VXLAN.vxlan_group)
+        # Map the segmentation ID to (one of) the group address(es)
+        return str(net.network +
+                   (int(segmentation_id) & int(net.hostmask)))
 
     def get_deletable_bridges(self):
         bridge_list = bridge_lib.get_bridge_names()
index 0fc3b5cd588fb2c53481fb133822d33ade4dff54..c738adeed4d09c39c9c6438e37bf3ef7f2f8adaf 100644 (file)
@@ -32,6 +32,8 @@ from neutron.plugins.ml2.drivers.linuxbridge.agent \
 from neutron.tests import base
 
 LOCAL_IP = '192.168.0.33'
+LOCAL_IPV6 = '2001:db8:1::33'
+VXLAN_GROUPV6 = 'ff05::/120'
 PORT_1 = 'abcdef01-12ddssdfds-fdsfsd'
 DEVICE_1 = 'tapabcdef01-12'
 NETWORK_ID = '57653b20-ed5b-4ed0-a31d-06f84e3fd909'
@@ -62,6 +64,7 @@ def get_linuxbridge_manager(bridge_mappings, interface_mappings):
             mock.patch.object(ip_lib, 'device_exists', return_value=True),\
             mock.patch.object(linuxbridge_neutron_agent.LinuxBridgeManager,
                               'check_vxlan_support'):
+        cfg.CONF.set_override('local_ip', LOCAL_IP, 'VXLAN')
         return linuxbridge_neutron_agent.LinuxBridgeManager(
             bridge_mappings, interface_mappings)
 
@@ -114,6 +117,7 @@ class TestLinuxBridgeAgent(base.BaseTestCase):
                              'neutron.agent.firewall.NoopFirewallDriver',
                              group='SECURITYGROUP')
         cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT')
+        cfg.CONF.set_override('local_ip', LOCAL_IP, 'VXLAN')
         self.get_devices_p = mock.patch.object(ip_lib.IPWrapper, 'get_devices')
         self.get_devices = self.get_devices_p.start()
         self.get_devices.return_value = [ip_lib.IPDevice('eth77')]
@@ -565,6 +569,31 @@ class TestLinuxBridgeManager(base.BaseTestCase):
             self.assertEqual(1, log.call_count)
             exit.assert_called_once_with(1)
 
+    def _test_vxlan_group_validation(self, bad_local_ip, bad_vxlan_group):
+        with mock.patch.object(ip_lib.IPWrapper,
+                               'get_device_by_ip',
+                               return_value=FAKE_DEFAULT_DEV),\
+                mock.patch.object(sys, 'exit') as exit,\
+                mock.patch.object(linuxbridge_neutron_agent.LOG,
+                                  'error') as log:
+            self.lbm.local_ip = bad_local_ip
+            cfg.CONF.set_override('vxlan_group', bad_vxlan_group, 'VXLAN')
+            self.lbm.validate_vxlan_group_with_local_ip()
+            self.assertEqual(1, log.call_count)
+            exit.assert_called_once_with(1)
+
+    def test_vxlan_group_validation_with_mismatched_local_ip(self):
+        self._test_vxlan_group_validation(LOCAL_IP, VXLAN_GROUPV6)
+
+    def test_vxlan_group_validation_with_unicast_group(self):
+        self._test_vxlan_group_validation(LOCAL_IP, '240.0.0.0')
+
+    def test_vxlan_group_validation_with_invalid_cidr(self):
+        self._test_vxlan_group_validation(LOCAL_IP, '224.0.0.1/')
+
+    def test_vxlan_group_validation_with_v6_unicast_group(self):
+        self._test_vxlan_group_validation(LOCAL_IPV6, '2001:db8::')
+
     def test_get_existing_bridge_name(self):
         phy_net = 'physnet0'
         self.assertEqual('br-eth2',
@@ -609,10 +638,17 @@ class TestLinuxBridgeManager(base.BaseTestCase):
         self.assertEqual('239.1.2.0', self.lbm.get_vxlan_group(vn_id))
         vn_id = 257
         self.assertEqual('239.1.2.1', self.lbm.get_vxlan_group(vn_id))
-        cfg.CONF.set_override('vxlan_group', '240.0.0.0', 'VXLAN')
-        self.assertIsNone(self.lbm.get_vxlan_group(vn_id))
-        cfg.CONF.set_override('vxlan_group', '224.0.0.1/', 'VXLAN')
-        self.assertIsNone(self.lbm.get_vxlan_group(vn_id))
+
+    def test_get_vxlan_group_with_ipv6(self):
+        cfg.CONF.set_override('local_ip', LOCAL_IPV6, 'VXLAN')
+        self.lbm.local_ip = LOCAL_IPV6
+        cfg.CONF.set_override('vxlan_group', VXLAN_GROUPV6, 'VXLAN')
+        vn_id = p_const.MAX_VXLAN_VNI
+        self.assertEqual('ff05::ff', self.lbm.get_vxlan_group(vn_id))
+        vn_id = 256
+        self.assertEqual('ff05::', self.lbm.get_vxlan_group(vn_id))
+        vn_id = 257
+        self.assertEqual('ff05::1', self.lbm.get_vxlan_group(vn_id))
 
     def test_get_deletable_bridges(self):
         br_list = ["br-int", "brq1", "brq2", "brq-user"]
@@ -1174,7 +1210,6 @@ class TestLinuxBridgeManager(base.BaseTestCase):
 
 class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
     def setUp(self):
-        cfg.CONF.set_override('local_ip', LOCAL_IP, 'VXLAN')
         super(TestLinuxBridgeRpcCallbacks, self).setUp()
 
         class FakeLBAgent(object):