Skip to content

Commit a972904

Browse files
vbnogueirajhsmttammela
authored
Add p4tc routing testcases (#5409)
Signed-off-by: Victor Nogueira <[email protected]> Co-authored-by: Jamal Hadi Salim <[email protected]> Co-authored-by: Pedro Tammela <[email protected]>
1 parent 33b6473 commit a972904

File tree

11 files changed

+1029
-7
lines changed

11 files changed

+1029
-7
lines changed

backends/tc/TCTests.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ if (ENABLE_P4TC_STF_TESTS)
5353
p4tc_add_test_with_args("p4tc" ${P4TC_COMPILER_DRIVER} FALSE "testdata/p4tc_samples_stf/simple_l3.p4" "testdata/p4tc_samples_stf/simple_l3.p4" "-tf ${P4C_SOURCE_DIR}/testdata/p4tc_samples_stf/simple_l3.stf" "")
5454
p4tc_add_test_with_args("p4tc" ${P4TC_COMPILER_DRIVER} FALSE "testdata/p4tc_samples_stf/calc_128.p4" "testdata/p4tc_samples_stf/calc_128.p4" "-tf ${P4C_SOURCE_DIR}/testdata/p4tc_samples_stf/calc_128.stf" "")
5555
p4tc_add_test_with_args("p4tc" ${P4TC_COMPILER_DRIVER} FALSE "testdata/p4tc_samples_stf/redirect_l2.p4" "testdata/p4tc_samples_stf/redirect_l2.p4" "-tf ${P4C_SOURCE_DIR}/testdata/p4tc_samples_stf/redirect_l2.stf" "")
56+
p4tc_add_test_with_args("p4tc" ${P4TC_COMPILER_DRIVER} FALSE "testdata/p4tc_samples_stf/routing_default" "testdata/p4tc_samples_stf/routing.p4" "-e InternetChecksum -tf ${P4C_SOURCE_DIR}/testdata/p4tc_samples_stf/routing_default.stf" "")
57+
p4tc_add_test_with_args("p4tc" ${P4TC_COMPILER_DRIVER} FALSE "testdata/p4tc_samples_stf/routing.p4" "testdata/p4tc_samples_stf/routing.p4" "-e InternetChecksum -tf ${P4C_SOURCE_DIR}/testdata/p4tc_samples_stf/routing.stf" "")
5658
endif()

backends/tc/run-tc-test.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,38 @@
7777
action='store_true',
7878
help=("Replace"),
7979
)
80+
PARSER.add_argument(
81+
"-e",
82+
"--externs",
83+
dest="externs",
84+
action='store',
85+
nargs='+',
86+
help=("Specifies list of externs used in testcase"),
87+
)
88+
89+
extern_to_mod_name = {
90+
'Register': 'native',
91+
'Random': 'native',
92+
'InternetChecksum': 'ext_csum',
93+
'Checksum': 'ext_csum',
94+
'Hash': 'ext_csum',
95+
'Counter': 'ext_Counter',
96+
'DirectCounter': 'ext_Counter',
97+
'Meter': 'ext_Meter',
98+
'DirectMeter': 'ext_Meter',
99+
}
100+
101+
102+
def validate_externs(externs):
103+
extern_mods = {}
104+
for extern in externs:
105+
if extern not in extern_to_mod_name:
106+
testutils.log.error(f"Unknown extern {extern}")
107+
sys.exit(1)
108+
if extern_to_mod_name[extern] != 'native':
109+
extern_mods[extern_to_mod_name[extern]] = True
110+
111+
return extern_mods
80112

81113

82114
class Options(object):
@@ -94,9 +126,10 @@ def __init__(self) -> None:
94126
self.testdir = ""
95127
self.runtimedir = str(FILE_DIR.joinpath("runtime"))
96128
self.compilerOptions = []
129+
self.extern_mods = {}
97130

98131

99-
def run_model(tc: TCInfra, testfile: Optional[Path]) -> int:
132+
def run_model(tc: TCInfra, testfile: Optional[Path], extern_mods) -> int:
100133
result = tc.compile_p4()
101134
if result != testutils.SUCCESS:
102135
return result
@@ -117,7 +150,7 @@ def run_model(tc: TCInfra, testfile: Optional[Path]) -> int:
117150
if result != testutils.SUCCESS:
118151
return result
119152

120-
result = tc.run(testfile)
153+
result = tc.run(testfile, extern_mods)
121154
if result != testutils.SUCCESS:
122155
return result
123156

@@ -139,7 +172,7 @@ def run_test(options: Options, argv: Any) -> int:
139172

140173
tc = TCInfra(outputfolder, options, template)
141174

142-
return run_model(tc, options.testfile)
175+
return run_model(tc, options.testfile, options.extern_mods)
143176

144177

145178
######################### main
@@ -193,6 +226,9 @@ def main(argv: Any) -> None:
193226
if args.replace:
194227
options.replace = True
195228

