Selaa lähdekoodia

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 commit 9ad9b84839)
(cherry picked from commit c42e7641f0)
(cherry picked from commit ca9963c294)
tags/1.9.2^0
Rodolfo Alonso Hernandez Matt Riedemann 11 kuukautta sitten
vanhempi
commit
7b84b527ec
5 muutettua tiedostoa jossa 64 lisäystä ja 4 poistoa
  1. +8
    -0
      releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml
  2. +17
    -0
      vif_plug_linux_bridge/linux_net.py
  3. +4
    -2
      vif_plug_linux_bridge/tests/unit/test_linux_net.py
  4. +17
    -0
      vif_plug_ovs/linux_net.py
  5. +18
    -2
      vif_plug_ovs/tests/unit/test_linux_net.py

+ 8
- 0
releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml Näytä tiedosto

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

+ 17
- 0
vif_plug_linux_bridge/linux_net.py Näytä tiedosto

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


+ 4
- 2
vif_plug_linux_bridge/tests/unit/test_linux_net.py Näytä tiedosto

@@ -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'),


+ 17
- 0
vif_plug_ovs/linux_net.py Näytä tiedosto

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



+ 18
- 2
vif_plug_ovs/tests/unit/test_linux_net.py Näytä tiedosto

@@ -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…
Peruuta
Tallenna