blob: 0bcd72f163cd22cee0ca8bfd80b5ab9f081b8de5 [file] [log] [blame]
# 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)