229+
if args.externs:
230+
options.extern_mods = validate_externs(args.externs)
231+
196232
argv = argv[1:]
197233

198234
if "P4TEST_REPLACE" in os.environ:

backends/tc/test_infra.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
sys.path.append(str(FILE_DIR.joinpath("../../tools")))
4141
sys.path.append(str(FILE_DIR.joinpath("../ebpf/targets")))
4242

43+
import ipaddress
44+
4345
import testutils
4446
from ptf.pcap_writer import LINKTYPE_ETHERNET, PcapWriter, rdpcap
4547
from stf.stf_parser import STFParser
@@ -126,20 +128,37 @@ def int_to_mac(int_val):
126128
return mac
127129

128130

131+
def hex_to_ipv4(hex_ipv4):
132+
ipv4_address = ipaddress.IPv4Address(hex_ipv4)
133+
return str(ipv4_address)
134+
135+
136+
def hex_to_ipv6(hex_ipv6):
137+
ipv6_address = ipaddress.IPv6Address(hex_ipv6)
138+
return str(ipv6_address)
139+
140+
129141
def map_value(value, port_mapping, json_type):
130142
if json_type == 'dev':
131143
return port_mapping[int(value, 16)]
132144

133145
if json_type == 'macaddr':
134146
return int_to_mac(int(value, 16))
135147

148+
if json_type == "ipv4":
149+
return hex_to_ipv4(int(value, 16))
150+
151+
if json_type == "ipv6":
152+
return hex_to_ipv6(int(value, 16))
153+
136154
return value
137155

138156

139157
def build_runtime_file(rules_file, json_file, cmds, port_mapping):
140158
json_data = parse_p4_json(json_file)
159+
generated = ""
141160
for index, cmd in enumerate(cmds):
142-
generated = "$TC p4ctrl create "
161+
generated += "$TC p4ctrl create "
143162
table_name = cmd.table
144163
table_name = table_name.replace(".", "/")
145164
generated += f"{cmd.pipe_name}/table/{table_name} "
@@ -158,8 +177,8 @@ def build_runtime_file(rules_file, json_file, cmds, port_mapping):
158177
# Support for LPM key
159178
generated += f"{field} "
160179
if isinstance(key_field_val[1], tuple):
161-
key_field_val = map_value(key_field_val[1][0], port_mapping, key_json['type'])
162-
generated += f"{key_field_val[1][0]}/{key_field_val[1][1]} "
180+
value = map_value(key_field_val[1][0], port_mapping, key_json['type'])
181+
generated += f"{value}/{key_field_val[1][1]} "
163182
else:
164183
key_field_val = map_value(key_field_val[1], port_mapping, key_json['type'])
165184
generated += f"{key_field_val} "
@@ -171,6 +190,7 @@ def build_runtime_file(rules_file, json_file, cmds, port_mapping):
171190
param_json = action_json['params'][i]
172191
param_val = map_value(val_field[1], port_mapping, param_json['type'])
173192
generated += f"param {val_field[0]} {param_val} "
193+
generated += "\n"
174194
file = open(rules_file, "w")
175195
file.write(generated)
176196
st = os.stat(rules_file)
@@ -448,7 +468,11 @@ def _init_tcpdump_listeners(self):
448468
def send_packets(self, input_pkts):
449469
return self._send_pcap_files(input_pkts)
450470

