Skip to content

Commit 2e57b6c

Browse files
committed
Add PTF test file for basic forwarding exercise
1 parent 5f4d62d commit 2e57b6c

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

exercises/basic/ptf/basic_fwd.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2026 Andrew Nguyen
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import logging
18+
19+
import ptf
20+
import ptf.testutils as tu
21+
from ptf.base_tests import BaseTest
22+
import p4runtime_sh.shell as sh
23+
import p4runtime_shell_utils as shu
24+
25+
26+
# Configure logging
27+
logger = logging.getLogger(None)
28+
ch = logging.StreamHandler()
29+
ch.setLevel(logging.INFO)
30+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
31+
ch.setFormatter(formatter)
32+
logger.addHandler(ch)
33+
34+
35+
class BasicFwdTest(BaseTest):
36+
def setUp(self):
37+
self.dataplane = ptf.dataplane_instance
38+
self.dataplane.flush()
39+
40+
logging.debug("BasicFwdTest.setUp()")
41+
grpc_addr = tu.test_param_get("grpcaddr")
42+
if grpc_addr is None:
43+
grpc_addr = 'localhost:9559'
44+
p4info_txt_fname = tu.test_param_get("p4info")
45+
p4prog_binary_fname = tu.test_param_get("config")
46+
sh.setup(device_id=0,
47+
grpc_addr=grpc_addr,
48+
election_id=(0, 1),
49+
config=sh.FwdPipeConfig(p4info_txt_fname, p4prog_binary_fname),
50+
verbose=False)
51+
52+
def tearDown(self):
53+
logging.debug("BasicFwdTest.tearDown()")
54+
sh.teardown()
55+
56+
57+
######################################################################
58+
# Helper function to add entries to ipv4_lpm table
59+
######################################################################
60+
61+
def add_ipv4_lpm_entry(ipv4_addr_str, prefix_len, dst_mac_str, port):
62+
te = sh.TableEntry('MyIngress.ipv4_lpm')(action='MyIngress.ipv4_forward')
63+
te.match['hdr.ipv4.dstAddr'] = '%s/%d' % (ipv4_addr_str, prefix_len)
64+
te.action['dstAddr'] = dst_mac_str
65+
te.action['port'] = '%d' % port
66+
te.insert()
67+
68+
69+
class DropTest(BasicFwdTest):
70+
"""Test that packets are dropped when no table entries are installed."""
71+
def runTest(self):
72+
in_dmac = 'ee:30:ca:9d:1e:00'
73+
in_smac = 'ee:cd:00:7e:70:00'
74+
ip_dst = '10.0.1.1'
75+
ig_port = 1
76+
77+
pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac,
78+
ip_dst=ip_dst, ip_ttl=64)
79+
tu.send_packet(self, ig_port, pkt)
80+
tu.verify_no_other_packets(self)
81+
82+
83+
class FwdTest(BasicFwdTest):
84+
"""Test that a packet is forwarded correctly with one table entry."""
85+
def runTest(self):
86+
in_dmac = 'ee:30:ca:9d:1e:00'
87+
in_smac = 'ee:cd:00:7e:70:00'
88+
ip_dst = '10.0.1.1'
89+
ig_port = 1
90+
91+
eg_port = 2
92+
out_dmac = '08:00:00:00:02:22'
93+
94+
# Add a forwarding entry
95+
add_ipv4_lpm_entry(ip_dst, 32, out_dmac, eg_port)
96+
97+
# Send packet
98+
pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac,
99+
ip_dst=ip_dst, ip_ttl=64)
100+
101+
# Expected: srcAddr = old dstAddr, dstAddr = new MAC, TTL decremented
102+
exp_pkt = tu.simple_tcp_packet(eth_src=in_dmac, eth_dst=out_dmac,
103+
ip_dst=ip_dst, ip_ttl=63)
104+
tu.send_packet(self, ig_port, pkt)
105+
tu.verify_packets(self, exp_pkt, [eg_port])
106+
107+
108+
class MultiEntryTest(BasicFwdTest):
109+
"""Test multiple LPM entries route to different ports correctly."""
110+
def runTest(self):
111+
in_dmac = 'ee:30:ca:9d:1e:00'
112+
in_smac = 'ee:cd:00:7e:70:00'
113+
ig_port = 0
114+
115+
entries = []
116+
entries.append({'ip_dst': '10.0.1.1',
117+
'prefix_len': 32,
118+
'pkt_dst': '10.0.1.1',
119+
'eg_port': 1,
120+
'out_dmac': '08:00:00:00:01:11'})
121+
entries.append({'ip_dst': '10.0.2.0',
122+
'prefix_len': 24,
123+
'pkt_dst': '10.0.2.99',
124+
'eg_port': 2,
125+
'out_dmac': '08:00:00:00:02:22'})
126+
entries.append({'ip_dst': '10.0.3.0',
127+
'prefix_len': 24,
128+
'pkt_dst': '10.0.3.1',
129+
'eg_port': 3,
130+
'out_dmac': '08:00:00:00:03:33'})
131+
132+
# Add all entries
133+
for e in entries:
134+
add_ipv4_lpm_entry(e['ip_dst'], e['prefix_len'],
135+
e['out_dmac'], e['eg_port'])
136+
137+
# Test each entry
138+
ttl_in = 64
139+
for e in entries:
140+
pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac,
141+
ip_dst=e['pkt_dst'], ip_ttl=ttl_in)
142+
exp_pkt = tu.simple_tcp_packet(eth_src=in_dmac, eth_dst=e['out_dmac'],
143+
ip_dst=e['pkt_dst'],
144+
ip_ttl=ttl_in - 1)
145+
tu.send_packet(self, ig_port, pkt)
146+
tu.verify_packets(self, exp_pkt, [e['eg_port']])
147+
ttl_in -= 10

0 commit comments

Comments
 (0)