From e34e67b8e6ad4a95762287161184a9f9ce81b429 Mon Sep 17 00:00:00 2001 From: John Nielsen Date: Wed, 22 Jul 2015 12:43:04 -0600 Subject: [PATCH] Extend vxlan_group option to allow a range of group addresses If vxlan_group is specified in CIDR notation, it is interpreted as a range of group addresses. VXLAN VNIs are mapped to group addresses in a many-to-one round robin fashion, or one-to-one if a large enough range is provided. Since VNIs are 24 bits, a /8 such as 239.0.0.0/8 allows each VNI to use a unique multicast group. (239.0.0.0/8 also happens to be the "site-local" multicast range.) With multiple VNIs on a single multicast group, it is likely that VTEPs will unnecessarily receive broadcast/unknown/multicast datagrams for VNIs in which they do not participate. Using a range of groups mitigates or eliminates this issue. It is thus an alternative to the l2_population extension and driver for environments where both multicast and linuxbridge are used. The default setting is unchanged, but the comments in the ini file suggest 239.0.0.0/8 as an alternative. Administrators are free to use any valid multicast range that can be expressed in CIDR notation, and should choose a size and starting address that make sense for their environment. DocImpact Closes-Bug: #1477331 Change-Id: If9a3487a28ba2b02a6ef934c5421cec5d505b53c --- etc/neutron/plugins/ml2/linuxbridge_agent.ini | 8 +++++-- .../linuxbridge/agent/common/config.py | 6 +++++- .../agent/linuxbridge_neutron_agent.py | 21 +++++++++++++++++-- .../agent/test_linuxbridge_neutron_agent.py | 13 ++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/etc/neutron/plugins/ml2/linuxbridge_agent.ini b/etc/neutron/plugins/ml2/linuxbridge_agent.ini index d1a01ba72ee..2ea019ed620 100644 --- a/etc/neutron/plugins/ml2/linuxbridge_agent.ini +++ b/etc/neutron/plugins/ml2/linuxbridge_agent.ini @@ -21,8 +21,12 @@ # (IntOpt) use specific TOS for vxlan interface protocol packets # tos = # -# (StrOpt) multicast group to use for broadcast emulation. -# This group must be the same on all the agents. +# (StrOpt) multicast group or group range to use for broadcast emulation. +# Specifying a range allows different VNIs to use different group addresses, +# reducing or eliminating spurious broadcast traffic to the tunnel endpoints. +# Ranges are specified by using CIDR notation. 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. # vxlan_group = 224.0.0.1 # # (StrOpt) Local IP address to use for VXLAN endpoints (required) diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py index 6f15236ba8f..daac8000049 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py @@ -30,7 +30,11 @@ vxlan_opts = [ cfg.IntOpt('tos', help=_("TOS for vxlan interface protocol packets.")), cfg.StrOpt('vxlan_group', default=DEFAULT_VXLAN_GROUP, - help=_("Multicast group for vxlan interface.")), + help=_("Multicast group(s) for vxlan interface. A range of " + "group addresses may be specified by using CIDR " + "notation. 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.BoolOpt('l2_population', default=False, 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 b9747a4ec04..fddace246d2 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -26,6 +26,7 @@ import time import eventlet eventlet.monkey_patch() +import netaddr from oslo_config import cfg from oslo_log import log as logging import oslo_messaging @@ -128,6 +129,22 @@ class LinuxBridgeManager(object): LOG.warning(_LW("Invalid Segmentation ID: %s, will lead to " "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.network.is_multicast() and + net.broadcast.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) + def get_all_neutron_bridges(self): neutron_bridge_list = [] bridge_list = os.listdir(BRIDGE_FS) @@ -241,7 +258,7 @@ class LinuxBridgeManager(object): 'segmentation_id': segmentation_id}) args = {'dev': self.local_int} if self.vxlan_mode == lconst.VXLAN_MCAST: - args['group'] = cfg.CONF.VXLAN.vxlan_group + args['group'] = self.get_vxlan_group(segmentation_id) if cfg.CONF.VXLAN.ttl: args['ttl'] = cfg.CONF.VXLAN.ttl if cfg.CONF.VXLAN.tos: @@ -547,7 +564,7 @@ class LinuxBridgeManager(object): def vxlan_mcast_supported(self): if not cfg.CONF.VXLAN.vxlan_group: - LOG.warning(_LW('VXLAN muticast group must be provided in ' + LOG.warning(_LW('VXLAN muticast group(s) must be provided in ' 'vxlan_group option to enable VXLAN MCAST mode')) return False if not ip_lib.iproute_arg_supported( 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 55b9bd821b4..5ba7796ccfa 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 @@ -397,6 +397,19 @@ class TestLinuxBridgeManager(base.BaseTestCase): "vxlan-" + str(vn_id)) self.assertIsNone(self.lbm.get_vxlan_device_name(vn_id + 1)) + def test_get_vxlan_group(self): + cfg.CONF.set_override('vxlan_group', '239.1.2.3/24', 'VXLAN') + vn_id = p_const.MAX_VXLAN_VNI + self.assertEqual('239.1.2.255', self.lbm.get_vxlan_group(vn_id)) + vn_id = 256 + 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_all_neutron_bridges(self): br_list = ["br-int", "brq1", "brq2", "br-ex"] with mock.patch.object(os, 'listdir') as listdir_fn: