ovs-fw: Clear conntrack information before egress pipeline

In case where Neutron logical port is placed directly to hypervisor,
hypervisor does a conntrack lookup before packets reach OVS integration
bridge. This patch introduces a rule with high priority that is placed
at the beginning of the egress pipeline. This rule removes conntrack
information from all packets if conntrack information is present. Then
packets continue in the egress pipeline.

That means all packets in egress pipeline are not tracked and ovs
firewall can do a lookup in correct zone. As for ingress pipeline, it
distinguishes between tracked - which are packets coming from egress
pipeline, and not tracked, which are inbound packets coming not from a
local port.

Change-Id: Ia4f524adce2b5ee6d98d3921cfb03d56ad6d0813
Closes-bug: #1747082
This commit is contained in:
Jakub Libosvar 2018-03-09 14:25:23 +00:00
parent b95434816f
commit 3327db80be
3 changed files with 57 additions and 13 deletions

View File

@ -192,6 +192,15 @@ information from conntrack. It says every port has its own conntrack zone
defined by value in ``register 6``. It's there to avoid accepting established
traffic that belongs to different port with same conntrack parameters.
The very first rule in ``table 71`` is a rule removing conntrack information
for a use-case where Neutron logical port is placed directly to the hypervisor.
In such case kernel does conntrack lookup before packet reaches Open vSwitch
bridge. Tracked packets are sent back for processing by the same table after
conntrack information is cleared.
::
table=71, priority=110,ct_state=+trk actions=ct_clear,resubmit(,71)
Rules below allow ICMPv6 traffic for multicast listeners, neighbour
solicitation and neighbour advertisement.
@ -236,14 +245,14 @@ combinations. All other packets are dropped.
::
table=71, priority=65,ct_state=-trk,ip,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,nw_src=192.168.0.1 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ct_state=-trk,ip,reg5=0x1,in_port=1,dl_src=fa:16:3e:8c:84:13,nw_src=10.0.0.1 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ct_state=-trk,ip,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,nw_src=192.168.0.2 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ct_state=-trk,ip,reg5=0x2,in_port=2,dl_src=fa:16:3e:8c:84:14,nw_src=10.1.0.0/24 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ct_state=-trk,ipv6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,ipv6_src=fe80::f816:3eff:fea4:2210 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ct_state=-trk,ipv6,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,ipv6_src=fe80::f816:3eff:fe24:57c7 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=10,ct_state=-trk,reg5=0x1,in_port=1 actions=resubmit(,93)
table=71, priority=10,ct_state=-trk,reg5=0x2,in_port=2 actions=resubmit(,93)
table=71, priority=65,ip,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,nw_src=192.168.0.1 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ip,reg5=0x1,in_port=1,dl_src=fa:16:3e:8c:84:13,nw_src=10.0.0.1 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ip,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,nw_src=192.168.0.2 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ip,reg5=0x2,in_port=2,dl_src=fa:16:3e:8c:84:14,nw_src=10.1.0.0/24 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ipv6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,ipv6_src=fe80::f816:3eff:fea4:2210 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=65,ipv6,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,ipv6_src=fe80::f816:3eff:fe24:57c7 actions=ct(table=72,zone=NXM_NX_REG6[0..15])
table=71, priority=10,reg5=0x1,in_port=1 actions=resubmit(,93)
table=71, priority=10,reg5=0x2,in_port=2 actions=resubmit(,93)
table=71, priority=0 actions=drop

View File

@ -394,6 +394,7 @@ class OVSFirewallDriver(firewall.FirewallDriver):
self.sg_to_delete = set()
self._deferred = False
self._drop_all_unmatched_flows()
self._initialize_common_flows()
self._initialize_third_party_tables()
self.conj_ip_manager = ConjIPFlowManager(self)
@ -446,6 +447,16 @@ class OVSFirewallDriver(firewall.FirewallDriver):
for table in ovs_consts.OVS_FIREWALL_TABLES:
self.int_br.br.add_flow(table=table, priority=0, actions='drop')
def _initialize_common_flows(self):
# Remove conntrack information from tracked packets
self._add_flow(
table=ovs_consts.BASE_EGRESS_TABLE,
priority=110,
ct_state=ovsfw_consts.OF_STATE_TRACKED,
actions='ct_clear,'
'resubmit(,%d)' % ovs_consts.BASE_EGRESS_TABLE,
)
def _initialize_third_party_tables(self):
self.int_br.br.add_flow(
table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE,
@ -743,7 +754,6 @@ class OVSFirewallDriver(firewall.FirewallDriver):
table=ovs_consts.BASE_EGRESS_TABLE,
priority=65,
reg_port=port.ofport,
ct_state=ovsfw_consts.OF_STATE_NOT_TRACKED,
dl_type=constants.ETHERTYPE_IP,
in_port=port.ofport,
dl_src=mac_addr,
@ -762,7 +772,6 @@ class OVSFirewallDriver(firewall.FirewallDriver):
priority=65,
reg_port=port.ofport,
in_port=port.ofport,
ct_state=ovsfw_consts.OF_STATE_NOT_TRACKED,
dl_type=constants.ETHERTYPE_IPV6,
dl_src=mac_addr,
ipv6_src=ip_addr,
@ -815,14 +824,14 @@ class OVSFirewallDriver(firewall.FirewallDriver):
actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE
)
# Drop all remaining not tracked egress connections
# Drop all remaining egress connections
self._add_flow(
table=ovs_consts.BASE_EGRESS_TABLE,
priority=10,
ct_state=ovsfw_consts.OF_STATE_NOT_TRACKED,
in_port=port.ofport,
reg_port=port.ofport,
actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE
actions='ct_clear,'
'resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE
)
# Fill in accept_or_ingress table by checking that traffic is ingress

View File

@ -30,6 +30,7 @@ import testscenarios
from neutron.agent import firewall
from neutron.agent.linux import iptables_firewall
from neutron.agent.linux import openvswitch_firewall
from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts
from neutron.cmd.sanity import checks
from neutron.common import constants as n_const
from neutron.conf.agent import securitygroups_rpc as security_config
@ -629,6 +630,31 @@ class FirewallTestCase(BaseFirewallTestCase):
self.tester.assert_net_unreachable(self.tester.EGRESS, '1.2.3.4')
@skip_if_firewall('iptables')
def test_tracked_connection(self):
# put an openflow rule to perform a CT lookup and hence packet will
# carry conntrack information
self.tester.bridge.add_flow(
table=0,
priority=200,
dl_type="0x0800",
ct_state=ovsfw_consts.OF_STATE_NOT_TRACKED,
actions="ct(table=0)"
)
# update the sg_group to make ping pass
sg_rules = [{'ethertype': constants.IPv4,
'direction': constants.INGRESS_DIRECTION,
'protocol': constants.PROTO_NAME_ICMP},
{'ethertype': constants.IPv4,
'direction': constants.EGRESS_DIRECTION}]
self.tester.assert_no_connection(protocol=self.tester.ICMP,
direction=self.tester.INGRESS)
self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules)
self.tester.assert_connection(protocol=self.tester.ICMP,
direction=self.tester.INGRESS)
class FirewallTestCaseIPv6(BaseFirewallTestCase):
scenarios = BaseFirewallTestCase.scenarios_ovs_fw_interfaces