Skip to content

Commit 6aa5202

Browse files
authored
Add multi binding acl test into everflow test scope (sonic-net#21022)
Add multi binding acl test into everflow test scope 1. Toggle dualtor setup to AS mode 2. Only send packet from upstream to standby downstream side 3. Match the bounce back ipinip tunnel packet my multi binding acl at active downstream side 4. Mirror the match result 5. Validate the inner packet Change-Id: I0cdafc78f407d0fc4150e03f20a8d6e3d8cb5c97
1 parent 1261f6a commit 6aa5202

File tree

3 files changed

+271
-20
lines changed

3 files changed

+271
-20
lines changed

tests/conftest.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from tests.common.utilities import InterruptableThread
5959
from tests.common.plugins.ptfadapter.dummy_testutils import DummyTestUtils
6060
from tests.common.helpers.multi_thread_utils import SafeThreadPoolExecutor
61+
from tests.common.mellanox_data import is_mellanox_device
6162

6263
try:
6364
from tests.common.macsec import MacsecPluginT2, MacsecPluginT0
@@ -2890,3 +2891,42 @@ def setup_pfc_test(
28902891

28912892
logger.info("setup_info : {}".format(setup_info))
28922893
yield setup_info
2894+
2895+
2896+
def is_sai_profile_multi_binding_enabled(duthost):
2897+
"""
2898+
Check if SAI_ACL_MULTI_BINDING_ENABLED is enabled in syncd docker's sai.profile
2899+
2900+
Args:
2901+
duthost: DUT host object
2902+
2903+
Returns:
2904+
bool: True if SAI_ACL_MULTI_BINDING_ENABLED=1 exists in sai.profile, False otherwise
2905+
"""
2906+
try:
2907+
# Check if sai.profile exists in syncd docker
2908+
result = duthost.shell(
2909+
"docker exec syncd ls /tmp/sai.profile", module_ignore_errors=True)
2910+
if result['rc'] != 0:
2911+
return False
2912+
2913+
# Check if SAI_ACL_MULTI_BINDING_ENABLED=1 exists in the file
2914+
result = duthost.shell(
2915+
"docker exec syncd grep 'SAI_ACL_MULTI_BINDING_ENABLED=1' /tmp/sai.profile", module_ignore_errors=True)
2916+
return result['rc'] == 0
2917+
except Exception as e:
2918+
logger.error("Failed to check sai.profile: %s", str(e))
2919+
return False
2920+
2921+
2922+
@pytest.fixture(scope="module")
2923+
def is_multi_binding_acl_enabled(duthosts, tbinfo):
2924+
"""
2925+
Check if multi-binding ACL is enabled on the DUT
2926+
"""
2927+
for duthost in duthosts:
2928+
if not is_sai_profile_multi_binding_enabled(duthost):
2929+
if is_mellanox_device(duthost) and 'dualtor' in tbinfo['topo']['name']:
2930+
pytest.fail(
2931+
"No multi-binding ACL supported on this platform, please check the sai.profile")
2932+
pytest.skip("No multi-binding ACL supported on this platform")

tests/everflow/everflow_test_utilities.py

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from tests.common.helpers.assertions import pytest_assert
1919
from tests.common.utilities import find_duthost_on_role
2020
from tests.common.helpers.constants import UPSTREAM_NEIGHBOR_MAP, DOWNSTREAM_NEIGHBOR_MAP
21+
from tests.common.helpers.sonic_db import AsicDbCli
2122
import json
2223

2324
# TODO: Add suport for CONFIGLET mode
@@ -378,6 +379,28 @@ def setup_info(duthosts, rand_one_dut_hostname, tbinfo, request, topo_scenario):
378379
time.sleep(60)
379380

380381

382+
def validate_asic_route(duthost, prefix):
383+
"""
384+
Check if a route exists in the routing table of the asic.
385+
"""
386+
asicdb = AsicDbCli(duthost)
387+
route_table = asicdb.dump("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY")
388+
if prefix in str(route_table):
389+
return True
390+
return False
391+
392+
393+
def validate_mirror_session_up(duthost, session_name):
394+
"""
395+
Check if a mirror session is up.
396+
"""
397+
cmd = f'sonic-db-cli STATE_DB HGET \"MIRROR_SESSION_TABLE|{session_name}\" status'
398+
mirror_status = duthost.command(cmd)['stdout']
399+
if 'active' in mirror_status:
400+
return True
401+
return False
402+
403+
381404
# TODO: This should be refactored to some common area of sonic-mgmt.
382405
def add_route(duthost, prefix, nexthop, namespace):
383406
"""
@@ -452,7 +475,7 @@ def setup_arp_responder(duthost, ptfhost, setup_info):
452475

453476

454477
# TODO: This should be refactored to some common area of sonic-mgmt.
455-
def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True):
478+
def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True, ip_version=4):
456479
"""
457480
Get the IP and MAC of the neighbor on the specified destination port.
458481
@@ -462,13 +485,13 @@ def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True):
462485
resolved: Whether to return a resolved route or not
463486
"""
464487
if not resolved:
465-
return "20.20.20.100"
488+
return "20.20.20.100" if ip_version == 4 else "2020::20:20:20:100"
466489

467490
mg_facts = duthost.get_extended_minigraph_facts(tbinfo)
468491

469492
for bgp_peer in mg_facts["minigraph_bgp"]:
470493
if bgp_peer["name"] == mg_facts["minigraph_neighbors"][dest_port]["name"] \
471-
and ipaddr.IPAddress(bgp_peer["addr"]).version == 4:
494+
and ipaddr.IPAddress(bgp_peer["addr"]).version == ip_version:
472495
peer_ip = bgp_peer["addr"]
473496
break
474497

@@ -745,6 +768,47 @@ def acl_stage(self):
745768
"""
746769
pass
747770

771+
def remove_outer_ip(self, packet_data):
772+
"""
773+
The mirror packet from IP in IP tunnel would take an external IP header.
774+
Remove the outer IP header from the IPinIP packet and keeps the original Ethernet header.
775+
776+
Args:
777+
packet_data: Original IPinIP packet
778+
779+
Returns:
780+
scapy.Ether: Original Ethernet header + Inner IP header + payload
781+
"""
782+
if isinstance(packet_data, bytes):
783+
outer_pkt = Ether(packet_data) # noqa: F821
784+
else:
785+
outer_pkt = packet_data
786+
787+
if not outer_pkt.haslayer(IP): # noqa: F821
788+
return None
789+
790+
outer_ip = outer_pkt[IP] # noqa: F821
791+
792+
if outer_ip.proto != 4:
793+
return None
794+
795+
# Extract the original Ethernet header
796+
original_eth = outer_pkt[Ether] # noqa: F821
797+
eth_dst = original_eth.dst
798+
eth_src = original_eth.src
799+
eth_type = 0x0800
800+
801+
inner_payload = outer_ip.payload
802+
803+
# If the payload is Raw type, we need to re-parse it as IP
804+
if isinstance(inner_payload, Raw): # noqa: F821
805+
inner_ip_packet = IP(bytes(inner_payload)) # noqa: F821
806+
else:
807+
inner_ip_packet = inner_payload
808+
new_packet = Ether(dst=eth_dst, src=eth_src, type=eth_type) / inner_ip_packet # noqa: F821
809+
810+
return new_packet
811+
748812
def send_and_check_mirror_packets(self,
749813
setup,
750814
mirror_session,
@@ -755,8 +819,8 @@ def send_and_check_mirror_packets(self,
755819
src_port=None,
756820
dest_ports=None,
757821
expect_recv=True,
758-
valid_across_namespace=True):
759-
822+
valid_across_namespace=True,
823+
multi_binding_acl=False):
760824
# In Below logic idea is to send traffic in such a way so that mirror traffic
761825
# will need to go across namespaces and within namespace. If source and mirror destination
762826
# namespace are different then traffic mirror will go across namespace via (backend asic)
@@ -797,7 +861,8 @@ def send_and_check_mirror_packets(self,
797861
duthost,
798862
direction,
799863
mirror_packet,
800-
src_port_metadata_map[src_port][1])
864+
src_port_metadata_map[src_port][1],
865+
multi_binding_acl=multi_binding_acl)
801866
# Avoid changing the original packet
802867
mirror_packet_sent = mirror_packet.copy()
803868
if src_port_metadata_map[src_port][0]:
@@ -819,7 +884,9 @@ def send_and_check_mirror_packets(self,
819884
_, received_packet = result
820885
logging.info("Received packet: %s", packet.Ether(received_packet).summary())
821886

822-
inner_packet = self._extract_mirror_payload(received_packet, len(mirror_packet_sent))
887+
inner_packet = self._extract_mirror_payload(received_packet, len(mirror_packet_sent),
888+
multi_binding_acl=multi_binding_acl)
889+
823890
logging.info("Received inner packet: %s", inner_packet.summary())
824891

825892
inner_packet = Mask(inner_packet)
@@ -847,22 +914,37 @@ def send_and_check_mirror_packets(self,
847914
inner_packet.set_do_not_care_scapy(packet.Ether, "dst")
848915
inner_packet.set_do_not_care_scapy(packet.IP, "chksum")
849916

917+
if multi_binding_acl:
918+
inner_packet.set_do_not_care_scapy(packet.Ether, "dst")
919+
inner_packet.set_do_not_care_scapy(packet.Ether, "src")
920+
inner_packet.set_do_not_care_scapy(packet.IP, "chksum")
921+
inner_packet.set_do_not_care_scapy(packet.IP, "ttl")
922+
850923
logging.info("Expected inner packet: %s", mirror_packet_sent.summary())
851924
pytest_assert(inner_packet.pkt_match(mirror_packet_sent),
852925
"Mirror payload does not match received packet")
853926
else:
854927
testutils.verify_no_packet_any(ptfadapter, expected_mirror_packet, dest_ports)
855928

856929
@staticmethod
857-
def get_expected_mirror_packet(mirror_session, setup, duthost, direction, mirror_packet, ttl_dec):
930+
def get_expected_mirror_packet(mirror_session, setup, duthost, direction, mirror_packet, ttl_dec,
931+
multi_binding_acl=False):
858932
payload = mirror_packet.copy()
859933

860934
# Add vendor specific padding to the packet
861935
if duthost.facts["asic_type"] in ["mellanox"]:
862936
if six.PY2:
863-
payload = binascii.unhexlify("0" * 44) + str(payload)
937+
if multi_binding_acl:
938+
payload = binascii.unhexlify("0" * 44) + str(payload)[:24] + binascii.unhexlify("0" * 40) + \
939+
str(payload)[24:]
940+
else:
941+
payload = binascii.unhexlify("0" * 44) + str(payload)
864942
else:
865-
payload = binascii.unhexlify("0" * 44) + bytes(payload)
943+
if multi_binding_acl:
944+
payload = binascii.unhexlify("0" * 44) + bytes(payload)[:24] + binascii.unhexlify("0" * 40) + \
945+
bytes(payload)[24:]
946+
else:
947+
payload = binascii.unhexlify("0" * 44) + bytes(payload)
866948
if (
867949
duthost.facts["asic_type"] in ["barefoot", "cisco-8000", "marvell-teralynx"]
868950
or duthost.facts.get("platform_asic") in ["broadcom-dnx"]
@@ -908,11 +990,18 @@ def get_expected_mirror_packet(mirror_session, setup, duthost, direction, mirror
908990

909991
return expected_packet
910992

911-
def _extract_mirror_payload(self, encapsulated_packet, payload_size):
912-
pytest_assert(len(encapsulated_packet) >= OUTER_HEADER_SIZE,
913-
"Incomplete packet, expected at least {} header bytes".format(OUTER_HEADER_SIZE))
993+
def _extract_mirror_payload(self, encapsulated_packet, payload_size, multi_binding_acl=False):
994+
outer_header_size = OUTER_HEADER_SIZE
995+
if multi_binding_acl:
996+
outer_header_size += 20
997+
pytest_assert(len(encapsulated_packet) >= outer_header_size,
998+
"Incomplete packet, expected at least {} header bytes".format(outer_header_size))
914999

9151000
inner_frame = encapsulated_packet[-payload_size:]
1001+
if multi_binding_acl:
1002+
inner_frame = encapsulated_packet[-(payload_size + 20):]
1003+
inner_frame = self.remove_outer_ip(inner_frame)
1004+
return inner_frame
9161005
return packet.Ether(inner_frame)
9171006

9181007
@staticmethod

0 commit comments

Comments
 (0)