Merge "Co-existing between fwg and sg"

This commit is contained in:
Zuul 2018-01-02 18:12:31 +00:00 committed by Gerrit Code Review
commit 61887be145
4 changed files with 163 additions and 29 deletions

View File

@ -244,7 +244,7 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
def _accept_flow(self, **flow):
for f in rules.create_accept_flows(flow):
for f in rules.create_accept_flows(flow, self.sg_enabled):
self._add_flow(**f)
def _drop_flow(self, **flow):
@ -289,6 +289,9 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
# differs in constants
def _drop_all_unmatched_flows(self):
for table in fwaas_ovs_consts.OVS_FIREWALL_TABLES:
if (table == fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE and
self.sg_enabled):
continue
self.int_br.br.add_flow(table=table, priority=0, actions='drop')
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
@ -492,6 +495,19 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
self._initialize_egress(port)
self._initialize_ingress(port)
def _fwaas_process_colocated_ingress(self, port):
for mac_addr in port.all_allowed_macs:
self._add_flow(
table=ovs_consts.ACCEPT_OR_INGRESS_TABLE,
priority=105,
dl_dst=mac_addr,
reg_net=port.vlan_tag,
actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format(
port.ofport,
fwaas_ovs_consts.REG_PORT,
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
)
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
# which differs in constants (table numbers)
def _initialize_egress_ipv6_icmp(self, port):
@ -615,6 +631,9 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
)
# DHCP discovery
accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE
if self.sg_enabled:
accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE
for dl_type, src_port, dst_port in (
(constants.ETHERTYPE_IP, 68, 67),
(constants.ETHERTYPE_IPV6, 546, 547)):
@ -627,8 +646,7 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
nw_proto=lib_const.PROTO_NUM_UDP,
tp_src=src_port,
tp_dst=dst_port,
actions='resubmit(,{:d})'.format(
fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE)
actions='resubmit(,{:d})'.format(accept_or_ingress)
)
# Ban dhcp service running on an instance
for dl_type, src_port, dst_port in (
@ -670,33 +688,37 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
# Fill in accept_or_ingress table by checking that traffic is ingress
# and if not, accept it
for mac_addr in port.all_allowed_macs:
if self.sg_enabled:
self._fwaas_process_colocated_ingress(port)
else:
for mac_addr in port.all_allowed_macs:
self._add_flow(
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
priority=100,
dl_dst=mac_addr,
reg_net=port.vlan_tag,
actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format(
port.ofport,
fwaas_ovs_consts.REG_PORT,
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
)
for ethertype in [constants.ETHERTYPE_IP,
constants.ETHERTYPE_IPV6]:
self._add_flow(
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
priority=90,
dl_type=ethertype,
reg_port=port.ofport,
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),normal'.
format(fwaas_ovs_consts.REG_NET)
)
self._add_flow(
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
priority=100,
dl_dst=mac_addr,
reg_net=port.vlan_tag,
actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format(
port.ofport,
fwaas_ovs_consts.REG_PORT,
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
)
for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]:
self._add_flow(
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
priority=90,
dl_type=ethertype,
priority=80,
reg_port=port.ofport,
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),normal'.format(
fwaas_ovs_consts.REG_NET)
actions='normal'
)
self._add_flow(
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
priority=80,
reg_port=port.ofport,
actions='normal'
)
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
# which differs in constants (table numbers)
@ -935,13 +957,17 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
# which differs in constants (table numbers)
def delete_all_port_flows(self, port):
"""Delete all flows for given port"""
accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE
if self.sg_enabled:
accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE
for mac_addr in port.all_allowed_macs:
self._strict_delete_flow(priority=95,
table=ovs_consts.TRANSIENT_TABLE,
dl_dst=mac_addr,
dl_vlan=port.vlan_tag)
self._delete_flows(
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
table=accept_or_ingress,
dl_dst=mac_addr, reg_net=port.vlan_tag)
self._strict_delete_flow(priority=105,
table=ovs_consts.TRANSIENT_TABLE,

View File

@ -19,6 +19,8 @@ from oslo_log import log as logging
from neutron.agent import firewall
from neutron.common import utils
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \
as ovs_consts
from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \
import constants as fwaas_ovs_consts
@ -167,11 +169,24 @@ def create_icmp_flows(flow_template, rule):
return [flow]
def create_accept_flows(flow):
def resubmit_to_sg(flow):
if flow['table'] == fwaas_ovs_consts.FW_RULES_EGRESS_TABLE:
flow['actions'] = 'resubmit(,{:d})'.format(
ovs_consts.RULES_EGRESS_TABLE)
if flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE:
flow['actions'] = 'resubmit(,{:d})'.format(
ovs_consts.RULES_INGRESS_TABLE)
def create_accept_flows(flow, sg_enabled=False):
flow['ct_state'] = CT_STATES[0]
if sg_enabled:
resubmit_to_sg(flow)
result = [flow.copy()]
flow['ct_state'] = CT_STATES[1]
if flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE:
if sg_enabled:
resubmit_to_sg(flow)
elif flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE:
flow['actions'] = (
'ct(commit,zone=NXM_NX_REG{:d}[0..15]),{:s}'.format(
fwaas_ovs_consts.REG_NET, flow['actions']))

View File

@ -447,6 +447,45 @@ class TestOVSFirewallDriver(base.BaseTestCase):
for call in exp_ingress_classifier, exp_egress_classifier, filter_rule:
self.assertIn(call, calls)
def test_prepare_port_filter_in_coexistence_mode(self):
port_dict = {'device': 'port-id',
'firewall_group': 1,
'fixed_ips': [{'subnet_id': "some_subnet_id_here",
'ip_address': "10.0.0.1"}],
'lvlan': TESTING_VLAN_TAG}
self._prepare_firewall_group()
self.firewall.sg_enabled = True
self.firewall.prepare_port_filter(port_dict)
exp_egress_classifier = mock.call(
actions='set_field:{:d}->reg5,set_field:{:d}->reg6,'
'resubmit(,{:d})'.format(
self.port_ofport, TESTING_VLAN_TAG,
fwaas_ovs_consts.FW_BASE_EGRESS_TABLE),
in_port=self.port_ofport,
priority=105,
table=ovs_consts.TRANSIENT_TABLE)
exp_ingress_classifier = mock.call(
actions='set_field:{:d}->reg5,set_field:{:d}->reg6,'
'strip_vlan,resubmit(,{:d})'.format(
self.port_ofport, TESTING_VLAN_TAG,
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
dl_dst=self.port_mac,
dl_vlan='0x%x' % TESTING_VLAN_TAG,
priority=95,
table=ovs_consts.TRANSIENT_TABLE)
filter_rule = mock.call(
actions='resubmit(,{:d})'.format(ovs_consts.RULES_INGRESS_TABLE),
dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP),
nw_proto=constants.PROTO_NUM_TCP,
priority=70,
reg5=self.port_ofport,
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE,
tcp_dst='0x007b')
calls = self.mock_bridge.br.add_flow.call_args_list
for call in exp_ingress_classifier, exp_egress_classifier, filter_rule:
self.assertIn(call, calls)
def test_prepare_port_filter_port_security_disabled(self):
port_dict = {'device': 'port-id',
'firewall_group': 1,
@ -504,6 +543,44 @@ class TestOVSFirewallDriver(base.BaseTestCase):
self.mock_bridge.br.add_flow.assert_has_calls(filter_rules,
any_order=True)
def test_update_port_filter_in_coexistence_mode(self):
port_dict = {'device': 'port-id',
'firewall_group': 1,
'lvlan': TESTING_VLAN_TAG}
self._prepare_firewall_group()
self.firewall.sg_enabled = True
self.firewall.prepare_port_filter(port_dict)
port_dict['firewall_group'] = 2
self.mock_bridge.reset_mock()
self.firewall.update_port_filter(port_dict)
self.assertTrue(self.mock_bridge.br.delete_flows.called)
filter_rules = [
mock.call(
actions='resubmit(,{:d})'.format(
ovs_consts.RULES_EGRESS_TABLE),
dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP),
nw_proto=constants.PROTO_NUM_UDP,
priority=71,
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
reg5=self.port_ofport,
table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE),
# XXX FIXME NOTE(ivasilevskaya) this test originally tested that
# flows for SG with remote_group=this group were generated with
# proper conjunction action. If the original idea that conj_manager
# isn't needed for firewall groups proves to be wrong this needs to
# be revizited and properly fixed/covered with tests
mock.call(
actions='resubmit(,{:d})'.format(
ovs_consts.RULES_EGRESS_TABLE),
ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY,
dl_type=mock.ANY,
nw_proto=6,
priority=70, reg5=self.port_ofport,
table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE)]
self.mock_bridge.br.add_flow.assert_has_calls(filter_rules,
any_order=True)
def test_update_port_filter_create_new_port_if_not_present(self):
port_dict = {'device': 'port-id',
'firewall_group': 1}

View File

@ -0,0 +1,16 @@
---
prelude: >
Coexistence between security group and firewall group.
features:
- L2 firewall group driver based OVS can work in coexistence mode.
That means, if a port is associated with both firewall group and
security group, then a packet must be allowed by both features.
other:
- If a port is associated with both firewall group & security group and
there is a security group logging, which is enabled to collect ``DROP``
events for this port, then most of invalid packets will be dropped at
firewall group for performance reason except first dropped packet, which
is allowed by firewall group but not accepted by security group. So not
every dropped packet will be logged (like in case of security group
works in standalone mode).