| # SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) |
| # Copyright (c) 2020 Nvidia All rights reserved. See COPYING file |
| """ |
| Test module for pyverbs' mlx5 dr module. |
| """ |
| |
| from os import path, system |
| import unittest |
| import struct |
| import socket |
| import errno |
| import math |
| |
| from pyverbs.providers.mlx5.dr_action import DrActionQp, DrActionModify, \ |
| DrActionFlowCounter, DrActionDrop, DrActionTag, DrActionDestTable, \ |
| DrActionPopVLan, DrActionPushVLan, DrActionDestAttr, DrActionDestArray, \ |
| DrActionDefMiss, DrActionVPort, DrActionIBPort, DrActionDestTir, DrActionPacketReformat,\ |
| DrFlowSamplerAttr, DrActionFlowSample, DrFlowMeterAttr, DrActionFlowMeter |
| from pyverbs.providers.mlx5.mlx5dv import Mlx5DevxObj, Mlx5Context, Mlx5DVContextAttr |
| from tests.utils import skip_unsupported, requires_eswitch_on, PacketConsts, requires_cap_net_raw,\ |
| requires_eth, requires_root |
| from tests.mlx5_base import Mlx5RDMATestCase, PyverbsAPITestCase, MELLANOX_VENDOR_ID |
| from pyverbs.providers.mlx5.mlx5dv_flow import Mlx5FlowMatchParameters |
| from pyverbs.pyverbs_error import PyverbsRDMAError, PyverbsUserError |
| from pyverbs.providers.mlx5.dr_matcher import DrMatcher |
| from pyverbs.providers.mlx5.dr_domain import DrDomain |
| from pyverbs.providers.mlx5.dr_table import DrTable |
| from pyverbs.providers.mlx5.dr_rule import DrRule |
| from pyverbs.providers.mlx5.mlx5_enums import mlx5dv_dr_domain_type, mlx5dv_dr_action_flags, \ |
| mlx5dv_dr_matcher_layout_flags, mlx5dv_dr_action_dest_type, mlx5dv_dr_action_flags, \ |
| MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL_, \ |
| MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL_, \ |
| MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TUNNEL_TO_L2_, \ |
| MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L3_TUNNEL_TO_L2_ |
| |
| from tests.test_mlx5_flow import requires_reformat_support |
| from pyverbs.cq import CqInitAttrEx, CQEX, CQ |
| from pyverbs.wq import WQInitAttr, WQ, WQAttr |
| from tests.base import RawResources |
| from pyverbs.libibverbs_enums import ibv_wq_attr_mask, ibv_wq_state, ibv_wr_opcode, ibv_create_cq_wc_flags, \ |
| IBV_WC_STANDARD_FLAGS |
| import tests.utils as u |
| |
| SET_ACTION = 0x1 |
| MAX_MATCH_PARAM_SIZE = 0x180 |
| PF_VPORT = 0x0 |
| GENEVE_PACKET_OUTER_LENGTH = 50 |
| ROCE_PACKET_OUTER_LENGTH = 58 |
| SAMPLER_ERROR_MARGIN = 0.2 |
| SAMPLE_RATIO = 4 |
| METADATA_C_FIELDS = ['metadata_reg_c_0', 'metadata_reg_c_1', 'metadata_reg_c_2', |
| 'metadata_reg_c_3', 'metadata_reg_c_4', 'metadata_reg_c_5'] |
| FLOW_METER_GREEN = 2 |
| FLOW_METER_RED = 0 |
| REG_C_DATA = 0x1234 |
| |
| |
| class ModifyFields: |
| """ |
| Supported SW steering modify fields. |
| """ |
| OUT_SMAC_47_16 = 0x1 |
| OUT_SMAC_15_0 = 0x2 |
| META_DATA_REG_C_0 = 0x51 |
| META_DATA_REG_C_1 = 0x52 |
| |
| |
| class ModifyFieldsLen: |
| """ |
| Supported SW steering modify fields length. |
| """ |
| MAC_47_16 = 32 |
| MAC_15_0 = 16 |
| META_DATA_REG_C = 32 |
| |
| |
| def skip_if_has_geneve_tx_bug(ctx): |
| """ |
| Some mlx5 devices such as CX5 and CX6 has a bug matching on Geneve fields |
| on TX side. |
| Raises unittest.SkipTest if that's the case. |
| :param ctx: Mlx5 Context |
| """ |
| dev_attrs = ctx.query_device() |
| mlx5_cx5_cx6 = [0x1017, 0x1018, 0x1019, 0x101a, 0x101b] |
| if dev_attrs.vendor_id == MELLANOX_VENDOR_ID and \ |
| dev_attrs.vendor_part_id in mlx5_cx5_cx6: |
| raise unittest.SkipTest('This test is not supported on cx5/6') |
| |
| |
| def requires_geneve_fields_rx_support(func): |
| def func_wrapper(instance): |
| nic_tbl_caps = u.query_nic_flow_table_caps(instance) |
| field_support = nic_tbl_caps.flow_table_properties_nic_receive.ft_field_support |
| if not (field_support.outer_geneve_vni and field_support.outer_geneve_oam and |
| field_support.outer_geneve_protocol_type and field_support.outer_geneve_opt_len): |
| raise unittest.SkipTest('NIC flow table does not support geneve fields') |
| return func(instance) |
| return func_wrapper |
| |
| |
| def requires_flow_counter_support(func): |
| def func_wrapper(instance): |
| nic_tbl_caps = u.query_nic_flow_table_caps(instance) |
| rx_counter_support = nic_tbl_caps.flow_table_properties_nic_receive.flow_counter |
| tx_counter_support = nic_tbl_caps.flow_table_properties_nic_transmit.flow_counter |
| if not (rx_counter_support and tx_counter_support): |
| raise unittest.SkipTest('NIC flow tables do not support counter action') |
| return func(instance) |
| return func_wrapper |
| |
| |
| class Mlx5DrResources(RawResources): |
| """ |
| Test various functionalities of the mlx5 direct rules class. |
| """ |
| def create_context(self): |
| mlx5dv_attr = Mlx5DVContextAttr() |
| try: |
| self.ctx = Mlx5Context(mlx5dv_attr, name=self.dev_name) |
| except PyverbsUserError as ex: |
| raise unittest.SkipTest(f'Could not open mlx5 context ({ex})') |
| except PyverbsRDMAError: |
| raise unittest.SkipTest('Opening mlx5 context is not supported') |
| |
| def __init__(self, dev_name, ib_port, gid_index=0, wc_flags=0, msg_size=1024, qp_count=1): |
| self.wc_flags = wc_flags |
| super().__init__(dev_name=dev_name, ib_port=ib_port, gid_index=gid_index, |
| msg_size=msg_size, qp_count=qp_count) |
| |
| @requires_cap_net_raw() |
| @requires_eth() |
| def create_qps(self): |
| super().create_qps() |
| |
| def create_cq(self): |
| """ |
| Create an Extended CQ. |
| """ |
| wc_flags = IBV_WC_STANDARD_FLAGS | self.wc_flags |
| cia = CqInitAttrEx(cqe=self.num_msgs, wc_flags=wc_flags) |
| try: |
| self.cq = CQEX(self.ctx, cia) |
| except PyverbsRDMAError as ex: |
| if ex.error_code == errno.EOPNOTSUPP: |
| raise unittest.SkipTest('Create Extended CQ is not supported') |
| raise ex |
| |
| def get_first_flow_meter_reg_id(self): |
| """ |
| Queries hca caps for supported reg C indexes for flow meter. |
| :return: First reg C index that is supported |
| """ |
| from tests.mlx5_prm_structs import QueryHcaCapIn, QueryQosCapOut, DevxOps |
| query_cap_in = QueryHcaCapIn(op_mod=DevxOps.MLX5_CMD_OP_QUERY_QOS_CAP << 1) |
| cmd_res = self.ctx.devx_general_cmd(query_cap_in, len(QueryQosCapOut())) |
| query_cap_out = QueryQosCapOut(cmd_res) |
| if query_cap_out.status: |
| raise PyverbsRDMAError(f'QUERY_HCA_CAP has failed with status ({query_cap_out.status}) ' |
| f'and syndrome ({query_cap_out.syndrome})') |
| bit_regs = query_cap_out.capability.flow_meter_reg_id |
| if bit_regs == 0: |
| raise unittest.SkipTest('Reg C is not supported') |
| return int(math.log2(bit_regs & -bit_regs)) |
| |
| |
| class Mlx5DrTirResources(Mlx5DrResources): |
| def __init__(self, dev_name, ib_port, gid_index=0, wc_flags=0, msg_size=1024, |
| qp_count=1, server=False): |
| self.server = server |
| super().__init__(dev_name=dev_name, ib_port=ib_port, gid_index=gid_index, |
| wc_flags=wc_flags, msg_size=msg_size, qp_count=qp_count) |
| |
| def create_cq(self): |
| self.cq = CQ(self.ctx, cqe=self.num_msgs) |
| |
| @requires_cap_net_raw() |
| @requires_eth() |
| def create_qps(self): |
| if not self.server: |
| super().create_qps() |
| else: |
| from tests.mlx5_prm_structs import Tirc, CreateTirIn, CreateTirOut |
| self.qps = [WQ(self.ctx, WQInitAttr(wq_pd=self.pd, wq_cq=self.cq))] |
| self.qps[0].modify(WQAttr(attr_mask=ibv_wq_attr_mask.IBV_WQ_ATTR_STATE, wq_state=ibv_wq_state.IBV_WQS_RDY)) |
| tir_ctx = Tirc(inline_rqn=self.qps[0].wqn) |
| self.tir = Mlx5DevxObj(self.ctx, CreateTirIn(tir_context=tir_ctx), len(CreateTirOut())) |
| |
| |
| class Mlx5DrTest(Mlx5RDMATestCase): |
| def setUp(self): |
| super().setUp() |
| self.iters = 10 |
| self.server = None |
| self.client = None |
| self.rules = [] |
| |
| def tearDown(self): |
| if self.server: |
| self.server.ctx.close() |
| if self.client: |
| self.client.ctx.close() |
| |
| @skip_unsupported |
| def create_rx_recv_rules_based_on_match_params(self, mask_param, val_param, actions, |
| match_criteria=u.MatchCriteriaEnable.OUTER, |
| domain=None, log_matcher_size=None, |
| root_only=False): |
| """ |
| Creates a rule on RX domain that forwards packets that match on the provided parameters |
| to the SW steering flow table and another rule on that table |
| with provided actions. |
| :param mask_param: The FlowTableEntryMatchParamSW mask matcher value. |
| :param val_param: The FlowTableEntryMatchParamSW value matcher value. |
| :param actions: List of actions to attach to the recv rule. |
| :param match_criteria: the match criteria enable flag to match on |
| :param domain: RX DR domain to use if provided, otherwise create default RX domain. |
| :param log_matcher_size: Size of the matcher table |
| :param root_only : If True, rules are created only on root table |
| :return: Non-root table and dest table action to it if root=false else root_table |
| """ |
| self.domain_rx = domain if domain else DrDomain(self.server.ctx, |
| mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| root_table = DrTable(self.domain_rx, 0) |
| if not root_only: |
| non_root_table = DrTable(self.domain_rx, 1) |
| table = root_table if root_only else non_root_table |
| self.matcher = DrMatcher(table, 1, match_criteria, mask_param) |
| if log_matcher_size: |
| self.matcher.set_layout(log_matcher_size) |
| self.rules.append(DrRule(self.matcher, val_param, actions)) |
| if not root_only: |
| self.root_matcher = DrMatcher(root_table, 0, match_criteria, mask_param) |
| self.dest_table_action = DrActionDestTable(table) |
| self.rules.append(DrRule(self.root_matcher, val_param, [self.dest_table_action])) |
| return table, self.dest_table_action |
| return table |
| |
| @skip_unsupported |
| def create_rx_recv_rules(self, smac_value, actions, log_matcher_size=None, domain=None, |
| root_only=False): |
| """ |
| Creates a rule on RX domain that forwards packets that match the smac in the matcher |
| to the SW steering flow table and another rule on that table with provided actions. |
| :param smac_value: The smac matcher value. |
| :param actions: List of actions to attach to the recv rule. |
| :param log_matcher_size: Size of the matcher table |
| :param domain: RX DR domain to use if provided, otherwise create default RX domain. |
| :param root_only : If True, rules are created only on root table |
| :return: Non-root table and dest table action to it if root_only=false else root_table |
| """ |
| smac_mask = bytes([0xff] * 6) + bytes(2) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| # Size of the matcher value should be modulo 4 |
| smac_value = smac_value if root_only else smac_value + bytes(2) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| return self.create_rx_recv_rules_based_on_match_params(mask_param, value_param, actions, |
| u.MatchCriteriaEnable.OUTER, |
| domain, log_matcher_size, |
| root_only=root_only) |
| |
| def send_client_raw_packets(self, iters, src_mac=None): |
| """ |
| Send raw packets. |
| :param iters: Number of packets to send. |
| :param src_mac: If set, src mac to set in the packets. |
| """ |
| c_send_wr, _, _ = u.get_send_elements_raw_qp(self.client, src_mac=src_mac) |
| poll_cq = u.poll_cq_ex if isinstance(self.client.cq, CQEX) else u.poll_cq |
| for _ in range(iters): |
| u.send(self.client, c_send_wr, ibv_wr_opcode.IBV_WR_SEND) |
| poll_cq(self.client.cq) |
| |
| def send_server_fdb_to_nic_packets(self, iters): |
| """ |
| Server sends and receives raw packets. |
| :param iters: Number of packets to send. |
| """ |
| s_recv_wr = u.get_recv_wr(self.server) |
| u.post_recv(self.server, s_recv_wr, qp_idx=0) |
| c_send_wr, _, msg = u.get_send_elements_raw_qp(self.server) |
| for _ in range(iters): |
| u.send(self.server, c_send_wr, ibv_wr_opcode.IBV_WR_SEND) |
| u.poll_cq_ex(self.server.cq) |
| u.poll_cq_ex(self.server.cq) |
| u.post_recv(self.server, s_recv_wr, qp_idx=0) |
| msg_received = self.server.mr.read(self.server.msg_size, 0) |
| u.validate_raw(msg_received, msg, []) |
| |
| def dest_port(self, is_vport=True): |
| """ |
| Creates FDB domain, root table with matcher on source mac on the server |
| side. Create a rule to forward all traffic to the non-root table. |
| On this table apply VPort/IBPort action goto PF. |
| On the server open another RX domain on PF with QP action and validate |
| packets by sending traffic from client, catch all traffic with |
| VPort/IBPort action goto PF, open another RX domain on PF with QP |
| action and validate packets. |
| :param is_vport: A flag to indicate if to use VPort or IBPort action. |
| """ |
| self.client = Mlx5DrResources(**self.dev_info) |
| self.server = Mlx5DrResources(**self.dev_info) |
| self.domain_fdb = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_FDB) |
| port_action = DrActionVPort(self.domain_fdb, PF_VPORT) if is_vport \ |
| else DrActionIBPort(self.domain_fdb, self.ib_port) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.fdb_table, self.fdb_dest_act = self.create_rx_recv_rules(smac_value, [port_action], |
| domain=self.domain_fdb) |
| self.domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| rx_table = DrTable(self.domain_rx, 0) |
| qp_action = DrActionQp(self.server.qp) |
| smac_mask = bytes([0xff] * 6) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| rx_matcher = DrMatcher(rx_table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| self.rules.append(DrRule(rx_matcher, value_param, [qp_action])) |
| # Validate traffic on RX |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @staticmethod |
| def create_dest_mac_params(): |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| |
| eth_match_mask = FlowTableEntryMatchParamSW() |
| eth_match_mask.outer_headers.dmac = PacketConsts.MAC_MASK |
| eth_match_value = FlowTableEntryMatchParamSW() |
| eth_match_value.outer_headers.dmac = PacketConsts.DST_MAC |
| mask_param = Mlx5FlowMatchParameters(len(eth_match_mask), eth_match_mask) |
| value_param = Mlx5FlowMatchParameters(len(eth_match_value), eth_match_value) |
| return mask_param, value_param |
| |
| @staticmethod |
| def create_counter(ctx): |
| """ |
| Create flow counter. |
| :param ctx: The player context to create the counter on. |
| :return: The counter object and the flow counter ID . |
| """ |
| from tests.mlx5_prm_structs import AllocFlowCounterIn, AllocFlowCounterOut |
| counter = Mlx5DevxObj(ctx, AllocFlowCounterIn(), len(AllocFlowCounterOut())) |
| flow_counter_id = AllocFlowCounterOut(counter.out_view).flow_counter_id |
| return counter, flow_counter_id |
| |
| @staticmethod |
| def query_counter_packets(counter, flow_counter_id): |
| """ |
| Query flow counter packets count. |
| :param counter: The counter for the query. |
| :param flow_counter_id: The flow counter ID for the query. |
| :return: Number of packets on this counter. |
| """ |
| from tests.mlx5_prm_structs import QueryFlowCounterIn, QueryFlowCounterOut |
| query_in = QueryFlowCounterIn(flow_counter_id=flow_counter_id) |
| counter_out = QueryFlowCounterOut(counter.query(query_in, len(QueryFlowCounterOut()))) |
| return counter_out.flow_statistics.packets |
| |
| @staticmethod |
| def gen_gre_tunnel_encap_header(msg_size, is_l2_tunnel=True): |
| gre_ether_type = PacketConsts.ETHER_TYPE_ETH if is_l2_tunnel else \ |
| PacketConsts.ETHER_TYPE_IPV4 |
| gre_header = u.gen_gre_header(ether_type=gre_ether_type) |
| ip_header = u.gen_ipv4_header(packet_len=msg_size + len(gre_header), |
| next_proto=socket.IPPROTO_GRE) |
| mac_header = u.gen_ethernet_header() |
| return mac_header + ip_header + gre_header |
| |
| @staticmethod |
| def gen_geneve_tunnel_encap_header(msg_size, is_l2_tunnel=True): |
| proto = PacketConsts.ETHER_TYPE_ETH if is_l2_tunnel else PacketConsts.ETHER_TYPE_IPV4 |
| geneve_header = u.gen_geneve_header(proto=proto) |
| udp_header = u.gen_udp_header(packet_len=msg_size + len(geneve_header), |
| dst_port=PacketConsts.GENEVE_PORT) |
| ip_header = u.gen_ipv4_header(packet_len=msg_size + len(udp_header) + len(geneve_header)) |
| mac_header = u.gen_ethernet_header() |
| return mac_header + ip_header + udp_header + geneve_header |
| |
| @staticmethod |
| def create_geneve_params(): |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| geneve_mask = FlowTableEntryMatchParamSW() |
| geneve_mask.misc_parameters.geneve_vni = 0xffffff |
| geneve_mask.misc_parameters.geneve_oam = 1 |
| geneve_value = FlowTableEntryMatchParamSW() |
| geneve_value.misc_parameters.geneve_vni = PacketConsts.GENEVE_VNI |
| geneve_value.misc_parameters.geneve_oam = PacketConsts.GENEVE_OAM |
| mask_param = Mlx5FlowMatchParameters(len(geneve_mask), geneve_mask) |
| value_param = Mlx5FlowMatchParameters(len(geneve_value), geneve_value) |
| return mask_param, value_param |
| |
| @staticmethod |
| def gen_roce_bth_header(msg_size): |
| mac_header = u.gen_ethernet_header() |
| ip_header = u.gen_ipv4_header(packet_len=msg_size + PacketConsts.UDP_HEADER_SIZE + |
| PacketConsts.BTH_HEADER_SIZE) |
| udp_header = u.gen_udp_header(packet_len=msg_size + PacketConsts.BTH_HEADER_SIZE, |
| dst_port=PacketConsts.ROCE_PORT) |
| bth_header = u.gen_bth_header() |
| return mac_header + ip_header + udp_header + bth_header |
| |
| @staticmethod |
| def create_roce_bth_params(): |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| roce_mask = FlowTableEntryMatchParamSW() |
| roce_mask.misc_parameters.bth_opcode = 0xff |
| roce_mask.misc_parameters.bth_dst_qp = 0xffffff |
| roce_mask.misc_parameters.bth_a = 0x1 |
| roce_value = FlowTableEntryMatchParamSW() |
| roce_value.misc_parameters.bth_opcode = PacketConsts.BTH_OPCODE |
| roce_value.misc_parameters.bth_dst_qp = PacketConsts.BTH_DST_QP |
| roce_value.misc_parameters.bth_a = PacketConsts.BTH_A |
| mask_param = Mlx5FlowMatchParameters(len(roce_mask), roce_mask) |
| value_param = Mlx5FlowMatchParameters(len(roce_value), roce_value) |
| return mask_param, value_param |
| |
| def create_empty_matcher_go_to_tbl(self, src_tbl, dst_tbl): |
| """ |
| Create rule that forward all packets (by empty matcher) from src_tbl to |
| dst_tbl. |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| |
| empty_param = Mlx5FlowMatchParameters(len(FlowTableEntryMatchParamSW()), |
| FlowTableEntryMatchParamSW()) |
| matcher = DrMatcher(src_tbl, 0, u.MatchCriteriaEnable.NONE, empty_param) |
| go_to_tbl_action = DrActionDestTable(dst_tbl) |
| self.rules.append(DrRule(matcher, empty_param, [go_to_tbl_action])) |
| return go_to_tbl_action |
| |
| @requires_eswitch_on |
| @skip_unsupported |
| def test_dest_vport(self): |
| self.dest_port() |
| |
| @requires_eswitch_on |
| @skip_unsupported |
| def test_dest_ib_port(self): |
| self.dest_port(False) |
| |
| @skip_unsupported |
| def add_qp_rule_and_send_pkts(self, root_only=False): |
| """ |
| :param root_only : If True, rules are created only on root table |
| """ |
| self.create_players(Mlx5DrResources) |
| self.qp_action = DrActionQp(self.server.qp) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.create_rx_recv_rules(smac_value, [self.qp_action], root_only=root_only) |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @requires_root() |
| def test_tbl_qp_rule(self): |
| """ |
| Creates RX domain, SW table with matcher on source mac. Creates QP action |
| and a rule with this action on the matcher. |
| """ |
| self.add_qp_rule_and_send_pkts() |
| |
| def test_root_tbl_qp_rule(self): |
| """ |
| Creates RX domain, SW table with matcher on source mac. Creates QP action |
| and a rule with this action on the matcher. |
| """ |
| self.add_qp_rule_and_send_pkts(root_only=True) |
| |
| @skip_unsupported |
| def modify_tx_smac_and_send_pkts(self, root_only=False): |
| """ |
| Create a rule on TX domain that modifies smac of matched packet and |
| sends it to the wire. |
| :param root_only : If True, rules are created only on root table |
| """ |
| from tests.mlx5_prm_structs import SetActionIn |
| self.create_players(Mlx5DrResources) |
| self.domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| root_table_tx = DrTable(self.domain_tx, 0) |
| if not root_only: |
| non_root_table_tx = DrTable(self.domain_tx, 1) |
| self.move_action = self.create_empty_matcher_go_to_tbl(root_table_tx, non_root_table_tx) |
| table = root_table_tx if root_only else non_root_table_tx |
| smac_mask = bytes([0xff] * 6) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| matcher_tx = DrMatcher(table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| smac_value += bytes(2) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| action1 = SetActionIn(action_type=SET_ACTION, field=ModifyFields.OUT_SMAC_47_16, |
| data=0x88888888, length=ModifyFieldsLen.MAC_47_16) |
| action2 = SetActionIn(action_type=SET_ACTION, field=ModifyFields.OUT_SMAC_15_0, |
| data=0x8888, length=ModifyFieldsLen.MAC_15_0) |
| flags = mlx5dv_dr_action_flags.MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL if root_only else 0 |
| self.modify_action_tx = DrActionModify(self.domain_tx, flags, [action1, action2]) |
| self.rules.append(DrRule(matcher_tx, value_param, [self.modify_action_tx])) |
| src_mac = struct.pack('!6s', bytes.fromhex("88:88:88:88:88:88".replace(':', ''))) |
| self.qp_action = DrActionQp(self.server.qp) |
| self.create_rx_recv_rules(src_mac, [self.qp_action], root_only=root_only) |
| exp_packet = u.gen_packet(self.client.msg_size, src_mac=src_mac) |
| u.raw_traffic(self.client, self.server, self.iters, expected_packet=exp_packet) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_tbl_modify_header_rule(self): |
| """ |
| Creates TX domain, SW table with matcher on source mac and modify the smac. |
| Then creates RX domain and rule that forwards packets with the new smac |
| to server QP. Perform traffic that do this flow. |
| """ |
| self.modify_tx_smac_and_send_pkts() |
| |
| @skip_unsupported |
| def test_root_tbl_modify_header_rule(self): |
| """ |
| Creates TX domain, root table with matcher on source mac and modify the smac. |
| Then creates RX domain and rule that forwards packets with the new smac |
| to server QP. Perform traffic that do this flow. |
| """ |
| self.modify_tx_smac_and_send_pkts(root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_metadata_modify_action_set_copy_match(self): |
| """ |
| Verify modify header with set and copy actions. |
| TX and RX: |
| - Root table: |
| Match empty (hit all): |
| Rule: prio 0 - val empty. Action: Go TO Table 1 |
| - Table 1: |
| Match empty (hit all): |
| Rule: prio 0 - val empty. Action: Modify Header (set reg_c_0 to REG_C_DATA) |
| + Go TO Table 2 |
| - Table 2: |
| Match empty (hit all): |
| Rule: prio 0 - val empty. Action: Modify Header (copy reg_c_0 to reg_c_1) |
| + Go To Table 3 |
| TX: |
| - Table 3: |
| Match reg_c_0 and reg_c_1: |
| Rule: prio 0 - val REG_C_DATA. Action: Counter |
| RX: |
| - Table 3: |
| Match reg_c_0 and reg_c_1: |
| Rule: prio 0 - val REG_C_DATA. Action: Go To QP |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW, FlowTableEntryMatchSetMisc2, \ |
| SetActionIn, CopyActionIn |
| |
| self.create_players(Mlx5DrResources) |
| match_param = FlowTableEntryMatchParamSW() |
| empty_param = Mlx5FlowMatchParameters(len(match_param), match_param) |
| mask_metadata = FlowTableEntryMatchParamSW(misc_parameters_2= |
| FlowTableEntryMatchSetMisc2(metadata_reg_c_0=0xffff, metadata_reg_c_1=0xffff)) |
| mask_param = Mlx5FlowMatchParameters(len(match_param), mask_metadata) |
| value_metadata = FlowTableEntryMatchParamSW(misc_parameters_2= |
| FlowTableEntryMatchSetMisc2(metadata_reg_c_0=REG_C_DATA, |
| metadata_reg_c_1=REG_C_DATA)) |
| value_param = Mlx5FlowMatchParameters(len(match_param), value_metadata) |
| self.client.domain = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| self.server.domain = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| for player in [self.client, self.server]: |
| player.tables = [] |
| player.matchers = [] |
| for i in range(4): |
| player.tables.append(DrTable(player.domain, i)) |
| for i in range(2): |
| player.matchers.append(DrMatcher(player.tables[i + 1], 0, |
| u.MatchCriteriaEnable.NONE, empty_param)) |
| player.matchers.append(DrMatcher(player.tables[3], 0, |
| u.MatchCriteriaEnable.MISC_2, mask_param)) |
| player.go_to_tbl1_action = self.create_empty_matcher_go_to_tbl(player.tables[0], |
| player.tables[1]) |
| set_reg = SetActionIn(field=ModifyFields.META_DATA_REG_C_0, |
| length=ModifyFieldsLen.META_DATA_REG_C, data=REG_C_DATA) |
| player.modify_action_set = DrActionModify(player.domain, 0, [set_reg]) |
| player.go_to_tbl2_action = DrActionDestTable(player.tables[2]) |
| self.rules.append(DrRule(player.matchers[0], empty_param, [player.modify_action_set, |
| player.go_to_tbl2_action])) |
| copy_reg = CopyActionIn(src_field=ModifyFields.META_DATA_REG_C_0, |
| length=ModifyFields.META_DATA_REG_C_0, |
| dst_field=ModifyFields.META_DATA_REG_C_1) |
| player.modify_action_copy = DrActionModify(player.domain, 0, [copy_reg]) |
| player.go_to_tbl3_action = DrActionDestTable(player.tables[3]) |
| self.rules.append(DrRule(player.matchers[1], empty_param, [player.modify_action_copy, |
| player.go_to_tbl3_action])) |
| counter, flow_counter_id = self.create_counter(self.client.ctx) |
| counter_action = DrActionFlowCounter(counter) |
| self.rules.append(DrRule(self.client.matchers[2], value_param, [counter_action])) |
| qp_action = DrActionQp(self.server.qp) |
| self.rules.append(DrRule(self.server.matchers[2], value_param, [qp_action])) |
| u.raw_traffic(self.client, self.server, self.iters) |
| sent_packets = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(sent_packets, self.iters, 'Counter of metadata missed some sent packets') |
| |
| @skip_unsupported |
| def add_counter_action_and_send_pkts(self, root_only=False): |
| """ |
| :param root_only : If True, rules are created only on root table |
| """ |
| self.create_players(Mlx5DrResources) |
| counter, flow_counter_id = self.create_counter(self.server.ctx) |
| self.server_counter_action = DrActionFlowCounter(counter) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.qp_action = DrActionQp(self.server.qp) |
| self.create_rx_recv_rules(smac_value, [self.qp_action, self.server_counter_action], |
| root_only=root_only) |
| u.raw_traffic(self.client, self.server, self.iters) |
| recv_packets = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets, self.iters, 'Counter missed some recv packets') |
| |
| @skip_unsupported |
| def test_root_tbl_counter_action(self): |
| """ |
| Create flow counter object, on root table attach it to a rule using counter action |
| and perform traffic that hit this rule. Verify that the packets counter |
| increased. |
| """ |
| self.add_counter_action_and_send_pkts(root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_tbl_counter_action(self): |
| """ |
| Create flow counter object, on non-root table attach it to a rule using counter action |
| and perform traffic that hit this rule. Verify that the packets counter |
| increased. |
| """ |
| self.add_counter_action_and_send_pkts() |
| |
| |
| @skip_unsupported |
| @requires_root() |
| def test_prevent_duplicate_rule(self): |
| """ |
| Creates RX domain, sets duplicate rule to be not allowed on that domain, |
| try creating duplicate rule. Fail if creation succeeded. |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| self.server = Mlx5DrResources(**self.dev_info) |
| domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| domain_rx.allow_duplicate_rules(False) |
| table = DrTable(domain_rx, 1) |
| empty_param = Mlx5FlowMatchParameters(len(FlowTableEntryMatchParamSW()), |
| FlowTableEntryMatchParamSW()) |
| matcher = DrMatcher(table, 0, u.MatchCriteriaEnable.NONE, empty_param) |
| self.qp_action = DrActionQp(self.server.qp) |
| self.drop_action = DrActionDrop() |
| self.rules.append(DrRule(matcher, empty_param, [self.qp_action])) |
| with self.assertRaises(PyverbsRDMAError) as ex: |
| self.rules.append(DrRule(matcher, empty_param, [self.drop_action])) |
| self.assertEqual(ex.exception.error_code, errno.EEXIST) |
| |
| def _drop_action(self, root_only=False): |
| self.create_players(Mlx5DrResources) |
| # Initiate the sender side |
| domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| tx_root_table = DrTable(domain_tx, 0) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| if not root_only: |
| tx_non_root_table = DrTable(domain_tx, 1) |
| tx_dest_table_action = self.fwd_packets_to_table(tx_root_table, tx_non_root_table) |
| smac_value += bytes(2) |
| tx_test_table = tx_root_table if root_only else tx_non_root_table |
| mask_param = Mlx5FlowMatchParameters(len(bytes([0xff] * 6)), bytes([0xff] * 6)) |
| matcher = DrMatcher(tx_test_table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| self.tx_drop_action = DrActionDrop() |
| self.rules.append(DrRule(matcher, value_param, [self.tx_drop_action])) |
| # Initiate the receiver side |
| domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| rx_root_table = DrTable(domain_rx, 0) |
| if not root_only: |
| rx_non_root_table = DrTable(domain_rx, 1) |
| rx_dest_table_action = self.fwd_packets_to_table(rx_root_table, rx_non_root_table) |
| rx_test_table = rx_root_table if root_only else rx_non_root_table |
| # Create server counter. |
| counter, flow_counter_id = self.create_counter(self.server.ctx) |
| self.server_counter_action = DrActionFlowCounter(counter) |
| mask_param, value_param = self.create_dest_mac_params() |
| matcher = DrMatcher(rx_test_table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| self.rx_drop_action = DrActionDrop() |
| self.rules.append(DrRule(matcher, value_param, [self.server_counter_action, |
| self.rx_drop_action])) |
| # Send packets with two different smacs and expect half to be dropped. |
| src_mac_drop = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| src_mac_non_drop = struct.pack('!6s', bytes.fromhex("88:88:88:88:88:88".replace(':', ''))) |
| self.send_client_raw_packets(int(self.iters / 2), src_mac=src_mac_drop) |
| recv_packets = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets, 0, 'Drop action did not drop the TX packets') |
| self.send_client_raw_packets(int(self.iters / 2), src_mac=src_mac_non_drop) |
| recv_packets = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets, int(self.iters/2), |
| 'Drop action dropped TX packets that not matched the rule') |
| |
| @skip_unsupported |
| def test_root_tbl_drop_action(self): |
| """ |
| Create root drop actions on TX and RX. Verify using counter on the server RX that |
| only packets which miss the drop rule arrived to the server RX. |
| """ |
| self._drop_action(root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_tbl_drop_action(self): |
| """ |
| Create non-root drop actions on TX and RX. Verify using counter on the server RX that |
| only packets that which the drop rule arrived to the server RX. |
| """ |
| self._drop_action() |
| |
| @skip_unsupported |
| def add_qp_tag_rule_and_send_pkts(self, root_only=False): |
| """ |
| Creates RX domain, table with matcher on source mac. Creates QP action |
| and tag action. Creates a rule with those actions on the matcher. |
| Verifies traffic and tag. |
| :param root_only : If True, rules are created only on root table |
| """ |
| self.wc_flags = ibv_create_cq_wc_flags.IBV_WC_EX_WITH_FLOW_TAG |
| self.create_players(Mlx5DrResources, wc_flags=ibv_create_cq_wc_flags.IBV_WC_EX_WITH_FLOW_TAG) |
| qp_action = DrActionQp(self.server.qp) |
| tag = 0x123 |
| tag_action = DrActionTag(tag) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.create_rx_recv_rules(smac_value, [tag_action, qp_action], root_only=root_only) |
| self.domain_rx.sync() |
| u.raw_traffic(self.client, self.server, self.iters) |
| # Verify tag |
| self.assertEqual(self.server.cq.read_flow_tag(), tag, 'Wrong tag value') |
| |
| @skip_unsupported |
| @requires_root() |
| def test_tbl_qp_tag_rule(self): |
| """ |
| Creates RX domain, non-root table with matcher on source mac. Creates QP action |
| and tag action. Creates a rule with those actions on the matcher. |
| Verifies traffic and tag. |
| """ |
| self.add_qp_tag_rule_and_send_pkts() |
| |
| @skip_unsupported |
| def test_root_tbl_qp_tag_rule(self): |
| """ |
| Creates RX domain, root table with matcher on source mac. Creates QP action |
| and tag action. Creates a rule with those actions on the matcher. |
| Verifies traffic and tag. |
| """ |
| self.add_qp_tag_rule_and_send_pkts(root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_set_matcher_layout(self): |
| """ |
| Creates a non root matcher and sets its size. Creates a rule on that |
| matcher and increases the matcher size. Verifies the rule. |
| """ |
| log_matcher_size = 5 |
| self.create_players(Mlx5DrResources) |
| self.qp_action = DrActionQp(self.server.qp) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.create_rx_recv_rules(smac_value, [self.qp_action], log_matcher_size) |
| self.matcher.set_layout(log_matcher_size + 1) |
| u.raw_traffic(self.client, self.server, self.iters) |
| self.matcher.set_layout(flags=mlx5dv_dr_matcher_layout_flags.MLX5DV_DR_MATCHER_LAYOUT_RESIZABLE) |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_push_vlan(self): |
| """ |
| Creates RX domain, root table with matcher on source mac. Create a rule to forward |
| all traffic to the non-root table. Creates QP action and push VLAN action. |
| Creates a rule with those actions on the matcher. |
| Verifies traffic and packet with specified VLAN. |
| """ |
| self.client = Mlx5DrResources(**self.dev_info) |
| vlan_hdr = struct.pack('!HH', PacketConsts.VLAN_TPID, (PacketConsts.VLAN_PRIO << 13) + |
| (PacketConsts.VLAN_CFI << 12) + PacketConsts.VLAN_ID) |
| self.server = Mlx5DrResources(msg_size=self.client.msg_size + PacketConsts.VLAN_HEADER_SIZE, |
| **self.dev_info) |
| self.domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| push_action = DrActionPushVLan(self.domain_tx, struct.unpack('I', vlan_hdr)[0]) |
| self.tx_table, self.tx_dest_act = self.create_rx_recv_rules(smac_value, [push_action], |
| domain=self.domain_tx) |
| self.domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| qp_action = DrActionQp(self.server.qp) |
| self.create_rx_recv_rules(smac_value, [qp_action], domain=self.domain_rx) |
| exp_packet = u.gen_packet(self.client.msg_size + PacketConsts.VLAN_HEADER_SIZE, |
| with_vlan=True) |
| u.raw_traffic(self.client, self.server, self.iters, expected_packet=exp_packet) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_pop_vlan(self): |
| """ |
| Creates RX domain, root table with matcher on source mac. Create a rule to forward |
| all traffic to the non-root table. Creates QP action and pop VLAN action. |
| Creates a rule with those actions on the matcher. |
| Verifies packets received without VLAN header. |
| """ |
| self.server = Mlx5DrResources(**self.dev_info) |
| self.client = Mlx5DrResources(**self.dev_info) |
| exp_packet = u.gen_packet(self.server.msg_size - PacketConsts.VLAN_HEADER_SIZE) |
| qp_action = DrActionQp(self.server.qp) |
| pop_action = DrActionPopVLan() |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.create_rx_recv_rules(smac_value, [pop_action, qp_action]) |
| u.raw_traffic(self.client, self.server, self.iters, with_vlan=True, expected_packet=exp_packet) |
| |
| @skip_unsupported |
| def dest_array(self, root_only=False): |
| """ |
| Creates RX domain, root table with matcher on source mac. Create a rule |
| to forward all traffic to the non-root table. On this table add a rule |
| with multi dest array action which include destination QP actions and |
| next FT (also with QP action). |
| Validate on all QPs the received packets. |
| :param root_only : If True, rules are created only on root table |
| """ |
| max_actions = 8 |
| self.client = Mlx5DrResources(qp_count=max_actions, **self.dev_info) |
| self.server = Mlx5DrResources(qp_count=max_actions, **self.dev_info) |
| self.domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| actions = [] |
| dest_attrs = [] |
| for qp in self.server.qps[:-1]: |
| qp_action = DrActionQp(qp) |
| actions.append(qp_action) |
| dest_attrs.append(DrActionDestAttr(mlx5dv_dr_action_dest_type.MLX5DV_DR_ACTION_DEST, qp_action)) |
| ft_action = DrTable(self.domain_rx, 0xff) |
| last_table_action = DrActionDestTable(ft_action) |
| smac_mask = bytes([0xff] * 6) + bytes(2) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| last_matcher = DrMatcher(ft_action, 1, u.MatchCriteriaEnable.OUTER, mask_param) |
| dest_attrs.append(DrActionDestAttr(mlx5dv_dr_action_dest_type.MLX5DV_DR_ACTION_DEST, last_table_action)) |
| last_qp_action = DrActionQp(self.server.qps[max_actions - 1]) |
| smac_value = struct.pack('!6s2s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', '')), |
| bytes(2)) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| self.rules.append(DrRule(last_matcher, value_param, [last_qp_action])) |
| multi_dest_a = DrActionDestArray(self.domain_rx, len(dest_attrs), dest_attrs) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.create_rx_recv_rules(smac_value, [multi_dest_a], domain=self.domain_rx, |
| root_only=root_only) |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_root_dest_array(self): |
| """ |
| Creates RX domain, root table with matcher on source mac.on root table |
| add a rule with multi dest array action which include destination QP actions and |
| next FT (also with QP action). |
| Validate on all QPs the received packets. |
| """ |
| self.dest_array(root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_dest_array(self): |
| """ |
| Creates RX domain, non-root table with matcher on source mac. Create a rule |
| to forward all traffic to the non-root table. On this table add a rule |
| with multi dest array action which include destination QP actions and |
| next FT (also with QP action). |
| Validate on all QPs the received packets. |
| """ |
| self.dest_array() |
| |
| @skip_unsupported |
| @requires_root() |
| def test_tx_def_miss_action(self): |
| """ |
| Create TX root table and forward all traffic to next SW steering table, |
| create two matchers with different priorities, one with default miss |
| action (on TX it's go to wire action) and one with drop action, default |
| miss action should occur before the drop action hence packets |
| should reach server side which has RX rule with QP action. |
| """ |
| self.create_players(Mlx5DrResources) |
| self.domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| tx_def_miss = DrActionDefMiss() |
| tx_drop_action = DrActionDrop() |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.tx_table, self.tx_dest_act = self.create_rx_recv_rules(smac_value, [tx_def_miss], |
| domain=self.domain_tx) |
| qp_action = DrActionQp(self.server.qp) |
| self.create_rx_recv_rules(smac_value, [qp_action]) |
| smac_mask = bytes([0xff] * 6) + bytes(2) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| matcher_tx2 = DrMatcher(self.tx_table, 2, u.MatchCriteriaEnable.OUTER, mask_param) |
| smac_value += bytes(2) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| self.rules.append(DrRule(matcher_tx2, value_param, [tx_drop_action])) |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @skip_unsupported |
| def add_dest_tir_action_send_pkts(self, root_only=False): |
| """ |
| :param root_only: If True, rules are created only on root table |
| """ |
| self.client = Mlx5DrTirResources(**self.dev_info) |
| self.server = Mlx5DrTirResources(**self.dev_info, server=True) |
| tir_action = DrActionDestTir(self.server.tir) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.create_rx_recv_rules(smac_value, [tir_action], root_only=root_only) |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_dest_tir(self): |
| self.add_dest_tir_action_send_pkts() |
| |
| @skip_unsupported |
| def test_root_dest_tir(self): |
| self.add_dest_tir_action_send_pkts(root_only=True) |
| |
| def packet_reformat_actions(self, outer, root_only=False, l2_ref_type=True): |
| """ |
| Creates packet reformat actions on TX (encap) and on RX (decap). |
| :param outer: The outer header to encap. |
| :param root_only: If True create actions only on root tables |
| :param l2_ref_type: If False use L2 to L3 tunneling reformat |
| """ |
| smac_mask = bytes([0xff] * 6) + bytes(2) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| reformat_flag = mlx5dv_dr_action_flags.MLX5DV_DR_ACTION_FLAGS_ROOT_LEVEL if root_only else 0 |
| # TX |
| domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| tx_root_table = DrTable(domain_tx, 0) |
| tx_root_matcher = DrMatcher(tx_root_table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| if not root_only: |
| tx_table = DrTable(domain_tx, 1) |
| tx_matcher = DrMatcher(tx_table, 1, u.MatchCriteriaEnable.OUTER, mask_param) |
| dest_table_action_tx = DrActionDestTable(tx_table) |
| self.rules.append(DrRule(tx_root_matcher, value_param, [dest_table_action_tx])) |
| reformat_matcher = tx_root_matcher if root_only else tx_matcher |
| # Create encap action |
| tx_reformat_type = MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL_ if \ |
| l2_ref_type else MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL_ |
| reformat_action_tx = DrActionPacketReformat(domain=domain_tx, flags=reformat_flag, |
| reformat_type=tx_reformat_type, data=outer) |
| smac_value_tx = smac_value + bytes(2) |
| value_param = Mlx5FlowMatchParameters(len(smac_value_tx), smac_value_tx) |
| self.rules.append(DrRule(reformat_matcher, value_param, [reformat_action_tx])) |
| # RX |
| domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| # Create decap action |
| data = struct.pack('!6s6s', |
| bytes.fromhex(PacketConsts.DST_MAC.replace(':', '')), |
| bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| data += PacketConsts.ETHER_TYPE_IPV4.to_bytes(2, 'big') |
| rx_reformat_type = MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TUNNEL_TO_L2_ if \ |
| l2_ref_type else MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L3_TUNNEL_TO_L2_ |
| reformat_action_rx = DrActionPacketReformat(domain=domain_rx, flags=reformat_flag, |
| reformat_type=rx_reformat_type, |
| data=None if l2_ref_type else data) |
| qp_action = DrActionQp(self.server.qp) |
| if root_only: |
| rx_root_table = DrTable(domain_rx, 0) |
| rx_root_matcher = DrMatcher(rx_root_table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| self.rules.append(DrRule(rx_root_matcher, value_param, [reformat_action_rx, qp_action])) |
| else: |
| self.create_rx_recv_rules(smac_value, [reformat_action_rx, qp_action], domain=domain_rx) |
| |
| # Send traffic and validate packet |
| u.raw_traffic(self.client, self.server, self.iters) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_flow_sampler(self): |
| """ |
| Flow sampler has a default table (all the packets are forwarded to it) |
| and a sampler actions (for sampled packets) |
| The default table has counter action. |
| For NIC RX table sampler actions are counter and TIR. |
| Verify that default counter counts all the packets. |
| Verify that sampled packets counter and receiving them on QP(from TIR) |
| """ |
| self.client = Mlx5DrTirResources(**self.dev_info) |
| self.server = Mlx5DrTirResources(**self.dev_info, server=True) |
| self.iters = 1000 |
| # Create tir & counter actions for sampler attr |
| tir_action = DrActionDestTir(self.server.tir) |
| counter_1, flow_counter_id_1 = self.create_counter(self.server.ctx) |
| self.server_counter_action = DrActionFlowCounter(counter_1) |
| # Create resources |
| smac_value = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| rx_domain = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| default_tbl = DrTable(rx_domain, 2) |
| # Create sampler action on NIC RX table |
| sample_actions = [self.server_counter_action, tir_action] |
| sampler_attr = DrFlowSamplerAttr(sample_ratio=SAMPLE_RATIO, default_next_table=default_tbl, |
| sample_actions=sample_actions) |
| sampler_action = DrActionFlowSample(sampler_attr) |
| tbl, _ = self.create_rx_recv_rules(smac_value, [sampler_action], domain=rx_domain) |
| smac_mask = bytes([0xff] * 6) + bytes(2) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| self.default_matcher = DrMatcher(default_tbl, 1, u.MatchCriteriaEnable.OUTER, mask_param) |
| # Size of the matcher value should be modulo 4 |
| smac_value += bytes(2) |
| value_param = Mlx5FlowMatchParameters(len(smac_value), smac_value) |
| # Create Counter action on default table |
| counter_2, flow_counter_id_2 = self.create_counter(self.server.ctx) |
| self.server_counter_action_2 = DrActionFlowCounter(counter_2) |
| self.rules.append(DrRule(self.default_matcher, value_param, [self.server_counter_action_2])) |
| # Send traffic and validate packet |
| u.sampler_traffic(self.client, self.server, self.iters) |
| recv_packets = self.query_counter_packets(counter=counter_1, |
| flow_counter_id=flow_counter_id_1) |
| exp_packets = math.ceil((self.iters / SAMPLE_RATIO)) |
| max_exp_packets = int(exp_packets * (1 + SAMPLER_ERROR_MARGIN)) |
| min_exp_packets = int(exp_packets * (1 - SAMPLER_ERROR_MARGIN)) |
| is_sampled_packets_in_error_margin = min_exp_packets <= recv_packets <= max_exp_packets |
| self.assertTrue(is_sampled_packets_in_error_margin, |
| f'Expected sampled packets {exp_packets} is more than ' |
| f'{SAMPLER_ERROR_MARGIN * 100}% \ndiffernt from actual {recv_packets}') |
| recv_packets_from_default_tbl = \ |
| self.query_counter_packets(counter=counter_2, flow_counter_id=flow_counter_id_2) |
| self.assertEqual(recv_packets_from_default_tbl, self.iters, |
| 'Counter on default table missed some recv packets') |
| |
| @skip_unsupported |
| def geneve_match_rx(self, root_only=False): |
| """ |
| Creates matcher on RX to match on Geneve related fields with counter and qp action, |
| sends packets and verifies the matcher. |
| :param root_only: If True, rules are created only on root table |
| """ |
| self.create_players(Mlx5DrResources) |
| geneve_mask, geneve_val = self.create_geneve_params() |
| domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| counter, flow_counter_id = self.create_counter(self.server.ctx) |
| self.server_counter_action = DrActionFlowCounter(counter) |
| self.qp_action = DrActionQp(self.server.qp) |
| self.create_rx_recv_rules_based_on_match_params(geneve_mask, geneve_val, |
| [self.qp_action, self.server_counter_action], |
| match_criteria=u.MatchCriteriaEnable.MISC, |
| domain=domain_rx, root_only=root_only) |
| inner_msg_size = self.client.msg_size - GENEVE_PACKET_OUTER_LENGTH |
| outer = self.gen_geneve_tunnel_encap_header(inner_msg_size) |
| packet_to_send = outer + u.gen_packet(msg_size=inner_msg_size) |
| # Send traffic and validate packet |
| u.raw_traffic(self.client, self.server, self.iters, packet_to_send=packet_to_send) |
| recv_packets_rx = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets_rx, self.iters, 'Counter rx missed some recv packets') |
| src_mac = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.send_client_raw_packets(self.iters, src_mac=src_mac) |
| recv_packets_rx = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets_rx, self.iters, |
| 'Counter rx counts more than expected recv packets') |
| |
| @requires_geneve_fields_rx_support |
| def test_root_geneve_match_rx(self): |
| """ |
| Creates matcher on RX root table to match on Geneve related fields |
| with counter and qp action, sends packets and verifies the matcher. |
| """ |
| self.geneve_match_rx(root_only=True) |
| |
| @requires_geneve_fields_rx_support |
| @requires_root() |
| def test_geneve_match_rx(self): |
| """ |
| Creates matcher on RX non-root table to match on Geneve related |
| fields with counter and qp action, sends packets and verifies the matcher. |
| """ |
| self.geneve_match_rx() |
| |
| @skip_unsupported |
| def test_geneve_match_tx(self): |
| """ |
| Creates matcher on TX to match on Geneve related fields with counter action, |
| sends packets and verifies the matcher. |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| self.create_players(Mlx5DrResources) |
| skip_if_has_geneve_tx_bug(self.client.ctx) |
| geneve_mask, geneve_val = self.create_geneve_params() |
| # TX |
| self.domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| tx_root_table = DrTable(self.domain_tx, 0) |
| tx_root_matcher = DrMatcher(tx_root_table, 0, u.MatchCriteriaEnable.MISC, geneve_mask) |
| tx_table = DrTable(self.domain_tx, 1) |
| self.tx_matcher = DrMatcher(tx_table, 1, u.MatchCriteriaEnable.MISC, geneve_mask) |
| counter, flow_counter_id = self.create_counter(self.client.ctx) |
| self.client_counter_action = DrActionFlowCounter(counter) |
| self.dest_table_action_tx = DrActionDestTable(tx_table) |
| self.rules.append(DrRule(tx_root_matcher, geneve_val, [self.dest_table_action_tx])) |
| self.rules.append(DrRule(self.tx_matcher, geneve_val, [self.client_counter_action])) |
| # RX |
| domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| self.qp_action = DrActionQp(self.server.qp) |
| empty_param = Mlx5FlowMatchParameters(len(FlowTableEntryMatchParamSW()), |
| FlowTableEntryMatchParamSW()) |
| self.create_rx_recv_rules_based_on_match_params\ |
| (empty_param, empty_param, [self.qp_action], |
| match_criteria=u.MatchCriteriaEnable.NONE, domain=domain_rx) |
| inner_msg_size = self.client.msg_size - GENEVE_PACKET_OUTER_LENGTH |
| outer = self.gen_geneve_tunnel_encap_header(inner_msg_size) |
| packet_to_send = outer + u.gen_packet(msg_size=inner_msg_size) |
| # Send traffic and validate packet |
| u.raw_traffic(self.client, self.server, self.iters, packet_to_send=packet_to_send) |
| recv_packets_tx = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets_tx, self.iters, 'Counter tx missed some recv packets') |
| src_mac = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.send_client_raw_packets(self.iters, src_mac=src_mac) |
| recv_packets_tx = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets_tx, self.iters, |
| 'Counter tx counts more than expected recv packets') |
| |
| def roce_bth_match(self, domain_flag=mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX): |
| """ |
| Creates RoCE BTH rule on RX/TX domain. For RX domain, will match on BTH related |
| fields with counter and qp action. For TX domain, will match on BTH relate fields |
| with counter action. And then generate and send RoCE BTH hit and miss traffic according |
| to the matcher and validate the result. |
| :param domain_flag: RX/TX Domain for the test. |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| self.create_players(Mlx5DrResources) |
| roce_bth_mask, roce_bth_val = self.create_roce_bth_params() |
| empty_param = Mlx5FlowMatchParameters(len(FlowTableEntryMatchParamSW()), |
| FlowTableEntryMatchParamSW()) |
| self.domain = DrDomain(self.server.ctx, domain_flag) |
| root_table = DrTable(self.domain, 0) |
| root_matcher = DrMatcher(root_table, 0, u.MatchCriteriaEnable.NONE, empty_param) |
| table = DrTable(self.domain, 1) |
| self.matcher = DrMatcher(table, 1, u.MatchCriteriaEnable.MISC, roce_bth_mask) |
| if domain_flag == mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX: |
| counter, flow_counter_id = self.create_counter(self.server.ctx) |
| else: |
| counter, flow_counter_id = self.create_counter(self.client.ctx) |
| self.dest_tbl_action = DrActionDestTable(table) |
| self.qp_action = DrActionQp(self.server.qp) |
| self.counter_action = DrActionFlowCounter(counter) |
| self.rules.append(DrRule(root_matcher, empty_param, [self.dest_tbl_action])) |
| if domain_flag == mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX: |
| self.rules.append(DrRule(self.matcher, roce_bth_val, [self.qp_action, self.counter_action])) |
| else: |
| self.rules.append(DrRule(self.matcher, roce_bth_val, [self.counter_action])) |
| if domain_flag == mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX: |
| domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| self.create_rx_recv_rules_based_on_match_params\ |
| (empty_param, empty_param, [self.qp_action], |
| match_criteria=u.MatchCriteriaEnable.NONE, domain=domain_rx) |
| inner_msg_size = self.client.msg_size - ROCE_PACKET_OUTER_LENGTH |
| outer = self.gen_roce_bth_header(inner_msg_size) |
| packet_to_send = outer + u.gen_packet(msg_size=inner_msg_size) |
| # Send traffic hit the rule and validate by the counter action |
| u.raw_traffic(self.client, self.server, self.iters, packet_to_send=packet_to_send) |
| recv_packets = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets, self.iters, 'Counter missed some recv packets') |
| # Send traffic miss the rule and validate by the counter action |
| src_mac = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| self.send_client_raw_packets(self.iters, src_mac=src_mac) |
| recv_packets = self.query_counter_packets(counter, flow_counter_id) |
| self.assertEqual(recv_packets, self.iters, |
| 'Counter counts more than expected recv packets') |
| |
| @u.requires_roce_disabled |
| @skip_unsupported |
| def test_roce_bth_match_rx(self): |
| """ |
| Verify RX matching on RoCE BTH. |
| """ |
| self.roce_bth_match() |
| |
| @u.requires_roce_disabled |
| @skip_unsupported |
| def test_roce_bth_match_tx(self): |
| """ |
| Verify TX matching on RoCE BTH. |
| """ |
| self.roce_bth_match(domain_flag=mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_packet_reformat_l2_gre(self): |
| """ |
| Creates GRE packet with non-root l2 to l2 reformat actions on TX (encap) |
| and on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_gre_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=True) |
| self.packet_reformat_actions(outer=encap_header) |
| |
| @requires_reformat_support |
| @u.requires_encap_disabled_if_eswitch_on |
| @skip_unsupported |
| def test_packet_reformat_root_l2_gre(self): |
| """ |
| Creates GRE packet with root l2 to l2 reformat actions on TX (encap) and |
| on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_gre_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=True) |
| self.packet_reformat_actions(outer=encap_header, root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_packet_reformat_l3_gre(self): |
| """ |
| Creates GRE packet with non-root l2 to l3 reformat actions on TX (encap) |
| and on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_gre_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=False) |
| self.packet_reformat_actions(outer=encap_header, l2_ref_type=False) |
| |
| @requires_reformat_support |
| @u.requires_encap_disabled_if_eswitch_on |
| @skip_unsupported |
| def test_packet_reformat_root_l3_gre(self): |
| """ |
| Creates GRE packet with root l2 to l3 reformat actions on TX (encap) and |
| on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_gre_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=False) |
| self.packet_reformat_actions(outer=encap_header, root_only=True, l2_ref_type=False) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_packet_reformat_l2_geneve(self): |
| """ |
| Creates Geneve packet with non-root l2 to l2 reformat actions on TX |
| (encap) and on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_geneve_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=True) |
| self.packet_reformat_actions(outer=encap_header) |
| |
| @requires_reformat_support |
| @u.requires_encap_disabled_if_eswitch_on |
| @skip_unsupported |
| def test_packet_reformat_root_l2_geneve(self): |
| """ |
| Creates Geneve packet with root l2 to l2 reformat actions on TX (encap) |
| and on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_geneve_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=True) |
| self.packet_reformat_actions(outer=encap_header, root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_packet_reformat_l3_geneve(self): |
| """ |
| Creates Geneve packet with non-root l2 to l3 tunnel reformat actions on |
| TX (encap) and on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_geneve_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=False) |
| self.packet_reformat_actions(outer=encap_header, l2_ref_type=False) |
| |
| @requires_reformat_support |
| @u.requires_encap_disabled_if_eswitch_on |
| @skip_unsupported |
| def test_packet_reformat_root_l3_geneve(self): |
| """ |
| Creates Geneve packet with root l2 to l3 reformat actions on TX (encap) |
| and on RX (decap). |
| """ |
| self.create_players(Mlx5DrResources) |
| encap_header = self.gen_geneve_tunnel_encap_header(self.client.msg_size, is_l2_tunnel=False) |
| self.packet_reformat_actions(outer=encap_header, root_only=True, l2_ref_type=False) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_flow_meter(self): |
| """ |
| Create flow meter actions on TX and RX non-root tables. Add green and |
| red counters to the meter rules to verify the packets split to different |
| colors. Send minimal traffic to see that both counters increased. |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW, FlowTableEntryMatchSetMisc2,\ |
| FlowMeterParams |
| self.create_players(Mlx5DrResources) |
| # Common resources |
| matcher_len = len(FlowTableEntryMatchParamSW()) |
| empty_param = Mlx5FlowMatchParameters(matcher_len, FlowTableEntryMatchParamSW()) |
| reg_c_idx = self.client.get_first_flow_meter_reg_id() |
| reg_c_field = METADATA_C_FIELDS[reg_c_idx] |
| meter_param = FlowMeterParams(valid=0x1, bucket_overflow=0x1, start_color=0x2, |
| cir_mantissa=1, cir_exponent=6) # 15.625MBps |
| reg_c_mask = Mlx5FlowMatchParameters(matcher_len, FlowTableEntryMatchParamSW( |
| misc_parameters_2=FlowTableEntryMatchSetMisc2(**{reg_c_field: 0xffffffff}))) |
| reg_c_green = Mlx5FlowMatchParameters(matcher_len, FlowTableEntryMatchParamSW( |
| misc_parameters_2=FlowTableEntryMatchSetMisc2(**{reg_c_field: FLOW_METER_GREEN}))) |
| reg_c_red = Mlx5FlowMatchParameters(matcher_len, FlowTableEntryMatchParamSW( |
| misc_parameters_2=FlowTableEntryMatchSetMisc2(**{reg_c_field: FLOW_METER_RED}))) |
| |
| self.client.domain = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| self.server.domain = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| |
| for player in [self.client, self.server]: |
| player.root_table = DrTable(player.domain, 0) |
| player.table = DrTable(player.domain, 1) |
| player.next_table = DrTable(player.domain, 2) |
| player.root_matcher = DrMatcher(player.root_table, 0, u.MatchCriteriaEnable.NONE, |
| empty_param) |
| player.matcher = DrMatcher(player.table, 0, u.MatchCriteriaEnable.NONE, empty_param) |
| player.reg_c_matcher = DrMatcher(player.next_table, 2, u.MatchCriteriaEnable.MISC_2, |
| reg_c_mask) |
| meter_attr = DrFlowMeterAttr(player.next_table, 1, reg_c_idx, meter_param) |
| player.meter_action = DrActionFlowMeter(meter_attr) |
| player.dest_action = DrActionDestTable(player.table) |
| self.rules.append(DrRule(player.root_matcher, empty_param, [player.dest_action])) |
| self.rules.append(DrRule(player.matcher, empty_param, [player.meter_action])) |
| player.counter_green, player.flow_counter_id_green = self.create_counter(player.ctx) |
| player.counter_action_green = DrActionFlowCounter(player.counter_green) |
| player.counter_red, player.flow_counter_id_red = self.create_counter(player.ctx) |
| player.counter_action_red = DrActionFlowCounter(player.counter_red) |
| self.rules.append(DrRule(player.reg_c_matcher, reg_c_green, |
| [player.counter_action_green])) |
| self.rules.append(DrRule(player.reg_c_matcher, reg_c_red, [player.counter_action_red])) |
| |
| packet = u.gen_packet(self.client.msg_size) |
| # We want to send at least at 30MBps speed |
| rate_limit = 30 |
| u.high_rate_send(self.client, packet, rate_limit) |
| |
| for name, player in {'client': self.client, 'server': self.server}.items(): |
| green_packets = self.query_counter_packets(player.counter_green, |
| player.flow_counter_id_green) |
| red_packets = self.query_counter_packets(player.counter_red, player.flow_counter_id_red) |
| self.assertTrue(green_packets > 0, f'No packet of {name} got green color') |
| self.assertTrue(red_packets > 0, f'No packet of {name} got red color') |
| |
| def fwd_packets_to_table(self, src_table, dst_table): |
| """ |
| Forward all traffic from one table to another using empty matcher |
| :param src_table: Source table |
| :param dst_table: Destination table |
| :return: DrActionDestTable used to move the packets from src_table to dst_table |
| """ |
| from tests.mlx5_prm_structs import FlowTableEntryMatchParamSW |
| empty_param = Mlx5FlowMatchParameters(len(FlowTableEntryMatchParamSW()), |
| FlowTableEntryMatchParamSW()) |
| matcher = DrMatcher(src_table, 0, u.MatchCriteriaEnable.NONE, empty_param) |
| dest_table_action = DrActionDestTable(dst_table) |
| self.rules.append(DrRule(matcher, empty_param, [dest_table_action])) |
| return dest_table_action |
| |
| def gen_two_smac_rules(self, table, actions): |
| """ |
| Generate two rules that match over different smacs values. |
| The rules use the same actions and matchers. |
| :param table: The table the rules are applied on |
| :param actions: SMAC rule actions |
| :return: The two generated smacs |
| """ |
| smac_mask = bytes([0xff] * 6) + bytes(2) |
| mask_param = Mlx5FlowMatchParameters(len(smac_mask), smac_mask) |
| matcher = DrMatcher(table, 0, u.MatchCriteriaEnable.OUTER, mask_param) |
| src_mac_1 = struct.pack('!6s', bytes.fromhex(PacketConsts.SRC_MAC.replace(':', ''))) |
| src_mac_2 = struct.pack('!6s', bytes.fromhex("88:88:88:88:88:88".replace(':', ''))) |
| src_mac_1_for_matcher = src_mac_1 + bytes(2) |
| src_mac_2_for_matcher = src_mac_2 + bytes(2) |
| value_param_1 = Mlx5FlowMatchParameters(len(src_mac_1_for_matcher), src_mac_1_for_matcher) |
| value_param_2 = Mlx5FlowMatchParameters(len(src_mac_2_for_matcher), src_mac_2_for_matcher) |
| self.rules.append(DrRule(matcher, value_param_1, actions)) |
| self.rules.append(DrRule(matcher, value_param_2, actions)) |
| return src_mac_1, src_mac_2 |
| |
| def reuse_action_and_matcher(self, root_only=False): |
| """ |
| Creates rules with same matcher and actions, the rules match over different smacs. |
| Over TX side, creates rule to counter action, over RX side - creates rule to counter and |
| drop actions. Send traffic to match the rules, verify them by querying the counters. |
| :param root_only: If True, rules are created only on root table. |
| """ |
| self.create_players(Mlx5DrResources) |
| # Create TX resources |
| self.domain_tx = DrDomain(self.client.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_TX) |
| tx_root_table = DrTable(self.domain_tx, 0) |
| if not root_only: |
| tx_non_root_table = DrTable(self.domain_tx, 1) |
| tx_dest_table_action = self.fwd_packets_to_table(tx_root_table, tx_non_root_table) |
| tx_table = tx_root_table if root_only else tx_non_root_table |
| # Create client counter. |
| client_counter, tx_flow_counter_id = self.create_counter(self.client.ctx) |
| self.client_counter_action = DrActionFlowCounter(client_counter) |
| tx_actions = [self.client_counter_action] |
| self.gen_two_smac_rules(tx_table, tx_actions) |
| # Create RX resources |
| self.domain_rx = DrDomain(self.server.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| rx_root_table = DrTable(self.domain_rx, 0) |
| if not root_only: |
| rx_non_root_table = DrTable(self.domain_rx, 1) |
| rx_dest_table_action = self.fwd_packets_to_table(rx_root_table, rx_non_root_table) |
| rx_table = rx_root_table if root_only else rx_non_root_table |
| # Create server counter. |
| server_counter, rx_flow_counter_id = self.create_counter(self.server.ctx) |
| self.server_counter_action = DrActionFlowCounter(server_counter) |
| self.rx_drop_action = DrActionDrop() |
| actions = [self.server_counter_action, self.rx_drop_action] |
| src_mac_1, src_mac_2 = self.gen_two_smac_rules(rx_table, actions) |
| # Send packets with two different smacs which are used and reused in action and matcher |
| self.send_client_raw_packets(int(self.iters / 2), src_mac=src_mac_1) |
| self.send_client_raw_packets(int(self.iters / 2), src_mac=src_mac_2) |
| matched_packets_tx = self.query_counter_packets(client_counter, tx_flow_counter_id) |
| self.assertEqual(matched_packets_tx, self.iters, 'Reuse action or matcher failed on TX') |
| matched_packets_rx = self.query_counter_packets(server_counter, rx_flow_counter_id) |
| self.assertEqual(matched_packets_rx, self.iters, 'Reuse action or matcher failed on RX') |
| |
| @skip_unsupported |
| @requires_flow_counter_support |
| def test_root_reuse_action_and_matcher(self): |
| """ |
| Create root rules on TX and RX that use the same matcher and actions |
| """ |
| self.reuse_action_and_matcher(root_only=True) |
| |
| @skip_unsupported |
| @requires_root() |
| def test_reuse_action_and_matcher(self): |
| """ |
| Create non-root rules on TX and RX that use the same matcher and actions |
| """ |
| self.reuse_action_and_matcher() |
| |
| |
| class Mlx5DrDumpTest(PyverbsAPITestCase): |
| def setUp(self): |
| super().setUp() |
| self.res = None |
| |
| def tearDown(self): |
| super().tearDown() |
| if self.res: |
| self.res.ctx.close() |
| |
| @skip_unsupported |
| def test_domain_dump(self): |
| dump_file = '/tmp/dump.txt' |
| self.res = Mlx5DrResources(self.dev_name, self.ib_port) |
| self.domain_rx = DrDomain(self.res.ctx, mlx5dv_dr_domain_type.MLX5DV_DR_DOMAIN_TYPE_NIC_RX) |
| self.domain_rx.dump(dump_file) |
| self.assertTrue(path.isfile(dump_file), 'Dump file does not exist.') |
| self.assertGreater(path.getsize(dump_file), 0, 'Dump file is empty') |