Merge "Prevent binding IPv6 addresses to Neutron interfaces" into stable/liberty

This commit is contained in:
Jenkins 2016-03-23 20:33:54 +00:00 committed by Gerrit Code Review
commit 1c51a0a9e6
6 changed files with 91 additions and 56 deletions

View File

@ -16,12 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from neutron.agent.linux import ip_lib
from neutron.i18n import _LE
LOG = logging.getLogger(__name__)
class BridgeDevice(ip_lib.IPDevice):
@ -30,32 +25,6 @@ class BridgeDevice(ip_lib.IPDevice):
ip_wrapper = ip_lib.IPWrapper(self.namespace)
return ip_wrapper.netns.execute(cmd, run_as_root=True)
def _sysctl(self, cmd):
"""execute() doesn't return the exit status of the command it runs,
it returns stdout and stderr. Setting check_exit_code=True will cause
it to raise a RuntimeError if the exit status of the command is
non-zero, which in sysctl's case is an error. So we're normalizing
that into zero (success) and one (failure) here to mimic what
"echo $?" in a shell would be.
This is all because sysctl is too verbose and prints the value you
just set on success, unlike most other utilities that print nothing.
execute() will have dumped a message to the logs with the actual
output on failure, so it's not lost, and we don't need to print it
here.
"""
cmd = ['sysctl', '-w'] + cmd
ip_wrapper = ip_lib.IPWrapper(self.namespace)
try:
ip_wrapper.netns.execute(cmd, run_as_root=True,
check_exit_code=True)
except RuntimeError:
LOG.exception(_LE("Failed running %s"), cmd)
return 1
return 0
@classmethod
def addbr(cls, name, namespace=None):
bridge = cls(name, namespace)
@ -76,7 +45,3 @@ class BridgeDevice(ip_lib.IPDevice):
def disable_stp(self):
return self._brctl(['stp', self.name, 'off'])
def disable_ipv6(self):
cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % self.name
return self._sysctl([cmd])

View File

@ -328,6 +328,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
root_dev, ns_dev = ip.add_veth(tap_name,
device_name,
namespace2=namespace)
root_dev.disable_ipv6()
else:
ns_dev = ip.device(device_name)
@ -435,6 +436,7 @@ class IVSInterfaceDriver(LinuxInterfaceDriver):
tap_name = self._get_tap_name(device_name, prefix)
root_dev, ns_dev = ip.add_veth(tap_name, device_name)
root_dev.disable_ipv6()
self._ivs_add_port(tap_name, port_id, mac_address)
@ -482,6 +484,7 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
# Create ns_veth in a namespace if one is configured.
root_veth, ns_veth = ip.add_veth(tap_name, device_name,
namespace2=namespace)
root_veth.disable_ipv6()
ns_veth.link.set_address(mac_address)
if self.conf.network_device_mtu:

View File

@ -203,6 +203,12 @@ class IPWrapper(SubProcessBase):
if self.namespace:
device.link.set_netns(self.namespace)
def add_vlan(self, name, physical_interface, vlan_id):
cmd = ['add', 'link', physical_interface, 'name', name,
'type', 'vlan', 'id', vlan_id]
self._as_root([], 'link', cmd)
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):
cmd = ['add', name, 'type', 'vxlan', 'id', vni]
@ -284,6 +290,37 @@ class IPDevice(SubProcessBase):
LOG.exception(_LE("Failed deleting egress connection state of"
" floatingip %s"), ip_str)
def _sysctl(self, cmd):
"""execute() doesn't return the exit status of the command it runs,
it returns stdout and stderr. Setting check_exit_code=True will cause
it to raise a RuntimeError if the exit status of the command is
non-zero, which in sysctl's case is an error. So we're normalizing
that into zero (success) and one (failure) here to mimic what
"echo $?" in a shell would be.
This is all because sysctl is too verbose and prints the value you
just set on success, unlike most other utilities that print nothing.
execute() will have dumped a message to the logs with the actual
output on failure, so it's not lost, and we don't need to print it
here.
"""
cmd = ['sysctl', '-w'] + cmd
ip_wrapper = IPWrapper(self.namespace)
try:
ip_wrapper.netns.execute(cmd, run_as_root=True,
check_exit_code=True)
except RuntimeError:
LOG.exception(_LE("Failed running %s"), cmd)
return 1
return 0
def disable_ipv6(self):
sysctl_name = re.sub(r'\.', '/', self.name)
cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % sysctl_name
return self._sysctl([cmd])
class IpCommandBase(object):
COMMAND = ''
@ -870,6 +907,14 @@ class IpNetnsCommand(IpCommandBase):
return False
def vlan_in_use(segmentation_id, namespace=None):
"""Return True if VLAN ID is in use by an interface, else False."""
ip_wrapper = IPWrapper(namespace=namespace)
interfaces = ip_wrapper.netns.execute(["ip", "-d", "link", "list"],
check_exit_code=True)
return '802.1Q id %s ' % segmentation_id in interfaces
def vxlan_in_use(segmentation_id, namespace=None):
"""Return True if VXLAN VNID is in use by an interface, else False."""
ip_wrapper = IPWrapper(namespace=namespace)

