2525from ptf .base_tests import BaseTest
2626from scapy .all import IP , TCP , Ether , Packet , ShortField , bind_layers
2727
28+ # Custom Tunnel
2829TYPE_MYTUNNEL = 0x1212
2930TYPE_IPV4 = 0x0800
3031
@@ -37,6 +38,7 @@ class MyTunnel(Packet):
3738bind_layers (Ether , MyTunnel , type = TYPE_MYTUNNEL )
3839bind_layers (MyTunnel , IP , proto_id = TYPE_IPV4 )
3940
41+
4042# Import p4runtime_lib from the tutorials repo utils directory
4143sys .path .append (
4244 os .path .join (os .path .dirname (os .path .abspath (__file__ )), "../../../utils/" )
@@ -46,6 +48,7 @@ class MyTunnel(Packet):
4648from p4runtime_lib .switch import ShutdownAllSwitchConnections
4749
4850
51+ # Configure Logging
4952logger = logging .getLogger (None )
5053handler = logging .StreamHandler ()
5154handler .setLevel (logging .INFO )
@@ -60,25 +63,39 @@ def setUp(self):
6063 self .dataplane = ptf .dataplane_instance
6164 self .dataplane .flush ()
6265
66+ logging .debug ("BasicTunnelTest.setUp()" )
67+
68+ # Get test parameters
6369 grpc_addr = tu .test_param_get ("grpcaddr" ) or "localhost:9559"
6470 p4info_txt_fname = tu .test_param_get ("p4info" )
6571 p4prog_binary_fname = tu .test_param_get ("config" )
6672
73+ # Create P4Info helper for building the table entries
6774 self .p4info_helper = p4runtime_lib .helper .P4InfoHelper (p4info_txt_fname )
75+
76+ # Connect to the switch via gRPC
6877 self .sw = p4runtime_lib .bmv2 .Bmv2SwitchConnection (
6978 name = "s1" ,
7079 address = grpc_addr ,
7180 device_id = 0 ,
72- proto_dump_file = "logs/s1-p4runtime-requests.txt" ,
73- )
81+ proto_dump_file = "logs/s1-p4runtime-requests.txt" )
82+
83+ # Establish as master controller
7484 self .sw .MasterArbitrationUpdate ()
85+
86+ # Load the P4 Program onto the switch
7587 self .sw .SetForwardingPipelineConfig (
76- p4info = self .p4info_helper .p4info , bmv2_json_file_path = p4prog_binary_fname
77- )
88+ p4info = self .p4info_helper .p4info , bmv2_json_file_path = p4prog_binary_fname )
7889
7990 def tearDown (self ):
91+ logging .debug ("BasicTunnelTest.tearDown()" )
8092 ShutdownAllSwitchConnections ()
8193
94+
95+ ######################################################################
96+ # Helper function to add entries to ipv4_lpm table
97+ ######################################################################
98+
8299 def add_ipv4_lpm_entry (self , ipv4_addr_str , prefix_len , dst_mac_str , port ):
83100 table_entry = self .p4info_helper .buildTableEntry (
84101 table_name = "MyIngress.ipv4_lpm" ,
@@ -99,6 +116,7 @@ def add_tunnel_entry(self, dst_id, port):
99116
100117
101118class Ipv4DropOnMissTest (BasicTunnelTest ):
119+ """Verify that a plain IPv4 packet is dropped when no LPM table entry exists."""
102120 def runTest (self ):
103121 pkt = tu .simple_tcp_packet (
104122 eth_src = "ee:cd:00:7e:70:00" ,
@@ -111,6 +129,7 @@ def runTest(self):
111129
112130
113131class Ipv4ForwardTest (BasicTunnelTest ):
132+ """Verify that a plain IPv4 packet is forwarded correctly with one table entry."""
114133 def runTest (self ):
115134 in_dmac = "ee:30:ca:9d:1e:00"
116135 in_smac = "ee:cd:00:7e:70:00"
@@ -131,6 +150,7 @@ def runTest(self):
131150
132151
133152class TunnelForwardTest (BasicTunnelTest ):
153+ """Verify that a tunneled packet is forwarded correctly when a valid table entry exists."""
134154 def runTest (self ):
135155 in_pkt = (
136156 Ether (src = "00:11:22:33:44:55" , dst = "ff:ff:ff:ff:ff:ff" , type = TYPE_MYTUNNEL )
@@ -145,6 +165,7 @@ def runTest(self):
145165
146166
147167class TunnelDropOnMissTest (BasicTunnelTest ):
168+ """Verify that a tunneled packet is dropped when no matching table entry exists."""
148169 def runTest (self ):
149170 in_pkt = (
150171 Ether (src = "00:11:22:33:44:66" , dst = "ff:ff:ff:ff:ff:ff" , type = TYPE_MYTUNNEL )
@@ -155,3 +176,78 @@ def runTest(self):
155176 )
156177 tu .send_packet (self , 0 , in_pkt )
157178 tu .verify_no_other_packets (self )
179+
180+
181+ class TtlBoundaryTest (BasicTunnelTest ):
182+ """Verify IPv4 TTL is decremented to 0 correctly when input TTL is 1."""
183+ def runTest (self ):
184+ in_dmac = "ee:30:ca:9d:1e:00"
185+ in_smac = "ee:cd:00:7e:70:00"
186+ ip_dst = "10.0.9.9"
187+ ig_port = 1
188+ eg_port = 3
189+ out_dmac = "08:00:00:00:09:99"
190+
191+ self .add_ipv4_lpm_entry (ip_dst , 32 , out_dmac , eg_port )
192+
193+ pkt = tu .simple_tcp_packet (
194+ eth_src = in_smac , eth_dst = in_dmac ,
195+ ip_dst = ip_dst , ip_ttl = 1
196+ )
197+ exp_pkt = tu .simple_tcp_packet (
198+ eth_src = in_dmac , eth_dst = out_dmac ,
199+ ip_dst = ip_dst , ip_ttl = 0
200+ )
201+ tu .send_packet (self , ig_port , pkt )
202+ tu .verify_packets (self , exp_pkt , [eg_port ])
203+
204+
205+ class TunnelUnknownProtoTest (BasicTunnelTest ):
206+ """Verify tunnel packet with non-IPv4 proto_id is still forwarded by dst_id."""
207+ def runTest (self ):
208+ self .add_tunnel_entry (dst_id = 5 , port = 2 )
209+
210+ pkt = (
211+ Ether (src = "00:11:22:33:44:55" , dst = "ff:ff:ff:ff:ff:ff" , type = TYPE_MYTUNNEL )
212+ / MyTunnel (proto_id = 0x9999 , dst_id = 5 )
213+ / "unknown-proto-payload"
214+ )
215+ tu .send_packet (self , 0 , pkt )
216+ tu .verify_packets (self , pkt , [2 ])
217+
218+
219+ class MixedTrafficTest (BasicTunnelTest ):
220+ """Verify IPv4 and tunnel traffic are handled independently correctly via separate tables."""
221+ def runTest (self ):
222+ in_dmac = "ee:30:ca:9d:1e:00"
223+ in_smac = "ee:cd:00:7e:70:00"
224+ ip_dst = "10.0.2.2"
225+ out_dmac = "08:00:00:00:02:22"
226+ ipv4_port = 2
227+ tunnel_port = 3
228+
229+ # add both table entries
230+ self .add_ipv4_lpm_entry (ip_dst , 32 , out_dmac , ipv4_port )
231+ self .add_tunnel_entry (dst_id = 2 , port = tunnel_port )
232+
233+ # test plain IPv4 which should hit ipv4_lpm table
234+ ipv4_pkt = tu .simple_tcp_packet (
235+ eth_src = in_smac , eth_dst = in_dmac ,
236+ ip_dst = ip_dst , ip_ttl = 64
237+ )
238+ exp_ipv4_pkt = tu .simple_tcp_packet (
239+ eth_src = in_dmac , eth_dst = out_dmac ,
240+ ip_dst = ip_dst , ip_ttl = 63
241+ )
242+ tu .send_packet (self , 1 , ipv4_pkt )
243+ tu .verify_packets (self , exp_ipv4_pkt , [ipv4_port ])
244+
245+ # test tunnel packet which should hit myTunnel_exact table
246+ tunnel_pkt = (
247+ Ether (src = "00:11:22:33:44:55" , dst = "ff:ff:ff:ff:ff:ff" , type = TYPE_MYTUNNEL )
248+ / MyTunnel (proto_id = TYPE_IPV4 , dst_id = 2 )
249+ / IP (src = "10.0.1.1" , dst = "10.0.3.3" , ttl = 64 )
250+ / TCP (sport = 12345 , dport = 1234 )
251+ )
252+ tu .send_packet (self , 0 , tunnel_pkt )
253+ tu .verify_packets (self , tunnel_pkt , [tunnel_port ])
0 commit comments