From 456a230375120cd21b8242df6bf12fe390b7063c Mon Sep 17 00:00:00 2001 From: Mateusz Kossakowski Date: Wed, 25 May 2022 18:55:47 +0200 Subject: [PATCH] Add support for ternary const entries (#3347) * Add support for ternary const entries in ebpf-PSA backend --- backends/ebpf/psa/ebpfPsaGen.cpp | 18 +- backends/ebpf/psa/ebpfPsaTable.cpp | 375 ++++++++++++++++++ backends/ebpf/psa/ebpfPsaTable.h | 19 + .../p4testdata/action-default-ternary.p4 | 161 ++++++++ .../tests/p4testdata/const-entry-ternary.p4 | 138 +++++++ backends/ebpf/tests/ptf/test.py | 31 ++ 6 files changed, 735 insertions(+), 7 deletions(-) create mode 100644 backends/ebpf/tests/p4testdata/action-default-ternary.p4 create mode 100644 backends/ebpf/tests/p4testdata/const-entry-ternary.p4 diff --git a/backends/ebpf/psa/ebpfPsaGen.cpp b/backends/ebpf/psa/ebpfPsaGen.cpp index 0af41cbdd92..6da7d0d2c17 100644 --- a/backends/ebpf/psa/ebpfPsaGen.cpp +++ b/backends/ebpf/psa/ebpfPsaGen.cpp @@ -314,6 +314,10 @@ void PSAEbpfGenerator::emitHelperFunctions(CodeBuilder *builder) const { builder->appendLine(meterExecuteFunc); builder->newline(); } + + cstring addPrefixFunc = EBPFTablePSA::addPrefixFunc(options.emitTraceMessages); + builder->appendLine(addPrefixFunc); + builder->newline(); } // =====================PSAArchTC============================= @@ -360,20 +364,20 @@ void PSAArchTC::emit(CodeBuilder *builder) const { emitInstances(builder); /* - * 6. BPF map initialization + * 6. Helper functions for ingress and egress program. */ - emitInitializer(builder); - builder->newline(); + emitHelperFunctions(builder); /* - * 7. XDP helper program. + * 7. BPF map initialization. */ - xdp->emit(builder); + emitInitializer(builder); + builder->newline(); /* - * 8. Helper functions for ingress and egress program. + * 8. XDP helper program. */ - emitHelperFunctions(builder); + xdp->emit(builder); /* * 9. TC Ingress program. diff --git a/backends/ebpf/psa/ebpfPsaTable.cpp b/backends/ebpf/psa/ebpfPsaTable.cpp index 20d1b22b247..1d663ff14ae 100644 --- a/backends/ebpf/psa/ebpfPsaTable.cpp +++ b/backends/ebpf/psa/ebpfPsaTable.cpp @@ -307,6 +307,17 @@ void EBPFTablePSA::emitValueStructStructure(CodeBuilder* builder) { void EBPFTablePSA::emitInstance(CodeBuilder *builder) { if (isTernaryTable()) { emitTernaryInstance(builder); + if (hasConstEntries()) { + auto entries = getConstEntriesGroupedByPrefix(); + // A number of tuples is equal to number of unique prefixes + int nrOfTuples = entries.size(); + for (int i = 0; i < nrOfTuples; i++) { + builder->target->emitTableDecl(builder, + instanceName + "_tuple_" + std::to_string(i), + TableHash,"struct " + keyTypeName, + "struct " + valueTypeName, size); + } + } } else { TableKind kind = isLPMTable() ? TableLPMTrie : TableHash; builder->target->emitTableDecl(builder, instanceName, kind, @@ -360,6 +371,10 @@ void EBPFTablePSA::emitInitializer(CodeBuilder *builder) { } void EBPFTablePSA::emitConstEntriesInitializer(CodeBuilder *builder) { + if (isTernaryTable()) { + emitTernaryConstEntriesInitializer(builder); + return; + } CodeGenInspector cg(program->refMap, program->typeMap); cg.setBuilder(builder); const IR::EntriesList* entries = table->container->getEntries(); @@ -564,4 +579,364 @@ bool EBPFTablePSA::dropOnNoMatchingEntryFound() const { return false; return EBPFTable::dropOnNoMatchingEntryFound(); } + +void EBPFTablePSA::emitTernaryConstEntriesInitializer(CodeBuilder *builder) { + std::vector> entriesGroupedByPrefix = + getConstEntriesGroupedByPrefix(); + if (entriesGroupedByPrefix.empty()) + return; + + CodeGenInspector cg(program->refMap, program->typeMap); + cg.setBuilder(builder); + std::vector keyMasksNames; + cstring uniquePrefix = instanceName; + int tuple_id = 0; // We have preallocated tuple maps with ids starting from 0 + + // emit key head mask + cstring headName = program->refMap->newName("key_mask"); + builder->emitIndent(); + builder->appendFormat("struct %s_mask %s = {0}", keyTypeName, headName); + builder->endOfStatement(true); + + // emit key masks + emitKeyMasks(builder, entriesGroupedByPrefix, keyMasksNames); + + builder->newline(); + + // add head + cstring valueMask = program->refMap->newName("value_mask"); + cstring nextMask = keyMasksNames[0]; + int noTupleId = -1; + emitValueMask(builder, valueMask, nextMask, noTupleId); + builder->newline(); + + builder->emitIndent(); + builder->appendFormat("%s(0, 0, &%s, &%s, &%s, &%s, NULL, NULL)", + addPrefixFunctionName, tuplesMapName, + prefixesMapName, headName, valueMask); + builder->endOfStatement(true); + builder->newline(); + + // emit values + updates + for (size_t i = 0; i < entriesGroupedByPrefix.size(); i++) { + auto samePrefixEntries = entriesGroupedByPrefix[i]; + valueMask = program->refMap->newName("value_mask"); + std::vector keyNames; + std::vector valueNames; + cstring keysArray = program->refMap->newName("keys"); + cstring valuesArray = program->refMap->newName("values"); + cstring keyMaskVarName = keyMasksNames[i]; + + if (entriesGroupedByPrefix.size() > i + 1) { + nextMask = keyMasksNames[i + 1]; + } + emitValueMask(builder, valueMask, nextMask, tuple_id); + builder->newline(); + emitKeysAndValues(builder, samePrefixEntries, keyNames, valueNames); + + // construct keys array + builder->newline(); + builder->emitIndent(); + builder->appendFormat("void *%s[] = {", keysArray); + for (auto keyName : keyNames) + builder->appendFormat("&%s,", keyName); + builder->append("}"); + builder->endOfStatement(true); + + // construct values array + builder->emitIndent(); + builder->appendFormat("void *%s[] = {", valuesArray); + for (auto valueName : valueNames) + builder->appendFormat("&%s,", valueName); + builder->append("}"); + builder->endOfStatement(true); + + builder->newline(); + builder->emitIndent(); + builder->appendFormat("%s(%s, %s, &%s, &%s, &%s, &%s, %s, %s)", + addPrefixFunctionName, + cstring::to_cstring(samePrefixEntries.size()), + cstring::to_cstring(tuple_id), + tuplesMapName, + prefixesMapName, + keyMaskVarName, + valueMask, + keysArray, + valuesArray); + builder->endOfStatement(true); + + tuple_id++; + } +} + +void EBPFTablePSA::emitKeysAndValues(CodeBuilder *builder, + std::vector &samePrefixEntries, + std::vector &keyNames, + std::vector &valueNames) { + CodeGenInspector cg(program->refMap, program->typeMap); + cg.setBuilder(builder); + + for (auto entry : samePrefixEntries) { + cstring keyName = program->refMap->newName("key"); + cstring valueName = program->refMap->newName("value"); + keyNames.push_back(keyName); + valueNames.push_back(valueName); + // construct key + builder->emitIndent(); + builder->appendFormat("struct %s %s = {}", keyTypeName.c_str(), keyName.c_str()); + builder->endOfStatement(true); + for (size_t k = 0; k < keyGenerator->keyElements.size(); k++) { + auto keyElement = keyGenerator->keyElements[k]; + cstring fieldName = get(keyFieldNames, keyElement); + CHECK_NULL(fieldName); + builder->emitIndent(); + builder->appendFormat("%s.%s = ", keyName.c_str(), fieldName.c_str()); + auto mtdecl = program->refMap->getDeclaration(keyElement->matchType->path, true); + auto matchType = mtdecl->getNode()->to(); + auto expr = entry->keys->components[k]; + auto ebpfType = get(keyTypes, keyElement); + if (auto km = expr->to()) { + km->left->apply(cg); + builder->append(" & "); + km->right->apply(cg); + } else { + expr->apply(cg); + builder->append(" & "); + emitMaskForExactMatch(builder, fieldName, ebpfType); + } + builder->endOfStatement(true); + } + + // construct value + auto *mce = entry->action->to(); + emitTableValue(builder, mce, valueName.c_str()); + } +} + +void EBPFTablePSA::emitKeyMasks(CodeBuilder *builder, + std::vector> &entriesGrpedByPrefix, + std::vector &keyMasksNames) { + CodeGenInspector cg(program->refMap, program->typeMap); + cg.setBuilder(builder); + + for (auto samePrefixEntries : entriesGrpedByPrefix) { + auto firstEntry = samePrefixEntries.front(); + cstring keyFieldName = program->refMap->newName("key_mask"); + keyMasksNames.push_back(keyFieldName); + + builder->emitIndent(); + builder->appendFormat("struct %s_mask %s = {0}", keyTypeName, keyFieldName); + builder->endOfStatement(true); + + builder->emitIndent(); + cstring keyFieldNamePtr = program->refMap->newName(keyFieldName + "_ptr"); + builder->appendFormat("char *%s = &%s.mask", keyFieldNamePtr, keyFieldName); + builder->endOfStatement(true); + + for (size_t i = 0; i < keyGenerator->keyElements.size(); i++) { + auto keyElement = keyGenerator->keyElements[i]; + auto expr = firstEntry->keys->components[i]; + cstring fieldName = program->refMap->newName("field"); + auto ebpfType = get(keyTypes, keyElement); + builder->emitIndent(); + ebpfType->declare(builder, fieldName, false); + builder->append(" = "); + if (auto mask = expr->to()) { + mask->right->apply(cg); + builder->endOfStatement(true); + } else { + // MidEnd transforms 0xffff... masks into exact match + // So we receive there a Constant same as exact match + // So we have to create 0xffff... mask on our own + emitMaskForExactMatch(builder, fieldName, ebpfType); + } + builder->emitIndent(); + builder->appendFormat("__builtin_memcpy(%s, &%s, sizeof(%s))", + keyFieldNamePtr, fieldName, fieldName); + builder->endOfStatement(true); + builder->appendFormat("%s = %s + sizeof(%s)", keyFieldNamePtr, + keyFieldNamePtr, fieldName); + builder->endOfStatement(true); + } + } +} + +void EBPFTablePSA::emitMaskForExactMatch(CodeBuilder *builder, + cstring &fieldName, + EBPFType *ebpfType) const { + unsigned width = 0; + if (auto hasWidth = ebpfType->to()) { + width = hasWidth->widthInBits(); + if (width <= 8) { + width = 8; + } else if (width <= 16) { + width = 16; + } else if (width <= 32) { + width = 32; + } else if (width <= 64) { + width = 64; + } else { + // TODO: handle width > 64 bits + error(ErrorType::ERR_UNSUPPORTED, + "%1%: fields wider than 64 bits are not supported yet", + fieldName); + } + } else { + BUG("Cannot assess field bit width"); + } + builder->append("0x"); + for (int j = 0; j < width / 8; j++) + builder->append("ff"); + builder->endOfStatement(true); +} + +void EBPFTablePSA::emitValueMask(CodeBuilder *builder, const cstring valueMask, + const cstring nextMask, int tupleId) const { + builder->emitIndent(); + builder->appendFormat("struct %s_mask %s = {0}", valueTypeName, valueMask); + builder->endOfStatement(true); + + builder->emitIndent(); + builder->appendFormat("%s.tuple_id = %s", valueMask, cstring::to_cstring(tupleId)); + builder->endOfStatement(true); + builder->emitIndent(); + if (nextMask.isNullOrEmpty()) { + builder->appendFormat("%s.has_next = 0", valueMask); + builder->endOfStatement(true); + } else { + builder->appendFormat("%s.next_tuple_mask = %s", valueMask, nextMask); + builder->endOfStatement(true); + builder->emitIndent(); + builder->appendFormat("%s.has_next = 1", valueMask); + builder->endOfStatement(true); + } +} + +/** + * This method groups entries with the same prefix into separate lists. + * For example four entries which have two different prefixes + * will give as a result a vector of two vectors (each with two entries). + * @return a vector of vectors with const entries that have the same prefix + */ +std::vector> EBPFTablePSA::getConstEntriesGroupedByPrefix() { + std::vector> entriesGroupedByPrefix; + const IR::EntriesList* entries = table->container->getEntries(); + + if (!entries) + return entriesGroupedByPrefix; + + for (int i = 0; i < (int)entries->entries.size(); i++) { + auto mainEntr = entries->entries[i]; + if (!entriesGroupedByPrefix.empty()) { + auto last = entriesGroupedByPrefix.back(); + auto it = std::find(last.begin(), last.end(), mainEntr); + if (it != last.end()) { + // If this entry was added in a previous iteration + continue; + } + } + std::vector samePrefEntries; + samePrefEntries.push_back(mainEntr); + for (int j = i; j < (int)entries->entries.size(); j++) { + auto refEntr = entries->entries[j]; + if (i != j) { + bool isTheSamePrefix = true; + for (size_t k = 0; k < mainEntr->keys->components.size(); k++) { + auto k1 = mainEntr->keys->components[k]; + auto k2 = refEntr->keys->components[k]; + if (auto k1Mask = k1->to()) { + if (auto k2Mask = k2->to()) { + auto val1 = k1Mask->right->to(); + auto val2 = k2Mask->right->to(); + if (val1->value != val2->value) { + isTheSamePrefix = false; + break; + } + } + } + } + if (isTheSamePrefix) { + samePrefEntries.push_back(refEntr); + } + } + } + entriesGroupedByPrefix.push_back(samePrefEntries); + } + + return entriesGroupedByPrefix; +} + +bool EBPFTablePSA::hasConstEntries() { + const IR::EntriesList* entries = table->container->getEntries(); + return entries && entries->size() > 0; +} + +cstring EBPFTablePSA::addPrefixFunc(bool trace) { + cstring addPrefixFunc = + "static __always_inline\n" + "void add_prefix_and_entries(__u32 nr_entries,\n" + " __u32 tuple_id,\n" + " void *tuples_map,\n" + " void *prefixes_map,\n" + " void *key_mask,\n" + " void *value_mask,\n" + " void *keysPtrs[],\n" + " void *valuesPtrs[]) {\n" + " int ret = bpf_map_update_elem(prefixes_map, key_mask, value_mask, BPF_ANY);\n" + " if (ret) {\n" + "%trace_msg_prefix_map_fail%" + " return;\n" + " }\n" + " if (nr_entries == 0) {\n" + " return;\n" + " }\n" + " struct bpf_elf_map *tuple = bpf_map_lookup_elem(tuples_map, &tuple_id);\n" + " if (tuple) {\n" + " for (__u32 i = 0; i < nr_entries; i++) {\n" + " int ret = bpf_map_update_elem(tuple, keysPtrs[i], valuesPtrs[i], " + "BPF_ANY);\n" + " if (ret) {\n" + "%trace_msg_tuple_update_fail%" + " return;\n" + " } else {\n" + "%trace_msg_tuple_update_success%" + " }\n" + " }\n" + " } else {\n" + "%trace_msg_tuple_not_found%" + " return;\n" + " }\n" + "}"; + + if (trace) { + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_prefix_map_fail%", + " bpf_trace_message(\"Prefixes map update failed\\n\");\n"); + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_tuple_update_fail%", + " bpf_trace_message(\"Tuple map update failed\\n\");\n"); + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_tuple_update_success%", + " bpf_trace_message(\"Tuple map update succeed\\n\");\n"); + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_tuple_not_found%", + " bpf_trace_message(\"Tuple not found\\n\");\n"); + } else { + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_prefix_map_fail%", + ""); + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_tuple_update_fail%", + ""); + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_tuple_update_success%", + ""); + addPrefixFunc = addPrefixFunc.replace( + "%trace_msg_tuple_not_found%", + ""); + } + + return addPrefixFunc; +} + } // namespace EBPF diff --git a/backends/ebpf/psa/ebpfPsaTable.h b/backends/ebpf/psa/ebpfPsaTable.h index 9ccb530aeef..6b85215546e 100644 --- a/backends/ebpf/psa/ebpfPsaTable.h +++ b/backends/ebpf/psa/ebpfPsaTable.h @@ -27,6 +27,14 @@ namespace EBPF { class EBPFTableImplementationPSA; class EBPFTablePSA : public EBPFTable { + private: + std::vector> getConstEntriesGroupedByPrefix(); + bool hasConstEntries(); + void emitMaskForExactMatch(CodeBuilder *builder, cstring &fieldName, EBPFType *ebpfType) const; + const cstring addPrefixFunctionName = "add_prefix_and_entries"; + const cstring tuplesMapName = instanceName + "_tuples_map"; + const cstring prefixesMapName = instanceName + "_prefixes"; + protected: ActionTranslationVisitor* createActionTranslationVisitor( cstring valueName, const EBPFProgram* program) const override; @@ -39,8 +47,18 @@ class EBPFTablePSA : public EBPFTable { cstring valueName); void emitDefaultActionInitializer(CodeBuilder *builder); void emitConstEntriesInitializer(CodeBuilder *builder); + void emitTernaryConstEntriesInitializer(CodeBuilder *builder); void emitMapUpdateTraceMsg(CodeBuilder *builder, cstring mapName, cstring returnCode) const; + void emitValueMask(CodeBuilder *builder, cstring valueMask, + cstring nextMask, int tupleId) const; + void emitKeyMasks(CodeBuilder *builder, + std::vector> &entriesGrpedByPrefix, + std::vector &keyMasksNames); + void emitKeysAndValues(CodeBuilder *builder, + std::vector &samePrefixEntries, + std::vector &keyNames, + std::vector &valueNames); const IR::PathExpression* getActionNameExpression(const IR::Expression* expr) const; @@ -64,6 +82,7 @@ class EBPFTablePSA : public EBPFTable { void emitLookupDefault(CodeBuilder* builder, cstring key, cstring value, cstring actionRunVariable) override; bool dropOnNoMatchingEntryFound() const override; + static cstring addPrefixFunc(bool trace); EBPFCounterPSA* getDirectCounter(cstring name) const { auto result = std::find_if(counters.begin(), counters.end(), diff --git a/backends/ebpf/tests/p4testdata/action-default-ternary.p4 b/backends/ebpf/tests/p4testdata/action-default-ternary.p4 new file mode 100644 index 00000000000..8d72107b62a --- /dev/null +++ b/backends/ebpf/tests/p4testdata/action-default-ternary.p4 @@ -0,0 +1,161 @@ +#include +#include + +typedef bit<48> EthernetAddress; + +header ethernet_t { + EthernetAddress dstAddr; + EthernetAddress 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; + bit<32> srcAddr; + bit<32> dstAddr; +} + +struct fwd_metadata_t { +} + +struct empty_t {} + +struct metadata { + fwd_metadata_t fwd_metadata; +} + +struct headers { + ethernet_t ethernet; + ipv4_t ipv4; +} + + +parser IngressParserImpl(packet_in buffer, + out headers parsed_hdr, + inout metadata user_meta, + in psa_ingress_parser_input_metadata_t istd, + in empty_t resubmit_meta, + in empty_t recirculate_meta) +{ + state start { + buffer.extract(parsed_hdr.ethernet); + transition select(parsed_hdr.ethernet.etherType) { + 0x0800: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + buffer.extract(parsed_hdr.ipv4); + transition accept; + } +} + +parser EgressParserImpl(packet_in buffer, + out headers parsed_hdr, + inout metadata user_meta, + in psa_egress_parser_input_metadata_t istd, + in empty_t normal_meta, + in empty_t clone_i2e_meta, + in empty_t clone_e2e_meta) +{ + state start { + buffer.extract(parsed_hdr.ethernet); + transition select(parsed_hdr.ethernet.etherType) { + 0x0800: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + buffer.extract(parsed_hdr.ipv4); + transition accept; + } +} + +control ingress(inout headers hdr, + inout metadata user_meta, + in psa_ingress_input_metadata_t istd, + inout psa_ingress_output_metadata_t ostd) +{ + + action do_forward(PortId_t egress_port) { + send_to_port(ostd, egress_port); + } + + table tbl_ternary { + key = { + hdr.ipv4.dstAddr : ternary; + } + actions = { do_forward; NoAction; } + const default_action = do_forward((PortId_t) 5); + } + + apply { + tbl_ternary.apply(); + } +} + +control egress(inout headers hdr, + inout metadata user_meta, + in psa_egress_input_metadata_t istd, + inout psa_egress_output_metadata_t ostd) +{ + apply { } +} + +control CommonDeparserImpl(packet_out packet, + inout headers hdr) +{ + apply { + packet.emit(hdr.ethernet); + packet.emit(hdr.ipv4); + } +} + +control IngressDeparserImpl(packet_out buffer, + out empty_t clone_i2e_meta, + out empty_t resubmit_meta, + out empty_t normal_meta, + inout headers hdr, + in metadata meta, + in psa_ingress_output_metadata_t istd) +{ + CommonDeparserImpl() cp; + apply { + cp.apply(buffer, hdr); + } +} + +control EgressDeparserImpl(packet_out buffer, + out empty_t clone_e2e_meta, + out empty_t recirculate_meta, + inout headers hdr, + in metadata meta, + in psa_egress_output_metadata_t istd, + in psa_egress_deparser_input_metadata_t edstd) +{ + CommonDeparserImpl() cp; + apply { + cp.apply(buffer, hdr); + } +} + +IngressPipeline(IngressParserImpl(), + ingress(), + IngressDeparserImpl()) ip; + +EgressPipeline(EgressParserImpl(), + egress(), + EgressDeparserImpl()) ep; + +PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main; diff --git a/backends/ebpf/tests/p4testdata/const-entry-ternary.p4 b/backends/ebpf/tests/p4testdata/const-entry-ternary.p4 new file mode 100644 index 00000000000..9adf80e4b08 --- /dev/null +++ b/backends/ebpf/tests/p4testdata/const-entry-ternary.p4 @@ -0,0 +1,138 @@ +#include +#include +#include "common_headers.p4" + +struct metadata { +} + +struct headers { + ethernet_t ethernet; + ipv4_t ipv4; +} + +parser IngressParserImpl(packet_in buffer, + out headers parsed_hdr, + inout metadata user_meta, + in psa_ingress_parser_input_metadata_t istd, + in empty_t resubmit_meta, + in empty_t recirculate_meta) +{ + state start { + buffer.extract(parsed_hdr.ethernet); + transition select(parsed_hdr.ethernet.etherType) { + 0x0800: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + buffer.extract(parsed_hdr.ipv4); + transition accept; + } +} + +parser EgressParserImpl(packet_in buffer, + out headers parsed_hdr, + inout metadata user_meta, + in psa_egress_parser_input_metadata_t istd, + in empty_t normal_meta, + in empty_t clone_i2e_meta, + in empty_t clone_e2e_meta) +{ + state start { + buffer.extract(parsed_hdr.ethernet); + transition select(parsed_hdr.ethernet.etherType) { + 0x0800: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + buffer.extract(parsed_hdr.ipv4); + transition accept; + } +} + +control ingress(inout headers hdr, + inout metadata user_meta, + in psa_ingress_input_metadata_t istd, + inout psa_ingress_output_metadata_t ostd) +{ + + action do_forward(PortId_t egress_port) { + send_to_port(ostd, egress_port); + } + + table tbl_ternary { + key = { + hdr.ethernet.srcAddr : lpm; + hdr.ipv4.dstAddr : ternary; + hdr.ipv4.srcAddr : ternary; + hdr.ipv4.protocol : exact; + } + actions = { do_forward; NoAction; } + const entries = { + (0x5555555555 &&& 0x000000000000, 0x11223300 &&& 0xFFFF00FF, 0x33333333 &&& 0xFFFFFFFF, 0x00) : do_forward((PortId_t) 6); + (0x7777777777 &&& 0x000000000000, 0x11223355 &&& 0xFF00FFFF, 0x33333333 &&& 0xFFFFFFFF, 0x00) : do_forward((PortId_t) 5); + } + } + + apply { + tbl_ternary.apply(); + } +} + +control egress(inout headers hdr, + inout metadata user_meta, + in psa_egress_input_metadata_t istd, + inout psa_egress_output_metadata_t ostd) +{ + apply { } +} + +control CommonDeparserImpl(packet_out packet, + inout headers hdr) +{ + apply { + packet.emit(hdr.ethernet); + packet.emit(hdr.ipv4); + } +} + +control IngressDeparserImpl(packet_out buffer, + out empty_t clone_i2e_meta, + out empty_t resubmit_meta, + out empty_t normal_meta, + inout headers hdr, + in metadata meta, + in psa_ingress_output_metadata_t istd) +{ + CommonDeparserImpl() cp; + apply { + cp.apply(buffer, hdr); + } +} + +control EgressDeparserImpl(packet_out buffer, + out empty_t clone_e2e_meta, + out empty_t recirculate_meta, + inout headers hdr, + in metadata meta, + in psa_egress_output_metadata_t istd, + in psa_egress_deparser_input_metadata_t edstd) +{ + CommonDeparserImpl() cp; + apply { + cp.apply(buffer, hdr); + } +} + +IngressPipeline(IngressParserImpl(), + ingress(), + IngressDeparserImpl()) ip; + +EgressPipeline(EgressParserImpl(), + egress(), + EgressDeparserImpl()) ep; + +PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main; diff --git a/backends/ebpf/tests/ptf/test.py b/backends/ebpf/tests/ptf/test.py index 4f48d94fc1b..d2f591917cf 100644 --- a/backends/ebpf/tests/ptf/test.py +++ b/backends/ebpf/tests/ptf/test.py @@ -561,3 +561,34 @@ def runTest(self): pkt[IP].dst = '255.255.255.255' pkt[UDP].chksum = 0x044D testutils.verify_packet(self, pkt, PORT1) + + +class ActionDefaultTernaryPSATest(P4EbpfTest): + + p4_file_path = "p4testdata/action-default-ternary.p4" + + def runTest(self): + pkt = testutils.simple_ip_packet() + + # Test default action for ternary match + testutils.send_packet(self, PORT0, pkt) + testutils.verify_packet(self, pkt, PORT1) + + +class ConstEntryTernaryPSATest(P4EbpfTest): + + p4_file_path = "p4testdata/const-entry-ternary.p4" + + def runTest(self): + pkt = testutils.simple_ip_packet() + pkt[IP].src = 0x33333333 + + # via ternary const entry + pkt[Ether].src = "55:55:55:55:55:11" # mask is 0xFFFFFFFFFF00 + pkt[IP].dst = 0x11229900 # mask is 0xFFFF00FF + testutils.send_packet(self, PORT0, pkt) + testutils.verify_packet(self, pkt, PORT2) + pkt[Ether].src = "77:77:77:77:11:11" # mask is 0xFFFFFFFF0000 + pkt[IP].dst = 0x11993355 # mask is 0xFF00FFFF + testutils.send_packet(self, PORT0, pkt) + testutils.verify_packet(self, pkt, PORT1)