[ML2/OVS] Maintain learning OF rules for GARP/ND in br-tun
This patch adds 3 new OF rules in the br-tun bridge to learn with higher priority GARP/ND packets which are sent during e.g. failover. With those rules, packets will not be send to the old VXLAN tunnels after failover as GARP/ND packets sent will force to learn new tunnels for the MAC address. Closes-bug: #2123911 Change-Id: I6e74214908e70e3a35af266dee524d6cf90bad42 Signed-off-by: Slawek Kaplonski <skaplons@redhat.com>
This commit is contained in:
@@ -15,8 +15,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib import constants as lib_constants
|
||||
from neutron_lib.plugins.ml2 import ovs_constants as constants
|
||||
from os_ken.lib.packet import ether_types
|
||||
from os_ken.lib.packet import icmpv6
|
||||
from os_ken.lib.packet import in_proto
|
||||
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
import br_dvr_process
|
||||
@@ -33,6 +36,62 @@ class OVSTunnelBridge(ovs_bridge.OVSAgentBridge,
|
||||
dvr_process_next_table_id = constants.PATCH_LV_TO_TUN
|
||||
of_tables = constants.TUN_BR_ALL_TABLES
|
||||
|
||||
def _setup_learn_flows(self, ofpp, patch_int_ofport):
|
||||
flow_specs = [
|
||||
ofpp.NXFlowSpecMatch(src=('vlan_tci', 0),
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=12),
|
||||
ofpp.NXFlowSpecMatch(src=('eth_src', 0),
|
||||
dst=('eth_dst', 0),
|
||||
n_bits=48),
|
||||
ofpp.NXFlowSpecLoad(src=0,
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=16),
|
||||
ofpp.NXFlowSpecLoad(src=('tunnel_id', 0),
|
||||
dst=('tunnel_id', 0),
|
||||
n_bits=64),
|
||||
ofpp.NXFlowSpecOutput(src=('in_port', 0),
|
||||
dst='',
|
||||
n_bits=32),
|
||||
]
|
||||
actions = [
|
||||
ofpp.NXActionLearn(table_id=constants.UCAST_TO_TUN,
|
||||
cookie=self.default_cookie,
|
||||
priority=1,
|
||||
hard_timeout=300,
|
||||
specs=flow_specs),
|
||||
ofpp.OFPActionOutput(patch_int_ofport, 0),
|
||||
]
|
||||
|
||||
arp_match = ofpp.OFPMatch(
|
||||
eth_type=ether_types.ETH_TYPE_ARP,
|
||||
arp_tha=lib_constants.BROADCAST_MAC
|
||||
)
|
||||
ipv6_ra_match = ofpp.OFPMatch(
|
||||
eth_type=ether_types.ETH_TYPE_IPV6,
|
||||
ip_proto=in_proto.IPPROTO_ICMPV6,
|
||||
icmpv6_type=icmpv6.ND_ROUTER_ADVERT) # icmp_type=134
|
||||
ipv6_na_match = ofpp.OFPMatch(
|
||||
eth_type=ether_types.ETH_TYPE_IPV6,
|
||||
ip_proto=in_proto.IPPROTO_ICMPV6,
|
||||
icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT) # icmp_type=136
|
||||
|
||||
self.install_apply_actions(table_id=constants.LEARN_FROM_TUN,
|
||||
priority=2,
|
||||
match=arp_match,
|
||||
actions=actions)
|
||||
self.install_apply_actions(table_id=constants.LEARN_FROM_TUN,
|
||||
priority=2,
|
||||
match=ipv6_ra_match,
|
||||
actions=actions)
|
||||
self.install_apply_actions(table_id=constants.LEARN_FROM_TUN,
|
||||
priority=2,
|
||||
match=ipv6_na_match,
|
||||
actions=actions)
|
||||
self.install_apply_actions(table_id=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
actions=actions)
|
||||
|
||||
def setup_default_table(
|
||||
self, patch_int_ofport, arp_responder_enabled, dvr_enabled):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
@@ -81,34 +140,7 @@ class OVSTunnelBridge(ovs_bridge.OVSAgentBridge,
|
||||
# dynamically set-up flows in UCAST_TO_TUN corresponding to remote mac
|
||||
# addresses (assumes that lvid has already been set by a previous flow)
|
||||
# Once remote mac addresses are learnt, output packet to patch_int
|
||||
flow_specs = [
|
||||
ofpp.NXFlowSpecMatch(src=('vlan_tci', 0),
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=12),
|
||||
ofpp.NXFlowSpecMatch(src=('eth_src', 0),
|
||||
dst=('eth_dst', 0),
|
||||
n_bits=48),
|
||||
ofpp.NXFlowSpecLoad(src=0,
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=16),
|
||||
ofpp.NXFlowSpecLoad(src=('tunnel_id', 0),
|
||||
dst=('tunnel_id', 0),
|
||||
n_bits=64),
|
||||
ofpp.NXFlowSpecOutput(src=('in_port', 0),
|
||||
dst='',
|
||||
n_bits=32),
|
||||
]
|
||||
actions = [
|
||||
ofpp.NXActionLearn(table_id=constants.UCAST_TO_TUN,
|
||||
cookie=self.default_cookie,
|
||||
priority=1,
|
||||
hard_timeout=300,
|
||||
specs=flow_specs),
|
||||
ofpp.OFPActionOutput(patch_int_ofport, 0),
|
||||
]
|
||||
self.install_apply_actions(table_id=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
actions=actions)
|
||||
self._setup_learn_flows(ofpp, patch_int_ofport)
|
||||
|
||||
# Egress unicast will be handled in table UCAST_TO_TUN, where remote
|
||||
# mac addresses will be learned. For now, just add a default flow that
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from neutron_lib import constants as lib_constants
|
||||
from neutron_lib.plugins.ml2 import ovs_constants as ovs_const
|
||||
|
||||
from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
@@ -48,6 +49,73 @@ class OVSTunnelBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
|
||||
self.setup_bridge_mock('br-tun', self.br_tun_cls)
|
||||
self.stamp = self.br.default_cookie
|
||||
|
||||
def _get_learn_flows(self, ofpp, patch_int_ofport):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
# flows_data is list of tuples (priority, match)
|
||||
flows_data = [
|
||||
(2, ofpp.OFPMatch(
|
||||
eth_type=self.ether_types.ETH_TYPE_ARP,
|
||||
arp_tha=lib_constants.BROADCAST_MAC
|
||||
)),
|
||||
(2, ofpp.OFPMatch(
|
||||
eth_type=self.ether_types.ETH_TYPE_IPV6,
|
||||
ip_proto=self.in_proto.IPPROTO_ICMPV6,
|
||||
icmpv6_type=self.icmpv6.ND_ROUTER_ADVERT
|
||||
)),
|
||||
(2, ofpp.OFPMatch(
|
||||
eth_type=self.ether_types.ETH_TYPE_IPV6,
|
||||
ip_proto=self.in_proto.IPPROTO_ICMPV6,
|
||||
icmpv6_type=self.icmpv6.ND_NEIGHBOR_ADVERT
|
||||
)),
|
||||
(1, ofpp.OFPMatch())
|
||||
]
|
||||
learn_flows = []
|
||||
for priority, match in flows_data:
|
||||
learn_flows.append(
|
||||
call._send_msg(
|
||||
ofpp.OFPFlowMod(
|
||||
dp,
|
||||
cookie=self.stamp,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
|
||||
ofpp.NXActionLearn(
|
||||
cookie=self.stamp,
|
||||
hard_timeout=300,
|
||||
priority=1,
|
||||
specs=[
|
||||
ofpp.NXFlowSpecMatch(
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=12,
|
||||
src=('vlan_tci', 0)),
|
||||
ofpp.NXFlowSpecMatch(
|
||||
dst=('eth_dst', 0),
|
||||
n_bits=48,
|
||||
src=('eth_src', 0)),
|
||||
ofpp.NXFlowSpecLoad(
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=16,
|
||||
src=0),
|
||||
ofpp.NXFlowSpecLoad(
|
||||
dst=('tunnel_id', 0),
|
||||
n_bits=64,
|
||||
src=('tunnel_id', 0)),
|
||||
ofpp.NXFlowSpecOutput(
|
||||
dst='',
|
||||
n_bits=32,
|
||||
src=('in_port', 0)),
|
||||
],
|
||||
table_id=20),
|
||||
ofpp.OFPActionOutput(patch_int_ofport, 0),
|
||||
]),
|
||||
],
|
||||
match=match,
|
||||
priority=priority,
|
||||
table_id=10),
|
||||
active_bundle=None
|
||||
)
|
||||
)
|
||||
return learn_flows
|
||||
|
||||
def test_setup_default_table(self):
|
||||
patch_int_ofport = 5555
|
||||
arp_responder_enabled = False
|
||||
@@ -110,47 +178,9 @@ class OVSTunnelBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
|
||||
instructions=[],
|
||||
match=ofpp.OFPMatch(),
|
||||
priority=0, table_id=6),
|
||||
active_bundle=None),
|
||||
call._send_msg(
|
||||
ofpp.OFPFlowMod(
|
||||
dp,
|
||||
cookie=self.stamp,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
|
||||
ofpp.NXActionLearn(
|
||||
cookie=self.stamp,
|
||||
hard_timeout=300,
|
||||
priority=1,
|
||||
specs=[
|
||||
ofpp.NXFlowSpecMatch(
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=12,
|
||||
src=('vlan_tci', 0)),
|
||||
ofpp.NXFlowSpecMatch(
|
||||
dst=('eth_dst', 0),
|
||||
n_bits=48,
|
||||
src=('eth_src', 0)),
|
||||
ofpp.NXFlowSpecLoad(
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=16,
|
||||
src=0),
|
||||
ofpp.NXFlowSpecLoad(
|
||||
dst=('tunnel_id', 0),
|
||||
n_bits=64,
|
||||
src=('tunnel_id', 0)),
|
||||
ofpp.NXFlowSpecOutput(
|
||||
dst='',
|
||||
n_bits=32,
|
||||
src=('in_port', 0)),
|
||||
],
|
||||
table_id=20),
|
||||
ofpp.OFPActionOutput(patch_int_ofport, 0),
|
||||
]),
|
||||
],
|
||||
match=ofpp.OFPMatch(),
|
||||
priority=1,
|
||||
table_id=10),
|
||||
active_bundle=None),
|
||||
active_bundle=None)]
|
||||
expected += self._get_learn_flows(ofpp, patch_int_ofport)
|
||||
expected += [
|
||||
call._send_msg(
|
||||
ofpp.OFPFlowMod(dp,
|
||||
cookie=self.stamp,
|
||||
@@ -243,47 +273,9 @@ class OVSTunnelBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
|
||||
instructions=[],
|
||||
match=ofpp.OFPMatch(),
|
||||
priority=0, table_id=6),
|
||||
active_bundle=None),
|
||||
call._send_msg(
|
||||
ofpp.OFPFlowMod(
|
||||
dp,
|
||||
cookie=self.stamp,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
|
||||
ofpp.NXActionLearn(
|
||||
cookie=self.stamp,
|
||||
hard_timeout=300,
|
||||
priority=1,
|
||||
specs=[
|
||||
ofpp.NXFlowSpecMatch(
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=12,
|
||||
src=('vlan_tci', 0)),
|
||||
ofpp.NXFlowSpecMatch(
|
||||
dst=('eth_dst', 0),
|
||||
n_bits=48,
|
||||
src=('eth_src', 0)),
|
||||
ofpp.NXFlowSpecLoad(
|
||||
dst=('vlan_tci', 0),
|
||||
n_bits=16,
|
||||
src=0),
|
||||
ofpp.NXFlowSpecLoad(
|
||||
dst=('tunnel_id', 0),
|
||||
n_bits=64,
|
||||
src=('tunnel_id', 0)),
|
||||
ofpp.NXFlowSpecOutput(
|
||||
dst='',
|
||||
n_bits=32,
|
||||
src=('in_port', 0)),
|
||||
],
|
||||
table_id=20),
|
||||
ofpp.OFPActionOutput(patch_int_ofport, 0),
|
||||
]),
|
||||
],
|
||||
match=ofpp.OFPMatch(),
|
||||
priority=1,
|
||||
table_id=10),
|
||||
active_bundle=None),
|
||||
active_bundle=None)]
|
||||
expected += self._get_learn_flows(ofpp, patch_int_ofport)
|
||||
expected += [
|
||||
call._send_msg(
|
||||
ofpp.OFPFlowMod(dp,
|
||||
cookie=self.stamp,
|
||||
|
Reference in New Issue
Block a user