diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 8f2148623b8..cdd37299e21 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -237,7 +237,7 @@ class IPWrapper(SubProcessBase): return IPDevice(name, namespace=self.namespace) def add_vxlan(self, name, vni, group=None, dev=None, ttl=None, tos=None, - local=None, port=None, proxy=False): + local=None, srcport=None, dstport=None, proxy=False): cmd = ['add', name, 'type', 'vxlan', 'id', vni] if group: cmd.extend(['group', group]) @@ -252,10 +252,13 @@ class IPWrapper(SubProcessBase): if proxy: cmd.append('proxy') # tuple: min,max - if port and len(port) == 2: - cmd.extend(['port', port[0], port[1]]) - elif port: - raise n_exc.NetworkVxlanPortRangeError(vxlan_range=port) + if srcport: + if len(srcport) == 2 and srcport[0] <= srcport[1]: + cmd.extend(['srcport', str(srcport[0]), str(srcport[1])]) + else: + raise n_exc.NetworkVxlanPortRangeError(vxlan_range=srcport) + if dstport: + cmd.extend(['dstport', str(dstport)]) self._as_root([], 'link', cmd) return (IPDevice(name, namespace=self.namespace)) diff --git a/neutron/conf/plugins/ml2/drivers/linuxbridge.py b/neutron/conf/plugins/ml2/drivers/linuxbridge.py index d1210874a14..6273162f8d7 100644 --- a/neutron/conf/plugins/ml2/drivers/linuxbridge.py +++ b/neutron/conf/plugins/ml2/drivers/linuxbridge.py @@ -47,6 +47,18 @@ vxlan_opts = [ "value must match the value of the 'overlay_ip_version' " "option in the ML2 plug-in configuration file on the " "neutron server node(s).")), + cfg.PortOpt('udp_srcport_min', default=0, + help=_("The minimum of the UDP source port range used for " + "VXLAN communication.")), + cfg.PortOpt('udp_srcport_max', default=0, + help=_("The maximum of the UDP source port range used for " + "VXLAN communication.")), + cfg.PortOpt('udp_dstport', + help=_("The UDP port used for VXLAN communication. By " + "default, the Linux kernel doesn't use the IANA " + "assigned standard value, so if you want to use it, " + "this option must be set to 4789. It is not set by " + "default because of backward compatibiltiy.")), cfg.BoolOpt('l2_population', default=False, help=_("Extension to use alongside ml2 plugin's l2population " "mechanism driver. It enables the plugin to populate " 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 61e2b6f1d08..c3c87dbdbcb 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -321,15 +321,17 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase): "VNI %(segmentation_id)s", {'interface': interface, 'segmentation_id': segmentation_id}) - args = {'dev': self.local_int} + args = {'dev': self.local_int, + 'srcport': (cfg.CONF.VXLAN.udp_srcport_min, + cfg.CONF.VXLAN.udp_srcport_max), + 'dstport': cfg.CONF.VXLAN.udp_dstport, + 'ttl': cfg.CONF.VXLAN.ttl, + 'tos': cfg.CONF.VXLAN.tos} if self.vxlan_mode == lconst.VXLAN_MCAST: args['group'] = self.get_vxlan_group(segmentation_id) - if cfg.CONF.VXLAN.ttl: - args['ttl'] = cfg.CONF.VXLAN.ttl - if cfg.CONF.VXLAN.tos: - args['tos'] = cfg.CONF.VXLAN.tos if cfg.CONF.VXLAN.l2_population: args['proxy'] = cfg.CONF.VXLAN.arp_responder + try: int_vxlan = self.ip.add_vxlan(interface, segmentation_id, **args) diff --git a/neutron/tests/unit/agent/linux/test_ip_lib.py b/neutron/tests/unit/agent/linux/test_ip_lib.py index f2844d2ee30..3ece9a41965 100644 --- a/neutron/tests/unit/agent/linux/test_ip_lib.py +++ b/neutron/tests/unit/agent/linux/test_ip_lib.py @@ -496,13 +496,13 @@ class TestIpWrapper(base.BaseTestCase): run_as_root=True, namespace=None, log_fail_as_error=True) - def test_add_vxlan_valid_port_length(self): + def test_add_vxlan_valid_srcport_length(self): retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0', group='group0', dev='dev0', ttl='ttl0', tos='tos0', local='local0', proxy=True, - port=('1', '2')) + srcport=(1, 2)) self.assertIsInstance(retval, ip_lib.IPDevice) self.assertEqual(retval.name, 'vxlan0') self.execute.assert_called_once_with([], 'link', @@ -511,17 +511,47 @@ class TestIpWrapper(base.BaseTestCase): 'group0', 'dev', 'dev0', 'ttl', 'ttl0', 'tos', 'tos0', 'local', 'local0', 'proxy', - 'port', '1', '2'], + 'srcport', '1', '2'], run_as_root=True, namespace=None, log_fail_as_error=True) - def test_add_vxlan_invalid_port_length(self): + def test_add_vxlan_invalid_srcport_length(self): wrapper = ip_lib.IPWrapper() self.assertRaises(n_exc.NetworkVxlanPortRangeError, wrapper.add_vxlan, 'vxlan0', 'vni0', group='group0', dev='dev0', ttl='ttl0', tos='tos0', local='local0', proxy=True, - port=('1', '2', '3')) + srcport=('1', '2', '3')) + + def test_add_vxlan_invalid_srcport_range(self): + wrapper = ip_lib.IPWrapper() + self.assertRaises(n_exc.NetworkVxlanPortRangeError, + wrapper.add_vxlan, 'vxlan0', 'vni0', group='group0', + dev='dev0', ttl='ttl0', tos='tos0', + local='local0', proxy=True, + srcport=(2000, 1000)) + + def test_add_vxlan_dstport(self): + retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0', + group='group0', + dev='dev0', ttl='ttl0', + tos='tos0', + local='local0', proxy=True, + srcport=(1, 2), + dstport=4789) + + self.assertIsInstance(retval, ip_lib.IPDevice) + self.assertEqual(retval.name, 'vxlan0') + self.execute.assert_called_once_with([], 'link', + ['add', 'vxlan0', 'type', + 'vxlan', 'id', 'vni0', 'group', + 'group0', 'dev', 'dev0', + 'ttl', 'ttl0', 'tos', 'tos0', + 'local', 'local0', 'proxy', + 'srcport', '1', '2', + 'dstport', '4789'], + run_as_root=True, namespace=None, + log_fail_as_error=True) def test_add_device_to_namespace(self): dev = mock.Mock() 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 ede6dc9eacd..5c3f6a80b05 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 @@ -392,6 +392,10 @@ class TestLinuxBridgeManager(base.BaseTestCase): self.assertEqual("vxlan-" + seg_id, retval) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", + srcport=(0, 0), + dstport=None, + ttl=None, + tos=None, dev=self.lbm.local_int) dv6_fn.assert_called_once_with() cfg.CONF.set_override('l2_population', 'True', 'VXLAN') @@ -399,6 +403,10 @@ class TestLinuxBridgeManager(base.BaseTestCase): self.lbm.ensure_vxlan(seg_id)) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", + srcport=(0, 0), + dstport=None, + ttl=None, + tos=None, dev=self.lbm.local_int, proxy=expected_proxy) diff --git a/releasenotes/notes/linuxbridge-vxlan-udp-ports-73b260efefa15a46.yaml b/releasenotes/notes/linuxbridge-vxlan-udp-ports-73b260efefa15a46.yaml new file mode 100644 index 00000000000..c8d5dd0e6cf --- /dev/null +++ b/releasenotes/notes/linuxbridge-vxlan-udp-ports-73b260efefa15a46.yaml @@ -0,0 +1,8 @@ +--- +features: + - UDP ports used by VXLAN in the LinuxBridge agent + can be configured now with the VXLAN.udp_srcport_min, + VXLAN.udp_srcport_max and VXLAN.udp_dstport config + options. To use the IANA assigned port number, set + VXLAN.udp_dstport to 4789. The default is not changed + from the Linux kernel default 8472.