From 91f9fef3ebc4a28fa0d777bc26e51cb3e5fd3c1e Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Wed, 6 Sep 2017 15:12:32 -0700 Subject: [PATCH] Add test of v1model update_checksum implementation (#882) * 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 --- backends/bmv2/CMakeLists.txt | 4 + backends/p4test/CMakeLists.txt | 4 + testdata/p4_16_samples/checksum1-bmv2.p4 | 212 ++++++++++++++++++++++ testdata/p4_16_samples/checksum1-bmv2.stf | 42 +++++ 4 files changed, 262 insertions(+) create mode 100755 testdata/p4_16_samples/checksum1-bmv2.p4 create mode 100644 testdata/p4_16_samples/checksum1-bmv2.stf diff --git a/backends/bmv2/CMakeLists.txt b/backends/bmv2/CMakeLists.txt index 42f0ba6c684..a1d62d153e1 100644 --- a/backends/bmv2/CMakeLists.txt +++ b/backends/bmv2/CMakeLists.txt @@ -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) diff --git a/backends/p4test/CMakeLists.txt b/backends/p4test/CMakeLists.txt index 3eaa7dab9ba..930903544fe 100644 --- a/backends/p4test/CMakeLists.txt +++ b/backends/p4test/CMakeLists.txt @@ -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}") diff --git a/testdata/p4_16_samples/checksum1-bmv2.p4 b/testdata/p4_16_samples/checksum1-bmv2.p4 new file mode 100755 index 00000000000..b8c5601065e --- /dev/null +++ b/testdata/p4_16_samples/checksum1-bmv2.p4 @@ -0,0 +1,212 @@ +#include +#include + +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().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(parserI(), + vc(), + cIngress(), + cEgress(), + uc(), + DeparserI()) main; diff --git a/testdata/p4_16_samples/checksum1-bmv2.stf b/testdata/p4_16_samples/checksum1-bmv2.stf new file mode 100644 index 00000000000..7b75cc98647 --- /dev/null +++ b/testdata/p4_16_samples/checksum1-bmv2.stf @@ -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