Merge "Add VLAN type conntrack direct flow" into stable/stein

This commit is contained in:
Zuul 2020-03-11 05:32:01 +00:00 committed by Gerrit Code Review
commit 2472180911
4 changed files with 169 additions and 4 deletions

View File

@ -207,6 +207,8 @@ identifying the port (for egress traffic based on the switch port number, and
for ingress traffic based on the network id and destination MAC address);
``register 6`` contains a value identifying the network (which is also the
OVSDB port tag) to isolate connections into separate conntrack zones.
For VLAN networks, the physical VLAN tag will be used to act as an extra
match rule to do such identifying work as well.
::

View File

@ -67,6 +67,22 @@ def create_reg_numbers(flow_params):
flow_params, ovsfw_consts.REG_REMOTE_GROUP, 'reg_remote_group')
def get_segmentation_id_from_other_config(bridge, port_name):
"""Return segmentation_id stored in OVSDB other_config metadata.
:param bridge: OVSBridge instance where port is.
:param port_name: Name of the port.
"""
try:
other_config = bridge.db_get_val(
'Port', port_name, 'other_config')
network_type = other_config.get('network_type')
if lib_const.TYPE_VLAN == network_type:
return int(other_config.get('segmentation_id'))
except (TypeError, ValueError):
pass
def get_tag_from_other_config(bridge, port_name):
"""Return tag stored in OVSDB other_config metadata.
@ -120,9 +136,10 @@ class SecurityGroup(object):
class OFPort(object):
def __init__(self, port_dict, ovs_port, vlan_tag):
def __init__(self, port_dict, ovs_port, vlan_tag, segment_id=None):
self.id = port_dict['device']
self.vlan_tag = vlan_tag
self.segment_id = segment_id
self.mac = ovs_port.vif_mac
self.lla_address = str(netutils.get_ipv6_addr_by_EUI64(
lib_const.IPv6_LLA_PREFIX, self.mac))
@ -518,6 +535,10 @@ class OVSFirewallDriver(firewall.FirewallDriver):
def _get_port_vlan_tag(self, port_name):
return get_tag_from_other_config(self.int_br.br, port_name)
def _get_port_segmentation_id(self, port_name):
return get_segmentation_id_from_other_config(
self.int_br.br, port_name)
def get_ofport(self, port):
port_id = port['device']
return self.sg_port_map.ports.get(port_id)
@ -533,12 +554,16 @@ class OVSFirewallDriver(firewall.FirewallDriver):
of_port = self.sg_port_map.ports[port_id]
except KeyError:
port_vlan_id = self._get_port_vlan_tag(ovs_port.port_name)
of_port = OFPort(port, ovs_port, port_vlan_id)
segment_id = self._get_port_segmentation_id(
ovs_port.port_name)
of_port = OFPort(port, ovs_port, port_vlan_id,
segment_id)
self.sg_port_map.create_port(of_port, port)
else:
if of_port.ofport != ovs_port.ofport:
self.sg_port_map.remove_port(of_port)
of_port = OFPort(port, ovs_port, of_port.vlan_tag)
of_port = OFPort(port, ovs_port, of_port.vlan_tag,
of_port.segment_id)
self.sg_port_map.create_port(of_port, port)
else:
self.sg_port_map.update_port(of_port, port)
@ -708,6 +733,33 @@ class OVSFirewallDriver(firewall.FirewallDriver):
return {id_: port.neutron_port_dict
for id_, port in self.sg_port_map.ports.items()}
def install_vlan_direct_flow(self, mac, segment_id, ofport, local_vlan):
# If the port segment_id is not None/0, the
# port's network type must be VLAN type.
if segment_id:
self._add_flow(
table=ovs_consts.TRANSIENT_TABLE,
priority=90,
dl_dst=mac,
dl_vlan='0x%x' % segment_id,
actions='set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'strip_vlan,resubmit(,{:d})'.format(
ofport,
ovsfw_consts.REG_PORT,
# This always needs the local vlan.
local_vlan,
ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)
)
def delete_vlan_direct_flow(self, mac, segment_id):
if segment_id:
self._strict_delete_flow(priority=90,
table=ovs_consts.TRANSIENT_TABLE,
dl_dst=mac,
dl_vlan=segment_id)
def initialize_port_flows(self, port):
"""Set base flows for port
@ -731,6 +783,9 @@ class OVSFirewallDriver(firewall.FirewallDriver):
# Identify ingress flows
for mac_addr in port.all_allowed_macs:
self.install_vlan_direct_flow(
mac_addr, port.segment_id, port.ofport, port.vlan_tag)
self._add_flow(
table=ovs_consts.TRANSIENT_TABLE,
priority=90,
@ -1234,6 +1289,7 @@ class OVSFirewallDriver(firewall.FirewallDriver):
table=ovs_consts.TRANSIENT_TABLE,
dl_dst=mac_addr,
dl_vlan=port.vlan_tag)
self.delete_vlan_direct_flow(mac_addr, port.segment_id)
self._delete_flows(table=ovs_consts.ACCEPT_OR_INGRESS_TABLE,
dl_dst=mac_addr, reg_net=port.vlan_tag)
self._strict_delete_flow(priority=100,

View File

@ -32,12 +32,14 @@ from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \
from neutron.tests import base
TESTING_VLAN_TAG = 1
TESTING_SEGMENT = 1000
def create_ofport(port_dict):
ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1,
port_name="port-name")
return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG)
return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG,
segment_id=TESTING_SEGMENT)
class TestCreateRegNumbers(base.BaseTestCase):
@ -580,6 +582,103 @@ class TestOVSFirewallDriver(base.BaseTestCase):
self.firewall.prepare_port_filter(port_dict)
self.assertFalse(m_init_flows.called)
def test_initialize_port_flows_vlan_dvr_conntrack_direct(self):
port_dict = {
'device': 'port-id',
'security_groups': [1]}
of_port = create_ofport(port_dict)
self.firewall.sg_port_map.ports[of_port.id] = of_port
port = self.firewall.get_or_create_ofport(port_dict)
self.firewall.initialize_port_flows(port)
call_args1 = {
'table': ovs_consts.TRANSIENT_TABLE,
'priority': 100,
'in_port': port.ofport,
'actions': 'set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'resubmit(,{:d})'.format(
port.ofport,
ovsfw_consts.REG_PORT,
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_EGRESS_TABLE)}
egress_flow_call = mock.call(**call_args1)
call_args2 = {
'table': ovs_consts.TRANSIENT_TABLE,
'priority': 90,
'dl_dst': port.mac,
'dl_vlan': '0x%x' % port.segment_id,
'actions': 'set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'strip_vlan,resubmit(,{:d})'.format(
port.ofport,
ovsfw_consts.REG_PORT,
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)}
ingress_flow_call1 = mock.call(**call_args2)
call_args3 = {
'table': ovs_consts.TRANSIENT_TABLE,
'priority': 90,
'dl_dst': port.mac,
'dl_vlan': '0x%x' % port.vlan_tag,
'actions': 'set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'strip_vlan,resubmit(,{:d})'.format(
port.ofport,
ovsfw_consts.REG_PORT,
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)}
ingress_flow_call2 = mock.call(**call_args3)
self.mock_bridge.br.add_flow.assert_has_calls(
[egress_flow_call, ingress_flow_call1, ingress_flow_call2])
def test_delete_all_port_flows(self):
port_dict = {
'device': 'port-id',
'security_groups': [1]}
of_port = create_ofport(port_dict)
self.firewall.sg_port_map.ports[of_port.id] = of_port
port = self.firewall.get_or_create_ofport(port_dict)
self.firewall.delete_all_port_flows(port)
call_args1 = {"strict": True,
"priority": 90,
"table": ovs_consts.TRANSIENT_TABLE,
"dl_dst": port.mac,
"dl_vlan": port.vlan_tag}
flow1 = mock.call(**call_args1)
call_args2 = {"strict": True,
"priority": 90,
"table": ovs_consts.TRANSIENT_TABLE,
"dl_dst": port.mac,
"dl_vlan": port.segment_id}
flow2 = mock.call(**call_args2)
call_args3 = {"table": ovs_consts.ACCEPT_OR_INGRESS_TABLE,
"dl_dst": port.mac,
"reg6": port.vlan_tag}
flow3 = mock.call(**call_args3)
call_args4 = {"in_port": port.ofport,
"strict": True,
"priority": 100,
"table": ovs_consts.TRANSIENT_TABLE}
flow4 = mock.call(**call_args4)
call_args5 = {"reg5": port.ofport}
flow5 = mock.call(**call_args5)
self.mock_bridge.br.delete_flows.assert_has_calls(
[flow1, flow2, flow3, flow4, flow5])
def test_prepare_port_filter_initialized_port(self):
port_dict = {'device': 'port-id',
'security_groups': [1]}

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Add a new match rule based on physical VLAN tag for OpenFlow firewall
traffic identifying mechanism to the TRANSIENT table. This fixes the
distributed router east-west traffic between VLAN type networks.
For more information, see bug
`1831534 <https://bugs.launchpad.net/neutron/+bug/1831534>`_.