Merge "ovs-agent: prevent ARP requests with faked IP addresses"
This commit is contained in:
commit
6ba78cec1a
|
@ -783,20 +783,18 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
addresses += [p['ip_address']
|
addresses += [p['ip_address']
|
||||||
for p in port_details['allowed_address_pairs']]
|
for p in port_details['allowed_address_pairs']]
|
||||||
|
|
||||||
# allow ARP replies as long as they match addresses that actually
|
# allow ARPs as long as they match addresses that actually
|
||||||
# belong to the port.
|
# belong to the port.
|
||||||
for ip in addresses:
|
for ip in addresses:
|
||||||
if netaddr.IPNetwork(ip).version != 4:
|
if netaddr.IPNetwork(ip).version != 4:
|
||||||
continue
|
continue
|
||||||
bridge.add_flow(
|
bridge.add_flow(table=constants.ARP_SPOOF_TABLE, priority=2,
|
||||||
table=constants.ARP_SPOOF_TABLE, priority=2,
|
proto='arp', arp_spa=ip, in_port=vif.ofport,
|
||||||
proto='arp', arp_op=constants.ARP_REPLY, arp_spa=ip,
|
actions="NORMAL")
|
||||||
in_port=vif.ofport, actions="NORMAL")
|
|
||||||
|
|
||||||
# drop any ARP replies in this table that aren't explicitly allowed
|
# drop any ARPs in this table that aren't explicitly allowed
|
||||||
bridge.add_flow(
|
bridge.add_flow(table=constants.ARP_SPOOF_TABLE, priority=1,
|
||||||
table=constants.ARP_SPOOF_TABLE, priority=1, proto='arp',
|
proto='arp', actions="DROP")
|
||||||
arp_op=constants.ARP_REPLY, actions="DROP")
|
|
||||||
|
|
||||||
# Now that the rules are ready, direct ARP traffic from the port into
|
# Now that the rules are ready, direct ARP traffic from the port into
|
||||||
# the anti-spoof table.
|
# the anti-spoof table.
|
||||||
|
@ -804,7 +802,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||||
# on ARP headers will just process traffic normally.
|
# on ARP headers will just process traffic normally.
|
||||||
bridge.add_flow(table=constants.LOCAL_SWITCHING,
|
bridge.add_flow(table=constants.LOCAL_SWITCHING,
|
||||||
priority=10, proto='arp', in_port=vif.ofport,
|
priority=10, proto='arp', in_port=vif.ofport,
|
||||||
arp_op=constants.ARP_REPLY,
|
|
||||||
actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))
|
actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))
|
||||||
|
|
||||||
def port_unbound(self, vif_id, net_uuid=None):
|
def port_unbound(self, vif_id, net_uuid=None):
|
||||||
|
|
|
@ -13,12 +13,14 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.cmd.sanity import checks
|
from neutron.cmd.sanity import checks
|
||||||
from neutron.plugins.openvswitch.agent import ovs_neutron_agent as ovsagt
|
from neutron.plugins.openvswitch.agent import ovs_neutron_agent as ovsagt
|
||||||
from neutron.tests.common import machine_fixtures
|
from neutron.tests.common import machine_fixtures
|
||||||
from neutron.tests.common import net_helpers
|
from neutron.tests.common import net_helpers
|
||||||
from neutron.tests.functional.agent import test_ovs_lib
|
from neutron.tests.functional.agent import test_ovs_lib
|
||||||
from neutron.tests.functional import base
|
from neutron.tests.functional import base
|
||||||
|
from neutron.tests import tools
|
||||||
|
|
||||||
|
|
||||||
class ARPSpoofTestCase(test_ovs_lib.OVSBridgeTestBase,
|
class ARPSpoofTestCase(test_ovs_lib.OVSBridgeTestBase,
|
||||||
|
@ -73,6 +75,21 @@ class ARPSpoofTestCase(test_ovs_lib.OVSBridgeTestBase,
|
||||||
self.dst_p.addr.add('%s/24' % self.dst_addr)
|
self.dst_p.addr.add('%s/24' % self.dst_addr)
|
||||||
self.pinger.assert_no_ping(self.dst_addr)
|
self.pinger.assert_no_ping(self.dst_addr)
|
||||||
|
|
||||||
|
def test_arp_spoof_blocks_request(self):
|
||||||
|
# this will prevent the source from sending an ARP
|
||||||
|
# request with its own address
|
||||||
|
self._setup_arp_spoof_for_port(self.src_p.name, ['192.168.0.3'])
|
||||||
|
self.src_p.addr.add('%s/24' % self.src_addr)
|
||||||
|
self.dst_p.addr.add('%s/24' % self.dst_addr)
|
||||||
|
ns_ip_wrapper = ip_lib.IPWrapper(self.src_namespace)
|
||||||
|
try:
|
||||||
|
ns_ip_wrapper.netns.execute(['arping', '-I', self.src_p.name,
|
||||||
|
'-c1', self.dst_addr])
|
||||||
|
tools.fail("arping should have failed. The arp request should "
|
||||||
|
"have been blocked.")
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def test_arp_spoof_allowed_address_pairs(self):
|
def test_arp_spoof_allowed_address_pairs(self):
|
||||||
self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3',
|
self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3',
|
||||||
self.dst_addr])
|
self.dst_addr])
|
||||||
|
|
|
@ -1143,13 +1143,11 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||||
# make sure redirect into spoof table is installed
|
# make sure redirect into spoof table is installed
|
||||||
int_br.add_flow.assert_any_call(
|
int_br.add_flow.assert_any_call(
|
||||||
table=constants.LOCAL_SWITCHING, in_port=vif.ofport,
|
table=constants.LOCAL_SWITCHING, in_port=vif.ofport,
|
||||||
arp_op=constants.ARP_REPLY, proto='arp', actions=mock.ANY,
|
proto='arp', actions=mock.ANY, priority=10)
|
||||||
priority=10)
|
|
||||||
# make sure drop rule for replies is installed
|
# make sure drop rule for replies is installed
|
||||||
int_br.add_flow.assert_any_call(
|
int_br.add_flow.assert_any_call(
|
||||||
table=constants.ARP_SPOOF_TABLE,
|
table=constants.ARP_SPOOF_TABLE,
|
||||||
proto='arp', arp_op=constants.ARP_REPLY, actions='DROP',
|
proto='arp', actions='DROP', priority=mock.ANY)
|
||||||
priority=mock.ANY)
|
|
||||||
|
|
||||||
def test_arp_spoofing_fixed_and_allowed_addresses(self):
|
def test_arp_spoofing_fixed_and_allowed_addresses(self):
|
||||||
vif = FakeVif()
|
vif = FakeVif()
|
||||||
|
@ -1167,8 +1165,7 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||||
'192.168.44.103/32'):
|
'192.168.44.103/32'):
|
||||||
int_br.add_flow.assert_any_call(
|
int_br.add_flow.assert_any_call(
|
||||||
table=constants.ARP_SPOOF_TABLE, in_port=vif.ofport,
|
table=constants.ARP_SPOOF_TABLE, in_port=vif.ofport,
|
||||||
proto='arp', arp_op=constants.ARP_REPLY, actions='NORMAL',
|
proto='arp', actions='NORMAL', arp_spa=addr, priority=mock.ANY)
|
||||||
arp_spa=addr, priority=mock.ANY)
|
|
||||||
|
|
||||||
def test__get_ofport_moves(self):
|
def test__get_ofport_moves(self):
|
||||||
previous = {'port1': 1, 'port2': 2}
|
previous = {'port1': 1, 'port2': 2}
|
||||||
|
|
Loading…
Reference in New Issue