Merge "DVR: Modify DVR flows to allow ARP requests to hit ARP Responder table"

This commit is contained in:
Zuul 2019-06-13 09:57:32 +00:00 committed by Gerrit Code Review
commit 8b0f4495f9
7 changed files with 124 additions and 12 deletions

View File

@ -284,6 +284,7 @@
enable_distributed_routing: True
l2_population: True
tunnel_types: vxlan
arp_responder: True
ovs:
tunnel_bridge: br-tun
bridge_mappings: public:br-ex
@ -316,6 +317,7 @@
enable_distributed_routing: True
l2_population: True
tunnel_types: vxlan
arp_responder: True
ovs:
tunnel_bridge: br-tun
bridge_mappings: public:br-ex
@ -363,6 +365,7 @@
enable_distributed_routing: True
l2_population: True
tunnel_types: vxlan,gre
arp_responder: True
securitygroup:
firewall_driver: iptables_hybrid
$NEUTRON_L3_CONF:

View File

@ -44,7 +44,8 @@ LOCAL_SWITCHING = 0
# Various tables for DVR use of integration bridge flows
DVR_TO_SRC_MAC = 1
DVR_TO_SRC_MAC_VLAN = 2
ARP_DVR_MAC_TO_DST_MAC = 3
ARP_DVR_MAC_TO_DST_MAC_VLAN = 4
CANARY_TABLE = 23
# Table for ARP poison/spoofing prevention rules

View File

