Skip to content

Commit

Permalink
[ptftest] Add p4_dash_utils (sonic-net#640)
Browse files Browse the repository at this point in the history
This PR adds a module p4_dash_utils to include helper functions for p4
pipeline.
* Function `set_internal_config` aims to update pipeline internal
configuration, neighbor mac, data port mac, cpu port mac, etc.
* Decorator `use_flow` aims to enable flow lookup stage in p4 pipeline.
It wrappers method setUp/tearDown of test class.
  • Loading branch information
jimmyzhai authored Nov 20, 2024
1 parent aa2cde5 commit 62c9907
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 27 deletions.
1 change: 0 additions & 1 deletion dash-pipeline/bmv2/dash_headers.p4
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ header dash_packet_meta_t {
const bit<16> PACKET_META_HDR_SIZE=dash_packet_meta_t.minSizeInBytes();

#define DASH_ETHTYPE 0x876d
#define DPAPP_MAC 0x02fe23f0e413 /* FIXME temp hardcode */

struct headers_t {
/* packet metadata headers */
Expand Down
1 change: 1 addition & 0 deletions dash-pipeline/bmv2/dash_metadata.p4
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ struct metadata_t {
bit<16> dash_tunnel_next_hop_id;
bit<32> meter_class;
bit<8> local_region_id;
EthernetAddress cpu_mac;
}

#endif /* _SIRIUS_METADATA_P4_ */
2 changes: 1 addition & 1 deletion dash-pipeline/bmv2/stages/conntrack_lookup.p4
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ control conntrack_build_dash_header(inout headers_t hdr, in metadata_t meta,
hdr.packet_meta.length = length + PACKET_META_HDR_SIZE;

hdr.dp_ethernet.setValid();
hdr.dp_ethernet.dst_addr = DPAPP_MAC;
hdr.dp_ethernet.dst_addr = meta.cpu_mac;
hdr.dp_ethernet.src_addr = meta.u0_encap_data.underlay_smac;
hdr.dp_ethernet.ether_type = DASH_ETHTYPE;
}
Expand Down
2 changes: 2 additions & 0 deletions dash-pipeline/bmv2/stages/pre_pipeline.p4
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ control pre_pipeline_stage(inout headers_t hdr,

action set_internal_config(EthernetAddress neighbor_mac,
EthernetAddress mac,
EthernetAddress cpu_mac,
bit<1> flow_enabled) {
meta.u0_encap_data.underlay_dmac = neighbor_mac;
meta.u0_encap_data.underlay_smac = mac;
meta.cpu_mac = cpu_mac;
meta.flow_enabled = (bool)flow_enabled;
}

Expand Down
2 changes: 1 addition & 1 deletion dash-pipeline/dockerfiles/Dockerfile.saithrift-client-bldr
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ ADD requirements.txt /tests/
RUN apt update && apt install -y python3 python3-pip sudo && \
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.8 1 && \
sudo python3 -m pip install -r /tests/requirements.txt && \
sudo pip3 install scapy pysubnettree
sudo pip3 install scapy pysubnettree p4runtime

WORKDIR /

Expand Down
177 changes: 177 additions & 0 deletions test/test-cases/functional/ptf/p4_dash_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import grpc
from p4.v1 import p4runtime_pb2
from p4.v1 import p4runtime_pb2_grpc


def get_mac(interface):
try:
mac = open('/sys/class/net/'+interface+'/address').readline().strip()
except:
mac = "00:00:00:00:00:00"
return mac


def mac_in_bytes(mac):
return bytes(int(b, 16) for b in mac.split(":"))


class P4info():
def __init__(self, stub):
self.config = P4info.get_pipeline_config(stub)

@staticmethod
def get_pipeline_config(stub):
try:
req = p4runtime_pb2.GetForwardingPipelineConfigRequest()
req.device_id = 0
req.response_type = p4runtime_pb2.GetForwardingPipelineConfigRequest.ResponseType.P4INFO_AND_COOKIE
return stub.GetForwardingPipelineConfig(req).config.p4info
except Exception as e:
print(f'gRPC error: str({e})')
return None

def get_table(self, name):
for table in self.config.tables:
if table.preamble.name == name:
return table

return None

def get_action(self, name):
for action in self.config.actions:
if action.preamble.name == name:
return action

return None


def set_internal_config(neighbor_mac :bytes = None,
mac :bytes = None,
cpu_mac :bytes = None,
flow_enabled :bytes = None):
'''
Set dash pipeline internal config by updating table entry of internal_config.
if one argument is not specifed, the action param is not changed in the
existing table entry, otherwise set default value in new table entry.
'''
channel = grpc.insecure_channel('localhost:9559')
stub = p4runtime_pb2_grpc.P4RuntimeStub(channel)

p4info = P4info(stub)
internal_config = p4info.get_table("dash_ingress.dash_lookup_stage.pre_pipeline_stage.internal_config")

entry = p4runtime_pb2.TableEntry()
entry.table_id = internal_config.preamble.id
entry.priority = 1

match = entry.match.add()
match.field_id = 1
match.ternary.value = b'\x00'
match.ternary.mask = b'\xff'

req = p4runtime_pb2.ReadRequest()
req.device_id = 0
entity = req.entities.add()
entity.table_entry.CopyFrom(entry)
for response in stub.Read(req):
if not response.entities:
continue
entry = response.entities[0].table_entry
changed = 0

param = entry.action.action.params[0]
if neighbor_mac and neighbor_mac != param.value:
param.value = neighbor_mac
changed += 1

param = entry.action.action.params[1]
if mac and mac != param.value:
param.value = mac
changed += 1

param = entry.action.action.params[2]
if cpu_mac and cpu_mac != param.value:
param.value = cpu_mac
changed += 1

param = entry.action.action.params[3]
if flow_enabled and flow_enabled != param.value:
param.value = flow_enabled
changed += 1

if not changed:
return # none of change

req = p4runtime_pb2.WriteRequest()
req.device_id = 0
update = req.updates.add()
update.type = p4runtime_pb2.Update.MODIFY
update.entity.table_entry.CopyFrom(entry)
stub.Write(req)
return

# Add one entry
set_internal_config = p4info.get_action("dash_ingress.dash_lookup_stage.pre_pipeline_stage.set_internal_config")
entry.action.action.action_id = set_internal_config.preamble.id
action = entry.action.action

param = action.params.add()
param.param_id = 1
if neighbor_mac:
param.value = neighbor_mac
else: # default value
param.value = b'\x00\x00\x00\x00\x00\x00'

param = action.params.add()
param.param_id = 2
if mac:
param.value = mac
else: # default value
param.value = b'\x00\x00\x00\x00\x00\x00'

param = action.params.add()
param.param_id = 3
if cpu_mac:
param.value = cpu_mac
else: # default value
param.value = b'\x00\x00\x00\x00\x00\x00'

param = action.params.add()
param.param_id = 4
if flow_enabled:
param.value = flow_enabled
else: # default value
param.value = b'\x00'

req = p4runtime_pb2.WriteRequest()
req.device_id = 0
update = req.updates.add()
update.type = p4runtime_pb2.Update.INSERT
update.entity.table_entry.CopyFrom(entry)
stub.Write(req)


def use_flow(cls):
_setUp = getattr(cls, "setUp", None)
_tearDown = getattr(cls, "tearDown", None)

def setUp(self, *args, **kwargs):
if _setUp is not None:
_setUp(self, *args, **kwargs)
print(f'*** Enable Flow lookup')
set_internal_config(cpu_mac = mac_in_bytes(get_mac("veth5")),
flow_enabled = b'\x01')
return

def tearDown(self, *args, **kwargs):
print(f'*** Disable Flow lookup')
set_internal_config(flow_enabled = b'\x00')
if _tearDown is not None:
_tearDown(self, *args, **kwargs)
return

setattr(cls, "setUp", setUp)
setattr(cls, "tearDown", tearDown)
return cls

54 changes: 30 additions & 24 deletions test/test-cases/functional/ptf/saidashvnet_sanity.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from sai_thrift.sai_headers import *
from sai_base_test import *
from p4_dash_utils import *

class SaiThriftVnetOutboundUdpPktTest(SaiHelperSimplified):
""" Test saithrift vnet outbound"""
Expand All @@ -10,7 +11,6 @@ def setUp(self):
self.outbound_vni = 60
self.vnet_vni = 100
self.eni_mac = "00:cc:cc:cc:cc:cc"
self.our_mac = "00:00:02:03:04:05"
self.dst_ca_mac = "00:dd:dd:dd:dd:dd"
self.vip = "172.16.1.100"
self.outbound_vni = 100
Expand All @@ -25,6 +25,11 @@ def setUp(self):
# SAI address family
self.sai_ip_addr_family = SAI_IP_ADDR_FAMILY_IPV4

self.dut_mac = get_mac("veth0")
self.neighbor_mac = get_mac("veth1")
set_internal_config(neighbor_mac = mac_in_bytes(self.neighbor_mac),
mac = mac_in_bytes(self.dut_mac))

# Flag to indicate whether configureVnet were successful or not.
self.configured = False

Expand Down Expand Up @@ -157,16 +162,15 @@ def configureVnet(self):
def trafficTest(self):

src_vm_ip = "10.1.1.10"
outer_smac = "00:00:05:06:06:06"

# check VIP drop
wrong_vip = "172.16.100.100"
inner_pkt = simple_udp_packet(eth_dst="02:02:02:02:02:02",
eth_src=self.eni_mac,
ip_dst=self.dst_ca_ip,
ip_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=wrong_vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -184,8 +188,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ip_dst=wrong_dst_ca,
ip_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=self.vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -203,8 +207,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ip_dst=wrong_dst_ca,
ip_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=self.vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -221,8 +225,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ip_dst=self.dst_ca_ip,
ip_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=self.vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -234,8 +238,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ip_dst=self.dst_ca_ip,
ip_src=src_vm_ip)
vxlan_exp_pkt = simple_vxlan_packet(eth_dst="00:00:00:00:00:00",
eth_src="00:00:00:00:00:00",
vxlan_exp_pkt = simple_vxlan_packet(eth_dst=self.neighbor_mac,
eth_src=self.dut_mac,
ip_dst=self.dst_pa_ip,
ip_src=self.vip,
udp_sport=0, # TODO: Fix sport in pipeline
Expand Down Expand Up @@ -283,6 +287,10 @@ def tearDown(self):
# Run standard PTF teardown
super(SaiThriftVnetOutboundUdpPktTest, self).tearDown()

# restore default internal_config
set_internal_config(neighbor_mac = b'\x00\x00\x00\x00\x00\x00',
mac = b'\x00\x00\x00\x00\x00\x00')


class SaiThriftVnetOutboundUdpV6PktTest(SaiThriftVnetOutboundUdpPktTest):
""" Test saithrift vnet outbound ipv6"""
Expand All @@ -293,7 +301,6 @@ def setUp(self):
self.outbound_vni = 60
self.vnet_vni = 50
self.eni_mac = "00:aa:aa:aa:aa:aa"
self.our_mac = "00:00:06:07:08:09"
self.dst_ca_mac = "00:bb:bb:bb:bb:bb"
self.vip = "172.16.1.200"
self.outbound_vni = 50
Expand All @@ -311,16 +318,15 @@ def setUp(self):
def trafficTest(self):

src_vm_ip = "2000:aaaa::10a"
outer_smac = "00:00:03:06:06:06"

# check VIP drop
wrong_vip = "172.16.100.100"
inner_pkt = simple_udpv6_packet(eth_dst="02:02:02:02:02:02",
eth_src=self.eni_mac,
ipv6_dst=self.dst_ca_ip,
ipv6_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=wrong_vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -338,8 +344,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ipv6_dst=wrong_dst_ca,
ipv6_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=self.vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -357,8 +363,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ipv6_dst=wrong_dst_ca,
ipv6_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=self.vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -375,8 +381,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ipv6_dst=self.dst_ca_ip,
ipv6_src=src_vm_ip)
vxlan_pkt = simple_vxlan_packet(eth_dst=self.our_mac,
eth_src=outer_smac,
vxlan_pkt = simple_vxlan_packet(eth_dst=self.dut_mac,
eth_src=self.neighbor_mac,
ip_dst=self.vip,
ip_src=self.src_vm_pa_ip,
udp_sport=11638,
Expand All @@ -388,8 +394,8 @@ def trafficTest(self):
eth_src=self.eni_mac,
ipv6_dst=self.dst_ca_ip,
ipv6_src=src_vm_ip)
vxlan_exp_pkt = simple_vxlan_packet(eth_dst="00:00:00:00:00:00",
eth_src="00:00:00:00:00:00",
vxlan_exp_pkt = simple_vxlan_packet(eth_dst=self.neighbor_mac,
eth_src=self.dut_mac,
ip_dst=self.dst_pa_ip,
ip_src=self.vip,
udp_sport=0, # TODO: Fix sport in pipeline
Expand Down

0 comments on commit 62c9907

Please sign in to comment.