DVR: Modify DVR flows to allow ARP requests to hit ARP Responder table
DVR does the ARP table update through the control plane, and does not allow any ARP requests to get out of the node. In order to address the allowed address pair VRRP IP issue with DVR, we need to add an ARP entry into the ARP Responder table for the allowed address pair IP ( which is taken care by the patch in [1]) This patch adds a rule in the br-int to redirect the packet destinated to the router to the actual router-port and also moves the arp filtering rule to the tunnel or the physical port based on the configuration. By adding the above rule it allows the ARP requests to reach the ARP Responder table and filters the ARP requests before it reaches the physical network or the tunnel. [1] https://review.opendev.org/#/c/601336/ Related-Bug: #1774459 Change-Id: I3905ea56ca0ff35bdd96c818719e6d63a3eb5a72
This commit is contained in:
parent
e7629fc1c5
commit
52b537ca22
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -601,7 +629,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
|
||||
|
@ -240,7 +240,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()
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user