@ -18,6 +18,8 @@ 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.common import constants
class OVSDVRProcessMixin(object):
"""Common logic for br-tun and br-phys' DVR_PROCESS tables.
@ -37,7 +39,7 @@ class OVSDVRProcessMixin(object):
(_dp, ofp, ofpp) = self._get_dp()
match = self._dvr_process_ipv4_match(ofp, ofpp, vlan_tag=vlan_tag,
gateway_ip=gateway_ip)
self.install_drop(table_id=self.dvr_process_table_id,
self.install_drop(table_id=constants.FLOOD_TO_TUN,
priority=3,
match=match)
@ -45,7 +47,7 @@ class OVSDVRProcessMixin(object):
(_dp, ofp, ofpp) = self._get_dp()
match = self._dvr_process_ipv4_match(ofp, ofpp, vlan_tag=vlan_tag,
gateway_ip=gateway_ip)
self.uninstall_flows(table_id=self.dvr_process_table_id,
self.uninstall_flows(table_id=constants.FLOOD_TO_TUN,
match=match)
@staticmethod
@ -61,14 +63,14 @@ class OVSDVRProcessMixin(object):
(_dp, ofp, ofpp) = self._get_dp()
match = self._dvr_process_ipv6_match(ofp, ofpp, vlan_tag=vlan_tag,
gateway_mac=gateway_mac)
self.install_drop(table_id=self.dvr_process_table_id, priority=3,
self.install_drop(table_id=constants.FLOOD_TO_TUN, priority=3,
match=match)
def delete_dvr_process_ipv6(self, vlan_tag, gateway_mac):
(_dp, ofp, ofpp) = self._get_dp()
match = self._dvr_process_ipv6_match(ofp, ofpp, vlan_tag=vlan_tag,
gateway_mac=gateway_mac)
self.uninstall_flows(table_id=self.dvr_process_table_id,
self.uninstall_flows(table_id=constants.FLOOD_TO_TUN,
match=match)
@staticmethod

View File

@ -95,6 +95,42 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
match = self._local_vlan_match(ofp, ofpp, port, vlan_vid)
self.uninstall_flows(match=match)
@staticmethod
def _arp_dvr_dst_mac_match(ofp, ofpp, vlan, dvr_mac):
# If eth_dst is equal to the dvr mac of this host, then
# flag it as matched.
return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT,
eth_dst=dvr_mac)
@staticmethod
def _dvr_dst_mac_table_id(network_type):
if network_type == p_const.TYPE_VLAN:
return constants.ARP_DVR_MAC_TO_DST_MAC_VLAN
else:
return constants.ARP_DVR_MAC_TO_DST_MAC
def install_dvr_dst_mac_for_arp(self, network_type,
vlan_tag, gateway_mac, dvr_mac, rtr_port):
table_id = self._dvr_dst_mac_table_id(network_type)
# Match the destination MAC with the DVR MAC
(_dp, ofp, ofpp) = self._get_dp()
match = self._arp_dvr_dst_mac_match(ofp, ofpp, vlan_tag, dvr_mac)
# Incoming packet will come with destination MAC of DVR host MAC from
# the ARP Responder. The Source MAC in this case will have the source
# MAC of the port MAC that responded from the ARP responder.
# So just remove the DVR host MAC from the 'eth_dst' and replace it
# with the gateway-mac. The packet should end up in the right the table
# for the packet to reach the router interface.
actions = [
ofpp.OFPActionSetField(eth_dst=gateway_mac),
ofpp.OFPActionPopVlan(),
ofpp.OFPActionOutput(rtr_port, 0)
]
self.install_apply_actions(table_id=table_id,
priority=5,
match=match,
actions=actions)
@staticmethod
def _dvr_to_src_mac_match(ofp, ofpp, vlan_tag, dst_mac):
return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT,
@ -165,6 +201,37 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
in_port=port, eth_src=mac)
def delete_dvr_dst_mac_for_arp(self, network_type,
vlan_tag, gateway_mac, dvr_mac, rtr_port):
table_id = self._dvr_to_src_mac_table_id(network_type)
(_dp, ofp, ofpp) = self._get_dp()
match = self._arp_dvr_dst_mac_match(ofp, ofpp, vlan_tag, dvr_mac)
for table in table_id:
self.uninstall_flows(
strict=True, priority=5, table_id=table, match=match)
def add_dvr_gateway_mac_arp_vlan(self, mac, port):
self.install_goto(table_id=constants.LOCAL_SWITCHING,
priority=5,
in_port=port,
eth_dst=mac,
dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC_VLAN)
def remove_dvr_gateway_mac_arp_vlan(self, mac, port):
self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
eth_dst=mac)
def add_dvr_gateway_mac_arp_tun(self, mac, port):
self.install_goto(table_id=constants.LOCAL_SWITCHING,
priority=5,
in_port=port,
eth_dst=mac,
dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC)
def remove_dvr_gateway_mac_arp_tun(self, mac, port):
self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,
eth_dst=mac)
@staticmethod
def _arp_reply_match(ofp, ofpp, port):
return ofpp.OFPMatch(in_port=port,

View File

@ -119,7 +119,8 @@ class OVSDVRNeutronAgent(object):
patch_int_ofport=constants.OFPORT_INVALID,
patch_tun_ofport=constants.OFPORT_INVALID,
host=None, enable_tunneling=False,
enable_distributed_routing=False):
enable_distributed_routing=False,
arp_responder_enabled=False):
self.context = context
self.plugin_rpc = plugin_rpc
self.host = host
@ -133,6 +134,7 @@ class OVSDVRNeutronAgent(object):
patch_int_ofport, patch_tun_ofport)
self.reset_dvr_parameters()
self.dvr_mac_address = None
self.arp_responder_enabled = arp_responder_enabled
if self.enable_distributed_routing:
self.get_dvr_mac_address()
@ -262,6 +264,10 @@ class OVSDVRNeutronAgent(object):
phys_br.add_dvr_mac_vlan(mac=mac,
port=self.phys_ofports[physical_network])
def _add_arp_dvr_mac_for_phys_br(self, physical_network, mac):
self.int_br.add_dvr_gateway_mac_arp_vlan(
mac=mac, port=self.int_ofports[physical_network])
def _remove_dvr_mac_for_phys_br(self, physical_network, mac):
# REVISIT(yamamoto): match in_port as well?
self.int_br.remove_dvr_mac_vlan(mac=mac)
@ -273,6 +279,10 @@ class OVSDVRNeutronAgent(object):
self.int_br.add_dvr_mac_tun(mac=mac, port=self.patch_tun_ofport)
self.tun_br.add_dvr_mac_tun(mac=mac, port=self.patch_int_ofport)
def _add_arp_dvr_mac_for_tun_br(self, mac):
self.int_br.add_dvr_gateway_mac_arp_tun(
mac=mac, port=self.patch_tun_ofport)
def _remove_dvr_mac_for_tun_br(self, mac):
self.int_br.remove_dvr_mac_tun(mac=mac, port=self.patch_tun_ofport)
# REVISIT(yamamoto): match in_port as well?
@ -286,6 +296,13 @@ class OVSDVRNeutronAgent(object):
LOG.debug("Added DVR MAC flow for %s", mac)
self.registered_dvr_macs.add(mac)
def _add_dvr_mac_for_arp(self, mac):
for physical_network in self.bridge_mappings:
self._add_arp_dvr_mac_for_phys_br(physical_network, mac)
if self.enable_tunneling:
self._add_arp_dvr_mac_for_tun_br(mac)
LOG.debug("Added ARP DVR MAC flow for %s", mac)
def _remove_dvr_mac(self, mac):
for physical_network in self.bridge_mappings:
self._remove_dvr_mac_for_phys_br(physical_network, mac)
@ -301,6 +318,8 @@ class OVSDVRNeutronAgent(object):
c_mac = netaddr.EUI(mac['mac_address'],
dialect=netaddr.mac_unix_expanded)
if c_mac == self.dvr_mac_address:
self._add_dvr_mac_for_arp(c_mac)
LOG.debug("Added the DVR MAC rule for ARP %s", c_mac)
continue
self._add_dvr_mac(c_mac)
@ -406,6 +425,15 @@ class OVSDVRNeutronAgent(object):
gateway_mac=subnet_info['gateway_mac'],
dst_mac=comp_ovsport.get_mac(),
dst_port=comp_ovsport.get_ofport())
# Add the following flow rule only when ARP RESPONDER is
# enabled
if self.arp_responder_enabled:
self.int_br.install_dvr_dst_mac_for_arp(
lvm.network_type,
vlan_tag=lvm.vlan,
gateway_mac=port.vif_mac,
dvr_mac=self.dvr_mac_address,
rtr_port=port.ofport)
if lvm.network_type == n_const.TYPE_VLAN:
# TODO(vivek) remove the IPv6 related flows once SNAT is not
@ -600,7 +628,16 @@ class OVSDVRNeutronAgent(object):
network_type=network_type,
vlan_tag=vlan_to_use, dst_mac=comp_port.get_mac())
ldm.remove_all_compute_ofports()
# If ARP Responder enabled, remove the rule that redirects
# the dvr_mac_address destination to the router port, since
# the router port is removed or unbound.
if self.arp_responder_enabled:
self.int_br.delete_dvr_dst_mac_for_arp(
network_type=network_type,
vlan_tag=vlan_to_use,
gateway_mac=port.vif_mac,
dvr_mac=self.dvr_mac_address,
rtr_port=port.ofport)
if ldm.get_csnat_ofport() == constants.OFPORT_INVALID:
# if there is no csnat port for this subnet, remove
# this subnet from local_dvr_map, as no dvr (or) csnat

View File

@ -247,7 +247,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self.patch_tun_ofport,
host,
self.enable_tunneling,
self.enable_distributed_routing)
self.enable_distributed_routing,
self.arp_responder_enabled)
if self.enable_distributed_routing:
self.dvr_agent.setup_dvr_flows()

View File

@ -17,6 +17,7 @@
import mock
from oslo_utils import importutils
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \
import ovs_test_base
@ -169,7 +170,7 @@ class OVSDVRProcessTestMixin(object):
arp_tpa=gateway_ip,
vlan_vid=vlan_tag | ofp.OFPVID_PRESENT),
priority=3,
table_id=self.dvr_process_table_id),
table_id=constants.FLOOD_TO_TUN),
active_bundle=None),
]
self.assertEqual(expected, self.mock.mock_calls)
@ -181,7 +182,7 @@ class OVSDVRProcessTestMixin(object):
gateway_ip=gateway_ip)
(dp, ofp, ofpp) = self._get_dp()
expected = [
call.uninstall_flows(table_id=self.dvr_process_table_id,
call.uninstall_flows(table_id=constants.FLOOD_TO_TUN,
match=ofpp.OFPMatch(
eth_type=self.ether_types.ETH_TYPE_ARP,
arp_tpa=gateway_ip,
@ -206,7 +207,7 @@ class OVSDVRProcessTestMixin(object):
ip_proto=self.in_proto.IPPROTO_ICMPV6,
vlan_vid=vlan_tag | ofp.OFPVID_PRESENT),
priority=3,
table_id=self.dvr_process_table_id),
table_id=constants.FLOOD_TO_TUN),
active_bundle=None),
]
self.assertEqual(expected, self.mock.mock_calls)
@ -218,7 +219,7 @@ class OVSDVRProcessTestMixin(object):
gateway_mac=gateway_mac)
(dp, ofp, ofpp) = self._get_dp()
expected = [
call.uninstall_flows(table_id=self.dvr_process_table_id,
call.uninstall_flows(table_id=constants.FLOOD_TO_TUN,
match=ofpp.OFPMatch(
eth_src=gateway_mac,
eth_type=self.ether_types.ETH_TYPE_IPV6,