Skip to content

Commit

Permalink
Add test of v1model update_checksum implementation (#882)
Browse files Browse the repository at this point in the history
* Add test of v1model update_checksum implementation

It does not test the verify_checksum implementation.

* Mark the new test case as XFAIL until it is supported
  • Loading branch information
jafingerhut authored and Calin Cascaval committed Sep 6, 2017
1 parent faf5ec2 commit 91f9fef
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 0 deletions.
4 changes: 4 additions & 0 deletions backends/bmv2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ set (BMV2_TEST_SUITES
# so we allow it to fail on BMv2.
set (XFAIL_TESTS
testdata/p4_14_samples/issue60.p4
# Program checksum1-bmv2.p4 uses new verify_checksum and
# update_checksum extern functions that were added but then reverted
# until they can be deployed without breaking existing programs.
testdata/p4_16_samples/checksum1-bmv2.p4
)

if (HAVE_SIMPLE_SWITCH)
Expand Down
4 changes: 4 additions & 0 deletions backends/p4test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ set (P4TEST_SUITES
set (P4_XFAIL_TESTS
# issue #13
testdata/p4_16_samples/cast-call.p4
# Program checksum1-bmv2.p4 uses new verify_checksum and
# update_checksum extern functions that were added but then reverted
# until they can be deployed without breaking existing programs.
testdata/p4_16_samples/checksum1-bmv2.p4
)
p4c_add_tests("p4" ${P4TEST_DRIVER} "${P4TEST_SUITES}" "${P4_XFAIL_TESTS}")

Expand Down
212 changes: 212 additions & 0 deletions testdata/p4_16_samples/checksum1-bmv2.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
#include <core.p4>
#include <v1model.p4>

typedef bit<48> EthernetAddress;
typedef bit<32> IPv4Address;

header ethernet_t {
bit<48> dstAddr;
bit<48> srcAddr;
bit<16> etherType;
}

// IPv4 header _with_ options
header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
IPv4Address srcAddr;
IPv4Address dstAddr;
varbit<320> options;
}

header tcp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<3> res;
bit<3> ecn;
bit<6> ctrl;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}

header IPv4_up_to_ihl_only_h {
bit<4> version;
bit<4> ihl;
}

struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
tcp_t tcp;
}

struct mystruct1_t {
bit<4> a;
bit<4> b;
}

struct metadata {
mystruct1_t mystruct1;
}

typedef tuple<
bit<4>,
bit<4>,
bit<8>,
varbit<56>
> myTuple1;

// Declare user-defined errors that may be signaled during parsing
error {
IPv4HeaderTooShort,
IPv4IncorrectVersion,
IPv4ChecksumError
}

parser parserI(packet_in pkt,
out headers hdr,
inout metadata meta,
inout standard_metadata_t stdmeta)
{
state start {
pkt.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
0x0800: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
// The 4-bit IHL field of the IPv4 base header is the number
// of 32-bit words in the entire IPv4 header. It is an error
// for it to be less than 5. There are only IPv4 options
// present if the value is at least 6. The length of the IPv4
// options alone, without the 20-byte base header, is thus ((4
// * ihl) - 20) bytes, or 8 times that many bits.
pkt.extract(hdr.ipv4,
(bit<32>)
(8 *
(4 * (bit<9>) (pkt.lookahead<IPv4_up_to_ihl_only_h >().ihl)
- 20)));
verify(hdr.ipv4.version == 4w4, error.IPv4IncorrectVersion);
verify(hdr.ipv4.ihl >= 4w5, error.IPv4HeaderTooShort);
transition select (hdr.ipv4.protocol) {
6: parse_tcp;
default: accept;
}
}
state parse_tcp {
pkt.extract(hdr.tcp);
transition accept;
}
}

control cIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t stdmeta)
{
action foo() {
hdr.tcp.srcPort = hdr.tcp.srcPort + 1;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
hdr.ipv4.dstAddr = hdr.ipv4.dstAddr + 4;
}
table guh {
key = {
hdr.tcp.dstPort : exact;
}
actions = { foo; }
default_action = foo;
}
apply {
guh.apply();
}
}

control cEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t stdmeta)
{
apply {
}
}

control vc(in headers hdr,
inout metadata meta)
{
apply {
// There is code similar to this in Github repo p4lang/p4c in
// file testdata/p4_16_samples/flowlet_switching-bmv2.p4
// However in that file it is only for a fixed length IPv4
// header with no options. When I try to do this, it gives an
// error for having a varbit<> element in the tuple.

// The compiler does not give any error when one includes a
// varbit<> as an element of a tuple in a typedef, as you can
// see from the definition of myTuple1 above.
verify_checksum(true,
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.diffserv,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr,
hdr.ipv4.options
},
hdr.ipv4.hdrChecksum, HashAlgorithm.csum16);
}
}

control uc(inout headers hdr,
inout metadata meta)
{
apply {
update_checksum(true,
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.diffserv,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr,
hdr.ipv4.options
},
hdr.ipv4.hdrChecksum, HashAlgorithm.csum16);
}
}

control DeparserI(packet_out packet,
in headers hdr)
{
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.tcp);
}
}

V1Switch<headers, metadata>(parserI(),
vc(),
cIngress(),
cEgress(),
uc(),
DeparserI()) main;
42 changes: 42 additions & 0 deletions testdata/p4_16_samples/checksum1-bmv2.stf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# The packets in the 'packet' lines below were created using the
# Python Scapy library.
#
# First define these Python functions:
#
# def str_to_hex(s):
# return ''.join(map(lambda x: '%02x' % (ord(x)), s))
#
# import re
# def hex_to_str(hex_s):
# tmp = re.sub('[ \t]', '', hex_s)
# return str(bytearray.fromhex(tmp))

# The first packet was created with these Python statements:
# pkt1=Ether() / IP(dst='10.1.0.1') / TCP(sport=5793, dport=80)
# str_to_hex(str(pkt1))

# The packet in the first 'expect' line was created by running the P4
# program checksum1-bmv2.p4 using simple_switch, and sending in pkt1,
# and using tcpdump to record the output packet. It was verified
# correct by hand, and using Wireshark to very that the IPv4 header
# checksum is correct.

# Any of these packets can be converted back into a Scapy packet
# object with Python statements like this:

# s1='525400123502080027018bbc0800 45000028000100003f0665bb0a00020f0a010005 16a2005000000000000000005002200062e10000'
# pkt1=Ether(hex_to_str(s1))

packet 0 525400123502080027018bbc0800 4500002800010000400664bf0a00020f0a010001 16a1005000000000000000005002200062e10000
expect 0 525400123502080027018bbc0800 45000028000100003f0665bb0a00020f0a010005 16a2005000000000000000005002200062e10000

# The second packet was created with these Python statements:
# pkt2=Ether() / IP(dst='10.2.3.4', options=IPOption('\x83\x03\x10')) / TCP(sport=5501, dport=80)
# str_to_hex(str(pkt2))

# This tests that the P4 code for calculating a fresh IPv4 header
# checksum also works with a varbit field 'options' in the ipv4_t
# header type.

packet 0 525400123502080027018bbc0800 4600002c000100004006cdb30a00020f0a02030483031000 157d005000000000000000005002200061010000
expect 0 525400123502080027018bbc0800 4600002c000100003f06ceaf0a00020f0a02030883031000 157e005000000000000000005002200061010000

0 comments on commit 91f9fef

Please sign in to comment.