From 8a596f35bb70a2c8ab48f68327662310141bb518 Mon Sep 17 00:00:00 2001 From: Jiri Kotlin Date: Thu, 23 Jun 2016 14:04:07 +0000 Subject: [PATCH] VXLAN multicast groups in linuxbridge Enable creation of VXLANs with different multicast addresses allocated by VNI-address mappings. Dictionary of multicast addresses and corresponding VXLAN VNI IDs should be loaded from settings. Usable to not flood whole network when managing routers between more datacenters and can not use L2population because VXLAN points to external device. Co-Authored-By: Kevin Benton DocImpact: VXLAN addresses used by linux bridge can be specified per VNI Closes-Bug: #1579068 Change-Id: I24f272ccd6d61d9fa7ea3b6f256fabd381f5434a --- .../conf/plugins/ml2/drivers/linuxbridge.py | 6 +++ .../agent/linuxbridge_neutron_agent.py | 36 ++++++++++++++++- .../agent/test_linuxbridge_neutron_agent.py | 39 +++++++++++++++++++ ...ribution-linuxbridge-9337019c961c01a7.yaml | 17 ++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.yaml diff --git a/neutron/conf/plugins/ml2/drivers/linuxbridge.py b/neutron/conf/plugins/ml2/drivers/linuxbridge.py index b3659dbf1ba..d1210874a14 100644 --- a/neutron/conf/plugins/ml2/drivers/linuxbridge.py +++ b/neutron/conf/plugins/ml2/drivers/linuxbridge.py @@ -58,6 +58,12 @@ vxlan_opts = [ "fully compatible with the allowed-address-pairs " "extension.") ), + cfg.ListOpt('multicast_ranges', + default=[], + help=_("Optional comma-separated list of " + ":: triples " + "describing how to assign a multicast address to " + "VXLAN according to its VNI ID.")), ] bridge_opts = [ diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py index d44354c79d9..b961c68fce6 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -98,7 +98,30 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase): {'brq': bridge, 'net': physnet}) sys.exit(1) + def _is_valid_multicast_range(self, mrange): + try: + addr, vxlan_min, vxlan_max = mrange.split(':') + if int(vxlan_min) > int(vxlan_max): + raise ValueError() + try: + local_ver = netaddr.IPAddress(self.local_ip).version + n_addr = netaddr.IPAddress(addr) + if not n_addr.is_multicast() or n_addr.version != local_ver: + raise ValueError() + except netaddr.core.AddrFormatError: + raise ValueError() + except ValueError: + return False + return True + def validate_vxlan_group_with_local_ip(self): + for r in cfg.CONF.VXLAN.multicast_ranges: + if not self._is_valid_multicast_range(r): + LOG.error("Invalid multicast_range %(r)s. Must be in " + ":: format and " + "addresses must be in the same family as local IP " + "%(loc)s.", {'r': r, 'loc': self.local_ip}) + sys.exit(1) if not cfg.CONF.VXLAN.vxlan_group: return try: @@ -186,8 +209,19 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase): LOG.warning(_LW("Invalid Segmentation ID: %s, will lead to " "incorrect vxlan device name"), segmentation_id) + @staticmethod + def _match_multicast_range(segmentation_id): + for mrange in cfg.CONF.VXLAN.multicast_ranges: + addr, vxlan_min, vxlan_max = mrange.split(':') + if int(vxlan_min) <= segmentation_id <= int(vxlan_max): + return addr + def get_vxlan_group(self, segmentation_id): - net = netaddr.IPNetwork(cfg.CONF.VXLAN.vxlan_group) + mcast_addr = self._match_multicast_range(segmentation_id) + if mcast_addr: + net = netaddr.IPNetwork(mcast_addr) + else: + 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))) diff --git a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py index 0c4219b9c7a..94595cc9fec 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py @@ -222,6 +222,45 @@ class TestLinuxBridgeManager(base.BaseTestCase): vn_id = 257 self.assertEqual('239.1.2.1', self.lbm.get_vxlan_group(vn_id)) + def test_get_vxlan_group_with_multicast_address(self): + cfg.CONF.set_override('vxlan_group', '239.1.2.3/32', 'VXLAN') + cfg.CONF.set_override('multicast_ranges', + ('224.0.0.10:300:315', + '225.0.0.15:400:600'), 'VXLAN') + vn_id = 300 + self.assertEqual('224.0.0.10', self.lbm.get_vxlan_group(vn_id)) + vn_id = 500 + self.assertEqual('225.0.0.15', self.lbm.get_vxlan_group(vn_id)) + vn_id = 315 + self.assertEqual('224.0.0.10', self.lbm.get_vxlan_group(vn_id)) + vn_id = 4000 + # outside of range should fallback to group + self.assertEqual('239.1.2.3', self.lbm.get_vxlan_group(vn_id)) + + def test__is_valid_multicast_range(self): + bad_ranges = ['224.0.0.10:330:315', 'x:100:200', '10.0.0.1:100:200', + '224.0.0.10:100', '224.0.0.10:100:200:300'] + for r in bad_ranges: + self.assertFalse(self.lbm._is_valid_multicast_range(r), + 'range %s should have been invalid' % r) + good_ranges = ['224.0.0.10:315:330', '224.0.0.0:315:315'] + for r in good_ranges: + self.assertTrue(self.lbm._is_valid_multicast_range(r), + 'range %s should have been valid' % r) + # v4 ranges are bad when a v6 local_ip is present + self.lbm.local_ip = '2000::1' + for r in good_ranges: + self.assertFalse(self.lbm._is_valid_multicast_range(r), + 'range %s should have been invalid' % r) + + def test__match_multicast_range(self): + cfg.CONF.set_override('multicast_ranges', + ('224.0.0.10:300:315', + '225.0.0.15:400:600'), 'VXLAN') + self.assertEqual('224.0.0.10', self.lbm._match_multicast_range(307)) + self.assertEqual('225.0.0.15', self.lbm._match_multicast_range(407)) + self.assertIsNone(self.lbm._match_multicast_range(399)) + def test_get_vxlan_group_with_ipv6(self): cfg.CONF.set_override('local_ip', LOCAL_IPV6, 'VXLAN') self.lbm.local_ip = LOCAL_IPV6 diff --git a/releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.yaml b/releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.yaml new file mode 100644 index 00000000000..91de52f5ac0 --- /dev/null +++ b/releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.yaml @@ -0,0 +1,17 @@ +--- +prelude: > + Enable creation of VXLANs with different multicast + addresses allocated by VNI-address mappings. +features: + - The ability to control vni-multicast address + distribution in linuxbridge agent via new config + option - multicast_ranges. +other: + - Example configuration of `multicast_ranges` in + ml2_conf.ini under the `[vxlan]` config. section + multicast_ranges = 224.0.0.10:10:90,225.0.0.15:100:900 + For VNI between 10 and 90, the multicast address + 224.0.0.0.10 will be used, and for 100 through 900 + 225.0.0.15 will be used. Other VNI values will get + standard `vxlan_group` address. For more info see RFE + ``