From bb780428aebe28841bf2d4898561bf0d2a175699 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 9 Oct 2024 20:34:15 +0000 Subject: [PATCH 1/2] Handle pna_main_parser_input_metadata_t fields in the tc backend's parser As of now, referencing a field of pna_main_parser_input_metadata_t will cause the it to be generated in eBPF without any translation. For example: parser Ingress_Parser( packet_in pkt, out my_ingress_headers_t hdr, inout my_ingress_metadata_t meta, in pna_main_parser_input_metadata_t istd) { state mystate { ... meta->ingress_port = istd.input_port; ... } ... } Will generate something like: mystate: { ... meta->ingress_port = istd.input_port; ... } This will cause a compilation error because istd is not defined in the ebpf code. This commit fixes this by either translating the fields to something ebpf understands or simply throwing an error if the field is unsupported. Signed-off-by: Victor Nogueira --- backends/tc/ebpfCodeGen.cpp | 18 ++++++++++++++++++ backends/tc/ebpfCodeGen.h | 2 ++ p4include/tc/pna.p4 | 1 + 3 files changed, 21 insertions(+) diff --git a/backends/tc/ebpfCodeGen.cpp b/backends/tc/ebpfCodeGen.cpp index 962f29c1d29..17fdfe0e313 100644 --- a/backends/tc/ebpfCodeGen.cpp +++ b/backends/tc/ebpfCodeGen.cpp @@ -767,6 +767,24 @@ void PnaStateTranslationVisitor::compileExtractField(const IR::Expression *expr, builder->newline(); } +bool PnaStateTranslationVisitor::preorder(const IR::Member *m) { + if ((m->expr != nullptr) && (m->expr->type != nullptr)) { + if (auto st = m->expr->type->to()) { + if (st->name == "pna_main_parser_input_metadata_t") { + if (m->member.name == "input_port") { + builder->append("skb->ifindex"); + return false; + } else { + ::P4::error(ErrorType::ERR_UNSUPPORTED_ON_TARGET, + "%1%: this metadata field is not supported", m); + } + } + } + } + + return EBPF::PsaStateTranslationVisitor::preorder(m); +} + void PnaStateTranslationVisitor::compileLookahead(const IR::Expression *destination) { cstring msgStr = absl::StrFormat("Parser: lookahead for %s %s", state->parser->typeMap->getType(destination)->toString(), diff --git a/backends/tc/ebpfCodeGen.h b/backends/tc/ebpfCodeGen.h index f68df9b4a00..c72ad33d589 100644 --- a/backends/tc/ebpfCodeGen.h +++ b/backends/tc/ebpfCodeGen.h @@ -126,6 +126,8 @@ class PnaStateTranslationVisitor : public EBPF::PsaStateTranslationVisitor { EBPF::EBPFPsaParser *prsr) : EBPF::PsaStateTranslationVisitor(refMap, typeMap, prsr) {} + bool preorder(const IR::Member *expression) override; + protected: void compileExtractField(const IR::Expression *expr, const IR::StructField *field, unsigned hdrOffsetBits, EBPF::EBPFType *type) override; diff --git a/p4include/tc/pna.p4 b/p4include/tc/pna.p4 index 1924550d1c6..6c1efe2b1b4 100644 --- a/p4include/tc/pna.p4 +++ b/p4include/tc/pna.p4 @@ -590,6 +590,7 @@ enum PNA_Source_t { struct pna_main_parser_input_metadata_t { // common fields initialized for all packets that are input to main // parser, regardless of direction. + // Unsupported as of now. bool recirculated; // If this packet has FROM_NET source, input_port contains // the id of the network port on which the packet arrived. From bdac2d74c44581df6c5a1d507980ebe06641447f Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Thu, 10 Oct 2024 15:48:46 +0000 Subject: [PATCH 2/2] Add example to illustrate accessing parser metadata Signed-off-by: Victor Nogueira --- testdata/p4tc_samples/digest_parser_meta.p4 | 144 ++++++++ .../digest_parser_meta.json | 98 ++++++ .../digest_parser_meta.p4-stderr | 0 .../digest_parser_meta.template | 30 ++ .../digest_parser_meta_control_blocks.c | 309 ++++++++++++++++++ .../digest_parser_meta_parser.c | 136 ++++++++ .../digest_parser_meta_parser.h | 160 +++++++++ 7 files changed, 877 insertions(+) create mode 100644 testdata/p4tc_samples/digest_parser_meta.p4 create mode 100644 testdata/p4tc_samples_outputs/digest_parser_meta.json create mode 100644 testdata/p4tc_samples_outputs/digest_parser_meta.p4-stderr create mode 100755 testdata/p4tc_samples_outputs/digest_parser_meta.template create mode 100644 testdata/p4tc_samples_outputs/digest_parser_meta_control_blocks.c create mode 100644 testdata/p4tc_samples_outputs/digest_parser_meta_parser.c create mode 100644 testdata/p4tc_samples_outputs/digest_parser_meta_parser.h diff --git a/testdata/p4tc_samples/digest_parser_meta.p4 b/testdata/p4tc_samples/digest_parser_meta.p4 new file mode 100644 index 00000000000..25d4356acef --- /dev/null +++ b/testdata/p4tc_samples/digest_parser_meta.p4 @@ -0,0 +1,144 @@ +/* -*- P4_16 -*- */ + +#include +#include + +struct my_ingress_metadata_t { + @tc_type("dev") PortId_t ingress_port; +} + +/* + * CONST VALUES FOR TYPES + */ +const bit<8> IP_PROTO_TCP = 0x06; +const bit<16> ETHERTYPE_IPV4 = 0x0800; + +/* + * Standard ethernet header + */ +header ethernet_t { + @tc_type("macaddr") bit<48> dstAddr; + @tc_type("macaddr") bit<48> srcAddr; + bit<16> etherType; +} + +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; + @tc_type("ipv4") bit<32> srcAddr; + @tc_type("ipv4") bit<32> dstAddr; +} + +struct my_ingress_headers_t { + ethernet_t ethernet; + ipv4_t ipv4; +} + + /*********************** P A R S E R **************************/ +parser Ingress_Parser( + packet_in pkt, + out my_ingress_headers_t hdr, + inout my_ingress_metadata_t meta, + in pna_main_parser_input_metadata_t istd) +{ + + state start { + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + ETHERTYPE_IPV4: parse_ipv4; + default: reject; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + meta.ingress_port = istd.input_port; + transition accept; + } +} + +#define L3_TABLE_SIZE 2048 + +struct ipv4_learn_digest_t { + @tc_type("ipv4") bit<32> srcAddr; + @tc_type("dev") PortId_t ingress_port; +}; + +/***************** M A T C H - A C T I O N *********************/ + +control ingress( + inout my_ingress_headers_t hdr, + inout my_ingress_metadata_t meta, + in pna_main_input_metadata_t istd, + inout pna_main_output_metadata_t ostd +) +{ + action send_nh(@tc_type("dev") PortId_t port, @tc_type("macaddr") bit<48> srcMac, @tc_type("macaddr") bit<48> dstMac) { + hdr.ethernet.srcAddr = srcMac; + hdr.ethernet.dstAddr = dstMac; + send_to_port(port); + } + + action drop() { + drop_packet(); + } + + table nh_table { + key = { + hdr.ipv4.dstAddr : exact @tc_type("ipv4") @name("dstAddr"); + } + actions = { + send_nh; + drop; + } + size = L3_TABLE_SIZE; + const default_action = drop; + } + + apply { + if (hdr.ipv4.isValid()) { + nh_table.apply(); + } + } +} + + /********************* D E P A R S E R ************************/ + +control Ingress_Deparser( + packet_out pkt, + inout my_ingress_headers_t hdr, + in my_ingress_metadata_t meta, + in pna_main_output_metadata_t ostd) +{ + Digest() digest_inst; + + apply { + ipv4_learn_digest_t ipv4_learn_digest; + + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + + ipv4_learn_digest.srcAddr = hdr.ipv4.srcAddr; + ipv4_learn_digest.ingress_port = meta.ingress_port; + digest_inst.pack(ipv4_learn_digest); + } +} + +/************ F I N A L P A C K A G E ******************************/ + +PNA_NIC( + Ingress_Parser(), + ingress(), + Ingress_Deparser() +) main; diff --git a/testdata/p4tc_samples_outputs/digest_parser_meta.json b/testdata/p4tc_samples_outputs/digest_parser_meta.json new file mode 100644 index 00000000000..fad7b2d6f98 --- /dev/null +++ b/testdata/p4tc_samples_outputs/digest_parser_meta.json @@ -0,0 +1,98 @@ +{ + "schema_version" : "1.0.0", + "pipeline_name" : "digest_parser_meta", + "externs" : [ + { + "name" : "Digest", + "id" : "0x05000000", + "permissions" : "0x19b6", + "instances" : [ + { + "inst_name" : "Ingress_Deparser.digest_inst", + "inst_id" : 1, + "params" : [ + { + "id" : 1, + "name" : "index", + "type" : "bit32", + "attr" : "tc_key", + "bitwidth" : 32 + }, + { + "id" : 2, + "name" : "srcAddr", + "type" : "ipv4", + "attr" : "param", + "bitwidth" : 32 + }, + { + "id" : 3, + "name" : "ingress_port", + "type" : "dev", + "attr" : "param", + "bitwidth" : 32 + } + ] + } + ] + } + ], + "tables" : [ + { + "name" : "ingress/nh_table", + "id" : 1, + "tentries" : 2048, + "permissions" : "0x3da4", + "nummask" : 8, + "keysize" : 32, + "keyfields" : [ + { + "id" : 1, + "name" : "dstAddr", + "type" : "ipv4", + "match_type" : "exact", + "bitwidth" : 32 + } + ], + "actions" : [ + { + "id" : 1, + "name" : "ingress/send_nh", + "action_scope" : "TableAndDefault", + "annotations" : [], + "params" : [ + { + "id" : 1, + "name" : "port", + "type" : "dev", + "bitwidth" : 32 + }, + { + "id" : 2, + "name" : "srcMac", + "type" : "macaddr", + "bitwidth" : 48 + }, + { + "id" : 3, + "name" : "dstMac", + "type" : "macaddr", + "bitwidth" : 48 + } + ], + "default_hit_action" : false, + "default_miss_action" : false + }, + { + "id" : 2, + "name" : "ingress/drop", + "action_scope" : "TableAndDefault", + "annotations" : [], + "params" : [], + "default_hit_action" : false, + "default_miss_action" : true + } + ] + } + ] +} \ No newline at end of file diff --git a/testdata/p4tc_samples_outputs/digest_parser_meta.p4-stderr b/testdata/p4tc_samples_outputs/digest_parser_meta.p4-stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/testdata/p4tc_samples_outputs/digest_parser_meta.template b/testdata/p4tc_samples_outputs/digest_parser_meta.template new file mode 100755 index 00000000000..8596ef608ac --- /dev/null +++ b/testdata/p4tc_samples_outputs/digest_parser_meta.template @@ -0,0 +1,30 @@ +#!/bin/bash -x + +set -e + +: "${TC:="tc"}" +$TC p4template create pipeline/digest_parser_meta numtables 1 + +$TC p4template create action/digest_parser_meta/ingress/send_nh actid 1 \ + param port type dev \ + param srcMac type macaddr \ + param dstMac type macaddr +$TC p4template update action/digest_parser_meta/ingress/send_nh state active + +$TC p4template create action/digest_parser_meta/ingress/drop actid 2 +$TC p4template update action/digest_parser_meta/ingress/drop state active + +$TC p4template create extern/root/Digest extid 0x05000000 numinstances 1 tc_acl 0x19b6 has_exec_method + +$TC p4template create extern_inst/digest_parser_meta/Digest/Ingress_Deparser.digest_inst instid 1 \ +tc_numel 0 \ +control_path tc_key index ptype bit32 id 1 param srcAddr ptype ipv4 id 2 param ingress_port ptype dev id 3 + +$TC p4template create table/digest_parser_meta/ingress/nh_table \ + tblid 1 \ + type exact \ + keysz 32 permissions 0x3da4 tentries 2048 nummasks 1 \ + table_acts act name digest_parser_meta/ingress/send_nh \ + act name digest_parser_meta/ingress/drop +$TC p4template update table/digest_parser_meta/ingress/nh_table default_miss_action permissions 0x1024 action digest_parser_meta/ingress/drop +$TC p4template update pipeline/digest_parser_meta state ready \ No newline at end of file diff --git a/testdata/p4tc_samples_outputs/digest_parser_meta_control_blocks.c b/testdata/p4tc_samples_outputs/digest_parser_meta_control_blocks.c new file mode 100644 index 00000000000..87420dcad36 --- /dev/null +++ b/testdata/p4tc_samples_outputs/digest_parser_meta_control_blocks.c @@ -0,0 +1,309 @@ +#include "digest_parser_meta_parser.h" +struct p4tc_filter_fields p4tc_filter_fields; + +struct internal_metadata { + __u16 pkt_ether_type; +} __attribute__((aligned(4))); + +struct __attribute__((__packed__)) ingress_nh_table_key { + u32 keysz; + u32 maskid; + u32 field0; /* hdr.ipv4.dstAddr */ +} __attribute__((aligned(8))); +#define INGRESS_NH_TABLE_ACT_INGRESS_SEND_NH 1 +#define INGRESS_NH_TABLE_ACT_INGRESS_DROP 2 +#define INGRESS_NH_TABLE_ACT_NOACTION 0 +struct __attribute__((__packed__)) ingress_nh_table_value { + unsigned int action; + u32 hit:1, + is_default_miss_act:1, + is_default_hit_act:1; + union { + struct { + } _NoAction; + struct __attribute__((__packed__)) { + u32 port; + u64 srcMac; + u64 dstMac; + } ingress_send_nh; + struct { + } ingress_drop; + } u; +}; + +static __always_inline int process(struct __sk_buff *skb, struct my_ingress_headers_t *hdr, struct pna_global_metadata *compiler_meta__) +{ + struct hdr_md *hdrMd; + + unsigned ebpf_packetOffsetInBits_save = 0; + ParserError_t ebpf_errorCode = NoError; + void* pkt = ((void*)(long)skb->data); + u8* hdr_start = pkt; + void* ebpf_packetEnd = ((void*)(long)skb->data_end); + u32 ebpf_zero = 0; + u32 ebpf_one = 1; + unsigned char ebpf_byte; + u32 pkt_len = skb->len; + + struct my_ingress_metadata_t *meta; + hdrMd = BPF_MAP_LOOKUP_ELEM(hdr_md_cpumap, &ebpf_zero); + if (!hdrMd) + return TC_ACT_SHOT; + unsigned ebpf_packetOffsetInBits = hdrMd->ebpf_packetOffsetInBits; + hdr_start = pkt + BYTES(ebpf_packetOffsetInBits); + hdr = &(hdrMd->cpumap_hdr); + meta = &(hdrMd->cpumap_usermeta); +{ + u8 hit; + { +if (/* hdr->ipv4.isValid() */ + hdr->ipv4.ebpf_valid) { +/* nh_table_0.apply() */ + { + /* construct key */ + struct p4tc_table_entry_act_bpf_params__local params = { + .pipeid = p4tc_filter_fields.pipeid, + .tblid = 1 + }; + struct ingress_nh_table_key key; + __builtin_memset(&key, 0, sizeof(key)); + key.keysz = 32; + key.field0 = hdr->ipv4.dstAddr; + struct p4tc_table_entry_act_bpf *act_bpf; + /* value */ + struct ingress_nh_table_value *value = NULL; + /* perform lookup */ + act_bpf = bpf_p4tc_tbl_read(skb, ¶ms, sizeof(params), &key, sizeof(key)); + value = (struct ingress_nh_table_value *)act_bpf; + if (value == NULL) { + /* miss; find default action */ + hit = 0; + } else { + hit = value->hit; + } + if (value != NULL) { + /* run action */ + switch (value->action) { + case INGRESS_NH_TABLE_ACT_INGRESS_SEND_NH: + { + hdr->ethernet.srcAddr = value->u.ingress_send_nh.srcMac; + hdr->ethernet.dstAddr = value->u.ingress_send_nh.dstMac; + /* send_to_port(value->u.ingress_send_nh.port) */ + compiler_meta__->drop = false; + send_to_port(value->u.ingress_send_nh.port); + } + break; + case INGRESS_NH_TABLE_ACT_INGRESS_DROP: + { +/* drop_packet() */ + drop_packet(); + } + break; + case INGRESS_NH_TABLE_ACT_NOACTION: + { + } + break; + } + } else { + } + } +; } + + } + } + { + struct p4tc_ext_bpf_params ext_params = {}; + struct ipv4_learn_digest_t ipv4_learn_digest_0; + __builtin_memset((void *) &ipv4_learn_digest_0, 0, sizeof(struct ipv4_learn_digest_t )); +{ +; + ; + ipv4_learn_digest_0.srcAddr = hdr->ipv4.srcAddr; + ipv4_learn_digest_0.ingress_port = meta->ingress_port; + /* digest_inst_0.pack(ipv4_learn_digest_0) */ + + __builtin_memset(&ext_params, 0, sizeof(struct p4tc_ext_bpf_params)); + ext_params.pipe_id = p4tc_filter_fields.pipeid; + ext_params.ext_id = 0x05000000; + ext_params.inst_id = 1; + + __builtin_memcpy(ext_params.in_params, &ipv4_learn_digest_0, sizeof(struct ipv4_learn_digest_t )); + bpf_p4tc_extern_digest_pack(skb, &ext_params, sizeof(ext_params)); + } + + if (compiler_meta__->drop) { + return TC_ACT_SHOT; + } + int outHeaderLength = 0; + if (hdr->ethernet.ebpf_valid) { + outHeaderLength += 112; + } +; if (hdr->ipv4.ebpf_valid) { + outHeaderLength += 160; + } +;; + int outHeaderOffset = BYTES(outHeaderLength) - (hdr_start - (u8*)pkt); + if (outHeaderOffset != 0) { + int returnCode = 0; + returnCode = bpf_skb_adjust_room(skb, outHeaderOffset, 1, 0); + if (returnCode) { + return TC_ACT_SHOT; + } + } + pkt = ((void*)(long)skb->data); + ebpf_packetEnd = ((void*)(long)skb->data_end); + ebpf_packetOffsetInBits = 0; + if (hdr->ethernet.ebpf_valid) { + if (ebpf_packetEnd < pkt + BYTES(ebpf_packetOffsetInBits + 112)) { + return TC_ACT_SHOT; + } + + ebpf_byte = ((char*)(&hdr->ethernet.dstAddr))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.dstAddr))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.dstAddr))[2]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.dstAddr))[3]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.dstAddr))[4]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 4, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.dstAddr))[5]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 5, (ebpf_byte)); + ebpf_packetOffsetInBits += 48; + + ebpf_byte = ((char*)(&hdr->ethernet.srcAddr))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.srcAddr))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.srcAddr))[2]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.srcAddr))[3]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.srcAddr))[4]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 4, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.srcAddr))[5]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 5, (ebpf_byte)); + ebpf_packetOffsetInBits += 48; + + hdr->ethernet.etherType = bpf_htons(hdr->ethernet.etherType); + ebpf_byte = ((char*)(&hdr->ethernet.etherType))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ethernet.etherType))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_packetOffsetInBits += 16; + + } +; if (hdr->ipv4.ebpf_valid) { + if (ebpf_packetEnd < pkt + BYTES(ebpf_packetOffsetInBits + 160)) { + return TC_ACT_SHOT; + } + + ebpf_byte = ((char*)(&hdr->ipv4.version))[0]; + write_partial(pkt + BYTES(ebpf_packetOffsetInBits) + 0, 4, 4, (ebpf_byte >> 0)); + ebpf_packetOffsetInBits += 4; + + ebpf_byte = ((char*)(&hdr->ipv4.ihl))[0]; + write_partial(pkt + BYTES(ebpf_packetOffsetInBits) + 0, 4, 0, (ebpf_byte >> 0)); + ebpf_packetOffsetInBits += 4; + + ebpf_byte = ((char*)(&hdr->ipv4.diffserv))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_packetOffsetInBits += 8; + + hdr->ipv4.totalLen = bpf_htons(hdr->ipv4.totalLen); + ebpf_byte = ((char*)(&hdr->ipv4.totalLen))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.totalLen))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_packetOffsetInBits += 16; + + hdr->ipv4.identification = bpf_htons(hdr->ipv4.identification); + ebpf_byte = ((char*)(&hdr->ipv4.identification))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.identification))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_packetOffsetInBits += 16; + + ebpf_byte = ((char*)(&hdr->ipv4.flags))[0]; + write_partial(pkt + BYTES(ebpf_packetOffsetInBits) + 0, 3, 5, (ebpf_byte >> 0)); + ebpf_packetOffsetInBits += 3; + + hdr->ipv4.fragOffset = bpf_htons(hdr->ipv4.fragOffset << 3); + ebpf_byte = ((char*)(&hdr->ipv4.fragOffset))[0]; + write_partial(pkt + BYTES(ebpf_packetOffsetInBits) + 0, 5, 0, (ebpf_byte >> 3)); + write_partial(pkt + BYTES(ebpf_packetOffsetInBits) + 0 + 1, 3, 5, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.fragOffset))[1]; + write_partial(pkt + BYTES(ebpf_packetOffsetInBits) + 1, 5, 0, (ebpf_byte >> 3)); + ebpf_packetOffsetInBits += 13; + + ebpf_byte = ((char*)(&hdr->ipv4.ttl))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_packetOffsetInBits += 8; + + ebpf_byte = ((char*)(&hdr->ipv4.protocol))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_packetOffsetInBits += 8; + + hdr->ipv4.hdrChecksum = bpf_htons(hdr->ipv4.hdrChecksum); + ebpf_byte = ((char*)(&hdr->ipv4.hdrChecksum))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.hdrChecksum))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_packetOffsetInBits += 16; + + ebpf_byte = ((char*)(&hdr->ipv4.srcAddr))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.srcAddr))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.srcAddr))[2]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.srcAddr))[3]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte)); + ebpf_packetOffsetInBits += 32; + + ebpf_byte = ((char*)(&hdr->ipv4.dstAddr))[0]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.dstAddr))[1]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.dstAddr))[2]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte)); + ebpf_byte = ((char*)(&hdr->ipv4.dstAddr))[3]; + write_byte(pkt, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte)); + ebpf_packetOffsetInBits += 32; + + } +;; + } + return -1; +} +SEC("p4tc/main") +int tc_ingress_func(struct __sk_buff *skb) { + struct pna_global_metadata *compiler_meta__ = (struct pna_global_metadata *) skb->cb; + if (compiler_meta__->pass_to_kernel == true) return TC_ACT_OK; + compiler_meta__->drop = false; + if (!compiler_meta__->recirculated) { + compiler_meta__->mark = 153; + struct internal_metadata *md = (struct internal_metadata *)(unsigned long)skb->data_meta; + if ((void *) ((struct internal_metadata *) md + 1) <= (void *)(long)skb->data) { + __u16 *ether_type = (__u16 *) ((void *) (long)skb->data + 12); + if ((void *) ((__u16 *) ether_type + 1) > (void *) (long) skb->data_end) { + return TC_ACT_SHOT; + } + *ether_type = md->pkt_ether_type; + } + } + struct hdr_md *hdrMd; + struct my_ingress_headers_t *hdr; + int ret = -1; + ret = process(skb, (struct my_ingress_headers_t *) hdr, compiler_meta__); + if (ret != -1) { + return ret; + } + if (!compiler_meta__->drop && compiler_meta__->egress_port == 0) { + compiler_meta__->pass_to_kernel = true; + return bpf_redirect(skb->ifindex, BPF_F_INGRESS); + } + return bpf_redirect(compiler_meta__->egress_port, 0); +} +char _license[] SEC("license") = "GPL"; diff --git a/testdata/p4tc_samples_outputs/digest_parser_meta_parser.c b/testdata/p4tc_samples_outputs/digest_parser_meta_parser.c new file mode 100644 index 00000000000..8510a44e8b8 --- /dev/null +++ b/testdata/p4tc_samples_outputs/digest_parser_meta_parser.c @@ -0,0 +1,136 @@ +#include "digest_parser_meta_parser.h" + +struct p4tc_filter_fields p4tc_filter_fields; + +static __always_inline int run_parser(struct __sk_buff *skb, struct my_ingress_headers_t *hdr, struct pna_global_metadata *compiler_meta__) +{ + struct hdr_md *hdrMd; + + unsigned ebpf_packetOffsetInBits_save = 0; + ParserError_t ebpf_errorCode = NoError; + void* pkt = ((void*)(long)skb->data); + u8* hdr_start = pkt; + void* ebpf_packetEnd = ((void*)(long)skb->data_end); + u32 ebpf_zero = 0; + u32 ebpf_one = 1; + unsigned char ebpf_byte; + u32 pkt_len = skb->len; + + struct my_ingress_metadata_t *meta; + + hdrMd = BPF_MAP_LOOKUP_ELEM(hdr_md_cpumap, &ebpf_zero); + if (!hdrMd) + return TC_ACT_SHOT; + __builtin_memset(hdrMd, 0, sizeof(struct hdr_md)); + + unsigned ebpf_packetOffsetInBits = 0; + hdr = &(hdrMd->cpumap_hdr); + meta = &(hdrMd->cpumap_usermeta); + { + goto start; + parse_ipv4: { +/* extract(hdr->ipv4) */ + if ((u8*)ebpf_packetEnd < hdr_start + BYTES(160 + 0)) { + ebpf_errorCode = PacketTooShort; + goto reject; + } + + hdr->ipv4.version = (u8)((load_byte(pkt, BYTES(ebpf_packetOffsetInBits)) >> 4) & EBPF_MASK(u8, 4)); + ebpf_packetOffsetInBits += 4; + + hdr->ipv4.ihl = (u8)((load_byte(pkt, BYTES(ebpf_packetOffsetInBits))) & EBPF_MASK(u8, 4)); + ebpf_packetOffsetInBits += 4; + + hdr->ipv4.diffserv = (u8)((load_byte(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 8; + + hdr->ipv4.totalLen = (u16)((load_half(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 16; + + hdr->ipv4.identification = (u16)((load_half(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 16; + + hdr->ipv4.flags = (u8)((load_byte(pkt, BYTES(ebpf_packetOffsetInBits)) >> 5) & EBPF_MASK(u8, 3)); + ebpf_packetOffsetInBits += 3; + + hdr->ipv4.fragOffset = (u16)((load_half(pkt, BYTES(ebpf_packetOffsetInBits))) & EBPF_MASK(u16, 13)); + ebpf_packetOffsetInBits += 13; + + hdr->ipv4.ttl = (u8)((load_byte(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 8; + + hdr->ipv4.protocol = (u8)((load_byte(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 8; + + hdr->ipv4.hdrChecksum = (u16)((load_half(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 16; + + __builtin_memcpy(&hdr->ipv4.srcAddr, pkt + BYTES(ebpf_packetOffsetInBits), 4); + ebpf_packetOffsetInBits += 32; + + __builtin_memcpy(&hdr->ipv4.dstAddr, pkt + BYTES(ebpf_packetOffsetInBits), 4); + ebpf_packetOffsetInBits += 32; + + + hdr->ipv4.ebpf_valid = 1; + hdr_start += BYTES(160); + +; + meta->ingress_port = skb->ifindex; goto accept; + } + start: { +/* extract(hdr->ethernet) */ + if ((u8*)ebpf_packetEnd < hdr_start + BYTES(112 + 0)) { + ebpf_errorCode = PacketTooShort; + goto reject; + } + + __builtin_memcpy(&hdr->ethernet.dstAddr, pkt + BYTES(ebpf_packetOffsetInBits), 6); + ebpf_packetOffsetInBits += 48; + + __builtin_memcpy(&hdr->ethernet.srcAddr, pkt + BYTES(ebpf_packetOffsetInBits), 6); + ebpf_packetOffsetInBits += 48; + + hdr->ethernet.etherType = (u16)((load_half(pkt, BYTES(ebpf_packetOffsetInBits)))); + ebpf_packetOffsetInBits += 16; + + + hdr->ethernet.ebpf_valid = 1; + hdr_start += BYTES(112); + +; + u16 select_0; + select_0 = hdr->ethernet.etherType; + if (select_0 == 0x800)goto parse_ipv4; + if ((select_0 & 0x0) == (0x0 & 0x0))goto reject; + else goto reject; + } + + reject: { + if (ebpf_errorCode == 0) { + return TC_ACT_SHOT; + } + compiler_meta__->parser_error = ebpf_errorCode; + goto accept; + } + + } + + accept: + hdrMd->ebpf_packetOffsetInBits = ebpf_packetOffsetInBits; + return -1; +} + +SEC("p4tc/parse") +int tc_parse_func(struct __sk_buff *skb) { + struct pna_global_metadata *compiler_meta__ = (struct pna_global_metadata *) skb->cb; + struct hdr_md *hdrMd; + struct my_ingress_headers_t *hdr; + int ret = -1; + ret = run_parser(skb, (struct my_ingress_headers_t *) hdr, compiler_meta__); + if (ret != -1) { + return ret; + } + return TC_ACT_PIPE; + } +char _license[] SEC("license") = "GPL"; diff --git a/testdata/p4tc_samples_outputs/digest_parser_meta_parser.h b/testdata/p4tc_samples_outputs/digest_parser_meta_parser.h new file mode 100644 index 00000000000..ff5a50baff9 --- /dev/null +++ b/testdata/p4tc_samples_outputs/digest_parser_meta_parser.h @@ -0,0 +1,160 @@ +#include "ebpf_kernel.h" + +#include +#include +#include "pna.h" + +#define EBPF_MASK(t, w) ((((t)(1)) << (w)) - (t)1) +#define BYTES(w) ((w) / 8) +#define write_partial(a, w, s, v) do { *((u8*)a) = ((*((u8*)a)) & ~(EBPF_MASK(u8, w) << s)) | (v << s) ; } while (0) +#define write_byte(base, offset, v) do { *(u8*)((base) + (offset)) = (v); } while (0) +#define bpf_trace_message(fmt, ...) + + +struct my_ingress_metadata_t { + u32 ingress_port; /* PortId_t */ +}; +struct ethernet_t { + u64 dstAddr; /* bit<48> */ + u64 srcAddr; /* bit<48> */ + u16 etherType; /* bit<16> */ + u8 ebpf_valid; +}; +struct ipv4_t { + u8 version; /* bit<4> */ + u8 ihl; /* bit<4> */ + u8 diffserv; /* bit<8> */ + u16 totalLen; /* bit<16> */ + u16 identification; /* bit<16> */ + u8 flags; /* bit<3> */ + u16 fragOffset; /* bit<13> */ + u8 ttl; /* bit<8> */ + u8 protocol; /* bit<8> */ + u16 hdrChecksum; /* bit<16> */ + u32 srcAddr; /* bit<32> */ + u32 dstAddr; /* bit<32> */ + u8 ebpf_valid; +}; +struct my_ingress_headers_t { + struct ethernet_t ethernet; /* ethernet_t */ + struct ipv4_t ipv4; /* ipv4_t */ +}; +struct ipv4_learn_digest_t { + u32 srcAddr; /* bit<32> */ + u32 ingress_port; /* PortId_t */ +}; + +struct hdr_md { + struct my_ingress_headers_t cpumap_hdr; + struct my_ingress_metadata_t cpumap_usermeta; + unsigned ebpf_packetOffsetInBits; + __u8 __hook; +}; + +struct p4tc_filter_fields { + __u32 pipeid; + __u32 handle; + __u32 classid; + __u32 chain; + __u32 blockid; + __be16 proto; + __u16 prio; +}; + +REGISTER_START() +REGISTER_TABLE(hdr_md_cpumap, BPF_MAP_TYPE_PERCPU_ARRAY, u32, struct hdr_md, 2) +BPF_ANNOTATE_KV_PAIR(hdr_md_cpumap, u32, struct hdr_md) +REGISTER_END() + +static __always_inline +void crc16_update(u16 * reg, const u8 * data, u16 data_size, const u16 poly) { + if (data_size <= 8) + data += data_size - 1; + #pragma clang loop unroll(full) + for (u16 i = 0; i < data_size; i++) { + bpf_trace_message("CRC16: data byte: %x\n", *data); + *reg ^= *data; + for (u8 bit = 0; bit < 8; bit++) { + *reg = (*reg) & 1 ? ((*reg) >> 1) ^ poly : (*reg) >> 1; + } + if (data_size <= 8) + data--; + else + data++; + } +} +static __always_inline u16 crc16_finalize(u16 reg) { + return reg; +} +static __always_inline +void crc32_update(u32 * reg, const u8 * data, u16 data_size, const u32 poly) { + u32* current = (u32*) data; + u32 index = 0; + u32 lookup_key = 0; + u32 lookup_value = 0; + u32 lookup_value1 = 0; + u32 lookup_value2 = 0; + u32 lookup_value3 = 0; + u32 lookup_value4 = 0; + u32 lookup_value5 = 0; + u32 lookup_value6 = 0; + u32 lookup_value7 = 0; + u32 lookup_value8 = 0; + u16 tmp = 0; + if (crc32_table != NULL) { + for (u16 i = data_size; i >= 8; i -= 8) { + /* Vars one and two will have swapped byte order if data_size == 8 */ + if (data_size == 8) current = (u32 *)(data + 4); + bpf_trace_message("CRC32: data dword: %x\n", *current); + u32 one = (data_size == 8 ? __builtin_bswap32(*current--) : *current++) ^ *reg; + bpf_trace_message("CRC32: data dword: %x\n", *current); + u32 two = (data_size == 8 ? __builtin_bswap32(*current--) : *current++); + lookup_key = (one & 0x000000FF); + lookup_value8 = crc32_table[(u16)(1792 + (u8)lookup_key)]; + lookup_key = (one >> 8) & 0x000000FF; + lookup_value7 = crc32_table[(u16)(1536 + (u8)lookup_key)]; + lookup_key = (one >> 16) & 0x000000FF; + lookup_value6 = crc32_table[(u16)(1280 + (u8)lookup_key)]; + lookup_key = one >> 24; + lookup_value5 = crc32_table[(u16)(1024 + (u8)(lookup_key))]; + lookup_key = (two & 0x000000FF); + lookup_value4 = crc32_table[(u16)(768 + (u8)lookup_key)]; + lookup_key = (two >> 8) & 0x000000FF; + lookup_value3 = crc32_table[(u16)(512 + (u8)lookup_key)]; + lookup_key = (two >> 16) & 0x000000FF; + lookup_value2 = crc32_table[(u16)(256 + (u8)lookup_key)]; + lookup_key = two >> 24; + lookup_value1 = crc32_table[(u8)(lookup_key)]; + *reg = lookup_value8 ^ lookup_value7 ^ lookup_value6 ^ lookup_value5 ^ + lookup_value4 ^ lookup_value3 ^ lookup_value2 ^ lookup_value1; + tmp += 8; + } + volatile int std_algo_lookup_key = 0; + if (data_size < 8) { + unsigned char *currentChar = (unsigned char *) current; + currentChar += data_size - 1; + for (u16 i = tmp; i < data_size; i++) { + bpf_trace_message("CRC32: data byte: %x\n", *currentChar); + std_algo_lookup_key = (u32)(((*reg) & 0xFF) ^ *currentChar--); + if (std_algo_lookup_key >= 0) { + lookup_value = crc32_table[(u8)(std_algo_lookup_key & 255)]; + } + *reg = ((*reg) >> 8) ^ lookup_value; + } + } else { + /* Consume data not processed by slice-by-8 algorithm above, these data are in network byte order */ + unsigned char *currentChar = (unsigned char *) current; + for (u16 i = tmp; i < data_size; i++) { + bpf_trace_message("CRC32: data byte: %x\n", *currentChar); + std_algo_lookup_key = (u32)(((*reg) & 0xFF) ^ *currentChar++); + if (std_algo_lookup_key >= 0) { + lookup_value = crc32_table[(u8)(std_algo_lookup_key & 255)]; + } + *reg = ((*reg) >> 8) ^ lookup_value; + } + } + } +} +static __always_inline u32 crc32_finalize(u32 reg) { + return reg ^ 0xFFFFFFFF; +}