# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) # Copyright (c) 2020 Nvidia All rights reserved. See COPYING file """ Test module for pyverbs' mlx5 flow module. """ import unittest import errno from pyverbs.providers.mlx5.mlx5dv_flow import Mlx5FlowMatcher, \ Mlx5FlowMatcherAttr, Mlx5FlowMatchParameters, Mlx5FlowActionAttr, Mlx5Flow,\ Mlx5PacketReformatFlowAction from pyverbs.providers.mlx5.mlx5dv import Mlx5Context, Mlx5DVContextAttr from pyverbs.pyverbs_error import PyverbsRDMAError, PyverbsUserError from tests.utils import requires_root_on_eth, PacketConsts import pyverbs.providers.mlx5.mlx5_enums as dve from tests.mlx5_base import Mlx5RDMATestCase from tests.base import RawResources import pyverbs.enums as e import tests.utils as u import struct MAX_MATCH_PARAM_SIZE = 0x180 @u.skip_unsupported def requires_reformat_support(func): def func_wrapper(instance): nic_tbl_caps = u.query_nic_flow_table_caps(instance) # Verify that both NIC RX and TX support reformat actions by checking # the following PRM fields: encap_general_header, # log_max_packet_reformat, and reformat (for both RX and TX). if not(nic_tbl_caps.encap_general_header and nic_tbl_caps.log_max_packet_reformat_context and nic_tbl_caps.flow_table_properties_nic_receive.reformat and nic_tbl_caps.flow_table_properties_nic_transmit.reformat): raise unittest.SkipTest('NIC flow table does not support reformat') return func(instance) return func_wrapper def gen_vxlan_l2_tunnel_encap_header(msg_size): vxlan_header = u.gen_vxlan_header() udp_header = u.gen_udp_header(packet_len=msg_size + len(vxlan_header), dst_port=PacketConsts.VXLAN_PORT) ip_header = u.gen_ipv4_header(packet_len=msg_size + len(vxlan_header) + len(udp_header)) mac_header = u.gen_ethernet_header() return mac_header + ip_header + udp_header + vxlan_header class Mlx5FlowResources(RawResources): def create_matcher(self, mask, match_criteria_enable, flags=0, ft_type=dve.MLX5DV_FLOW_TABLE_TYPE_NIC_RX_): """ Creates a matcher from a provided mask. :param mask: The mask to match on (in bytes) :param match_criteria_enable: Bitmask representing which of the headers and parameters in match_criteria are used :param flags: Flow matcher flags :param ft_type: Flow table type :return: Resulting matcher """ try: flow_match_param = Mlx5FlowMatchParameters(len(mask), mask) attr = Mlx5FlowMatcherAttr(match_mask=flow_match_param, match_criteria_enable=match_criteria_enable, flags=flags, ft_type=ft_type) matcher = Mlx5FlowMatcher(self.ctx, attr) except PyverbsRDMAError as ex: if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]: raise unittest.SkipTest('Matcher creation is not supported') raise ex return matcher @requires_root_on_eth() def create_qps(self): super().create_qps() class Mlx5MatcherTest(Mlx5RDMATestCase): def setUp(self): super().setUp() self.iters = 10 self.server = None self.client = None @u.skip_unsupported def test_create_empty_matcher(self): """ Creates an empty matcher """ self.res = Mlx5FlowResources(**self.dev_info) empty_mask = bytes(MAX_MATCH_PARAM_SIZE) self.res.create_matcher(empty_mask, u.MatchCriteriaEnable.NONE) @u.skip_unsupported def test_create_smac_matcher(self): """ Creates a matcher to match on outer source mac """ self.res = Mlx5FlowResources(**self.dev_info) smac_mask = bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) self.res.create_matcher(smac_mask, u.MatchCriteriaEnable.OUTER) @u.skip_unsupported def test_smac_matcher_to_qp_flow(self): """ Creates a matcher to match on outer source mac and a flow that forwards packets to QP when matching on source mac. """ self.create_players(Mlx5FlowResources) smac_mask = bytes([0xff] * 6) matcher = self.server.create_matcher(smac_mask, u.MatchCriteriaEnable.OUTER) smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) action_qp = Mlx5FlowActionAttr(action_type=dve.MLX5DV_FLOW_ACTION_DEST_IBV_QP, qp=self.server.qp) self.server.flow = Mlx5Flow(matcher, value_param, [action_qp], 1) u.raw_traffic(self.client, self.server, self.iters) @requires_reformat_support @u.requires_encap_disabled_if_eswitch_on def test_tx_packet_reformat(self): """ Creates packet reformat (encap) action on TX and with QP action on RX verifies that the packet was encapsulated as expected. """ self.client = Mlx5FlowResources(**self.dev_info) outer = gen_vxlan_l2_tunnel_encap_header(self.client.msg_size) # Due to encapsulation action Ipv4 and UDP checksum of the outer header # will be recalculated, need to skip them during packet validation. ipv4_id_idx = [18, 19] ipv4_chksum_idx = [24, 25] udp_chksum_idx = [34, 35] # Server will receive encaped packet so message size must include the # length of the outer part. self.server = Mlx5FlowResources(msg_size=self.client.msg_size + len(outer), **self.dev_info) empty_bytes_arr = bytes(MAX_MATCH_PARAM_SIZE) empty_value_param = Mlx5FlowMatchParameters(len(empty_bytes_arr), empty_bytes_arr) # TX steering tx_matcher = self.client.create_matcher(empty_bytes_arr, u.MatchCriteriaEnable.NONE, e.IBV_FLOW_ATTR_FLAGS_EGRESS, dve.MLX5DV_FLOW_TABLE_TYPE_NIC_TX_) # Create encap action reformat_action = Mlx5PacketReformatFlowAction( self.client.ctx, data=outer, reformat_type=dve.MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL_, ft_type=dve.MLX5DV_FLOW_TABLE_TYPE_NIC_TX_) action_reformat_attr = Mlx5FlowActionAttr(flow_action=reformat_action, action_type=dve.MLX5DV_FLOW_ACTION_IBV_FLOW_ACTION) self.client.flow = Mlx5Flow(tx_matcher, empty_value_param, [action_reformat_attr], 1) # RX steering rx_matcher = self.server.create_matcher(empty_bytes_arr, u.MatchCriteriaEnable.NONE) action_qp_attr = Mlx5FlowActionAttr(action_type=dve.MLX5DV_FLOW_ACTION_DEST_IBV_QP, qp=self.server.qp) self.server.flow = Mlx5Flow(rx_matcher, empty_value_param, [action_qp_attr], 1) # Send traffic and validate packet packet = u.gen_packet(self.client.msg_size) u.raw_traffic(self.client, self.server, self.iters, expected_packet=outer + packet, skip_idxs=ipv4_id_idx + ipv4_chksum_idx + udp_chksum_idx)