diff --git a/neutron/agent/linux/openvswitch_firewall/firewall.py b/neutron/agent/linux/openvswitch_firewall/firewall.py index bea4f88c593..2dd25f07f68 100644 --- a/neutron/agent/linux/openvswitch_firewall/firewall.py +++ b/neutron/agent/linux/openvswitch_firewall/firewall.py @@ -771,32 +771,47 @@ 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: + def install_physical_direct_flow(self, mac, segment_id, + ofport, local_vlan, network_type): + actions = ('set_field:{:d}->reg{:d},' + 'set_field:{:d}->reg{:d},').format( + ofport, + ovsfw_consts.REG_PORT, + # This always needs the local vlan. + local_vlan, + ovsfw_consts.REG_NET) + if network_type == lib_const.TYPE_VLAN: + actions += 'strip_vlan,resubmit(,{:d})'.format( + ovs_consts.BASE_INGRESS_TABLE) 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) - ) + actions=actions) + elif network_type == lib_const.TYPE_FLAT: + # If the port belong to flat network, we need match vlan_tci and + # needn't pop vlan + actions += 'resubmit(,{:d})'.format( + ovs_consts.BASE_INGRESS_TABLE) + self._add_flow( + table=ovs_consts.TRANSIENT_TABLE, + priority=90, + dl_dst=mac, + vlan_tci=ovs_consts.FLAT_VLAN_TCI, + actions=actions) - def delete_vlan_direct_flow(self, mac, segment_id): + def delete_physical_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) + else: + self._strict_delete_flow(priority=90, + table=ovs_consts.TRANSIENT_TABLE, + dl_dst=mac, + vlan_tci=ovs_consts.FLAT_VLAN_TCI) def initialize_port_flows(self, port): """Set base flows for port @@ -821,8 +836,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.install_physical_direct_flow( + mac_addr, port.segment_id, port.ofport, + port.vlan_tag, port.network_type) self._add_flow( table=ovs_consts.TRANSIENT_TABLE, @@ -1406,7 +1422,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_physical_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) diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py index e08ee911741..3966fda6398 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py @@ -12,9 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -FLAT_VLAN_ID = -1 -LOCAL_VLAN_ID = -2 - # Supported VXLAN features VXLAN_NONE = 'not_supported' VXLAN_MCAST = 'multicast_flooding' diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py b/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py index 5676698e6a4..060ab85beb1 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py @@ -16,8 +16,8 @@ from neutron_lib import constants as p_const -# Special vlan_id value in ovs_vlan_allocations table indicating flat network -FLAT_VLAN_ID = -1 +# Special vlan_tci value indicating flat network +FLAT_VLAN_TCI = '0x0000/0x1fff' # Topic for tunnel notifications between the plugin and agent TUNNEL = 'tunnel' @@ -41,11 +41,14 @@ TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN, LOCAL_SWITCHING = 0 +# The pyhsical network types of support DVR router +DVR_PHYSICAL_NETWORK_TYPES = [p_const.TYPE_VLAN, p_const.TYPE_FLAT] + # Various tables for DVR use of integration bridge flows DVR_TO_SRC_MAC = 1 -DVR_TO_SRC_MAC_VLAN = 2 +DVR_TO_SRC_MAC_PHYSICAL = 2 ARP_DVR_MAC_TO_DST_MAC = 3 -ARP_DVR_MAC_TO_DST_MAC_VLAN = 4 +ARP_DVR_MAC_TO_DST_MAC_PHYSICAL = 4 CANARY_TABLE = 23 # Table for ARP poison/spoofing prevention rules @@ -82,7 +85,7 @@ ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE = 94 INT_BR_ALL_TABLES = ( LOCAL_SWITCHING, DVR_TO_SRC_MAC, - DVR_TO_SRC_MAC_VLAN, + DVR_TO_SRC_MAC_PHYSICAL, CANARY_TABLE, ARP_SPOOF_TABLE, MAC_SPOOF_TABLE, @@ -128,15 +131,15 @@ TUN_BR_ALL_TABLES = ( # --- Physical Bridges (phys_brs) # Various tables for DVR use of physical bridge flows -DVR_PROCESS_VLAN = 1 +DVR_PROCESS_PHYSICAL = 1 LOCAL_VLAN_TRANSLATION = 2 -DVR_NOT_LEARN_VLAN = 3 +DVR_NOT_LEARN_PHYSICAL = 3 PHY_BR_ALL_TABLES = ( LOCAL_SWITCHING, - DVR_PROCESS_VLAN, + DVR_PROCESS_PHYSICAL, LOCAL_VLAN_TRANSLATION, - DVR_NOT_LEARN_VLAN) + DVR_NOT_LEARN_PHYSICAL) # --- end of OpenFlow table IDs diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py index 515ebd34412..f52a3c2abad 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py @@ -21,7 +21,6 @@ import netaddr -from neutron_lib import constants as p_const from os_ken.lib.packet import ether_types from os_ken.lib.packet import icmpv6 from os_ken.lib.packet import in_proto @@ -103,13 +102,15 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): 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. + if not vlan: + return ofpp.OFPMatch(vlan_vid=ofp.OFPVID_NONE, eth_dst=dvr_mac) 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 + if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: + return constants.ARP_DVR_MAC_TO_DST_MAC_PHYSICAL else: return constants.ARP_DVR_MAC_TO_DST_MAC @@ -137,13 +138,16 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): @staticmethod def _dvr_to_src_mac_match(ofp, ofpp, vlan_tag, dst_mac): + if not vlan_tag: + # When the network is flat type, the vlan_tag will be None. + return ofpp.OFPMatch(vlan_vid=ofp.OFPVID_NONE, eth_dst=dst_mac) return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, eth_dst=dst_mac) @staticmethod def _dvr_to_src_mac_table_id(network_type): - if network_type == p_const.TYPE_VLAN: - return constants.DVR_TO_SRC_MAC_VLAN + if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: + return constants.DVR_TO_SRC_MAC_PHYSICAL else: return constants.DVR_TO_SRC_MAC @@ -164,10 +168,10 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): priority=20, match=match, instructions=instructions) - actions = [ - ofpp.OFPActionPopVlan(), - ofpp.OFPActionOutput(dst_port, 0), - ] + actions = [] + if vlan_tag: + actions.append(ofpp.OFPActionPopVlan()) + actions.append(ofpp.OFPActionOutput(dst_port, 0)) self.install_apply_actions(table_id=constants.TRANSIENT_TABLE, priority=20, match=match, @@ -182,12 +186,12 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): self.uninstall_flows( strict=True, priority=20, table_id=table, match=match) - def add_dvr_mac_vlan(self, mac, port): + def add_dvr_mac_physical(self, mac, port): self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=4, in_port=port, eth_src=mac, - dest_table_id=constants.DVR_TO_SRC_MAC_VLAN) + dest_table_id=constants.DVR_TO_SRC_MAC_PHYSICAL) def remove_dvr_mac_vlan(self, mac): # REVISIT(yamamoto): match in_port as well? @@ -214,11 +218,12 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): strict=True, priority=5, table_id=table_id, 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) + 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_PHYSICAL) def remove_dvr_gateway_mac_arp_vlan(self, mac, port): self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py index ec8259ee35a..50aa0b73192 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py @@ -26,7 +26,7 @@ class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge, """openvswitch agent physical bridge specific logic.""" # Used by OVSDVRProcessMixin - dvr_process_table_id = constants.DVR_PROCESS_VLAN + dvr_process_table_id = constants.DVR_PROCESS_PHYSICAL dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION of_tables = constants.PHY_BR_ALL_TABLES @@ -57,12 +57,12 @@ class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge, match = self._local_vlan_match(ofp, ofpp, port, lvid) self.uninstall_flows(match=match) - def add_dvr_mac_vlan(self, mac, port): - self.install_output(table_id=constants.DVR_NOT_LEARN_VLAN, + def add_dvr_mac_physical(self, mac, port): + self.install_output(table_id=constants.DVR_NOT_LEARN_PHYSICAL, priority=2, eth_src=mac, port=port) def remove_dvr_mac_vlan(self, mac): # REVISIT(yamamoto): match in_port as well? self.uninstall_flows( - table_id=constants.DVR_NOT_LEARN_VLAN, + table_id=constants.DVR_NOT_LEARN_PHYSICAL, eth_src=mac) diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py index 2a33ec3db1c..48bb7af123a 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py @@ -224,7 +224,7 @@ class OVSDVRNeutronAgent(object): # Insert 'drop' action as the default for Table DVR_TO_SRC_MAC self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1) - self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN, + self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_PHYSICAL, priority=1) for physical_network in self.bridge_mappings: @@ -256,12 +256,12 @@ class OVSDVRNeutronAgent(object): self.phys_brs[physical_network].install_goto( in_port=self.phys_ofports[physical_network], priority=2, - dest_table_id=constants.DVR_PROCESS_VLAN) + dest_table_id=constants.DVR_PROCESS_PHYSICAL) self.phys_brs[physical_network].install_goto( priority=1, - dest_table_id=constants.DVR_NOT_LEARN_VLAN) + dest_table_id=constants.DVR_NOT_LEARN_PHYSICAL) self.phys_brs[physical_network].install_goto( - table_id=constants.DVR_PROCESS_VLAN, + table_id=constants.DVR_PROCESS_PHYSICAL, priority=0, dest_table_id=constants.LOCAL_VLAN_TRANSLATION) self.phys_brs[physical_network].install_drop( @@ -269,15 +269,15 @@ class OVSDVRNeutronAgent(object): in_port=self.phys_ofports[physical_network], priority=2) self.phys_brs[physical_network].install_normal( - table_id=constants.DVR_NOT_LEARN_VLAN, + table_id=constants.DVR_NOT_LEARN_PHYSICAL, priority=1) def _add_dvr_mac_for_phys_br(self, physical_network, mac): - self.int_br.add_dvr_mac_vlan(mac=mac, - port=self.int_ofports[physical_network]) + self.int_br.add_dvr_mac_physical( + mac=mac, port=self.int_ofports[physical_network]) phys_br = self.phys_brs[physical_network] - phys_br.add_dvr_mac_vlan(mac=mac, - port=self.phys_ofports[physical_network]) + phys_br.add_dvr_mac_physical( + 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( @@ -402,7 +402,7 @@ class OVSDVRNeutronAgent(object): ldm.set_dvr_owned(True) vlan_to_use = lvm.vlan - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: vlan_to_use = lvm.segmentation_id subnet_info = ldm.get_subnet_info() @@ -456,7 +456,7 @@ class OVSDVRNeutronAgent(object): dvr_mac=self.dvr_mac_address, rtr_port=port.ofport) - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: # TODO(vivek) remove the IPv6 related flows once SNAT is not # used for IPv6 DVR. br = self.phys_brs[lvm.physical_network] @@ -517,7 +517,7 @@ class OVSDVRNeutronAgent(object): ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport vlan_to_use = lvm.vlan - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: vlan_to_use = lvm.segmentation_id # create a rule for this vm port self.int_br.install_dvr_to_src_mac( @@ -577,7 +577,7 @@ class OVSDVRNeutronAgent(object): ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport vlan_to_use = lvm.vlan - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: vlan_to_use = lvm.segmentation_id self.int_br.install_dvr_to_src_mac( network_type=lvm.network_type, @@ -591,8 +591,9 @@ class OVSDVRNeutronAgent(object): if not self.in_distributed_mode(): return - if local_vlan_map.network_type not in (constants.TUNNEL_NETWORK_TYPES + - [n_const.TYPE_VLAN]): + if (local_vlan_map.network_type not in + (constants.TUNNEL_NETWORK_TYPES + + constants.DVR_PHYSICAL_NETWORK_TYPES)): LOG.debug("DVR: Port %s is with network_type %s not supported" " for dvr plumbing", port.vif_id, local_vlan_map.network_type) @@ -629,7 +630,7 @@ class OVSDVRNeutronAgent(object): network_type = lvm.network_type physical_network = lvm.physical_network vlan_to_use = lvm.vlan - if network_type == n_const.TYPE_VLAN: + if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: vlan_to_use = lvm.segmentation_id # ensure we process for all the subnets laid on this removed port for sub_uuid in subnet_set: @@ -660,7 +661,7 @@ class OVSDVRNeutronAgent(object): # this subnet from local_dvr_map, as no dvr (or) csnat # ports available on this agent anymore self.local_dvr_map.pop(sub_uuid, None) - if network_type == n_const.TYPE_VLAN: + if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: br = self.phys_brs[physical_network] if network_type in constants.TUNNEL_NETWORK_TYPES: br = self.tun_br @@ -679,7 +680,7 @@ class OVSDVRNeutronAgent(object): self.firewall.delete_accepted_egress_direct_flow( subnet_info['gateway_mac'], lvm.vlan) - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: br = self.phys_brs[physical_network] if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: br = self.tun_br @@ -701,7 +702,7 @@ class OVSDVRNeutronAgent(object): ldm = self.local_dvr_map[sub_uuid] ldm.remove_compute_ofport(port.vif_id) vlan_to_use = lvm.vlan - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: vlan_to_use = lvm.segmentation_id # first remove this vm port rule self.int_br.delete_dvr_to_src_mac( @@ -722,7 +723,7 @@ class OVSDVRNeutronAgent(object): ldm = self.local_dvr_map[sub_uuid] ldm.set_csnat_ofport(constants.OFPORT_INVALID) vlan_to_use = lvm.vlan - if lvm.network_type == n_const.TYPE_VLAN: + if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES: vlan_to_use = lvm.segmentation_id # then remove csnat port rule self.int_br.delete_dvr_to_src_mac( diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index 1ba21a03943..bbabf12339d 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -896,18 +896,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, else: LOG.warning('Action %s not supported', action) - def _local_vlan_for_flat(self, lvid, physical_network): - phys_br = self.phys_brs[physical_network] - phys_port = self.phys_ofports[physical_network] - int_br = self.int_br - int_port = self.int_ofports[physical_network] - phys_br.provision_local_vlan(port=phys_port, lvid=lvid, - segmentation_id=None, - distributed=False) - int_br.provision_local_vlan(port=int_port, lvid=lvid, - segmentation_id=None) - - def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id): + def _local_vlan_for_physical(self, lvid, physical_network, + segmentation_id=None): distributed = self.enable_distributed_routing phys_br = self.phys_brs[physical_network] phys_port = self.phys_ofports[physical_network] @@ -988,7 +978,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, 'net_uuid': net_uuid}) elif network_type == n_const.TYPE_FLAT: if physical_network in self.phys_brs: - self._local_vlan_for_flat(lvid, physical_network) + self._local_vlan_for_physical(lvid, physical_network) else: LOG.error("Cannot provision flat network for " "net-id=%(net_uuid)s - no bridge for " @@ -997,8 +987,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, 'physical_network': physical_network}) elif network_type == n_const.TYPE_VLAN: if physical_network in self.phys_brs: - self._local_vlan_for_vlan(lvid, physical_network, - segmentation_id) + self._local_vlan_for_physical(lvid, physical_network, + segmentation_id) else: LOG.error("Cannot provision VLAN network for " "net-id=%(net_uuid)s - no bridge for " diff --git a/neutron/tests/functional/agent/test_ovs_flows.py b/neutron/tests/functional/agent/test_ovs_flows.py index e6b29a3d71d..c1fb16c7ead 100644 --- a/neutron/tests/functional/agent/test_ovs_flows.py +++ b/neutron/tests/functional/agent/test_ovs_flows.py @@ -344,7 +344,8 @@ class OVSFlowTestCase(OVSAgentTestBase): 'dst_mac': '12:34:56:78:cc:dd', 'dst_port': 123} self.br_int.install_dvr_to_src_mac(network_type='vlan', **kwargs) - self.br_int.add_dvr_mac_vlan(mac=other_dvr_mac, port=other_dvr_port) + self.br_int.add_dvr_mac_physical(mac=other_dvr_mac, + port=other_dvr_port) trace = self._run_trace(self.br.br_name, "in_port=%d," % other_dvr_port + diff --git a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py index fcb1ad44803..c970af3083b 100644 --- a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py +++ b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py @@ -38,11 +38,12 @@ TESTING_VLAN_TAG = 1 TESTING_SEGMENT = 1000 -def create_ofport(port_dict, network_type=None, physical_network=None): +def create_ofport(port_dict, network_type=None, + physical_network=None, segment_id=TESTING_SEGMENT): 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, - segment_id=TESTING_SEGMENT, + segment_id=segment_id, network_type=network_type, physical_network=physical_network) @@ -590,18 +591,23 @@ 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): + def _test_initialize_port_flows_dvr_conntrack_direct(self, network_type): port_dict = { 'device': 'port-id', 'security_groups': [1]} + segment_id = None + if network_type == constants.TYPE_VLAN: + segment_id = TESTING_SEGMENT of_port = create_ofport(port_dict, - network_type=constants.TYPE_VXLAN) + network_type=network_type, + segment_id=segment_id) self.firewall.sg_port_map.ports[of_port.id] = of_port port = self.firewall.get_or_create_ofport(port_dict) fake_patch_port = 999 self.mock_bridge.br.get_port_ofport.return_value = fake_patch_port + expected_calls = [] self.firewall.initialize_port_flows(port) call_args1 = { @@ -616,22 +622,39 @@ class TestOVSFirewallDriver(base.BaseTestCase): port.vlan_tag, ovsfw_consts.REG_NET, ovs_consts.BASE_EGRESS_TABLE)} - egress_flow_call = mock.call(**call_args1) + expected_calls.append(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) + if network_type == constants.TYPE_VLAN: + 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)} + expected_calls.append(mock.call(**call_args2)) + + if network_type == constants.TYPE_FLAT: + call_args2 = { + 'table': ovs_consts.TRANSIENT_TABLE, + 'priority': 90, + 'dl_dst': port.mac, + 'vlan_tci': ovs_consts.FLAT_VLAN_TCI, + '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_INGRESS_TABLE)} + expected_calls.append(mock.call(**call_args2)) call_args3 = { 'table': ovs_consts.TRANSIENT_TABLE, @@ -646,9 +669,20 @@ class TestOVSFirewallDriver(base.BaseTestCase): 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]) + expected_calls.append(mock.call(**call_args3)) + self.mock_bridge.br.add_flow.assert_has_calls(expected_calls) + + def test_initialize_port_flows_dvr_conntrack_direct_vxlan(self): + self._test_initialize_port_flows_dvr_conntrack_direct( + network_type='vxlan') + + def test_initialize_port_flows_dvr_conntrack_direct_vlan(self): + self._test_initialize_port_flows_dvr_conntrack_direct( + network_type='vlan') + + def test_initialize_port_flows_dvr_conntrack_direct_flat(self): + self._test_initialize_port_flows_dvr_conntrack_direct( + network_type='flat') def test_initialize_port_flows_vlan_dvr_conntrack_direct_vlan(self): port_dict = { diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py index ff9a134891c..4ce50c8e06c 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py @@ -286,6 +286,48 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): ] self.assertEqual(expected, self.mock.mock_calls) + def test_install_dvr_to_src_mac_flat(self): + network_type = 'flat' + gateway_mac = '08:60:6e:7f:74:e7' + dst_mac = '00:02:b3:13:fe:3d' + dst_port = 6666 + self.br.install_dvr_to_src_mac(network_type=network_type, + vlan_tag=None, + gateway_mac=gateway_mac, + dst_mac=dst_mac, + dst_port=dst_port) + (dp, ofp, ofpp) = self._get_dp() + expected = [ + call._send_msg(ofpp.OFPFlowMod(dp, + cookie=self.stamp, + instructions=[ + ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ + ofpp.OFPActionSetField(eth_src=gateway_mac), + ]), + ofpp.OFPInstructionGotoTable(table_id=60), + ], + match=ofpp.OFPMatch( + eth_dst=dst_mac, + vlan_vid=ofp.OFPVID_NONE), + priority=20, + table_id=2), + active_bundle=None), + call._send_msg(ofpp.OFPFlowMod(dp, + cookie=self.stamp, + instructions=[ + ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ + ofpp.OFPActionOutput(dst_port, 0), + ]), + ], + match=ofpp.OFPMatch( + eth_dst=dst_mac, + vlan_vid=ofp.OFPVID_NONE), + priority=20, + table_id=60), + active_bundle=None), + ] + self.assertEqual(expected, self.mock.mock_calls) + def test_delete_dvr_to_src_mac_vlan(self): network_type = 'vlan' vlan_tag = 1111 @@ -312,10 +354,36 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): ] self.assertEqual(expected, self.mock.mock_calls) - def test_add_dvr_mac_vlan(self): + def test_delete_dvr_to_src_mac_flat(self): + network_type = 'flat' + vlan_tag = None + dst_mac = '00:02:b3:13:fe:3d' + self.br.delete_dvr_to_src_mac(network_type=network_type, + vlan_tag=vlan_tag, + dst_mac=dst_mac) + (dp, ofp, ofpp) = self._get_dp() + expected = [ + call.uninstall_flows( + strict=True, + priority=20, + table_id=2, + match=ofpp.OFPMatch( + eth_dst=dst_mac, + vlan_vid=ofp.OFPVID_NONE)), + call.uninstall_flows( + strict=True, + priority=20, + table_id=60, + match=ofpp.OFPMatch( + eth_dst=dst_mac, + vlan_vid=ofp.OFPVID_NONE)), + ] + self.assertEqual(expected, self.mock.mock_calls) + + def test_add_dvr_mac_physical(self): mac = '00:02:b3:13:fe:3d' port = 8888 - self.br.add_dvr_mac_vlan(mac=mac, port=port) + self.br.add_dvr_mac_physical(mac=mac, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, @@ -488,12 +556,15 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): self.assertEqual(expected, self.mock.mock_calls) def _test_delete_dvr_dst_mac_for_arp(self, network_type): - if network_type == p_const.TYPE_VLAN: - table_id = constants.DVR_TO_SRC_MAC_VLAN + if network_type in (p_const.TYPE_VLAN, p_const.TYPE_FLAT): + table_id = constants.DVR_TO_SRC_MAC_PHYSICAL else: table_id = constants.DVR_TO_SRC_MAC - vlan_tag = 1111 + if network_type == p_const.TYPE_FLAT: + vlan_tag = None + else: + vlan_tag = 1111 gateway_mac = '00:02:b3:13:fe:3e' dvr_mac = '00:02:b3:13:fe:3f' rtr_port = 8888 @@ -503,15 +574,26 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): dvr_mac=dvr_mac, rtr_port=rtr_port) (dp, ofp, ofpp) = self._get_dp() - expected = [ - call.uninstall_flows( - strict=True, - priority=5, - table_id=table_id, - match=ofpp.OFPMatch( - eth_dst=dvr_mac, - vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), - ] + if network_type == p_const.TYPE_FLAT: + expected = [ + call.uninstall_flows( + strict=True, + priority=5, + table_id=table_id, + match=ofpp.OFPMatch( + eth_dst=dvr_mac, + vlan_vid=ofp.OFPVID_NONE)), + ] + else: + expected = [ + call.uninstall_flows( + strict=True, + priority=5, + table_id=table_id, + match=ofpp.OFPMatch( + eth_dst=dvr_mac, + vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), + ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_dst_mac_for_arp_vlan(self): @@ -520,6 +602,9 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): def test_delete_dvr_dst_mac_for_arp_tunnel(self): self._test_delete_dvr_dst_mac_for_arp(network_type='vxlan') + def test_delete_dvr_dst_mac_for_flat(self): + self._test_delete_dvr_dst_mac_for_arp(network_type='flat') + def test_install_dscp_marking_rule(self): test_port = 8888 test_mark = 38 diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py index 5d473d80ea4..0f41d65cf41 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py @@ -27,7 +27,7 @@ call = mock.call # short hand class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, ovs_bridge_test_base.OVSDVRProcessTestMixin): - dvr_process_table_id = ovs_const.DVR_PROCESS_VLAN + dvr_process_table_id = ovs_const.DVR_PROCESS_PHYSICAL dvr_process_next_table_id = ovs_const.LOCAL_VLAN_TRANSLATION def setUp(self): @@ -125,10 +125,10 @@ class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, ] self.assertEqual(expected, self.mock.mock_calls) - def test_add_dvr_mac_vlan(self): + def test_add_dvr_mac_physical(self): mac = '00:02:b3:13:fe:3d' port = 8888 - self.br.add_dvr_mac_vlan(mac=mac, port=port) + self.br.add_dvr_mac_physical(mac=mac, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py index 8c2be7bb39c..731cf7a1a5f 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py @@ -3078,8 +3078,8 @@ class TestOvsDvrNeutronAgent(object): ), ] - def _test_port_bound_for_dvr_on_vlan_network( - self, device_owner, ip_version=n_const.IP_VERSION_4): + def _test_port_bound_for_dvr_on_physical_network( + self, device_owner, network_type, ip_version=n_const.IP_VERSION_4): self._setup_for_dvr_test() if ip_version == n_const.IP_VERSION_4: gateway_ip = '1.1.1.10' @@ -3093,7 +3093,8 @@ class TestOvsDvrNeutronAgent(object): self._compute_port.vif_mac = '77:88:99:00:11:22' physical_network = self._physical_network segmentation_id = self._segmentation_id - network_type = n_const.TYPE_VLAN + if network_type == n_const.TYPE_FLAT: + segmentation_id = None int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) phys_br = mock.create_autospec(self.br_phys_cls('br-phys')) @@ -3256,10 +3257,19 @@ class TestOvsDvrNeutronAgent(object): phys_br.assert_not_called() def test_port_bound_for_dvr_with_compute_ports(self): - self._test_port_bound_for_dvr_on_vlan_network( - device_owner=DEVICE_OWNER_COMPUTE) - self._test_port_bound_for_dvr_on_vlan_network( + self._test_port_bound_for_dvr_on_physical_network( device_owner=DEVICE_OWNER_COMPUTE, + network_type=n_const.TYPE_VLAN) + self._test_port_bound_for_dvr_on_physical_network( + device_owner=DEVICE_OWNER_COMPUTE, + network_type=n_const.TYPE_VLAN, + ip_version=n_const.IP_VERSION_6) + self._test_port_bound_for_dvr_on_physical_network( + device_owner=DEVICE_OWNER_COMPUTE, + network_type=n_const.TYPE_FLAT) + self._test_port_bound_for_dvr_on_physical_network( + device_owner=DEVICE_OWNER_COMPUTE, + network_type=n_const.TYPE_FLAT, ip_version=n_const.IP_VERSION_6) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=DEVICE_OWNER_COMPUTE) @@ -3268,10 +3278,19 @@ class TestOvsDvrNeutronAgent(object): ip_version=n_const.IP_VERSION_6) def test_port_bound_for_dvr_with_dhcp_ports(self): - self._test_port_bound_for_dvr_on_vlan_network( - device_owner=n_const.DEVICE_OWNER_DHCP) - self._test_port_bound_for_dvr_on_vlan_network( + self._test_port_bound_for_dvr_on_physical_network( device_owner=n_const.DEVICE_OWNER_DHCP, + network_type=n_const.TYPE_VLAN) + self._test_port_bound_for_dvr_on_physical_network( + device_owner=n_const.DEVICE_OWNER_DHCP, + network_type=n_const.TYPE_VLAN, + ip_version=n_const.IP_VERSION_6) + self._test_port_bound_for_dvr_on_physical_network( + device_owner=n_const.DEVICE_OWNER_DHCP, + network_type=n_const.TYPE_FLAT) + self._test_port_bound_for_dvr_on_physical_network( + device_owner=n_const.DEVICE_OWNER_DHCP, + network_type=n_const.TYPE_FLAT, ip_version=n_const.IP_VERSION_6) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_DHCP) @@ -3737,8 +3756,9 @@ class TestOvsDvrNeutronAgent(object): mock.call.setup_canary_table(), mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1), - mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN, - priority=1), + mock.call.install_drop( + table_id=constants.DVR_TO_SRC_MAC_PHYSICAL, + priority=1), mock.call.install_drop(table_id=constants.LOCAL_SWITCHING, priority=2, in_port=ioport), @@ -3829,7 +3849,7 @@ class TestOvsDvrNeutronAgent(object): dvr_macs=[{'host': newhost, 'mac_address': newmac}]) expected_on_int_br = [ - mock.call.add_dvr_mac_vlan( + mock.call.add_dvr_mac_physical( mac=newmac, port=self.agent.int_ofports[physical_network]), mock.call.add_dvr_mac_tun( @@ -3842,7 +3862,7 @@ class TestOvsDvrNeutronAgent(object): port=self.agent.patch_int_ofport), ] expected_on_phys_br = [ - mock.call.add_dvr_mac_vlan( + mock.call.add_dvr_mac_physical( mac=newmac, port=self.agent.phys_ofports[physical_network]), ] diff --git a/releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml b/releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml new file mode 100644 index 00000000000..b106832619a --- /dev/null +++ b/releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + ``DVR`` routers now support ``flat`` networks. +fixes: + - | + Fixed bug `1876092 `_ + which caused DUP ICMP replies on the ``flat`` networks used with ``DVR`` + routers.