Browse Source

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

changes/88/715188/1
Zuul 3 months ago
committed by Gerrit Code Review
parent
commit
11def9e743
4 changed files with 169 additions and 4 deletions
  1. +2
    -0
      doc/source/contributor/internals/openvswitch_firewall.rst
  2. +59
    -3
      neutron/agent/linux/openvswitch_firewall/firewall.py
  3. +100
    -1
      neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py
  4. +8
    -0
      releasenotes/notes/vlan-type-conntrack-direct-d3d544f8471ed4ff.yaml

+ 2
- 0
doc/source/contributor/internals/openvswitch_firewall.rst View File

@@ -172,6 +172,8 @@ traffic.
Egress flow is determined by switch port number and ingress flow is determined
by destination mac address. ``register 6`` contains 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.

::



+ 59
- 3
neutron/agent/linux/openvswitch_firewall/firewall.py View File

@@ -66,6 +66,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.

@@ -119,9 +135,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))
@@ -516,6 +533,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)
@@ -531,12 +552,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)
@@ -702,6 +727,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

@@ -725,6 +777,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,
@@ -1228,6 +1283,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,


+ 100
- 1
neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py View File

@@ -33,12 +33,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):
@@ -581,6 +583,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]}


+ 8
- 0
releasenotes/notes/vlan-type-conntrack-direct-d3d544f8471ed4ff.yaml 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>`_.

Loading…
Cancel
Save