Prevent "qbr" Linux Bridge from replying to ARP messages
The Linux Bridge in between the VM TAP interface and OVS should [1][2]: - Reply only if the target IP address is local address configured on the incoming interface. - Always use the best local address. [1]http://kb.linuxvirtualserver.org/wiki/Using_arp_announce/arp_ignore_to_disable_ARP [2]http://linux-ip.net/html/ether-arp.html#ether-arp-flux Change-Id: I8721b680bbd9f59a67bd8e6855ffb291c208cdb8 Closes-Bug: #1825888 (cherry picked from commit9ad9b84839
) (cherry picked from commitc42e7641f0
) (cherry picked from commitca9963c294
)
This commit is contained in:
parent
e09d87900a
commit
7b84b527ec
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
security:
|
||||
- |
|
||||
Prevent Linux Bridge from replying to ARP messages. It should reply only if
|
||||
the target IP address is a local address configured on the incoming
|
||||
interface and it should always use the best local address. See `The ARP
|
||||
flux problem <http://linux-ip.net/html/ether-arp.html#ether-arp-flux>`_ for
|
||||
more information.
|
|
@ -56,6 +56,22 @@ def _ip_bridge_cmd(action, params, device):
|
|||
return cmd
|
||||
|
||||
|
||||
# TODO(ralonsoh): extract into common module
|
||||
def _arp_filtering(bridge):
|
||||
"""Prevent the bridge from replying to ARP messages with machine local IPs
|
||||
|
||||
1. Reply only if the target IP address is local address configured on the
|
||||
incoming interface.
|
||||
2. Always use the best local address.
|
||||
"""
|
||||
arp_params = [('/proc/sys/net/ipv4/conf/%s/arp_ignore' % bridge, '1'),
|
||||
('/proc/sys/net/ipv4/conf/%s/arp_announce' % bridge, '2')]
|
||||
for parameter, value in arp_params:
|
||||
if os.path.exists(parameter):
|
||||
with open(parameter, 'w') as f:
|
||||
f.write(value)
|
||||
|
||||
|
||||
@privsep.vif_plug.entrypoint
|
||||
def ensure_vlan_bridge(vlan_num, bridge, bridge_interface,
|
||||
net_attrs=None, mac_address=None,
|
||||
|
@ -141,6 +157,7 @@ def _ensure_bridge_privileged(bridge, interface, net_attrs, gateway,
|
|||
# instead it inherits the MAC address of the first device on the
|
||||
# bridge, which will either be the vlan interface, or a
|
||||
# physical NIC.
|
||||
_arp_filtering(bridge)
|
||||
ip_lib.set(bridge, state='up')
|
||||
|
||||
if interface:
|
||||
|
|
|
@ -138,12 +138,13 @@ class LinuxNetTest(testtools.TestCase):
|
|||
mock.call('fake-interface', state='up')]
|
||||
mock_ip_set.assert_has_calls(calls)
|
||||
|
||||
@mock.patch.object(linux_net, "_arp_filtering")
|
||||
@mock.patch.object(ip_lib, "set")
|
||||
@mock.patch.object(os.path, "exists", return_value=False)
|
||||
@mock.patch.object(processutils, "execute")
|
||||
@mock.patch.object(linux_net, "device_exists", return_value=False)
|
||||
def test_ensure_bridge_new_ipv4(self, mock_dev_exists, mock_exec,
|
||||
mock_path_exists, mock_ip_set):
|
||||
mock_path_exists, mock_ip_set, *args):
|
||||
linux_net.ensure_bridge("br0", None, filtering=False)
|
||||
|
||||
calls = [mock.call('brctl', 'addbr', 'br0'),
|
||||
|
@ -153,12 +154,13 @@ class LinuxNetTest(testtools.TestCase):
|
|||
mock_dev_exists.assert_called_once_with("br0")
|
||||
mock_ip_set.assert_called_once_with('br0', state='up')
|
||||
|
||||
@mock.patch.object(linux_net, "_arp_filtering")
|
||||
@mock.patch.object(ip_lib, "set")
|
||||
@mock.patch.object(os.path, "exists", return_value=True)
|
||||
@mock.patch.object(processutils, "execute")
|
||||
@mock.patch.object(linux_net, "device_exists", return_value=False)
|
||||
def test_ensure_bridge_new_ipv6(self, mock_dev_exists, mock_exec,
|
||||
mock_path_exists, mock_ip_set):
|
||||
mock_path_exists, mock_ip_set, *args):
|
||||
linux_net.ensure_bridge("br0", None, filtering=False)
|
||||
|
||||
calls = [mock.call('brctl', 'addbr', 'br0'),
|
||||
|
|
|
@ -83,6 +83,22 @@ def _create_ovs_bridge_cmd(bridge, datapath_type):
|
|||
'--', 'set', 'Bridge', bridge, 'datapath_type=%s' % datapath_type]
|
||||
|
||||
|
||||
# TODO(ralonsoh): extract into common module
|
||||
def _arp_filtering(bridge):
|
||||
"""Prevent the bridge from replying to ARP messages with machine local IPs
|
||||
|
||||
1. Reply only if the target IP address is local address configured on the
|
||||
incoming interface.
|
||||
2. Always use the best local address.
|
||||
"""
|
||||
arp_params = [('/proc/sys/net/ipv4/conf/%s/arp_ignore' % bridge, '1'),
|
||||
('/proc/sys/net/ipv4/conf/%s/arp_announce' % bridge, '2')]
|
||||
for parameter, value in arp_params:
|
||||
if os.path.exists(parameter):
|
||||
with open(parameter, 'w') as f:
|
||||
f.write(value)
|
||||
|
||||
|
||||
@privsep.vif_plug.entrypoint
|
||||
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id,
|
||||
mtu=None, interface_type=None, timeout=None,
|
||||
|
@ -173,6 +189,7 @@ def ensure_bridge(bridge):
|
|||
disv6,
|
||||
process_input='1',
|
||||
check_exit_code=[0, 1])
|
||||
_arp_filtering(bridge)
|
||||
# we bring up the bridge to allow it to switch packets
|
||||
set_interface_state(bridge, 'up')
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import testtools
|
|||
|
||||
from os_vif.internal.command import ip as ip_lib
|
||||
from oslo_concurrency import processutils
|
||||
from six.moves import builtins
|
||||
|
||||
from vif_plug_ovs import constants
|
||||
from vif_plug_ovs import exception
|
||||
|
@ -40,12 +41,13 @@ class LinuxNetTest(testtools.TestCase):
|
|||
check_exit_code=[0, 2, 254])
|
||||
mock_dev_exists.assert_has_calls([mock.call("br0")])
|
||||
|
||||
@mock.patch.object(linux_net, "_arp_filtering")
|
||||
@mock.patch.object(ip_lib, "set")
|
||||
@mock.patch.object(os.path, "exists", return_value=False)
|
||||
@mock.patch.object(processutils, "execute")
|
||||
@mock.patch.object(linux_net, "device_exists", return_value=False)
|
||||
def test_ensure_bridge_new_ipv4(self, mock_dev_exists, mock_execute,
|
||||
mock_path_exists, mock_ip_set):
|
||||
mock_path_exists, mock_ip_set, *args):
|
||||
linux_net.ensure_bridge("br0")
|
||||
|
||||
calls = [
|
||||
|
@ -61,12 +63,13 @@ class LinuxNetTest(testtools.TestCase):
|
|||
mock_ip_set.assert_called_once_with('br0', state='up',
|
||||
check_exit_code=[0, 2, 254])
|
||||
|
||||
@mock.patch.object(linux_net, "_arp_filtering")
|
||||
@mock.patch.object(ip_lib, "set")
|
||||
@mock.patch.object(os.path, "exists", return_value=True)
|
||||
@mock.patch.object(processutils, "execute")
|
||||
@mock.patch.object(linux_net, "device_exists", return_value=False)
|
||||
def test_ensure_bridge_new_ipv6(self, mock_dev_exists, mock_execute,
|
||||
mock_path_exists, mock_ip_set):
|
||||
mock_path_exists, mock_ip_set, *args):
|
||||
linux_net.ensure_bridge("br0")
|
||||
|
||||
calls = [
|
||||
|
@ -625,3 +628,16 @@ class LinuxNetTest(testtools.TestCase):
|
|||
linux_net.get_vf_num_by_pci_address,
|
||||
'0000:00:00.1'
|
||||
)
|
||||
|
||||
@mock.patch.object(os.path, 'exists', return_value=True)
|
||||
@mock.patch.object(builtins, 'open')
|
||||
def test__arp_filtering(self, mock_open, *args):
|
||||
mock_open.side_effect = mock.mock_open(read_data=mock.Mock())
|
||||
linux_net._arp_filtering("br0")
|
||||
|
||||
mock_open.assert_has_calls([
|
||||
mock.call('/proc/sys/net/ipv4/conf/br0/arp_ignore', 'w'),
|
||||
mock.call('/proc/sys/net/ipv4/conf/br0/arp_announce', 'w')])
|
||||
mock_open.side_effect.return_value.write.assert_has_calls([
|
||||
mock.call('1'),
|
||||
mock.call('2')])
|
||||
|
|
Loading…
Reference in New Issue