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 <kevin@benton.pub>
DocImpact: VXLAN addresses used by linux bridge can be specified per VNI
Closes-Bug: #1579068
Change-Id: I24f272ccd6d61d9fa7ea3b6f256fabd381f5434a
This commit is contained in:
Jiri Kotlin 2016-06-23 14:04:07 +00:00 committed by Kevin Benton
parent d20c0edfc6
commit 8a596f35bb
4 changed files with 97 additions and 1 deletions

View File

@ -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 "
"<multicast address>:<vni_min>:<vni_max> triples "
"describing how to assign a multicast address to "
"VXLAN according to its VNI ID.")),
]
bridge_opts = [

View File

@ -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 "
"<multicast address>:<vni_min>:<vni_max> 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)))

View File

@ -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

View File

@ -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
`<https://bugs.launchpad.net/neutron/+bug/1579068>`