451-
def run(self, testfile):
471+
def _load_extern_mods(self, extern_mods):
472+
for extern_mod, _ in extern_mods.items():
473+
self.virtme.run(f"modprobe {extern_mod}")
474+
475+
def run(self, testfile, extern_mods):
452476
# Root is necessary to load ebpf into the kernel
453477
if not testutils.check_root():
454478
testutils.log.warning("This test requires root privileges; skipping execution.")
@@ -458,6 +482,8 @@ def run(self, testfile):
458482
result = self.virtme.ns_init()
459483
if result != testutils.SUCCESS:
460484
return result
485+
486+
self._load_extern_mods(extern_mods)
461487
# Load template script
462488
self.base_template = os.path.basename(self.template)
463489
result = self.virtme.run_intros("{introspection}/" + f"{self.base_template}.template")
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/* -*- P4_16 -*- */
2+
3+
#include <core.p4>
4+
#include <tc/pna.p4>
5+
6+
typedef bit<48> macaddr_t;
7+
8+
struct metadata_t {}
9+
10+
header ethernet_t {
11+
@tc_type("macaddr") macaddr_t dstAddr;
12+
@tc_type("macaddr") macaddr_t srcAddr;
13+
bit<16> etherType;
14+
}
15+
16+
header ipv4_t {
17+
bit<4> version;
18+
bit<4> ihl;
19+
bit<8> diffserv;
20+
bit<16> totalLen;
21+
bit<16> identification;
22+
bit<3> flags;
23+
bit<13> fragOffset;
24+
bit<8> ttl;
25+
bit<8> protocol;
26+
bit<16> hdrChecksum;
27+
@tc_type("ipv4") bit<32> srcAddr;
28+
@tc_type("ipv4") bit<32> dstAddr;
29+
}
30+
31+
struct headers_t {
32+
ethernet_t ethernet;
33+
ipv4_t ip;
34+
}
35+
36+
#define ETHERTYPE_IPV4 0x0800
37+
38+
/*********************** P A R S E R **************************/
39+
parser Parser(
40+
packet_in pkt,
41+
out headers_t hdr,
42+
inout metadata_t meta,
43+
in pna_main_parser_input_metadata_t istd)
44+
{
45+
state start {
46+
transition parse_ethernet;
47+
}
48+
49+
state parse_ethernet {
50+
pkt.extract(hdr.ethernet);
51+
transition select(hdr.ethernet.etherType) {
52+
ETHERTYPE_IPV4: parse_ipv4;
53+
default: reject;
54+
}
55+
}
56+
57+
state parse_ipv4 {
58+
pkt.extract(hdr.ip);
59+
transition accept;
60+
}
61+
}
62+
63+
void ip_ttl_dec(InternetChecksum chk, inout ipv4_t ip) {
64+
65+
/* Decrement ttl
66+
* HC' = (~HC) + ~m + m'
67+
*/
68+
69+
chk.clear();
70+
chk.set_state(~ip.hdrChecksum);
71+
72+
chk.subtract({ ip.ttl, ip.protocol });
73+
ip.ttl = ip.ttl - 1;
74+
chk.add({ ip.ttl, ip.protocol });
75+
76+
ip.hdrChecksum = chk.get();
77+
}
78+
79+
/***************** M A T C H - A C T I O N *********************/
80+
81+
control Main(
82+
inout headers_t hdr,
83+
inout metadata_t meta,
84+
in pna_main_input_metadata_t istd,
85+
inout pna_main_output_metadata_t ostd
86+
)
87+
{
88+
bit<32> nh_index;
89+
90+
action drop() {
91+
drop_packet();
92+
}
93+
94+
action set_nh(@tc_type("macaddr") bit<48> dmac, @tc_type("dev") PortId_t port) {
95+
hdr.ethernet.dstAddr = dmac;
96+
send_to_port(port);
97+
}
98+
99+
table nh_table {
100+
key = {
101+
nh_index : exact;
102+
}
103+
actions = {
104+
drop;
105+
set_nh;
106+
}
107+
default_action = drop;
108+
}
109+
110+
action set_nhid(bit<32> index) {
111+
nh_index = index;
112+
}
113+
114+
table fib_table {
115+
key = {
116+
hdr.ip.dstAddr : lpm @tc_type("ipv4") @name("prefix");
117+
}
118+
actions = {
119+
set_nhid;
120+
}
121+
/* 0 is the default route */
122+
default_action = set_nhid(0);
123+
}
124+
125+
apply {
126+
if (hdr.ip.isValid() && hdr.ip.ttl > 1) {
127+
fib_table.apply();
128+
nh_table.apply();
129+
} else {
130+
drop_packet();
131+
}
132+
}
133+
}
134+
135+
/********************* D E P A R S E R ************************/
136+
control Deparser(
137+
packet_out pkt,
138+
inout headers_t hdr,
139+
in metadata_t meta,
140+
in pna_main_output_metadata_t ostd)
141+
{
142+
InternetChecksum() chk;
143+
144+
apply {
145+
pkt.emit(hdr.ethernet);
146+
ip_ttl_dec(chk, hdr.ip);
147+
pkt.emit(hdr.ip);
148+
}
149+
}
150+
151+
/************ F I N A L P A C K A G E ******************************/
152+
PNA_NIC(
153+
Parser(),
154+
Main(),
155+
Deparser()
156+
) main;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
add Main.fib_table prefix:0x0a000000/8 Main.set_nhid(index:0x1)
2+
add Main.nh_table nh_index:0x1 Main.set_nh(dmac:0x133713371337, port:0x1)
3+
4+
packet 0 0090fb65 d6fe0203 04050601 08004500 001c0001 00004011 656c0b00 00010a63 000104d3 10e10008 d4c5
5+
expect 1 13371337 13370203 04050601 08004500 001c0001 00003f11 666c0b00 00010a63 000104d3 10e10008 d4c5
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
add Main.nh_table nh_index:0x0 Main.set_nh(dmac:0xAABBCCDDEEFF port:0x1)
2+
3+
packet 0 0090fb65 d6fe0203 04050601 08004500 001c0001 00004011 656c0b00 00010a63 000104d3 10e10008 d4c5
4+
expect 1 aabbccdd eeff0203 04050601 08004500 001c0001 00003f11 666c0b00 00010a63 000104d3 10e10008 d4c5

0 commit comments

Comments
 (0)