Skip to content

Commit

Permalink
Support ebpf Hash and Checksum (#4378)
Browse files Browse the repository at this point in the history
* Generate EBPF checksum functions

* Add EBPF Hash implementation

* Add checksum error scenario

* Update testcase output files.
  • Loading branch information
komaljai authored Jan 30, 2024
1 parent c43b24a commit dce5a8c
Show file tree
Hide file tree
Showing 93 changed files with 4,561 additions and 258 deletions.
82 changes: 74 additions & 8 deletions backends/tc/ebpfCodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ void PNAEbpfGenerator::emitPipelineInstances(EBPF::CodeBuilder *builder) const {
"struct hdr_md", 2);
}

void PNAEbpfGenerator::emitCRC32LookupTableInstance(EBPF::CodeBuilder *builder) const {
builder->target->emitTableDecl(builder, cstring("crc_lookup_tbl"), EBPF::TableArray, "u32",
cstring("struct lookup_tbl_val"), 1);
}

void PNAEbpfGenerator::emitCRC32LookupTableTypes(EBPF::CodeBuilder *builder) const {
builder->append("struct lookup_tbl_val ");
builder->blockStart();
builder->emitIndent();
builder->append("u32 table[2048]");
builder->endOfStatement(true);
builder->blockEnd(false);
builder->endOfStatement(true);
}