View File

@ -293,14 +293,19 @@ class LinuxBridgeManager(object):
"%(physical_interface)s",
{'interface': interface, 'vlan_id': vlan_id,
'physical_interface': physical_interface})
if utils.execute(['ip', 'link', 'add', 'link',
physical_interface,
'name', interface, 'type', 'vlan', 'id',
vlan_id], run_as_root=True):
return
if utils.execute(['ip', 'link', 'set',
interface, 'up'], run_as_root=True):
return
try:
int_vlan = self.ip.add_vlan(interface, physical_interface,
vlan_id)
except RuntimeError:
with excutils.save_and_reraise_exception() as ctxt:
if ip_lib.vlan_in_use(vlan_id):
ctxt.reraise = False
LOG.error(_LE("Unable to create VLAN interface for "
"VLAN ID %s because it is in use by "
"another interface."), vlan_id)
return
int_vlan.disable_ipv6()
int_vlan.link.set_up()
LOG.debug("Done creating subinterface %s", interface)
return interface
@ -334,6 +339,7 @@ class LinuxBridgeManager(object):
"VNI %s because it is in use by another "
"interface."), segmentation_id)
return None
int_vxlan.disable_ipv6()
int_vxlan.link.set_up()
LOG.debug("Done creating vxlan interface %s", interface)
return interface

View File

@ -435,6 +435,17 @@ class TestIpWrapper(base.BaseTestCase):
self.assertNotIn(mock.call().delete('ns'),
ip_ns_cmd_cls.mock_calls)
def test_add_vlan(self):
retval = ip_lib.IPWrapper().add_vlan('eth0.1', 'eth0', '1')
self.assertIsInstance(retval, ip_lib.IPDevice)
self.assertEqual(retval.name, 'eth0.1')
self.execute.assert_called_once_with([], 'link',
['add', 'link', 'eth0',
'name', 'eth0.1',
'type', 'vlan', 'id', '1'],
run_as_root=True, namespace=None,
log_fail_as_error=True)
def test_add_vxlan_valid_port_length(self):
retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0',
group='group0',

View File

@ -52,6 +52,9 @@ class FakeIpDevice(object):
def __init__(self):
self.link = FakeIpLinkCommand()
def disable_ipv6(self):
pass
def get_linuxbridge_manager(bridge_mappings, interface_mappings):
with mock.patch.object(ip_lib.IPWrapper, 'get_device_by_ip',
@ -729,14 +732,14 @@ class TestLinuxBridgeManager(base.BaseTestCase):
de_fn.return_value = True
self.assertEqual(self.lbm.ensure_vlan("eth0", "1"), "eth0.1")
de_fn.return_value = False
with mock.patch.object(utils, 'execute') as exec_fn:
exec_fn.return_value = False
self.assertEqual(self.lbm.ensure_vlan("eth0", "1"), "eth0.1")
# FIXME(kevinbenton): validate the params to the exec_fn calls
self.assertEqual(exec_fn.call_count, 2)
exec_fn.return_value = True
self.assertIsNone(self.lbm.ensure_vlan("eth0", "1"))
self.assertEqual(exec_fn.call_count, 3)
vlan_dev = FakeIpDevice()
with mock.patch.object(vlan_dev, 'disable_ipv6') as dv6_fn,\
mock.patch.object(self.lbm.ip, 'add_vlan',
return_value=vlan_dev) as add_vlan_fn:
retval = self.lbm.ensure_vlan("eth0", "1")
self.assertEqual("eth0.1", retval)
add_vlan_fn.assert_called_with('eth0.1', 'eth0', '1')
dv6_fn.assert_called_once_with()
def test_ensure_vxlan(self):
seg_id = "12345678"
@ -746,14 +749,16 @@ class TestLinuxBridgeManager(base.BaseTestCase):
de_fn.return_value = True
self.assertEqual(self.lbm.ensure_vxlan(seg_id), "vxlan-" + seg_id)
de_fn.return_value = False
with mock.patch.object(self.lbm.ip,
'add_vxlan') as add_vxlan_fn:
add_vxlan_fn.return_value = FakeIpDevice()
self.assertEqual(self.lbm.ensure_vxlan(seg_id),
"vxlan-" + seg_id)
vxlan_dev = FakeIpDevice()
with mock.patch.object(vxlan_dev, 'disable_ipv6') as dv6_fn,\
mock.patch.object(self.lbm.ip, 'add_vxlan',
return_value=vxlan_dev) as add_vxlan_fn:
retval = self.lbm.ensure_vxlan(seg_id)
self.assertEqual("vxlan-" + seg_id, retval)
add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id,
group="224.0.0.1",
dev=self.lbm.local_int)
dv6_fn.assert_called_once_with()
cfg.CONF.set_override('l2_population', 'True', 'VXLAN')
self.assertEqual(self.lbm.ensure_vxlan(seg_id),
"vxlan-" + seg_id)