// =====================PNAArchTC=============================
void PNAArchTC::emit(EBPF::CodeBuilder *builder) const {
/**
Expand Down Expand Up @@ -134,17 +149,12 @@ void PNAArchTC::emit(EBPF::CodeBuilder *builder) const {
emitTypes(builder);

/*
* 4. BPF map definitions.
*/
emitInstances(builder);

/*
* 5. XDP helper program.
* 4. XDP helper program.
*/
xdp->emit(builder);

/*
* 6. TC Pipeline program for post-parser.
* 5. TC Pipeline program for post-parser.
*/
pipeline->emit(builder);

Expand All @@ -160,6 +170,7 @@ void PNAArchTC::emitInstances(EBPF::CodeBuilder *builder) const {
}

emitPipelineInstances(builder);
emitCRC32LookupTableInstance(builder);
builder->appendLine("REGISTER_END()");
builder->newline();
}
Expand All @@ -177,7 +188,6 @@ void PNAArchTC::emitParser(EBPF::CodeBuilder *builder) const {
builder->appendFormat("#include \"%s\"", headerFile);
builder->newline();
builder->newline();
emitInstances(builder);
pipeline->name = "tc-parse";
pipeline->sectionName = "classifier/" + pipeline->name;
pipeline->functionName = pipeline->name.replace("-", "_") + "_func";
Expand All @@ -196,6 +206,11 @@ void PNAArchTC::emitHeader(EBPF::CodeBuilder *builder) const {
PNAErrorCodesGen errorGen(builder);
pipeline->program->apply(errorGen);
emitGlobalHeadersMetadata(builder);
builder->newline();
emitCRC32LookupTableTypes(builder);
// BPF map definitions.
emitInstances(builder);
EBPF::EBPFHashAlgorithmTypeFactoryPSA::instance()->emitGlobals(builder);
}

// =====================TCIngressPipelinePNA=============================
Expand Down Expand Up @@ -1252,6 +1267,22 @@ bool ConvertToEBPFControlPNA::preorder(const IR::Declaration_Variable *decl) {
return true;
}

bool ConvertToEBPFControlPNA::preorder(const IR::ExternBlock *instance) {
auto di = instance->node->to<IR::Declaration_Instance>();
if (di == nullptr) return false;
cstring name = EBPF::EBPFObject::externalName(di);
cstring typeName = instance->type->getName().name;

if (typeName == "Hash") {
auto hash = new EBPF::EBPFHashPSA(program, di, name);
control->hashes.emplace(name, hash);
} else {
::error(ErrorType::ERR_UNEXPECTED, "Unexpected block %s nested within control", instance);
}

return false;
}

// =====================ConvertToEBPFDeparserPNA=============================
bool ConvertToEBPFDeparserPNA::preorder(const IR::ControlBlock *ctrl) {
deparser = new IngressDeparserPNA(program, ctrl, parserHeaders, istd);
Expand Down Expand Up @@ -1539,6 +1570,41 @@ void ControlBodyTranslatorPNA::processApply(const P4::ApplyMethod *method) {
builder->target->emitTraceMessage(builder, msgStr.c_str());
}

void ControlBodyTranslatorPNA::processMethod(const P4::ExternMethod *method) {
auto decl = method->object;
auto declType = method->originalExternType;
cstring name = EBPF::EBPFObject::externalName(decl);

if (declType->name.name == "Hash") {
auto hash = control->to<EBPF::EBPFControlPSA>()->getHash(name);
hash->processMethod(builder, method->method->name.name, method->expr, this);
return;
} else {
::error(ErrorType::ERR_UNSUPPORTED_ON_TARGET, "%1%: Unexpected method call", method->expr);
}
}

bool ControlBodyTranslatorPNA::preorder(const IR::AssignmentStatement *a) {
if (auto methodCallExpr = a->right->to<IR::MethodCallExpression>()) {
auto mi = P4::MethodInstance::resolve(methodCallExpr, control->program->refMap,
control->program->typeMap);
auto ext = mi->to<P4::ExternMethod>();
if (ext == nullptr) {
return false;
}

if (ext->originalExternType->name.name == "Hash") {
cstring name = EBPF::EBPFObject::externalName(ext->object);
auto hash = control->to<EBPF::EBPFControlPSA>()->getHash(name);
// Before assigning a value to a left expression we have to calculate a hash.
// Then the hash value is stored in a registerVar variable.
hash->calculateHash(builder, ext->expr, this);
builder->emitIndent();
}
}

return EBPF::CodeGenInspector::preorder(a);
}
// =====================ActionTranslationVisitorPNA=============================
ActionTranslationVisitorPNA::ActionTranslationVisitorPNA(const EBPF::EBPFProgram *program,
cstring valueName,
Expand Down
6 changes: 6 additions & 0 deletions backends/tc/ebpfCodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class PNAEbpfGenerator : public EBPF::EbpfCodeGenerator {
void emitGlobalHeadersMetadata(EBPF::CodeBuilder *builder) const override;
void emitPipelineInstances(EBPF::CodeBuilder *builder) const override;
cstring getProgramName() const;

void emitCRC32LookupTableTypes(EBPF::CodeBuilder *builder) const;
void emitCRC32LookupTableInstance(EBPF::CodeBuilder *builder) const;
};

// Similar to class PSAErrorCodesGen in backends/ebpf/psa/ebpfPsaGen.cpp
Expand Down Expand Up @@ -267,6 +270,7 @@ class ConvertToEBPFControlPNA : public Inspector {
bool preorder(const IR::Declaration_Variable *) override;
bool preorder(const IR::Member *m) override;
bool preorder(const IR::IfStatement *a) override;
bool preorder(const IR::ExternBlock *instance);
bool checkPnaTimestampMem(const IR::Member *m);
EBPF::EBPFControlPSA *getEBPFControl() { return control; }
};
Expand Down Expand Up @@ -305,6 +309,8 @@ class ControlBodyTranslatorPNA : public EBPF::ControlBodyTranslator {
void processApply(const P4::ApplyMethod *method);
bool checkPnaPortMem(const IR::Member *m);
virtual cstring getParamName(const IR::PathExpression *);
bool preorder(const IR::AssignmentStatement *a) override;
void processMethod(const P4::ExternMethod *method) override;
};

// Similar to class ActionTranslationVisitorPSA in backends/ebpf/psa/ebpfPsaControl.h
Expand Down
146 changes: 146 additions & 0 deletions testdata/p4tc_samples/checksum.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* -*- P4_16 -*- */

#include <core.p4>
#include <tc/pna.p4>

#define PORT_TABLE_SIZE 262144

/*
* 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;
}

/****** G L O B A L I N G R E S S M E T A D A T A *********/

struct my_ingress_metadata_t {
bit<16> checksum;
bit<16> state;
}

struct empty_metadata_t {
}

error {
BadIPv4HeaderChecksum
}

/*********************** 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)
{
InternetChecksum() ck;
const bit<16> ETHERTYPE_IPV4 = 0x0800;

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);
ck.clear();
ck.add({
/* 16-bit word 0 */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
/* 16-bit word 1 */ hdr.ipv4.totalLen,
/* 16-bit word 2 */ hdr.ipv4.identification,
/* 16-bit word 3 */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
/* 16-bit word 4 */ hdr.ipv4.ttl, hdr.ipv4.protocol,
/* 16-bit word 5 skip hdr.ipv4.hdrChecksum, */
/* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
/* 16-bit words 8-9 */ hdr.ipv4.dstAddr
});
verify(hdr.ipv4.hdrChecksum == ck.get(), error.BadIPv4HeaderChecksum);
hdr.ipv4.hdrChecksum = ck.get_state();
transition accept;
}
}

/***************** 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_id, @tc_type("macaddr") bit<48> dmac, @tc_type("macaddr") bit<48> smac) {
hdr.ethernet.srcAddr = smac;
hdr.ethernet.dstAddr = dmac;
send_to_port(port_id);
}
action drop() {
drop_packet();
}

table nh_table {
key = {
hdr.ipv4.srcAddr : exact;
}
actions = {
send_nh;
drop;
}
size = PORT_TABLE_SIZE;
const default_action = drop;
}

apply {
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)
{
apply {
pkt.emit(hdr.ethernet);
pkt.emit(hdr.ipv4);
}
}

/************ F I N A L P A C K A G E ******************************/

PNA_NIC(
Ingress_Parser(),
ingress(),
Ingress_Deparser()
) main;
Loading

0 comments on commit dce5a8c

Please sign in to comment.