From 46f974f32970e0446445cbfb63d438db3b7dd7b3 Mon Sep 17 00:00:00 2001 From: fruffy Date: Mon, 25 Nov 2024 18:56:14 -0500 Subject: [PATCH] Add the Tofino testgen target. Signed-off-by: fruffy --- .github/workflows/ci-test-debian.yml | 5 + .../common/core/abstract_execution_state.cpp | 2 +- .../p4tools/modules/testgen/CMakeLists.txt | 6 + .../testgen/core/small_step/expr_stepper.cpp | 4 +- .../testgen/targets/bmv2/expr_stepper.cpp | 5 +- .../testgen/targets/tofino/CMakeLists.txt | 58 + .../targets/tofino/check_parser_error.h | 51 + .../targets/tofino/compiler_result.cpp | 14 + .../testgen/targets/tofino/compiler_result.h | 27 + .../testgen/targets/tofino/concolic.cpp | 177 + .../modules/testgen/targets/tofino/concolic.h | 75 + .../testgen/targets/tofino/constants.cpp | 38 + .../testgen/targets/tofino/constants.h | 107 + .../testgen/targets/tofino/hash_utils.cpp | 112 + .../testgen/targets/tofino/hash_utils.h | 81 + .../targets/tofino/map_direct_externs.cpp | 62 + .../targets/tofino/map_direct_externs.h | 58 + .../modules/testgen/targets/tofino/register.h | 39 + .../testgen/targets/tofino/rename_keys.h | 139 + .../targets/tofino/shared_expr_stepper.cpp | 1410 ++++ .../targets/tofino/shared_expr_stepper.h | 100 + .../targets/tofino/shared_program_info.cpp | 139 + .../targets/tofino/shared_program_info.h | 99 + .../targets/tofino/shared_table_stepper.cpp | 473 ++ .../targets/tofino/shared_table_stepper.h | 96 + .../modules/testgen/targets/tofino/target.cpp | 306 + .../modules/testgen/targets/tofino/target.h | 111 + .../testgen/targets/tofino/test/P4Tests.cmake | 242 + .../targets/tofino/test/TestTemplate.cmake | 131 + .../targets/tofino/test/Tofino2PTFXfail.cmake | 132 + .../targets/tofino/test/Tofino2Xfail.cmake | 195 + .../targets/tofino/test/TofinoPTFXfail.cmake | 313 + .../targets/tofino/test/TofinoXfail.cmake | 406 + .../tofino/test/p4-programs/common/headers.p4 | 160 + .../tofino/test/p4-programs/common/util.p4 | 94 + .../test/p4-programs/multipipe_simple.p4 | 113 + .../tofino/test/p4-programs/parser_reject.p4 | 119 + .../test/p4-programs/port_metadata_unpack.p4 | 184 + .../p4-programs/tna_action_declaration.p4 | 179 + .../test/p4-programs/tna_action_profile_1.p4 | 206 + .../p4-programs/tna_action_profile_default.p4 | 166 + .../tofino/test/p4-programs/tna_advance.p4 | 140 + .../tofino/test/p4-programs/tna_cast.p4 | 153 + .../tofino/test/p4-programs/tna_drop.p4 | 148 + .../test/p4-programs/tna_emit_extern.p4 | 149 + .../tofino/test/p4-programs/tna_empty.p4 | 137 + .../tofino/test/p4-programs/tna_exit.p4 | 150 + .../test/p4-programs/tna_extract_emit_1.p4 | 151 + .../test/p4-programs/tna_extract_emit_2.p4 | 150 + .../test/p4-programs/tna_extract_emit_3.p4 | 153 + .../test/p4-programs/tna_extract_emit_4.p4 | 153 + .../test/p4-programs/tna_extract_emit_5.p4 | 154 + .../test/p4-programs/tna_extract_emit_6.p4 | 150 + .../test/p4-programs/tna_extract_emit_7.p4 | 149 + .../test/p4-programs/tna_extract_emit_8.p4 | 154 + .../tofino/test/p4-programs/tna_hash_1.p4 | 197 + .../tofino/test/p4-programs/tna_hash_10.p4 | 179 + .../tofino/test/p4-programs/tna_hash_2.p4 | 200 + .../tofino/test/p4-programs/tna_hash_3.p4 | 201 + .../tofino/test/p4-programs/tna_hash_4.p4 | 204 + .../tofino/test/p4-programs/tna_hash_5.p4 | 207 + .../tofino/test/p4-programs/tna_hash_6.p4 | 204 + .../tofino/test/p4-programs/tna_hash_7.p4 | 205 + .../tofino/test/p4-programs/tna_hash_8.p4 | 207 + .../tofino/test/p4-programs/tna_hash_9.p4 | 199 + .../tofino/test/p4-programs/tna_if_stmt.p4 | 151 + .../tofino/test/p4-programs/tna_mirror_2.p4 | 139 + .../tofino/test/p4-programs/tna_pvs_fixed.p4 | 139 + .../test/p4-programs/tna_random_extern.p4 | 145 + .../test/p4-programs/tna_register_action_1.p4 | 167 + .../test/p4-programs/tna_register_action_2.p4 | 167 + .../test/p4-programs/tna_simple_assign_1.p4 | 155 + .../test/p4-programs/tna_simple_assign_2.p4 | 143 + .../test/p4-programs/tna_simple_switch.p4 | 1555 ++++ .../p4-programs/tna_table_control_exact.p4 | 164 + .../test/p4-programs/tna_table_control_lpm.p4 | 164 + .../p4-programs/tna_table_control_ternary.p4 | 164 + .../tna_table_control_ternary_anno.p4 | 165 + .../p4-programs/tna_table_entries_range.p4 | 149 + .../test/p4-programs/tna_table_no_key.p4 | 161 + .../test/p4-programs/tna_tainted_table_key.p4 | 163 + .../tofino/test/p4-programs/tna_undefined.p4 | 154 + .../tofino1_only/nasa_bundle_translator.p4 | 810 ++ .../tofino1_only/switch_tofino_x1_mod.p4 | 5971 ++++++++++++++ .../test/p4-programs/tofino1_only/switchml.p4 | 1683 ++++ .../tofino2_only/switch_tofino2_y1_mod.p4 | 6869 +++++++++++++++++ .../testgen/targets/tofino/test_backend.cpp | 162 + .../testgen/targets/tofino/test_backend.h | 66 + .../targets/tofino/test_backend/ptf.cpp | 597 ++ .../testgen/targets/tofino/test_backend/ptf.h | 100 + .../targets/tofino/test_backend/stf.cpp | 300 + .../testgen/targets/tofino/test_backend/stf.h | 83 + .../testgen/targets/tofino/test_spec.cpp | 295 + .../testgen/targets/tofino/test_spec.h | 313 + .../testgen/targets/tofino/testgen_tofino.def | 11 + .../targets/tofino/tofino/cmd_stepper.cpp | 309 + .../targets/tofino/tofino/cmd_stepper.h | 60 + .../targets/tofino/tofino/expr_stepper.cpp | 48 + .../targets/tofino/tofino/expr_stepper.h | 48 + .../targets/tofino/tofino/program_info.cpp | 349 + .../targets/tofino/tofino/program_info.h | 75 + .../targets/tofino/tofino2/cmd_stepper.cpp | 311 + .../targets/tofino/tofino2/cmd_stepper.h | 59 + .../targets/tofino/tofino2/expr_stepper.cpp | 49 + .../targets/tofino/tofino2/expr_stepper.h | 48 + .../targets/tofino/tofino2/program_info.cpp | 355 + .../targets/tofino/tofino2/program_info.h | 75 + 107 files changed, 33613 insertions(+), 6 deletions(-) create mode 100644 backends/p4tools/modules/testgen/targets/tofino/CMakeLists.txt create mode 100644 backends/p4tools/modules/testgen/targets/tofino/check_parser_error.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/compiler_result.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/compiler_result.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/concolic.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/concolic.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/constants.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/constants.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/hash_utils.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/hash_utils.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/register.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/rename_keys.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/shared_program_info.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/target.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/target.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/P4Tests.cmake create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/TestTemplate.cmake create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/Tofino2PTFXfail.cmake create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/Tofino2Xfail.cmake create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/TofinoPTFXfail.cmake create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/TofinoXfail.cmake create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/headers.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/util.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/multipipe_simple.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/parser_reject.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/port_metadata_unpack.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_declaration.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_1.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_default.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_advance.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_cast.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_drop.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_emit_extern.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_empty.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_exit.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_1.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_2.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_3.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_4.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_5.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_6.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_7.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_8.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_1.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_10.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_2.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_3.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_4.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_5.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_6.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_7.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_8.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_9.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_if_stmt.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_mirror_2.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_pvs_fixed.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_random_extern.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_1.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_2.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_1.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_2.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_switch.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_exact.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_lpm.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary_anno.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_entries_range.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_no_key.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_tainted_table_key.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_undefined.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/nasa_bundle_translator.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switch_tofino_x1_mod.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switchml.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino2_only/switch_tofino2_y1_mod.p4 create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_backend.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_backend.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_spec.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/test_spec.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/testgen_tofino.def create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.cpp create mode 100644 backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h diff --git a/.github/workflows/ci-test-debian.yml b/.github/workflows/ci-test-debian.yml index ac2b2d5e4be..1cb1b7da7ce 100644 --- a/.github/workflows/ci-test-debian.yml +++ b/.github/workflows/ci-test-debian.yml @@ -51,6 +51,7 @@ jobs: BUILD_GENERATOR: Ninja ENABLE_GTESTS: ON ENABLE_TOFINO: ON + ENABLE_TEST_TOOLS: ON ENABLE_BMV2: OFF ENABLE_EBPF: OFF ENABLE_UBPF: OFF @@ -75,6 +76,10 @@ jobs: run: | tools/ci-build.sh + - name: Run tests (Ubuntu 20.04) + run: ctest --output-on-failure --schedule-random -R tofino + working-directory: ./build + # Build with GCC and test P4C on Ubuntu 20.04. test-ubuntu20: name: test-ubuntu20 (Unity ${{ matrix.unity }}, GTest ${{ matrix.gtest }}) diff --git a/backends/p4tools/common/core/abstract_execution_state.cpp b/backends/p4tools/common/core/abstract_execution_state.cpp index fac023cb443..edd5cb7576f 100644 --- a/backends/p4tools/common/core/abstract_execution_state.cpp +++ b/backends/p4tools/common/core/abstract_execution_state.cpp @@ -123,7 +123,7 @@ std::vector AbstractExecutionState::flattenComplexExpres auto subList = flattenComplexExpression(listElem->expression, flatValids); exprList.insert(exprList.end(), subList.begin(), subList.end()); } - if (auto headerExpr = structExpr->to()) { + if (const auto *headerExpr = structExpr->to()) { flatValids.emplace_back(headerExpr->validity); } } else if (const auto *headerStackExpr = inputExpression->to()) { diff --git a/backends/p4tools/modules/testgen/CMakeLists.txt b/backends/p4tools/modules/testgen/CMakeLists.txt index 9f168613b63..26dce3f80a1 100644 --- a/backends/p4tools/modules/testgen/CMakeLists.txt +++ b/backends/p4tools/modules/testgen/CMakeLists.txt @@ -77,6 +77,12 @@ set( ) set(TESTGEN_INCLUDES) +# Only enable Tofino tests if the Tofino backend is enabled. +# FIXME: Can we make this part of the extension? +if (NOT ENABLE_TOFINO) + set (ENABLE_TOOLS_TARGET_TOFINO OFF) +endif() + file(GLOB testgen_targets RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/targets ${CMAKE_CURRENT_SOURCE_DIR}/targets/*) foreach(ext ${testgen_targets}) set(testgen_targets_dir ${CMAKE_CURRENT_SOURCE_DIR}/targets/${ext}/) diff --git a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp index f10766105b2..8a0e4b8df82 100644 --- a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp +++ b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp @@ -510,10 +510,10 @@ bool ExprStepper::preorder(const IR::AbstractSlice *slice) { } void ExprStepper::stepNoMatch(std::string traceLog, const IR::Expression *condition) { - auto &noMatchState = condition ? state.clone() : state; + auto &noMatchState = (condition != nullptr) ? state.clone() : state; noMatchState.add(*new TraceEvents::GenericDescription("NoMatch"_cs, traceLog)); noMatchState.replaceTopBody(Continuation::Exception::NoMatch); - if (condition) { + if (condition != nullptr) { result->emplace_back(condition, state, noMatchState); } else { result->emplace_back(state); diff --git a/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp b/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp index d04b868c352..080dc01afdf 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp @@ -1021,8 +1021,7 @@ const Bmv2V1ModelExprStepper::ExternMethodImpls } } bool argsAreTainted = false; - for (size_t idx = 0; idx < externInfo.externArguments.size(); ++idx) { - const auto *arg = externInfo.externArguments.at(idx); + for (auto arg : externInfo.externArguments) { argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } // If any of the input arguments is tainted, the entire extern is unreliable. @@ -1631,7 +1630,7 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const ExternInfo &externInfo) } // Lastly, check whether we are calling an internal extern method. return ExprStepper::evalExternMethodCall(externInfo); -} // NOLINT +} bool Bmv2V1ModelExprStepper::preorder(const IR::P4Table *table) { // Delegate to the tableStepper. diff --git a/backends/p4tools/modules/testgen/targets/tofino/CMakeLists.txt b/backends/p4tools/modules/testgen/targets/tofino/CMakeLists.txt new file mode 100644 index 00000000000..08edfccbd93 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/CMakeLists.txt @@ -0,0 +1,58 @@ +if(ENABLE_TESTING) + # Include the test subdirectory. + message("-- Adding p4testgen tofino test suite") + include(test/P4Tests.cmake) +endif() + +# Source files for p4testgen. +set( +TESTGEN_SOURCES +${TESTGEN_SOURCES} + +${CMAKE_CURRENT_SOURCE_DIR}/test_backend/ptf.cpp +${CMAKE_CURRENT_SOURCE_DIR}/test_backend/stf.cpp +${CMAKE_CURRENT_SOURCE_DIR}/compiler_result.cpp +${CMAKE_CURRENT_SOURCE_DIR}/concolic.cpp +${CMAKE_CURRENT_SOURCE_DIR}/constants.cpp +${CMAKE_CURRENT_SOURCE_DIR}/hash_utils.cpp +${CMAKE_CURRENT_SOURCE_DIR}/map_direct_externs.cpp +${CMAKE_CURRENT_SOURCE_DIR}/shared_expr_stepper.cpp +${CMAKE_CURRENT_SOURCE_DIR}/shared_program_info.cpp +${CMAKE_CURRENT_SOURCE_DIR}/shared_table_stepper.cpp +${CMAKE_CURRENT_SOURCE_DIR}/target.cpp +${CMAKE_CURRENT_SOURCE_DIR}/test_backend.cpp +${CMAKE_CURRENT_SOURCE_DIR}/test_spec.cpp + +${CMAKE_CURRENT_SOURCE_DIR}/tofino/cmd_stepper.cpp +${CMAKE_CURRENT_SOURCE_DIR}/tofino/expr_stepper.cpp +${CMAKE_CURRENT_SOURCE_DIR}/tofino/program_info.cpp + +${CMAKE_CURRENT_SOURCE_DIR}/tofino2/cmd_stepper.cpp +${CMAKE_CURRENT_SOURCE_DIR}/tofino2/expr_stepper.cpp +${CMAKE_CURRENT_SOURCE_DIR}/tofino2/program_info.cpp + +PARENT_SCOPE +) + +# Override default settings in bf-p4c. We want those binaries to be built. +if (NOT TARGET p4cgraphs) + set(ENABLE_P4C_GRAPHS ON CACHE BOOL "TRUE" FORCE) + add_subdirectory(${P4C_SOURCE_DIR}/backends/graphs ${P4C_BINARY_DIR}/backends/graphs) +endif() + +# Ideally, we'd link against just the Barefoot midend and a minimal set of +# transitive dependencies, but the compiler doesn't cleanly separate like +# that. Those transitive dependencies turn out to be most of the compiler! +set( +TESTGEN_LIBS +# tofinobackend +# bfn_p4runtime +# p4cgraphs +# We do not use the target library variable here because top-level testgen will not find it. +# boost_system +${TESTGEN_LIBS} +PARENT_SCOPE +) + +# Custom IR constructs for P4Tools. +set(IR_DEF_FILES ${IR_DEF_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/testgen_tofino.def PARENT_SCOPE) diff --git a/backends/p4tools/modules/testgen/targets/tofino/check_parser_error.h b/backends/p4tools/modules/testgen/targets/tofino/check_parser_error.h new file mode 100644 index 00000000000..c0e0f3ccd7e --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/check_parser_error.h @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CHECK_PARSER_ERROR_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CHECK_PARSER_ERROR_H_ + +#include "ir/ir.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/** + * Walks a given IR node to detect whether it contains a reference to the Tofino parser error. + * This is primarily used to detect that an ingress control references the parser error variable. + * If that is the case, the semantics of parser errors change in the Tofino parser. It will not + * drop packets and instead forward the packet to the MAU. + */ +class CheckParserError : public Inspector { + private: + bool parserErrorFound = false; + + public: + CheckParserError() { setName("CheckParserError"); } + + bool hasParserError() const { return parserErrorFound; } + + void postorder(const IR::Member *member) override { + if (member->member == "parser_err") { + parserErrorFound = true; + } + } +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CHECK_PARSER_ERROR_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/compiler_result.cpp b/backends/p4tools/modules/testgen/targets/tofino/compiler_result.cpp new file mode 100644 index 00000000000..566121abd80 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/compiler_result.cpp @@ -0,0 +1,14 @@ +#include "backends/p4tools/modules/testgen/targets/tofino/compiler_result.h" + +#include + +namespace P4::P4Tools::P4Testgen::Tofino { + +TofinoCompilerResult::TofinoCompilerResult(TestgenCompilerResult compilerResult, + DirectExternMap directExternMap) + : TestgenCompilerResult(std::move(compilerResult)), + directExternMap(std::move(directExternMap)) {} + +const DirectExternMap &TofinoCompilerResult::getDirectExternMap() const { return directExternMap; } + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/compiler_result.h b/backends/p4tools/modules/testgen/targets/tofino/compiler_result.h new file mode 100644 index 00000000000..c6a0a7a5d7c --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/compiler_result.h @@ -0,0 +1,27 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_COMPILER_RESULT_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_COMPILER_RESULT_H_ + +#include "backends/p4tools/modules/testgen/core/compiler_result.h" +#include "backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/// Extends the CompilerResult with information specific to the V1Model running on BMv2. +class TofinoCompilerResult : public TestgenCompilerResult { + private: + /// The map of direct extern declarations which are attached to a table. + DirectExternMap directExternMap; + + public: + explicit TofinoCompilerResult(TestgenCompilerResult compilerResult, + DirectExternMap directExternMap); + + /// @returns the map of direct extern declarations which are attached to a table. + [[nodiscard]] const DirectExternMap &getDirectExternMap() const; + + DECLARE_TYPEINFO(TofinoCompilerResult, TestgenCompilerResult); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_COMPILER_RESULT_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/concolic.cpp b/backends/p4tools/modules/testgen/targets/tofino/concolic.cpp new file mode 100644 index 00000000000..51f53d934e1 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/concolic.cpp @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/concolic.h" + +#include +#include +#include + +#include "ir/irutils.h" +#include "lib/cstring.h" +#include "lib/error.h" +#include "lib/exceptions.h" +#include "lib/null.h" + +#include "backends/p4tools/modules/testgen/lib/exceptions.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/hash_utils.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const IR::ListExpression *SharedTofinoConcolic::evaluateListHashExpr( + const std::vector &exprList, const Model &completedModel, + Model::ExpressionMap *resolvedExpressions) { + auto *evaluatedListExpr = new IR::ListExpression({}); + for (const auto *expr : exprList) { + auto width = expr->type->width_bits(); + if (width > HASH_CHUNK_SIZE) { + auto chunks = width / HASH_CHUNK_SIZE; + auto remainder = width % HASH_CHUNK_SIZE; + for (int i = 0; i < chunks; ++i) { + auto hi = width - i * HASH_CHUNK_SIZE - 1; + auto lo = hi - HASH_CHUNK_SIZE + 1; + auto *slice = new IR::Slice(expr, hi, lo); + slice->type = IR::Type_Bits::get(HASH_CHUNK_SIZE); + const auto *evalConst = completedModel.evaluate(slice, true, resolvedExpressions); + evaluatedListExpr->components.push_back(evalConst); + } + if (remainder != 0) { + auto hi = width - chunks * HASH_CHUNK_SIZE - 1; + auto lo = 0; + auto *slice = new IR::Slice(expr, hi, lo); + slice->type = IR::Type_Bits::get(remainder); + const auto *evalConst = completedModel.evaluate(slice, true, resolvedExpressions); + evaluatedListExpr->components.push_back(evalConst); + } + } else { + const auto *evalConst = completedModel.evaluate(expr, true, resolvedExpressions); + evaluatedListExpr->components.push_back(evalConst); + } + } + return evaluatedListExpr; +} + +const ConcolicMethodImpls::ImplList SharedTofinoConcolic::SharedTofinoConcolicMethodImpls{ + /* ====================================================================================== + * Hash_get + * ====================================================================================== */ + + {"Hash_get"_cs, + {"data"_cs}, + [](cstring /*qualifiedMethodName*/, const IR::ConcolicVariable *var, + const ExecutionState & /*state*/, const Model &completedModel, + ConcolicVariableMap *resolvedConcolicVariables) { + const auto *returnType = var->type; + + auto externDecls = var->associatedNodes; + BUG_CHECK(!externDecls.empty(), + "Must have at least 1 declaration associated with this extern."); + const auto *externInstance = externDecls.at(0)->checkedTo(); + + const auto *externInstanceArgs = externInstance->arguments; + BUG_CHECK(externInstanceArgs->size() == 1 || externInstanceArgs->size() == 2, + "Hash types either have one instance argument or two."); + CHECK_NULL(externInstanceArgs->at(0)->expression); + const auto *symmetricAnno = externInstance->getAnnotation("symmetric"_cs); + std::vector symmetricList; + if (symmetricAnno != nullptr) { + ::warning("Symmetric annotations for hashes are currently not implemented."); + } + + const auto *args = var->arguments; + const auto *dataExpr = args->at(0)->expression; + std::vector exprList; + // We only support struct expressions as argument input for now. + if (const auto *dataStructExpr = dataExpr->to()) { + exprList = IR::flattenStructExpression(dataStructExpr); + } else if (dataExpr->is() || dataExpr->is()) { + exprList.emplace_back(dataExpr); + } else { + TESTGEN_UNIMPLEMENTED("Hash input %1% of type %2% is not supported.", dataExpr, + dataExpr->node_type_name()); + } + if (exprList.empty()) { + TESTGEN_UNIMPLEMENTED("Data input is empty. This case is not implemented."); + } + + // All the preprocessing is completed. Now pick the hash algorithm according to the + // argument given to the extern declaration. + const IR::Expression *computedResult = nullptr; + const IR::ListExpression *evaluatedListExpr = nullptr; + Model::ExpressionMap resolvedExpressions; + const auto *hashAlgoExpr = externInstance->arguments->at(0)->expression; + // In our case, enums have been converted to concrete values. + // TODO: Maybe look up the declaration? + auto hashAlgoIdx = hashAlgoExpr->checkedTo()->asInt(); + + if (hashAlgoIdx == TofinoHashAlgorithm::identity) { + const auto *concatExpr = exprList.at(0); + for (size_t idx = 1; idx < exprList.size(); idx++) { + const auto *expr = exprList.at(idx); + const auto *newWidth = + IR::Type_Bits::get(concatExpr->type->width_bits() + expr->type->width_bits()); + concatExpr = new IR::Concat(newWidth, concatExpr, expr); + } + computedResult = + IR::Constant::get(returnType, IR::getBigIntFromLiteral(completedModel.evaluate( + concatExpr, true, &resolvedExpressions))); + } else if (hashAlgoIdx == TofinoHashAlgorithm::random) { + BUG("Random hashes should not be encountered here."); + } else if (hashAlgoIdx == TofinoHashAlgorithm::custom) { + // For custom algorithms we need the CRCDeclaration. Try to find it as the second + // argument in the supplied extern declarations. + const IR::Declaration_Instance *crcDeclInstance = nullptr; + BUG_CHECK(externDecls.size() >= 2, + "The number of extern declarations supplied to this concolic call must at " + "least be 2."); + crcDeclInstance = externDecls.at(1)->checkedTo(); + evaluatedListExpr = + evaluateListHashExpr(exprList, completedModel, &resolvedExpressions); + computedResult = HashCompute::substituteCustomHash(crcDeclInstance, evaluatedListExpr, + returnType, &symmetricList); + } else { + evaluatedListExpr = + evaluateListHashExpr(exprList, completedModel, &resolvedExpressions); + computedResult = HashCompute::substituteOtherHash(hashAlgoExpr, evaluatedListExpr, + returnType, &symmetricList); + } + + // Assign a constraint for the concolic variable using the computed result. + if (returnType->is()) { + // Overwrite any previous assignment or result. + (*resolvedConcolicVariables)[*var] = computedResult; + } else { + TESTGEN_UNIMPLEMENTED("Hash return type %2% not supported", returnType); + } + // Generated equations for all the variables that were assigned a value in this iteration + // of concolic execution. + for (const auto &variable : resolvedExpressions) { + const auto *varName = variable.first; + const auto *varExpr = variable.second; + (*resolvedConcolicVariables)[varName] = varExpr; + } + }}, +}; + +const ConcolicMethodImpls::ImplList *SharedTofinoConcolic::getSharedTofinoConcolicMethodImpls() { + return &SharedTofinoConcolicMethodImpls; +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/concolic.h b/backends/p4tools/modules/testgen/targets/tofino/concolic.h new file mode 100644 index 00000000000..8fc9bc81f64 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/concolic.h @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CONCOLIC_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CONCOLIC_H_ + +#include + +#include "backends/p4tools/common/lib/model.h" +#include "ir/ir.h" + +#include "backends/p4tools/modules/testgen/lib/concolic.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class SharedTofinoConcolic : public Concolic { + private: + /// We are not using an enum class because we directly compare integers. This is because error + /// types are converted into integers in our interpreter. If we use an enum class, we have to + /// cast every enum access to int. + struct TofinoHashAlgorithm { + using Type = enum { + identity, + random, + xor8, + xor16, + xor32, + crc8, + crc16, + crc32, + crc64, + custom + }; + }; + + /// This represents the chunk size for a hash. Every type bits that is larger than this chunk + /// size will be split into multiple chunks of size HASH_CHUNK_SIZE; + static constexpr int HASH_CHUNK_SIZE = 64; + + /// This is the list of concolic functions that are implemented in this class. + static const ConcolicMethodImpls::ImplList SharedTofinoConcolicMethodImpls; + + /// Helper functions that converts an input list of expressions into a set of literals that have + /// been evaluated according to the current model. This will be the hash input. + /// @param resolvedExpressions stores all the variables that have been resolved while converting + /// the list expression. These variables are later used to create constraints for the SMT + /// solver that bind a variable to these solutions. + static const IR::ListExpression *evaluateListHashExpr( + const std::vector &exprList, const Model &completedModel, + Model::ExpressionMap *resolvedExpressions); + + public: + /// @returns the concolic functions that are implemented for this particular target. + static const ConcolicMethodImpls::ImplList *getSharedTofinoConcolicMethodImpls(); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CONCOLIC_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/constants.cpp b/backends/p4tools/modules/testgen/targets/tofino/constants.cpp new file mode 100644 index 00000000000..468eba48877 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/constants.cpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" + +#include "ir/irutils.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const IR::Member TofinoConstants::INGRESS_DROP_VAR = + IR::Member(IR::Type_Bits::get(3), new IR::PathExpression("*ig_intr_md_for_dprsr"), "drop_ctl"); + +const IR::Member JBayConstants::INGRESS_DROP_VAR = + IR::Member(IR::Type_Bits::get(3), new IR::PathExpression("*ig_intr_md_for_dprsr"), "drop_ctl"); + +const IR::Member TofinoConstants::EGRESS_DROP_VAR = + IR::Member(IR::Type_Bits::get(3), new IR::PathExpression("*eg_intr_md_for_dprsr"), "drop_ctl"); + +const IR::Member JBayConstants::EGRESS_DROP_VAR = + IR::Member(IR::Type_Bits::get(3), new IR::PathExpression("*eg_intr_md_for_dprsr"), "drop_ctl"); + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/constants.h b/backends/p4tools/modules/testgen/targets/tofino/constants.h new file mode 100644 index 00000000000..2274af89d61 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/constants.h @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CONSTANTS_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CONSTANTS_H_ + +#include + +#include "ir/ir.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class SharedTofinoConstants { + public: + // The minimum packet sizes required to run a successful PTF test. + static constexpr int64_t PTF_INPUT_PKT_MIN_SIZE = 512; + static constexpr int64_t PTF_OUTPUT_PKT_MIN_SIZE = 240; + /// Entries that can match a range. + static constexpr const char *MATCH_KIND_RANGE = "range"; + /// A match that is used as an argument for the selector. + static constexpr const char *MATCH_KIND_SELECTOR = "selector"; + /// A match that is as an argument for the atcam index. + static constexpr const char *MATCH_KIND_ATCAM = "atcam_partition_index"; + /// The port value that corresponds to a dropped packet. + static constexpr int DROP_CONSTANT = 511; + /// Bitmask which indicates which ports allowed to be used by generated tests. + /// Particularly useful for test environments which do not enable all ports to + /// be used. + // Seven least significant bits of port number determine the physical port (the rest + // determines pipe number). 0x7f allows all ports of the first pipe. + // For now, target only the first pipe. + // TODO: allow the mask to be specified by a parameter + static constexpr size_t VALID_INPUT_PORT_MASK = 0x7f; +}; + +class TofinoConstants { + public: + // Parser error codes, copied from tofino.p4. + static constexpr int PARSER_ERROR_OK = 0x0000; + static constexpr int PARSER_ERROR_NO_MATCH = 0x0001; + static constexpr int PARSER_ERROR_PARTIAL_HDR = 0x0002; + static constexpr int PARSER_ERROR_CTR_RANGE = 0x0004; + static constexpr int PARSER_ERROR_TIMEOUT_USER = 0x0008; + static constexpr int PARSER_ERROR_TIMEOUT_HW = 0x0010; + static constexpr int PARSER_ERROR_SRC_EXT = 0x0020; + static constexpr int PARSER_ERROR_DST_CONT = 0x0040; + static constexpr int PARSER_ERROR_PHV_OWNER = 0x0080; + static constexpr int PARSER_ERROR_MULTIWRITE = 0x0100; + static constexpr int PARSER_ERROR_ARAM_MBE = 0x0400; + static constexpr int PARSER_ERROR_FCS = 0x0800; + // The format of the port metadata appears to be unspecified, but is 64 bits on Tofino. + static constexpr int PORT_METADATA_SIZE = 64; + // Size of the frame check sequence appended to the packet. + static constexpr int64_t ETH_FCS_SIZE = 32; + // The variable corresponding to the parameter that controls packet drops. + static const IR::Member INGRESS_DROP_VAR; + static const IR::Member EGRESS_DROP_VAR; + /// Port width in bits. + static constexpr int PORT_BIT_WIDTH = 9; +}; + +class JBayConstants { + public: + // Parser error codes, copied from tofino2.p4. + static constexpr int PARSER_ERROR_OK = 0x0000; + static constexpr int PARSER_ERROR_NO_MATCH = 0x0001; + static constexpr int PARSER_ERROR_PARTIAL_HDR = 0x0002; + static constexpr int PARSER_ERROR_CTR_RANGE = 0x0004; + static constexpr int PARSER_ERROR_TIMEOUT_USER = 0x0008; + static constexpr int PARSER_ERROR_TIMEOUT_HW = 0x0010; + static constexpr int PARSER_ERROR_SRC_EXT = 0x0020; + static constexpr int PARSER_ERROR_DST_CONT = 0x0040; + static constexpr int PARSER_ERROR_PHV_OWNER = 0x0080; + static constexpr int PARSER_ERROR_MULTIWRITE = 0x0100; + static constexpr int PARSER_ERROR_ARAM_MBE = 0x0400; + static constexpr int PARSER_ERROR_FCS = 0x0800; + static constexpr int PARSER_ERROR_CSUM_MBE = 0x1000; + // The format of the port metadata appears to be unspecified, but is 192 bits on JBay. + static constexpr int PORT_METADATA_SIZE = 192; + // Size of the frame check sequence appended to the packet. + static constexpr int64_t ETH_FCS_SIZE = 32; + // The variable corresponding to the parameter that controls packet drops. + static const IR::Member INGRESS_DROP_VAR; + static const IR::Member EGRESS_DROP_VAR; + /// Port width in bits. + static constexpr int PORT_BIT_WIDTH = 9; +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_CONSTANTS_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/hash_utils.cpp b/backends/p4tools/modules/testgen/targets/tofino/hash_utils.cpp new file mode 100644 index 00000000000..7e555ed38d7 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/hash_utils.cpp @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/hash_utils.h" + +#include +#include + +#include +#include +#include + +#include "backends/tofino/bf-utils//include/dynamic_hash/bfn_hash_algorithm.h" +#include "backends/tofino/bf-utils//include/dynamic_hash/dynamic_hash.h" +#include "ir/irutils.h" +#include "lib/log.h" +#include "lib/safe_vector.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +hash_seed_t HashCompute::computeHash(IR::MAU::HashFunction &hashFunction, + const IR::ListExpression *hashList, const IR::Type *hashType, + const std::vector * /*symmetricList*/) { + bfn_hash_algorithm_t hashAlg; + hashFunction.build_algorithm_t(&hashAlg); + + safe_vector hashOutputs; + hash_matrix_output_t hashOutput; + hashOutput.gfm_start_bit = 0; + hashOutput.p4_hash_output_bit = 0; + hashOutput.bit_size = 0; + hashOutput.bit_size = hashType->width_bits(); + hashOutputs.push_back(hashOutput); + + int totalInputBits = 0; + + safe_vector hashInputs; + for (const auto *comp : boost::adaptors::reverse(hashList->components)) { + const auto *compExpr = comp->to(); + ixbar_input_t hashInput; + hashInput.type = ixbar_input_type::tCONST; + hashInput.u.constant = compExpr->asUint64(); + hashInput.ixbar_bit_position = 0; + hashInput.bit_size = compExpr->type->width_bits(); + hashInput.symmetric_info.is_symmetric = false; + totalInputBits += hashInput.bit_size; + hashInputs.push_back(hashInput); + } + + hash_seed_t hashSeed = {0ULL, 0ULL}; + + determine_seed(hashOutputs.data(), hashOutputs.size(), hashInputs.data(), hashInputs.size(), + totalInputBits, &hashAlg, &hashSeed); + + LOG1(" seed = " << std::hex << hashSeed.hash_seed_value << std::dec); + LOG1(" used = " << std::hex << hashSeed.hash_seed_used << std::dec); + + return hashSeed; +} + +const IR::Constant *HashCompute::substituteCustomHash(const IR::Declaration_Instance *polyInstance, + const IR::ListExpression *hashList, + const IR::Type *hashType, + const std::vector *symmetricList) { + auto *crcPolyRef = new IR::GlobalRef(polyInstance->srcInfo, polyInstance->type, polyInstance); + if (crcPolyRef == nullptr) { + return nullptr; + } + + IR::MAU::HashFunction hashFunction; + hashFunction.convertPolynomialExtern(crcPolyRef); + + // LOG2(" " << hashFunction); + + hash_seed_t hashSeed = computeHash(hashFunction, hashList, hashType, symmetricList); + + return IR::Constant::get(hashType->clone(), hashSeed.hash_seed_value); +} + +const IR::Constant *HashCompute::substituteOtherHash(const IR::Expression *hashAlgoExpr, + const IR::ListExpression *hashList, + const IR::Type *hashType, + const std::vector *symmetricList) { + IR::MAU::HashFunction hashFunction; + if (!hashFunction.setup(hashAlgoExpr)) { + return nullptr; + } + + // LOG2(" " << hashFunction); + + hash_seed_t hashSeed = computeHash(hashFunction, hashList, hashType, symmetricList); + + return IR::Constant::get(hashType->clone(), hashSeed.hash_seed_value); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/hash_utils.h b/backends/p4tools/modules/testgen/targets/tofino/hash_utils.h new file mode 100644 index 00000000000..54245d5a842 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/hash_utils.h @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_HASH_UTILS_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_HASH_UTILS_H_ + +#include + +#include "backends/tofino/bf-utils/include/dynamic_hash/dynamic_hash.h" +#include "ir/ir.h" +// Need to include this last. +#include "backends/tofino//bf-p4c/mau/hash_function.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/** This class defines helper functions that can be used to calculate Tofino hashes from a list of + * supplied constants. This code is lifted from the FoldConstantHashes in "fold_constant_hashes.h" + * of the bf-p4c midend. + */ +class HashCompute { + private: + /** + * Computes hash value based on the provided hash function and a list of expressions. + * @param[in] hashFunction The hash function to be used for the computation of the hash value + * @param[in] hashList The list of expressions used as an input for the hash function + * @param[in] hashType The type of the newly created constant expression + * @param[in] symmetricList A map indicating which values of the @param hashList are symmetric. + * @return Returns calculated hash value. + */ + static hash_seed_t computeHash(IR::MAU::HashFunction &hashFunction, + const IR::ListExpression *hashList, const IR::Type *hashType, + const std::vector *symmetricList); + + public: + /** + * Creates a constant expression whose value is computed by a hash function + * defined with a CRC polynomial. + * @param[in] crc_poly_path The path expression representing the CRC polynomial + * @param[in] hashList The list of expressions used as an input for the hash function + * @param[in] hashType The type of the newly created constant expression + * @param[in] symmetricList A map indicating which values of the @param hashList are symmetric. + * @return Returns newly created constant expression. + */ + static const IR::Constant *substituteCustomHash(const IR::Declaration_Instance *polyInstance, + const IR::ListExpression *hashList, + const IR::Type *hashType, + const std::vector *symmetricList); + /** + * Creates a constant expression whose value is computed by a pre-defined hash function + * (e.g. CRCn). + * @param[in] hashAlgoExpr The expression including the type of the hash function to be used + * @param[in] hashList The list of expressions used as an input for the hash function + * @param[in] hashType The type of the newly created constant expression + * @param[in] symmetricList A map indicating which values of the @param hashList are symmetric. + * @return Returns newly created constant expression. + */ + static const IR::Constant *substituteOtherHash(const IR::Expression *hashAlgoExpr, + const IR::ListExpression *hashList, + const IR::Type *hashType, + const std::vector *symmetricList); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_HASH_UTILS_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.cpp b/backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.cpp new file mode 100644 index 00000000000..be9c1aa8ec1 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.cpp @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +bool MapDirectExterns::preorder(const IR::Declaration_Instance *declInstance) { + declaredExterns[declInstance->name] = declInstance; + return true; +} + +bool MapDirectExterns::preorder(const IR::P4Table *table) { + // Try to either get registers from the table. + const auto *impl = table->properties->getProperty("registers"); + if (impl == nullptr) { + return false; + } + // Cannot map a temporary mapped direct extern without a register. + const auto *selectorExpr = impl->value->checkedTo(); + if (selectorExpr->expression->is()) { + return true; + } + // Try to find the extern in the declared instances. + const auto *implPath = selectorExpr->expression->to(); + auto implementationLabel = implPath->path->name.name; + + // If the extern is not in the list of declared instances, move on. + auto it = declaredExterns.find(implementationLabel); + if (it == declaredExterns.end()) { + return true; + } + // Tofino does not support direct externs attached to multiple tables. + const auto *declInstance = it->second; + if (directExternMap.find(declInstance->controlPlaneName()) != directExternMap.end()) { + FATAL_ERROR( + "Direct extern was already mapped to a table. It can not be attached to two tables. "); + return true; + } + directExternMap.emplace(declInstance->controlPlaneName(), table); + return true; +} + +const DirectExternMap &MapDirectExterns::getDirectExternMap() { return directExternMap; } + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h b/backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h new file mode 100644 index 00000000000..0c8ea3d6fea --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_MAP_DIRECT_EXTERNS_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_MAP_DIRECT_EXTERNS_H_ + +#include + +#include "ir/ir.h" +#include "ir/visitor.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/// A mapping of the control plane name of extern declarations which are associated with a table. +/// Such an extern is referred to as direct extern. +/// We are using the cstring name and not an IR::Declaration pointer for the mapping because other +/// passes may clone or transform the IR::Declaration node, invalidating this mapping. +using DirectExternMap = std::map; + +/// A lightweight visitor, which collects all the declarations in the program then checks whether a +/// table is referencing the declaration as an direct extern. The direct externs are collected in a +/// map. Currently checks for "registers" properties in the table. +class MapDirectExterns : public Inspector { + private: + /// List of the declared instances in the program. + std::map declaredExterns; + + /// Maps direct extern declarations to the table they are attached to. + DirectExternMap directExternMap; + + public: + bool preorder(const IR::Declaration_Instance *declInstance) override; + + bool preorder(const IR::P4Table *table) override; + + /// @returns the list of direct externs and their corresponding table. + const DirectExternMap &getDirectExternMap(); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /*BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_MAP_DIRECT_EXTERNS_H_*/ diff --git a/backends/p4tools/modules/testgen/targets/tofino/register.h b/backends/p4tools/modules/testgen/targets/tofino/register.h new file mode 100644 index 00000000000..c3e7ad5cb8e --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/register.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_REGISTER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_REGISTER_H_ + +#include "backends/p4tools/common/p4ctool.h" + +#include "backends/p4tools/modules/testgen/options.h" +#include "backends/p4tools/modules/testgen/targets/tofino/target.h" +#include "backends/p4tools/modules/testgen/testgen.h" + +namespace P4::P4Tools::P4Testgen { + +/// Register the Tofino testgen target with the testgen framework. +inline void tofinoRegisterTestgenTarget() { + Tofino::Tofino_TnaTestgenTarget::make(); + Tofino::JBay_T2naTestgenTarget::make(); +} + +} // namespace P4::P4Tools::P4Testgen + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_REGISTER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/rename_keys.h b/backends/p4tools/modules/testgen/targets/tofino/rename_keys.h new file mode 100644 index 00000000000..1b0d45fa312 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/rename_keys.h @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_RENAME_KEYS_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_RENAME_KEYS_H_ + +#include + +#include // NOLINT + +#include "ir/ir.h" +#include "lib/cstring.h" + +#include "backends/p4tools/modules/testgen/options.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/** Implements a pass that - + * 1. prunes all valid key matches with trailing '$'. + * 2. replaces header stack indices hdr[] with hdr$. + * + * 1. Frontend converts `isValid()` calls on headers and header unions + * to `$valid$` (note the trailing "$") as expected by P4Runtime. + * However, bf-p4c and Bfruntime use "$valid". + * + * 2. The context.json has header stack indices as hdr$]. + * + * @pre: None + * + * @post: Ensure that + * - key matches with header stack indices have the subscript + * operator replaced with a '$'. + * - valid key matches have the trailing '$' removed. + * - Header stack indices with hdr[] are replaced with hdr$ + * - For STF tests, slices are removed. + * + */ +class ProcessKeyElems : public Transform { + public: + ProcessKeyElems() { setName("ProcessKeyElems"); } + + /// Check if there is a header stack index field matching + /// the format hdr[], and replace with hdr$. + /// Check if there is a `$valid$` key match and remove the + /// trailing '$'. + IR::Node *postorder(IR::KeyElement *keyElem) override { + const auto *nameAnnot = keyElem->getAnnotation(IR::Annotation::nameAnnotation); + CHECK_NULL(nameAnnot); + cstring keyName = nameAnnot->getName(); + + auto isReplaced = false; + + const auto *foundValid = keyName.find(".$valid"); + if (foundValid != nullptr) { + // Remove the trailing '$'. + keyName = keyName.exceptLast(1); + isReplaced = true; + } + + // Replace header stack indices hdr[] with hdr$. This + // is output in the context.json + std::regex hdrStackRegex(R"(\[([0-9]+)\])"); + auto indexName = std::regex_replace(keyName.c_str(), hdrStackRegex, "$$$1"); + if (indexName != keyName.c_str()) { + keyName = cstring::to_cstring(indexName); + isReplaced = true; + } + + if (isReplaced) { + ::warning("Replacing invalid key %1% with %2% for key %3%. ", keyName, keyName, + keyElem); + // Update the key's annotation with the new fieldName. + keyElem->addOrReplaceAnnotation(IR::Annotation::nameAnnotation, + new IR::StringLiteral(keyName)); + return keyElem; + } + + return keyElem; + } +}; + +/// Check for a @ternary annotation attached to the parent table. +/// Rewrite every exact match key to be a ternary key. +class ProcessAnnotatedTables : public Transform { + class ReplaceExactKeyMatches : public Transform { + public: + ReplaceExactKeyMatches() { setName("ReplaceExactKeyMatches"); } + IR::KeyElement *preorder(IR::KeyElement *keyElem) override { + const auto *keyElemMatchType = keyElem->matchType; + if (keyElemMatchType->path->name == "exact") { + auto *newMatchType = keyElemMatchType->clone(); + newMatchType->path = new IR::Path("ternary"); + keyElem->matchType = newMatchType; + } + return keyElem; + } + }; + + public: + ProcessAnnotatedTables() { setName("ProcessAnnotatedTables"); } + const IR::Node *preorder(IR::P4Table *table) override { + if (table->getAnnotation("ternary"_cs) != nullptr) { + return table->apply(ReplaceExactKeyMatches()); + } + return table; + } +}; + +class RenameKeys : public PassManager { + public: + RenameKeys() { + setName("RenameKeys"); + passes.emplace_back(new ProcessKeyElems()); + if (TestgenOptions::get().testBackend == "STF") { + passes.emplace_back(new ProcessAnnotatedTables()); + } + } +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_RENAME_KEYS_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.cpp b/backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.cpp new file mode 100644 index 00000000000..5b4284e5134 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.cpp @@ -0,0 +1,1410 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/constants.h" +#include "backends/p4tools/common/lib/formulae.h" +#include "backends/p4tools/common/lib/symbolic_env.h" +#include "backends/p4tools/common/lib/taint.h" +#include "backends/p4tools/common/lib/trace_event.h" +#include "backends/p4tools/common/lib/trace_event_types.h" +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/common/lib/variables.h" +#include "ir/ir-generated.h" +#include "ir/irutils.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" +#include "lib/null.h" +#include "lib/ordered_map.h" +#include "lib/safe_vector.h" + +#include "backends/p4tools/modules/testgen/core/extern_info.h" +#include "backends/p4tools/modules/testgen/core/small_step/small_step.h" +#include "backends/p4tools/modules/testgen/core/target.h" +#include "backends/p4tools/modules/testgen/lib/continuation.h" +#include "backends/p4tools/modules/testgen/lib/exceptions.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/lib/packet_vars.h" +#include "backends/p4tools/modules/testgen/lib/test_spec.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +ExprStepper::PacketCursorAdvanceInfo SharedTofinoExprStepper::calculateSuccessfulParserAdvance( + const ExecutionState &state, int advanceSize) const { + // Calculate the necessary size of the packet to extract successfully. + // The minimum required size for a packet is the current cursor and the amount we are + // advancing into the packet minus whatever has been buffered in the current buffer. + auto minSize = state.getInputPacketCursor() + advanceSize; + auto *cond = new IR::Geq(IR::Type::Boolean::get(), ExecutionState::getInputPacketSizeVar(), + IR::Constant::get(&PacketVars::PACKET_SIZE_VAR_TYPE, minSize)); + auto fcsLeft = std::max(0, state.getProperty("fcsLeft"_cs)); + auto rejectSize = std::max(0, minSize - state.getPacketBufferSize() - fcsLeft); + const IR::Expression *notCond = nullptr; + // Do not generate short packets when the parser error is referenced. + // TODO: Model this exception. + if (!(state.hasProperty("parserErrReferenced"_cs) && + state.getProperty("parserErrReferenced"_cs))) { + notCond = new IR::Lss(IR::Type::Boolean::get(), ExecutionState::getInputPacketSizeVar(), + IR::Constant::get(&PacketVars::PACKET_SIZE_VAR_TYPE, rejectSize)); + } + + return {advanceSize, cond, advanceSize, notCond}; +} + +ExprStepper::PacketCursorAdvanceInfo SharedTofinoExprStepper::calculateAdvanceExpression( + const ExecutionState &state, const IR::Expression *advanceExpr, + const IR::Expression *restrictions) const { + const auto *packetSizeVarType = &PacketVars::PACKET_SIZE_VAR_TYPE; + + // Compute the accept case. + const auto *cursorConst = IR::Constant::get(packetSizeVarType, state.getInputPacketCursor()); + const auto *bufferSizeConst = IR::Constant::get(packetSizeVarType, state.getPacketBufferSize()); + const auto *advanceSum = new IR::Add(packetSizeVarType, cursorConst, advanceExpr); + auto *minSize = new IR::Sub(packetSizeVarType, advanceSum, bufferSizeConst); + // The packet size must be larger than the current parser cursor minus what is already + // present in the buffer. The advance expression, i.e., the size of the advance can be freely + // chosen. If bufferSizeConst is larger than the entire advance, this does not hold. + auto *cond = new IR::LOr( + new IR::Grt(bufferSizeConst, advanceSum), + new IR::Geq(IR::Type::Boolean::get(), ExecutionState::getInputPacketSizeVar(), minSize)); + + int advanceVal = 0; + const auto *advanceCond = new IR::LAnd(cond, restrictions); + const auto *advanceConst = evaluateExpression(advanceExpr, advanceCond); + // If we can not satisfy the advance, set the condition to nullptr. + if (advanceConst == nullptr) { + advanceCond = nullptr; + } else { + // Otherwise store both the condition and the computed value in the AdvanceInfo data + // structure. Do not forget to add the condition that the expression must be equal to the + // computed value. + advanceVal = advanceConst->checkedTo()->asInt(); + advanceCond = new IR::LAnd(advanceCond, new IR::Equ(advanceConst, advanceExpr)); + } + + // Compute the reject case. + auto fcsLeft = std::max(0, state.getProperty("fcsLeft"_cs)); + const auto *bufferSizeFcsConst = + IR::Constant::get(packetSizeVarType, state.getPacketBufferSize() + fcsLeft); + const auto *minSizeFcs = new IR::Sub(advanceSum, bufferSizeFcsConst); + const IR::Expression *notAdvanceCond = new IR::Lss(bufferSizeFcsConst, advanceSum); + notAdvanceCond = new IR::LAnd(notAdvanceCond, + new IR::Lss(ExecutionState::getInputPacketSizeVar(), minSizeFcs)); + notAdvanceCond = new IR::LAnd(notAdvanceCond, restrictions); + + int notAdvanceVal = 0; + const IR::Expression *notAdvanceConst = nullptr; + // TODO: We only generate packets that are too short if the parser error is not + // referenced within the control. This is because of behavior in some targets that + // pads packets that are too short. We currently do not model this. + // TODO: Remove this check and model it properly. + if (!(state.hasProperty("parserErrReferenced"_cs) && + state.getProperty("parserErrReferenced"_cs))) { + notAdvanceConst = evaluateExpression(advanceExpr, notAdvanceCond); + } + // If we can not satisfy the reject, set the condition to nullptr. + if (notAdvanceConst == nullptr) { + notAdvanceCond = nullptr; + } else { + // structure. Do not forget to add the condition that the expression must be equal to the + // computed value. + notAdvanceVal = notAdvanceConst->checkedTo()->asInt(); + notAdvanceCond = new IR::LAnd(notAdvanceCond, new IR::Equ(notAdvanceConst, advanceExpr)); + } + + return {advanceVal, advanceCond, notAdvanceVal, notAdvanceCond}; +} + +const IR::Expression *initializeRegisterParameters(const TestObject *registerState, + ExecutionState &nextState, + const ProgramInfo &programInfo, + cstring externInstanceName, + const IR::PathExpression *registerParamRef, + bool canConfigure, const IR::Expression *index) { + const IR::Expression *registerExpr = nullptr; + const auto *registerParamType = registerParamRef->type; + if (registerState != nullptr) { + if (index != nullptr) { + auto existingRegVal = registerState->checkedTo(); + registerExpr = existingRegVal->getValueAtIndex(index); + } else { + auto existingRegVal = registerState->checkedTo(); + registerExpr = existingRegVal->getInitialValue(); + } + if (const auto *registerValueList = registerExpr->to()) { + /// Set the register parameter based on the derived register value. + std::vector validFields; + const auto fields = nextState.getFlatFields(registerParamRef, &validFields); + for (size_t idx = 0; idx < fields.size(); ++idx) { + const auto &field = fields.at(idx); + const auto *fieldVal = registerValueList->components.at(idx); + nextState.set(field, fieldVal->expression); + } + // We also need to initialize the validity bits of the headers. These are true. + for (const auto &validField : validFields) { + nextState.set(validField, IR::BoolLiteral::get(true)); + } + } else { + /// Set the register parameter based on the derived register value. + nextState.set(registerParamRef, registerExpr); + } + } else { + if (const auto *structType = registerParamType->to()) { + const IR::StructExpression *registerValueList = nullptr; + IR::IndexedVector valueVector{}; + for (const auto *structField : structType->fields) { + const IR::NamedExpression *inputValue = nullptr; + if (canConfigure) { + inputValue = new IR::NamedExpression( + structField->name.name, + ToolsVariables::getSymbolicVariable( + structField->type, externInstanceName + "_" + structField->name.name)); + } else { + inputValue = new IR::NamedExpression( + structField->name.name, + programInfo.createTargetUninitialized(structField->type, true)); + } + valueVector.push_back(inputValue); + } + registerValueList = new IR::StructExpression(nullptr, valueVector); + /// Set the register parameter based on the derived register value. + std::vector validFields; + const auto fields = nextState.getFlatFields(registerParamRef, &validFields); + for (size_t idx = 0; idx < fields.size(); ++idx) { + const auto &field = fields.at(idx); + const auto *fieldVal = registerValueList->components.at(idx); + nextState.set(field, fieldVal->expression); + } + // We also need to initialize the validity bits of the headers. These are true. + for (const auto &validField : validFields) { + nextState.set(validField, IR::BoolLiteral::get(true)); + } + registerExpr = registerValueList; + } else { + if (canConfigure) { + registerExpr = + ToolsVariables::getSymbolicVariable(registerParamType, externInstanceName); + } else { + registerExpr = programInfo.createTargetUninitialized(registerParamType, true); + } + /// Set the register parameter based on the derived register value. + nextState.set(registerParamRef, registerExpr); + } + } + return registerExpr; +} + +/* ====================================================================================== + * (Direct)Meter.execute + * See also: https://wiki.ith.intel.com/display/BXDCOMPILER/Meters + * ====================================================================================== */ +const SharedTofinoExprStepper::ExternMethodImpls::MethodImpl + SharedTofinoExprStepper::METER_EXECUTE = [](const ExternInfo & /*externInfo*/, + SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // For now, we return 0, which corresponds to the GREEN enum value. + nextState.replaceTopBody(Continuation::Return(IR::Constant::get(IR::Type_Bits::get(8), 0))); + + stepper.result->emplace_back(nextState); + }; +/* ====================================================================================== + * DirectCount.count + * See also: + * https://wiki.ith.intel.com/pages/viewpage.action?pageId=1981604291#Externs-_sk26xix6q9fxCounters + * ====================================================================================== */ +const SharedTofinoExprStepper::ExternMethodImpls::MethodImpl + SharedTofinoExprStepper::COUNTER_COUNT = + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }; + +/* ====================================================================================== + * Checksum.update + * Calculate the checksum for a given list of fields. + * @param data : List of fields contributing to the checksum value. + * @param zeros_as_ones : encode all-zeros value as all-ones. + * ====================================================================================== */ +const SharedTofinoExprStepper::ExternMethodImpls::MethodImpl + SharedTofinoExprStepper::CHECKSUM_UPDATE = + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // For now, we return taint + // We actually should be calculating the checksum here. + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(IR::Type_Bits::get(16), true))); + + stepper.result->emplace_back(nextState); + }; +/* ====================================================================================== + * Mirror.emit + * Sends copy of the packet to a specified destination. + * See also: + * https://wiki.ith.intel.com/display/BXDCOMPILER/Externs#Externs-Mirror + * ====================================================================================== */ +// TODO: Currently implemented as no-op. We rely on the fact that when mirroring is not +// configured by the control plane the mirrored packets are discarded and hence not seen +// by the STF/PTF test. +const SharedTofinoExprStepper::ExternMethodImpls::MethodImpl + SharedTofinoExprStepper::MIRROR_EMIT = + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }; + +/* ====================================================================================== + * RegisterAction.execute + * See also: https://wiki.ith.intel.com/display/BXDCOMPILER/Stateful+ALU + * ====================================================================================== */ +const SharedTofinoExprStepper::ExternMethodImpls::MethodImpl + SharedTofinoExprStepper::REGISTER_ACTION_EXECUTE = [](const ExternInfo &externInfo, + SharedTofinoExprStepper &stepper) { + const auto &receiverPath = externInfo.externObjectRef; + const auto *specType = + externInfo.externObjectRef.type->checkedTo(); + BUG_CHECK(specType->arguments->size() == 3, + "Expected 3 type arguments for RegisterAction, received %1%", + specType->arguments->size()); + + const auto *index = externInfo.externArguments.at(0)->expression; + + const auto *declInstance = + stepper.state.findDecl(&receiverPath)->checkedTo(); + const auto *declInstanceArgs = declInstance->arguments; + BUG_CHECK(declInstanceArgs->size() == 1, + "RegisterAction types only have one single instance argument."); + CHECK_NULL(declInstanceArgs->at(0)->expression); + const auto &externInstancePath = + declInstanceArgs->at(0)->expression->checkedTo(); + const auto &externInstance = stepper.state.findDecl(externInstancePath); + + const auto *initializer = declInstance->initializer; + const auto *applyFunction = initializer->getDeclByName("apply")->checkedTo(); + CHECK_NULL(applyFunction); + + const auto *applyParams = applyFunction->getParameters(); + const auto *registerParam = applyParams->getParameter(0); + const auto *registerParamType = registerParam->type; + if (const auto *typeName = registerParamType->to()) { + registerParamType = stepper.state.resolveType(typeName); + } + // Check whether we can configure this register. + auto canConfigure = TestgenOptions::get().testBackend == "PTF"; + + auto &nextState = stepper.state.clone(); + /// Initialize the value of the apply call. + if (applyParams->size() > 1) { + const auto *param = applyParams->getParameter(1); + const auto &returnPath = + new IR::PathExpression(param->type, new IR::Path(param->getName())); + nextState.set(returnPath, IR::getDefaultValue(param->type)); + } + + const auto *registerState = + stepper.state.getTestObject("registervalues"_cs, externInstance->toString(), false); + const auto *registerParamRef = + new IR::PathExpression(registerParamType, new IR::Path(registerParam->getName())); + + // Initialize the parameters in the registerAction apply call and create a register + // object, if necessary. + const auto *registerExpr = initializeRegisterParameters( + registerState, nextState, stepper.programInfo, externInstance->toString(), + registerParamRef, canConfigure, index); + + if (canConfigure) { + CHECK_NULL(registerExpr); + const auto *registerValue = new TofinoRegisterValue( + externInstance->checkedTo(), registerExpr, index); + nextState.addTestObject("registervalues"_cs, externInstance->toString(), registerValue); + } + std::vector replacements; + + if (applyFunction->body != nullptr) { + replacements.emplace_back(applyFunction->body); + } + + // Write back the result of the register to the respective index. + // TODO: Currently, writing to a register has no effect in Tofino. + // Only the subsequent packet will be able to access the updated register value. + // const auto *writeBackStmt = + // new IR::MethodCallStatement(Utils::generateInternalMethodCall( + // "tofino_register_writeback", + // {new IR::StringLiteral(externInstance->toString()), registerParamRef, + // index})); + // replacements.emplace_back(writeBackStmt); + + if (applyParams->size() > 1) { + const auto *returnParam = applyParams->getParameter(1); + replacements.emplace_back(Continuation::Return( + new IR::PathExpression(returnParam->type, new IR::Path(returnParam->getName())))); + } else { + const auto *returnType = specType->arguments->at(2); + // Some registers have no return type. + if (!returnType->is()) { + replacements.emplace_back(Continuation::Return(IR::Constant::get(returnType, 0))); + } + } + + // Enter the function's namespace. + nextState.pushNamespace(applyFunction); + + nextState.replaceTopBody(&replacements); + stepper.result->emplace_back(nextState); + }; +/* ====================================================================================== + * DirectRegisterAction.execute + * Currently, this function is almost identical to RegisterAction.execute. The only + * difference is the amount of expected type arguments. + * ====================================================================================== */ +const SharedTofinoExprStepper::ExternMethodImpls::MethodImpl + SharedTofinoExprStepper::DIRECT_REGISTER_ACTION_EXECUTE = [](const ExternInfo &externInfo, + SharedTofinoExprStepper &stepper) { + const auto &receiverPath = externInfo.externObjectRef; + const auto *specType = + externInfo.externObjectRef.type->checkedTo(); + BUG_CHECK(specType->arguments->size() == 2, + "Expected 2 type arguments for DirectRegisterAction, received %1%", + specType->arguments->size()); + + auto &nextState = stepper.state.clone(); + const auto *declInstance = + nextState.findDecl(&receiverPath)->checkedTo(); + const auto *declInstanceArgs = declInstance->arguments; + BUG_CHECK(declInstanceArgs->size() == 1, + "DirectRegisterAction types only have one single instance argument."); + CHECK_NULL(declInstanceArgs->at(0)->expression); + const auto &externInstancePath = + declInstanceArgs->at(0)->expression->checkedTo(); + const auto &externInstance = stepper.state.findDecl(externInstancePath); + + const auto *progInfo = stepper.getProgramInfo().checkedTo(); + const auto *table = progInfo->getTableofDirectExtern(externInstance); + const TestObject *tableEntry = nullptr; + if (table != nullptr) { + tableEntry = + stepper.state.getTestObject("tableconfigs"_cs, table->controlPlaneName(), false); + } + // Check whether we can configure this register. + auto canConfigure = TestgenOptions::get().testBackend == "PTF" && tableEntry != nullptr; + + const auto *initializer = declInstance->initializer; + const auto *applyFunction = initializer->getDeclByName("apply")->checkedTo(); + CHECK_NULL(applyFunction); + + // Enter the function's namespace. + nextState.pushNamespace(applyFunction); + + const auto *applyParams = applyFunction->getParameters(); + const auto *registerParam = applyParams->getParameter(0); + const auto *registerParamType = registerParam->type; + if (const auto *typeName = registerParamType->to()) { + registerParamType = nextState.resolveType(typeName); + } + const auto *registerState = stepper.state.getTestObject("direct_registervalues"_cs, + externInstance->toString(), false); + const auto *registerParamRef = + new IR::PathExpression(registerParamType, new IR::Path(registerParam->getName())); + + // Initialize the parameters in the registerAction apply call and create a register + // object, if necessary. + const auto *registerExpr = initializeRegisterParameters( + registerState, nextState, stepper.programInfo, externInstance->toString(), + registerParamRef, canConfigure, nullptr); + + if (canConfigure) { + CHECK_NULL(registerExpr); + const auto *registerValue = new TofinoDirectRegisterValue( + externInstance->checkedTo(), registerExpr, table); + nextState.addTestObject("direct_registervalues"_cs, externInstance->toString(), + registerValue); + } + + std::vector replacements; + + if (applyFunction->body != nullptr) { + replacements.emplace_back(applyFunction->body); + } + + // Write back the result of the register to the respective index. + // TODO: Currently, writing to a register has no effect in Tofino. + // Only the subsequent packet will be able to access the updated register value. + // const auto *writeBackStmt = + // new IR::MethodCallStatement(Utils::generateInternalMethodCall( + // "tofino_register_writeback", + // {new IR::StringLiteral(externInstance->toString()), registerParamRef, + // index})); + // replacements.emplace_back(writeBackStmt); + + if (applyParams->size() > 1) { + const auto *returnParam = applyParams->getParameter(1); + replacements.emplace_back(Continuation::Return( + new IR::PathExpression(returnParam->type, new IR::Path(returnParam->getName())))); + } else { + const auto *returnType = specType->arguments->at(1); + // Some registers have no return type. + if (!returnType->is()) { + replacements.emplace_back(Continuation::Return(IR::Constant::get(returnType, 0))); + } + } + + // Enter the function's namespace. + nextState.pushNamespace(applyFunction); + + nextState.replaceTopBody(&replacements); + stepper.result->emplace_back(nextState); + }; + +// Provides implementations of Tofino externs. +const ExprStepper::ExternMethodImpls + SharedTofinoExprStepper::SHARED_TOFINO_EXTERN_METHOD_IMPLS({ + /* ====================================================================================== + * *.StartIngress internal method. + * The method models the ingress semantics of Tofino's P4 Switch instance. + * ====================================================================================== */ + {"*.StartIngress"_cs, + {}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + const auto *progInfo = stepper.getProgramInfo().to(); + const auto &pipes = progInfo->ingressCmds(); + for (size_t pipeIdx = 0; pipeIdx < pipes.size(); ++pipeIdx) { + const auto &pipeInfo = pipes.at(pipeIdx); + auto &pipeEntryState = stepper.state.clone(); + const auto &portVar = progInfo->getTargetInputPortVar(); + const auto &pipePortRangeCond = + progInfo->getPipePortRangeConstraint(portVar, pipeIdx); + pipeEntryState.replaceTopBody(&pipeInfo); + stepper.result->emplace_back(pipePortRangeCond, stepper.state, pipeEntryState); + } + }}, + /* ====================================================================================== + * *.StartEgress internal method. + * The method models the egress semantics of Tofino's P4 Switch instance. + * ====================================================================================== */ + {"*.StartEgress"_cs, + {}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + const auto *progInfo = stepper.getProgramInfo().to(); + const auto &pipes = progInfo->egressCmds(); + for (size_t pipeIdx = 0; pipeIdx < pipes.size(); ++pipeIdx) { + const auto &pipeInfo = pipes.at(pipeIdx); + auto &pipeEntryState = stepper.state.clone(); + const auto &portVar = progInfo->getTargetOutputPortVar(); + const auto &pipePortRangeCond = + progInfo->getPipePortRangeConstraint(portVar, pipeIdx); + pipeEntryState.replaceTopBody(&pipeInfo); + stepper.result->emplace_back(pipePortRangeCond, stepper.state, pipeEntryState); + } + }}, + /* ====================================================================================== + * Counter.count and its variants. + * We delegate to a separate implementation to save space. + * ====================================================================================== */ + // TODO: Add mechanism that test frameworks can use to check value correctness. + // Counter.count currently has no effect in the symbolic interpreter. + {"Counter.count"_cs, {"index"_cs}, COUNTER_COUNT}, + {"Counter.count"_cs, {"index"_cs, "adjust_byte_count"_cs}, COUNTER_COUNT}, + /* ====================================================================================== + * DirectCounter.count and its variants. + * We delegate to a separate implementation to save space. + * ====================================================================================== */ + // TODO: Add mechanism that test frameworks can use to check value correctness. + // DirectCounter.count currently has no effect in the symbolic interpreter. + {"DirectCounter.count"_cs, {}, COUNTER_COUNT}, + {"DirectCounter.count"_cs, {"adjust_byte_count"_cs}, COUNTER_COUNT}, + /* ====================================================================================== + * Meter.execute and its variants. + * We delegate to a separate implementation to save space. + * ====================================================================================== */ + // TODO: Add mechanism that test frameworks can use to check value correctness. + // Meter.execute currently has no effect in the symbolic interpreter. + {"Meter.execute"_cs, {"index"_cs}, METER_EXECUTE}, + {"Meter.execute"_cs, {"index"_cs, "adjust_byte_count"_cs}, METER_EXECUTE}, + {"Meter.execute"_cs, + {"index"_cs, "color"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // Return the color the meter was set to. + nextState.replaceTopBody( + Continuation::Return(externInfo.externArguments.at(1)->expression)); + + stepper.result->emplace_back(nextState); + }}, + {"Meter.execute"_cs, + {"index"_cs, "color"_cs, "adjust_byte_count"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // Return the color the meter was set to. + nextState.replaceTopBody( + Continuation::Return(externInfo.externArguments.at(1)->expression)); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * DirectMeter.execute and its variants. + * We delegate to a separate implementation to save space. + * ====================================================================================== */ + // TODO: Add mechanism that test frameworks can use to check value correctness. + // Meter.execute currently has no effect in the symbolic interpreter. + {"DirectMeter.execute"_cs, {}, METER_EXECUTE}, + {"DirectMeter.execute"_cs, + {"color"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // Return the color the meter was set to. + nextState.replaceTopBody( + Continuation::Return(externInfo.externArguments.at(0)->expression)); + + stepper.result->emplace_back(nextState); + }}, + {"DirectMeter.execute"_cs, {"adjust_byte_count"_cs}, METER_EXECUTE}, + {"DirectMeter.execute"_cs, + {"color"_cs, "adjust_byte_count"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // Return the color the meter was set to. + nextState.replaceTopBody( + Continuation::Return(externInfo.externArguments.at(0)->expression)); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * RegisterAction.execute + * See also: https://wiki.ith.intel.com/display/BXDCOMPILER/Stateful+ALU + * ====================================================================================== */ + {"RegisterAction.execute"_cs, {"index"_cs}, REGISTER_ACTION_EXECUTE}, + /* ====================================================================================== + * DirectRegisterAction.execute + * Currently, this function is almost identical to RegisterAction.execute. The only + * difference is the amount of expected type arguments. + * ====================================================================================== */ + {"DirectRegisterAction.execute"_cs, {}, DIRECT_REGISTER_ACTION_EXECUTE}, + /* ====================================================================================== + * RegisterParam.read + * Construct a read-only run-time configurable parameter that can only be + * used by RegisterAction. + * ====================================================================================== */ + {"RegisterParam.read"_cs, + {}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *specType = + externInfo.externObjectRef.type->checkedTo(); + BUG_CHECK(specType->arguments->size() == 1, + "Expected 1 type arguments for RegisterParam, received %1%", + specType->arguments->size()); + const auto *returnType = specType->arguments->at(0); + + const auto &receiverPath = externInfo.externObjectRef; + const auto *externInstance = + stepper.state.findDecl(&receiverPath)->checkedTo(); + auto externName = externInstance->toString(); + + const auto *testObject = + stepper.state.getTestObject("registerparams"_cs, externName, false); + const IR::Expression *returnVal = nullptr; + auto &nextState = stepper.state.clone(); + if (testObject != nullptr) { + returnVal = testObject->checkedTo()->getInitialValue(); + } else { + returnVal = ToolsVariables::getSymbolicVariable(returnType, externName); + auto *registerParam = new TofinoRegisterParam( + externInstance->checkedTo(), returnVal); + nextState.addTestObject("registerparams"_cs, externName, registerParam); + } + nextState.replaceTopBody(Continuation::Return(returnVal)); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * port_metadata_unpack + * See also + * https://wiki.ith.intel.com/pages/viewpage.action?pageId=2068055260#PacketPaths-Parser + * Like a call to extract, calling port_metadata_unpack advances the "pointer" in the + * packet being parsed. port_metadata_unpack always advances the pointer by the size of the + * port metadata, regardless of the definition of the struct type. See also, the + * implementation of extract in the extern_stepper. A lot of code is lifted from there. The + * major difference is that unpack returns a value. + * ====================================================================================== */ + {"*method.port_metadata_unpack"_cs, + {"pkt"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + // This argument is the structure being written by the extract. + const auto *typeArgs = externInfo.originalCall.typeArguments; + BUG_CHECK(typeArgs->size() == 1, + "Must have exactly 1 type argument for port metadata unpack."); + + const auto *extractedType = typeArgs->at(0); + if (const auto *typeName = extractedType->to()) { + extractedType = stepper.state.resolveType(typeName); + } + int unpackSize = 0; + const auto &arch = TestgenTarget::get().spec.archName; + if (arch == "tna") { + unpackSize = TofinoConstants::PORT_METADATA_SIZE; + } else if (arch == "t2na") { + unpackSize = JBayConstants::PORT_METADATA_SIZE; + } else { + BUG("Unexpected architecture"); + } + + // Calculate the necessary size of the packet to extract successfully. + auto minSize = std::max(0, stepper.state.getInputPacketCursor() + unpackSize); + const auto *cond = + new IR::Geq(IR::Type::Boolean::get(), ExecutionState::getInputPacketSizeVar(), + IR::Constant::get(&PacketVars::PACKET_SIZE_VAR_TYPE, minSize)); + // Record the condition we evaluate as string. + std::stringstream condStream; + condStream << "PortMetaDataUnpack Condition: "; + cond->dbprint(condStream); + { + auto &nextState = stepper.state.clone(); + const auto *unpackExpr = nextState.slicePacketBuffer(unpackSize); + + BUG_CHECK(extractedType->width_bits() <= unpackSize, + "Trying unpack %1% bits. Only %2% are available.", + extractedType->width_bits(), unpackSize); + + if (const auto *structType = extractedType->to()) { + auto *structExpr = new IR::ListExpression(structType, {}); + auto localOffset = 0; + // We only support flat assignments, so retrieve all fields from the input + // argument. + + for (const auto *field : structType->fields) { + const auto *fieldType = field->type; + if (const auto *typeName = fieldType->to()) { + fieldType = nextState.resolveType(typeName); + } + if (fieldType->is()) { + BUG("Unexpected struct field %1% of type %2%", field, fieldType); + } + auto fieldWidth = fieldType->width_bits(); + // If the width is zero, just add a zero bit vector. + if (fieldWidth == 0) { + structExpr->components.push_back( + IR::Constant::get(IR::Type_Bits::get(0), 0)); + continue; + } + // Assign a slice of the program packet to the field. + auto highIdx = unpackSize - localOffset - 1; + auto lowIdx = highIdx + 1 - fieldWidth; + IR::Expression *packetSlice = new IR::Slice(unpackExpr, highIdx, lowIdx); + packetSlice->type = fieldType; + if (const auto *bits = fieldType->to()) { + if (bits->isSigned) { + auto *intBits = bits->clone(); + intBits->isSigned = true; + packetSlice = new IR::Cast(intBits, packetSlice); + } + } else if (fieldType->is()) { + const auto *boolType = IR::Type_Boolean::get(); + packetSlice = new IR::Cast(boolType, packetSlice); + } + structExpr->components.push_back(packetSlice); + // Bookkeeping, advance the packet temporary packet cursor. + localOffset += fieldWidth; + } + unpackExpr = structExpr; + } else if (const auto *bitType = extractedType->to()) { + // Recast the slice to the correct type. + if (bitType->isSigned) { + unpackExpr = new IR::Cast(bitType, unpackExpr); + } + } else { + TESTGEN_UNIMPLEMENTED("Unsupported unpack type %1%, node %2%", extractedType, + extractedType->node_type_name()); + } + // Record the condition we are passing at this at this point. + nextState.add(*new TraceEvents::Generic(condStream.str())); + nextState.replaceTopBody(Continuation::Return(unpackExpr)); + stepper.result->emplace_back(cond, stepper.state, nextState); + } + auto fcsLeft = std::max(0, stepper.state.getProperty("fcsLeft"_cs)); + auto rejectSize = + std::max(0, minSize - stepper.state.getPacketBufferSize() - fcsLeft); + const auto *fcsCond = + new IR::Lss(IR::Type::Boolean::get(), ExecutionState::getInputPacketSizeVar(), + IR::Constant::get(&PacketVars::PACKET_SIZE_VAR_TYPE, rejectSize)); + // { + // auto*&fcsState = stepper.state.clone(); + // fcsState.setProperty("fcsLeft", fcsLeft - extractSize); + // // If we are dealing with a header, set the header valid. + // if (extractedType->is()) { + // setHeaderValidity(extractOutput, true, fcsState); + // } + + // // We only support flat assignments, so retrieve all fields from the input + // // argument. + // const auto flatFields = fcsState.getFlatFields(extractOutput, extractedType); + // for (const auto* fieldRef : flatFields) { + // auto fieldWidth = fcsState.get(fieldRef)->type->width_bits(); + // // If the width is zero, do not bother with extracting. + // if (fieldWidth == 0) { + // continue; + // } + + // // Assign a slice of the program packet to the field. + // const auto* pktVar = ToolsVariables::getTaintExpression(fieldRef->type); + + // fcsState.add(*new TraceEvents::Extract(fieldRef, pktVar)); + // fcsState.set(fieldRef, pktVar); + // } + // fcsState.add(*new TraceEvents::Extract(Utils::getHeaderValidity(extractOutput), + // Utils::getHeaderValidity(extractOutput))); + // // Record the condition we are passing at this at this point. + // fcsState.add(*new TraceEvents::Generic("Extract FCS")); + // fcsState.add(*new TraceEvents::Generic(condStream.str())); + // fcsState.popBody(); + // stepper.result->emplace_back(fcsCond, stepper.state, fcsState); + // } + if (!(stepper.state.hasProperty("parserErrReferenced"_cs) && + stepper.state.getProperty("parserErrReferenced"_cs))) { + // Handle the case where the packet is too short. + // TODO: We only generate packets that are too short if the parser error is not + // referenced within the control. This is because of behavior in some targets that + // pads packets that are too short. We currently do not model this. + // TODO: Remove this check. + auto &rejectState = stepper.state.clone(); + // Record the condition we are failing at this at this point. + rejectState.add( + *new TraceEvents::Generic("PortMetaDataUnpack: Packet too short"_cs)); + rejectState.add(*new TraceEvents::Generic(condStream.str())); + rejectState.replaceTopBody(Continuation::Exception::PacketTooShort); + stepper.result->emplace_back(fcsCond, stepper.state, rejectState); + } + }}, + /* ====================================================================================== + * Random.get + * See also: + * https://wiki.ith.intel.com/pages/viewpage.action?pageId=1981604291#ExternsOld-_9li9ntt34errRandom + * ====================================================================================== */ + {"Random.get"_cs, + {}, + + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *specType = + externInfo.externObjectRef.type->checkedTo(); + BUG_CHECK(specType->arguments->size() == 1, + "Expected 1 type arguments for Random.get, received %1%", + specType->arguments->size()); + const auto *returnType = specType->arguments->at(0); + auto &nextState = stepper.state.clone(); + BUG_CHECK(returnType->is(), + "Random.Get: return type is not a Type_Bits: %1%", returnType); + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(returnType, true))); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Checksum.add + * Add data to checksum. + * @param data : List of fields to be added to checksum calculation. The + * data must be byte aligned. + * ====================================================================================== */ + {"Checksum.add"_cs, + {"data"_cs}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Checksum.subtract + * Subtract data from existing checksum. + * @param data : List of fields to be subtracted from the checksum. The + * data must be byte aligned. + * ====================================================================================== */ + {"Checksum.subtract"_cs, + {"data"_cs}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Checksum.verify + * Verify whether the complemented sum is zero, i.e. the checksum is valid. + * @return : Boolean flag indicating whether the checksum is valid or not. + * ====================================================================================== */ + {"Checksum.verify"_cs, + {}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // For now, we return taint + // We actually should be calculating the checksum here. + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(IR::Type_Boolean::get(), true))); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Checksum.subtract_all_and_deposit + * Subtract all header fields after the current state and + * return the calculated checksum value. + * Marks the end position for residual checksum header. + * All header fields extracted after will be automatically subtracted. + * @param residual: The calculated checksum value for added fields. + * ====================================================================================== */ + {"Checksum.subtract_all_and_deposit"_cs, + {"residual"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *resultField = externInfo.externArguments.at(0)->expression; + const auto &fieldRef = ToolsVariables::convertReference(resultField); + + auto &nextState = stepper.state.clone(); + nextState.set(fieldRef, + stepper.programInfo.createTargetUninitialized(fieldRef.type, true)); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Checksum.get + * Get the calculated checksum value. + * @return : The calculated checksum value for added fields. + * ====================================================================================== */ + {"Checksum.get"_cs, + {}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + // For now, we return taint + // We actually should be calculating the checksum here. + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(IR::Type_Bits::get(16), true))); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Checksum.update + * We delegate to a separate implementation to save space. + * ====================================================================================== */ + {"Checksum.update"_cs, {"data"_cs}, CHECKSUM_UPDATE}, + {"Checksum.update"_cs, {"data"_cs, "zeros_as_ones"_cs}, CHECKSUM_UPDATE}, + /* ====================================================================================== + * Digest.pack + * Emit data into the stream. The p4 program can instantiate multiple + * Digest instances in the same deparser control block, and call the pack + * method once during a single execution of the control block + * See also: + * https://wiki.ith.intel.com/display/BXDCOMPILER/Externs#Externs-Digest + * ====================================================================================== */ + {"Digest.pack"_cs, + {"data"_cs}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Lpf.execute + * ====================================================================================== */ + // TODO: What is this? + {"Lpf.execute"_cs, + {"val"_cs, "index"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *lpfType = externInfo.externArguments.at(0)->expression->type; + + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(lpfType, true))); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * DirectLpf.execute + * ====================================================================================== */ + // TODO: What is this? + {"DirectLpf.execute"_cs, + {"val"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *lpfType = externInfo.externArguments.at(0)->expression->type; + + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(lpfType, true))); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Wred.execute + * ====================================================================================== */ + // TODO: What is this? + {"Wred.execute"_cs, + {"val"_cs, "index"_cs}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + const auto *wredType = IR::Type_Bits::get(8); + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(wredType, true))); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * DirectWred.execute + * ====================================================================================== */ + // TODO: What is this? + {"DirectWred.execute"_cs, + {"val"_cs}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + const auto *wredType = IR::Type_Bits::get(8); + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody(Continuation::Return( + stepper.programInfo.createTargetUninitialized(wredType, true))); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Hash.get + * See also: + * https://wiki.ith.intel.com/display/BXDCOMPILER/Externs#Externs-Hash + * ====================================================================================== */ + {"Hash.get"_cs, + {"data"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *specType = + externInfo.externObjectRef.type->checkedTo(); + BUG_CHECK(specType->arguments->size() == 1, + "Expected 1 type arguments for Hash.get, received %1%", + specType->arguments->size()); + + const IR::Type_Extern *externType = nullptr; + if (const auto *type = externInfo.externObjectRef.type->to()) { + externType = type; + } else if (const auto *specType = + externInfo.externObjectRef.type->to()) { + CHECK_NULL(specType->substituted); + externType = specType->substituted->checkedTo(); + } + + const auto *hashInput = externInfo.externArguments.at(0)->expression; + // If the input arguments is tainted, the entire extern is unreliable. + auto isTainted = Taint::hasTaint(hashInput); + + const auto *decl = stepper.state.findDecl(&externInfo.externObjectRef); + const auto *returnType = specType->arguments->at(0); + const auto *externInstance = decl->checkedTo(); + const auto *externDeclArgs = externInstance->arguments; + const auto *hashAlgoExpr = externInfo.externArguments.at(0)->expression; + IR::IndexedVector decls({externInstance}); + if (externDeclArgs->size() == 2) { + const auto *crcCustomPath = + externInfo.externArguments.at(1)->expression->checkedTo(); + const auto *crcDecl = stepper.state.findDecl(crcCustomPath); + decls.push_back(crcDecl->checkedTo()); + } + + // In our case, enums have been converted to concrete values. + auto hashAlgoIdx = hashAlgoExpr->checkedTo()->asInt(); + // If the hash algorithm is random (the value 0 in the enum) we have no control. We + // need to mark the output as tainted. + isTainted = isTainted || hashAlgoIdx == 1; + + auto externName = externType->name + "_" + externInfo.methodName; + auto &nextState = stepper.state.clone(); + const IR::Expression *hashReturn = nullptr; + if (returnType->is()) { + if (isTainted) { + hashReturn = stepper.programInfo.createTargetUninitialized(returnType, true); + } else { + const auto *concolicVar = new IR::ConcolicVariable( + returnType, externName, &externInfo.externArguments, + externInfo.originalCall.clone_id, 0, decls); + hashReturn = concolicVar; + } + } else { + TESTGEN_UNIMPLEMENTED("Hash return of type %2% not supported", returnType); + } + nextState.replaceTopBody(Continuation::Return{hashReturn}); + + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * Mirror.Emit + * ====================================================================================== */ + {"Mirror.emit"_cs, {"session_id"_cs}, MIRROR_EMIT}, + {"Mirror.emit"_cs, {"session_id"_cs, "hdr"_cs}, MIRROR_EMIT}, + /* ====================================================================================== + * sizeInBytes + * Return the width of the input in bytes. + * ====================================================================================== */ + {"*method.sizeInBytes"_cs, + {"h"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + BUG_CHECK(externInfo.externArguments.size() == 1, + "Expected 1 argument for sizeInBytes, received %1%", + externInfo.externArguments.size()); + const auto *argumentType = externInfo.externArguments.at(0)->expression->type; + auto typeWidth = argumentType->width_bits(); + auto typeWidthInBytes = typeWidth / 8; + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody( + Continuation::Return(IR::Constant::get(IR::Type_Bits::get(32), typeWidthInBytes))); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * sizeInBits + * Return the width of the input in bits. + * ====================================================================================== */ + {"*method.sizeInBits"_cs, + {"h"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + BUG_CHECK(externInfo.externArguments.size() == 1, + "Expected 1 argument for sizeInBits, received %1%", + externInfo.externArguments.size()); + const auto *argumentType = externInfo.externArguments.at(0)->expression->type; + auto typeWidth = argumentType->width_bits(); + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody( + Continuation::Return(IR::Constant::get(IR::Type_Bits::get(32), typeWidth))); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * invalidate + * The digest_type field has field validity (see Field Validity) and is initially invalid + * at the beginning of ingress processing. If at one point in your ingress P4 code you + * assign a value to digest_type, and then later want to undo the decision to generate a + * digest, call the invalidate extern function on the digest_type field. + * See also: + * https://wiki.ith.intel.com/display/BXDCOMPILER/Externs#Externs-Digest + * ====================================================================================== */ + {"*method.invalidate"_cs, + {"field"_cs}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * max + * Return the maximum of the two values. If either of the values is not a constant, a + * constraint will be added that mandates that either one of the values is + * greater-or-equals than the other. + * ====================================================================================== */ + {"*method.max"_cs, + {"t1"_cs, "t2"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + BUG_CHECK(externInfo.externArguments.size() == 2, + "Expected 2 arguments for max, received %1%", + externInfo.externArguments.size()); + + bool argsAreTainted = false; + // If any of the input arguments is tainted, the entire extern is unreliable. + for (const auto *arg : externInfo.externArguments) { + argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); + } + + const auto *t1 = externInfo.externArguments.at(0)->expression; + const auto *t2 = externInfo.externArguments.at(1)->expression; + + // Return taint when either of the arguments is tainted, + if (argsAreTainted) { + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody( + Continuation::Return(ToolsVariables::getTaintExpression(t1->type))); + stepper.result->emplace_back(nextState); + return; + } + // If both values are constants, just calculate the result. + if (const auto *t1Const = t1->to()) { + auto t1Val = t1Const->value; + if (const auto *t2Const = t2->to()) { + auto t2Val = t2Const->value; + auto &nextState = stepper.state.clone(); + auto maxVal = std::max(t1Val, t2Val); + nextState.replaceTopBody( + Continuation::Return(IR::Constant::get(t1->type, maxVal))); + stepper.result->emplace_back(nextState); + return; + } + } + // Alternatively, there are two possibilities. Either t1 or t2 is larger. Add two + // possible states and the appropriate constraints. + { + auto &t1State = stepper.state.clone(); + t1State.replaceTopBody(Continuation::Return(t1)); + stepper.result->emplace_back(new IR::Geq(t1, t2), stepper.state, t1State); + } + { + auto &t2State = stepper.state.clone(); + t2State.replaceTopBody(Continuation::Return(t2)); + stepper.result->emplace_back(new IR::Grt(t2, t1), stepper.state, t2State); + } + }}, + /* ====================================================================================== + * min + * Return the minimum of the two values. If either of the values is not a constant, a + * constraint will be added that mandates that either one of the values is + * lesser-or-equals than the other. + * ====================================================================================== */ + {"*method.min"_cs, + {"t1"_cs, "t2"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + BUG_CHECK(externInfo.externArguments.size() == 2, + "Expected 2 arguments for min, received %1%", + externInfo.externArguments.size()); + + bool argsAreTainted = false; + for (const auto *arg : externInfo.externArguments) { + argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); + } + + const auto *t1 = externInfo.externArguments.at(0)->expression; + const auto *t2 = externInfo.externArguments.at(1)->expression; + + // Return taint when either of the arguments is tainted, + if (argsAreTainted) { + auto &nextState = stepper.state.clone(); + nextState.replaceTopBody( + Continuation::Return(ToolsVariables::getTaintExpression(t1->type))); + stepper.result->emplace_back(nextState); + return; + } + // If both values are constants, just calculate the result. + if (const auto *t1Const = t1->to()) { + auto t1Val = t1Const->value; + if (const auto *t2Const = t2->to()) { + auto t2Val = t2Const->value; + auto &nextState = stepper.state.clone(); + auto minVal = std::min(t1Val, t2Val); + nextState.replaceTopBody( + Continuation::Return(IR::Constant::get(t1->type, minVal))); + stepper.result->emplace_back(nextState); + return; + } + } + // Alternatively, there are two possibilities. Either t1 or t2 is larger. Add two + // possible states and the appropriate constraints. + { + auto &t1State = stepper.state.clone(); + t1State.replaceTopBody(Continuation::Return(t1)); + stepper.result->emplace_back(new IR::Leq(t1, t2), stepper.state, t1State); + } + { + auto &t2State = stepper.state.clone(); + t2State.replaceTopBody(Continuation::Return(t2)); + stepper.result->emplace_back(new IR::Lss(t2, t1), stepper.state, t2State); + } + }}, + /* ====================================================================================== + * funnel_shift_right + * Performs a right-shift (@param shift_amount) on the combination of the two arguments + * (@param src1 and @param src2) into a single, double-the-length, value and writes it into + * @param dst. + * ====================================================================================== */ + {"*method.funnel_shift_right"_cs, + {"dst"_cs, "src1"_cs, "src2"_cs, "shift_amount"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + BUG_CHECK(externInfo.externArguments.size() == 4, + "Expected 4 arguments for funnel_shift_right, received %1%", + externInfo.externArguments.size()); + const auto *dst = externInfo.externArguments.at(0)->expression; + if (!(dst->is() || dst->is())) { + TESTGEN_UNIMPLEMENTED( + "funnel_shift_right target variable %1% of type %2% not supported", dst, + dst->type); + } + + const auto *src1 = externInfo.externArguments.at(1)->expression; + const auto *src2 = externInfo.externArguments.at(2)->expression; + const auto *shiftAmount = externInfo.externArguments.at(3)->expression; + auto concatWidth = src1->type->width_bits() + src2->type->width_bits(); + const auto *concatType = IR::Type_Bits::get(concatWidth); + auto &nextState = stepper.state.clone(); + auto *shift = + new IR::Shr(concatType, new IR::Concat(concatType, src1, src2), shiftAmount); + const auto *assign = new IR::AssignmentStatement(dst, new IR::Cast(dst->type, shift)); + nextState.replaceTopBody(assign); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * check_tofino_drop + * This internal extern checks whether the packet should be dropped. + * If yes, the packet is dropped and exit is called. + * This is done by pushing an exit continuation and setting the drop property to "true". + * ====================================================================================== + */ + {"*.check_tofino_drop"_cs, + {}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + const IR::Expression *egressDropVar = nullptr; + const IR::Expression *ingressDropVar = nullptr; + const auto &arch = TestgenTarget::get().spec.archName; + if (arch == "tna") { + ingressDropVar = stepper.state.get(&TofinoConstants::INGRESS_DROP_VAR); + egressDropVar = stepper.state.get(&TofinoConstants::EGRESS_DROP_VAR); + } else if (arch == "t2na") { + ingressDropVar = stepper.state.get(&JBayConstants::INGRESS_DROP_VAR); + egressDropVar = stepper.state.get(&JBayConstants::EGRESS_DROP_VAR); + } else { + BUG("Unexpected architecture"); + } + auto &dropState = stepper.state.clone(); + const auto *exitCallStmt = new IR::MethodCallStatement( + Utils::generateInternalMethodCall("tofino_drop"_cs, {})); + dropState.replaceTopBody(exitCallStmt); + + const auto &outputPortVar = stepper.programInfo.getTargetOutputPortVar(); + const auto *outputPort = dropState.get(outputPortVar); + const auto *outputPortType = outputPort->type; + + // The output port is tainted and has not been set. + // This means the packet is dropped. + if (outputPort->is()) { + dropState.set(outputPortVar, stepper.programInfo.createTargetUninitialized( + outputPortType, false)); + stepper.result->emplace_back(dropState); + return; + } + // If either of the drop members is tainted, we also mark the port tainted. + // We discard and drop tests with a tainted output port. + if (Taint::hasTaint(egressDropVar) || Taint::hasTaint(ingressDropVar) || + Taint::hasTaint(outputPort)) { + dropState.set(outputPortVar, + stepper.programInfo.createTargetUninitialized(outputPortType, true)); + stepper.result->emplace_back(dropState); + return; + } + // If neither the output port nor the port variables are tainted, + // check manually whether we need to drop. + const auto *egressCheck = + new IR::Equ(IR::Constant::get(egressDropVar->type, 1), egressDropVar); + const auto *ingressCheck = + new IR::Equ(IR::Constant::get(ingressDropVar->type, 1), ingressDropVar); + const auto *dropConstant = + IR::Constant::get(outputPortType, SharedTofinoConstants::DROP_CONSTANT); + const auto *dropCond = new IR::LOr(new IR::LOr(egressCheck, ingressCheck), + new IR::Equ(dropConstant, outputPort)); + // For Tofino PTF tests there are additional drop conditions, which we try to model. + if (TestgenOptions::get().testBackend == "PTF" && + stepper.state.getProperty("gress"_cs) == gress_t::INGRESS) { + const auto *pktSizeVar = ExecutionState::getInputPacketSizeVar(); + const auto *bitType = pktSizeVar->type; + auto *payloadSize = + new IR::Sub(bitType, pktSizeVar, + IR::Constant::get(bitType, stepper.state.getInputPacketSize())); + // PTF drops packets in ingress below a certain size. Assume it is 512 bits (64 + // bytes). + const auto *pktSize = new IR::Add( + bitType, IR::Constant::get(bitType, stepper.state.getPacketBufferSize()), + payloadSize); + const auto *lossCond = new IR::Lss( + pktSize, + IR::Constant::get(bitType, SharedTofinoConstants::PTF_OUTPUT_PKT_MIN_SIZE)); + dropState.add(*new TraceEvents::Expression( + pktSize, cstring("Drop packet smaller than ") + + std::to_string(SharedTofinoConstants::PTF_OUTPUT_PKT_MIN_SIZE) + + " for PTF.")); + dropCond = new IR::LOr(dropCond, lossCond); + } + stepper.result->emplace_back(dropCond, stepper.state, dropState); + + // The state if the drop condition is not true. + auto &passState = stepper.state.clone(); + passState.popBody(); + stepper.result->emplace_back(new IR::LNot(dropCond), stepper.state, passState); + }}, + /* ====================================================================================== + * tofino_drop + * This internal extern always drops a packet. + * This is done by pushing an exit continuation and setting the drop property to "true". + * ====================================================================================== + */ + {"*.tofino_drop"_cs, + {}, + [](const ExternInfo & /*externInfo*/, SharedTofinoExprStepper &stepper) { + auto &nextState = stepper.state.clone(); + nextState.add(*new TraceEvents::Generic("Packet marked dropped."_cs)); + nextState.setProperty("drop"_cs, true); + nextState.replaceTopBody(Continuation::Exception::Drop); + stepper.result->emplace_back(nextState); + }}, + /* ====================================================================================== + * tofino_register_writeback + * A helper function, which commits @param param as the value of the register at @param + * index. @param externInstance is used to look up the correct register test object. + * This helper function is necessary because RegisterActions typically execute a series + * of statements which may transform the register value. + * ====================================================================================== */ + {"*.tofino_register_writeback"_cs, + {"externInstance"_cs, "param"_cs, "index"_cs}, + [](const ExternInfo &externInfo, SharedTofinoExprStepper &stepper) { + const auto *externInstance = + externInfo.externArguments.at(0)->expression->checkedTo(); + const auto externParamRef = + ToolsVariables::convertReference(externInfo.externArguments.at(1)->expression); + const auto *externParamType = stepper.state.resolveType(externParamRef->type); + const auto *externIndex = externInfo.externArguments.at(2)->expression; + const auto *testObject = + stepper.state.getTestObject("registervalues"_cs, externInstance->value, false); + CHECK_NULL(testObject); + + const IR::Expression *updatedRegisterValue = nullptr; + if (const auto *structType = externParamType->to()) { + IR::IndexedVector valueVector{}; + for (const auto *field : structType->fields) { + const auto *fieldType = stepper.state.resolveType(field->type); + const auto *fieldRef = new IR::Member(fieldType, externParamRef, field->name); + valueVector.push_back( + new IR::NamedExpression(field->name.name, stepper.state.get(fieldRef))); + } + updatedRegisterValue = new IR::StructExpression(nullptr, valueVector); + } else { + updatedRegisterValue = stepper.state.get(externParamRef); + } + auto &nextState = stepper.state.clone(); + auto *registerValue = + new TofinoRegisterValue(*testObject->checkedTo()); + registerValue->writeToIndex(externIndex, updatedRegisterValue); + nextState.addTestObject("registervalues"_cs, externInstance->value, registerValue); + nextState.popBody(); + stepper.result->emplace_back(nextState); + }}, + }); + +void SharedTofinoExprStepper::evalExternMethodCall(const ExternInfo &externInfo) { + auto method = SHARED_TOFINO_EXTERN_METHOD_IMPLS.find( + externInfo.externObjectRef, externInfo.methodName, externInfo.externArguments); + if (method.has_value()) { + return method.value()(externInfo, *this); + } + // Lastly, check whether we are calling an internal extern method. + return ExprStepper::evalExternMethodCall(externInfo); +} + +bool SharedTofinoExprStepper::preorder(const IR::P4Table *table) { + // Delegate to the tableStepper. + TofinoTableStepper tableStepper(this, table); + + return tableStepper.eval(); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h b/backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h new file mode 100644 index 00000000000..0854e456c18 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_SHARED_EXPR_STEPPER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_SHARED_EXPR_STEPPER_H_ + +#include "ir/ir.h" +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/core/small_step/expr_stepper.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/* This unit contains expression operations that are shared between Tofino 1 and 2. The purpose is + * to avoid excessive code duplication. */ +class SharedTofinoExprStepper : public ExprStepper { + protected: + PacketCursorAdvanceInfo calculateSuccessfulParserAdvance(const ExecutionState &state, + int advanceSize) const override; + + PacketCursorAdvanceInfo calculateAdvanceExpression( + const ExecutionState &state, const IR::Expression *advanceExpr, + const IR::Expression *restrictions) const override; + + /* ====================================================================================== + * (Direct)Meter.execute + * See also: https://wiki.ith.intel.com/display/BXDCOMPILER/Meters + * ====================================================================================== */ + static const ExternMethodImpls::MethodImpl METER_EXECUTE; + /* ====================================================================================== + * DirectCount.count + * See also: + * https://wiki.ith.intel.com/pages/viewpage.action?pageId=1981604291#Externs-_sk26xix6q9fxCounters + * ====================================================================================== */ + static const ExternMethodImpls::MethodImpl COUNTER_COUNT; + /* ====================================================================================== + * Checksum.update + * Calculate the checksum for a given list of fields. + * @param data : List of fields contributing to the checksum value. + * @param zeros_as_ones : encode all-zeros value as all-ones. + * ====================================================================================== */ + static const ExternMethodImpls::MethodImpl CHECKSUM_UPDATE; + /* ====================================================================================== + * Mirror.emit + * Sends copy of the packet to a specified destination. + * See also: + * https://wiki.ith.intel.com/display/BXDCOMPILER/Externs#Externs-Mirror + * ====================================================================================== */ + // TODO: Currently implemented as no-op. We rely on the fact that when mirroring is not + // configured by the control plane the mirrored packets are discarded and hence not seen + // by the STF/PTF test. + static const ExternMethodImpls::MethodImpl MIRROR_EMIT; + + /* ====================================================================================== + * RegisterAction.execute + * See also: https://wiki.ith.intel.com/display/BXDCOMPILER/Stateful+ALU + * ====================================================================================== */ + static const ExternMethodImpls::MethodImpl REGISTER_ACTION_EXECUTE; + /* ====================================================================================== + * DirectRegisterAction.execute + * Currently, this function is almost identical to RegisterAction.execute. The only + * difference is the amount of expected type arguments. + * ====================================================================================== */ + static const ExternMethodImpls::MethodImpl + DIRECT_REGISTER_ACTION_EXECUTE; + + // Provides implementations of Tofino externs. + static const ExternMethodImpls SHARED_TOFINO_EXTERN_METHOD_IMPLS; + + public: + SharedTofinoExprStepper(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) + : ExprStepper(state, solver, programInfo) {} + + void evalExternMethodCall(const ExternInfo &externInfo) override; + + bool preorder(const IR::P4Table *table) override; +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_SHARED_EXPR_STEPPER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/shared_program_info.cpp b/backends/p4tools/modules/testgen/targets/tofino/shared_program_info.cpp new file mode 100644 index 00000000000..5dd0c681851 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/shared_program_info.cpp @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h" + +#include "backends/p4tools/common/lib/util.h" +#include "ir/irutils.h" + +#include "backends/p4tools/modules/testgen/lib/packet_vars.h" +#include "backends/p4tools/modules/testgen/options.h" +#include "backends/p4tools/modules/testgen/targets/tofino/compiler_result.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const IR::Type_Bits TofinoSharedProgramInfo::parserErrBits = IR::Type_Bits(16, false); + +TofinoSharedProgramInfo::TofinoSharedProgramInfo(const TofinoCompilerResult &compilerResult, + std::vector inputPipes, + std::map declIdToGress, + std::map declIdToPipe) + : ProgramInfo(compilerResult), + pipes(std::move(inputPipes)), + declIdToGress(std::move(declIdToGress)), + declIdToPipe(std::move(declIdToPipe)) { + pipelineSequence.push_back( + new IR::MethodCallStatement(Utils::generateInternalMethodCall("StartIngress", {}))); + pipelineSequence.push_back( + new IR::MethodCallStatement(Utils::generateInternalMethodCall("StartEgress", {}))); + + // The size of an input packet must at least be 64 bytes for the Tofino PTF model. + targetConstraints = + new IR::Geq(IR::Type::Boolean::get(), ExecutionState::getInputPacketSizeVar(), + IR::Constant::get(&PacketVars::PACKET_SIZE_VAR_TYPE, + SharedTofinoConstants::PTF_INPUT_PKT_MIN_SIZE)); + // Map the block names to their canonical counter parts. + for (const auto &pipeInfo : pipes) { + for (const auto &declTuple : pipeInfo.pipes) { + blockMap.emplace(declTuple.second->getName(), declTuple.first); + } + } +} + +gress_t TofinoSharedProgramInfo::getGress(const IR::Type_Declaration *decl) const { + return declIdToGress.at(decl->declid); +} + +size_t TofinoSharedProgramInfo::getPipeIdx(const IR::Type_Declaration *decl) const { + return declIdToPipe.at(decl->declid); +} + +bool TofinoSharedProgramInfo::isMultiPipe() const { return pipes.size() > 1; } + +const IR::P4Table *TofinoSharedProgramInfo::getTableofDirectExtern( + const IR::IDeclaration *directExternDecl) const { + const auto &directExternMap = getCompilerResult().getDirectExternMap(); + auto it = directExternMap.find(directExternDecl->controlPlaneName()); + if (it == directExternMap.end()) { + return nullptr; + } + return it->second; +} + +const std::vector *TofinoSharedProgramInfo::getPipes() const { + return &pipes; +} + +const IR::Type_Bits *TofinoSharedProgramInfo::getParserErrorType() const { return &parserErrBits; } + +const IR::Expression *TofinoSharedProgramInfo::getBackendConstraint( + const IR::StateVariable &portVar) { + if (TestgenOptions::get().testBackend == "STF") { + // The STF framework doesn't work well with multi-pipe. Some tests crash the + // model, and control plane names differ from PTF - STF doesn't load bfrt.json, + // only context.json for every pipe, which isn't enough to disabiguate conflicting + // control plane names. + return new IR::Lss(portVar, IR::Constant::get(portVar->type, 64)); + } + if (TestgenOptions::get().testBackend == "PTF") { + // TODO: by default, tofino model sets up 16 ports. + return new IR::Lss(portVar, IR::Constant::get(portVar->type, 16)); + } + return nullptr; +} + +IR::StateVariable TofinoSharedProgramInfo::getParserParamVar(const IR::P4Parser *parser, + const IR::Type *type, + size_t paramIndex, + cstring paramLabel) const { + auto gress = getGress(parser); + + // We need to explicitly map the parser error, if the parameter is set. + // If the optional parser metadata parameter is not present, write directly to the + // global parser metadata state. Otherwise, we retrieve the parameter name. + auto parserApplyParams = parser->getApplyParameters()->parameters; + cstring structLabel = nullptr; + if (parserApplyParams.size() > paramIndex) { + const auto *paramString = parserApplyParams.at(paramIndex); + structLabel = paramString->name; + } else { + switch (gress) { + case INGRESS: { + structLabel = getArchSpec().getParamName("IngressParserT"_cs, paramIndex); + break; + } + + case EGRESS: { + structLabel = getArchSpec().getParamName("EgressParserT"_cs, paramIndex); + break; + } + + case GHOST: + default: + BUG("Unimplemented thread: %1%", gress); + } + } + return {new IR::Member(type, new IR::PathExpression(structLabel), paramLabel)}; +} + +const TofinoCompilerResult &TofinoSharedProgramInfo::getCompilerResult() const { + return ProgramInfo::getCompilerResult().as(); +} +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h b/backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h new file mode 100644 index 00000000000..fa15aa3157b --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef TESTGEN_TARGETS_TOFINO_SHARED_PROGRAM_INFO_H_ +#define TESTGEN_TARGETS_TOFINO_SHARED_PROGRAM_INFO_H_ + +#include + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/targets/tofino/compiler_result.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class TofinoSharedProgramInfo : public ProgramInfo { + public: + using PipeInfo = struct { + cstring pipeName; + ordered_map pipes; + }; + + protected: + /// The parser, MAU, and deparser, for each gress and pipe in the program. + const std::vector pipes; + + /// The declid of each top-level parser type, mapped to its corresponding gress. + const std::map declIdToGress; + + /// The declid of each top-level parser type, mapped to its corresponding Tofino pipe. + const std::map declIdToPipe; + + /// The bit width of standard_metadata.parser_error. + static const IR::Type_Bits parserErrBits; + + public: + explicit TofinoSharedProgramInfo(const TofinoCompilerResult &compilerResult, + std::vector inputPipes, + std::map declIdToGress, + std::map declIdToPipe); + + /// @returns the gress associated with the given top level control/parser declaration. + gress_t getGress(const IR::Type_Declaration *decl) const; + + /// @returns the pipe associated with the given top level control/parser declaration. + size_t getPipeIdx(const IR::Type_Declaration *decl) const; + + // @returns whether the current program is a multi-pipe program. + [[nodiscard]] bool isMultiPipe() const; + + /// @returns the table associated with the direct extern + const IR::P4Table *getTableofDirectExtern(const IR::IDeclaration *directExternDecl) const; + + /// @returns the top-level parser/control sequence. + [[nodiscard]] const std::vector *getPipes() const; + + /// @returns set of commands which constitute ingress pipeline. + [[nodiscard]] virtual std::vector> ingressCmds() const = 0; + + /// @returns set of commands which constitute egress pipeline. + [[nodiscard]] virtual std::vector> egressCmds() const = 0; + + /// @returns expression which limits which ports can be used, expressed as a constraint + /// over @p portVar + static const IR::Expression *getBackendConstraint(const IR::StateVariable &portVar); + + [[nodiscard]] virtual const IR::Expression *getValidPortConstraint( + const IR::StateVariable &portVar) const = 0; + + [[nodiscard]] virtual std::optional getPipePortRangeConstraint( + const IR::StateVariable &portVar, size_t pipeIdx) const = 0; + + IR::StateVariable getParserParamVar(const IR::P4Parser *parser, const IR::Type *type, + size_t paramIndex, cstring paramLabel) const; + + [[nodiscard]] const IR::Type_Bits *getParserErrorType() const override; + + [[nodiscard]] const TofinoCompilerResult &getCompilerResult() const override; + + DECLARE_TYPEINFO(TofinoSharedProgramInfo, TofinoSharedProgramInfo); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* TESTGEN_TARGETS_TOFINO_SHARED_PROGRAM_INFO_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.cpp b/backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.cpp new file mode 100644 index 00000000000..4f5783f1898 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.cpp @@ -0,0 +1,473 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.h" + +#include +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/trace_event_types.h" +#include "backends/p4tools/common/lib/variables.h" +#include "ir/ir-generated.h" +#include "ir/irutils.h" +#include "lib/error.h" +#include "lib/null.h" +#include "shared_program_info.h" + +#include "backends/p4tools/modules/testgen/lib/collect_coverable_nodes.h" +#include "backends/p4tools/modules/testgen/lib/continuation.h" +#include "backends/p4tools/modules/testgen/options.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const IR::Expression *TofinoTableStepper::computeTargetMatchType( + const TableUtils::KeyProperties &keyProperties, TableMatchMap *matches, + const IR::Expression *hitCondition) { + const IR::Expression *keyExpr = keyProperties.key->expression; + + // Ranges are not yet implemented for Tofino STF tests. + if (keyProperties.matchType == SharedTofinoConstants::MATCH_KIND_RANGE && + TestgenOptions::get().testBackend == "PTF") { + cstring minName = properties.tableName + "_range_min_" + keyProperties.name; + cstring maxName = properties.tableName + "_range_max_" + keyProperties.name; + // We can recover from taint by matching on the entire possible range. + const IR::Expression *minKey = nullptr; + const IR::Expression *maxKey = nullptr; + if (keyProperties.isTainted) { + minKey = IR::Constant::get(keyExpr->type, 0); + maxKey = IR::Constant::get(keyExpr->type, IR::getMaxBvVal(keyExpr->type)); + keyExpr = minKey; + } else { + minKey = ToolsVariables::getSymbolicVariable(keyExpr->type, minName); + maxKey = ToolsVariables::getSymbolicVariable(keyExpr->type, maxName); + } + matches->emplace(keyProperties.name, new Range(keyProperties.key, minKey, maxKey)); + return new IR::LAnd(hitCondition, new IR::LAnd(new IR::LAnd(new IR::Lss(minKey, maxKey), + new IR::Leq(minKey, keyExpr)), + new IR::Leq(keyExpr, maxKey))); + } + // Action selector entries are not part of the match. + if (keyProperties.matchType == SharedTofinoConstants::MATCH_KIND_SELECTOR) { + tofinoProperties.actionSelectorKeys.emplace_back(keyExpr); + return hitCondition; + } + // Atcam partition indices are not yet implemented but do not influence the table semantics. + if (keyProperties.matchType == SharedTofinoConstants::MATCH_KIND_ATCAM) { + ::warning("Key match type atcam partition index not fully implemented."); + return hitCondition; + } + + // If the custom match type does not match, delete to the core match types. + return TableStepper::computeTargetMatchType(keyProperties, matches, hitCondition); +} + +void TofinoTableStepper::evalTableActionProfile( + const std::vector &tableActionList) { + const auto *keys = table->getKey(); + // If we have no keys, there is nothing to match. + if (keys == nullptr) { + return; + } + const auto *state = getExecutionState(); + + // First, we compute the hit condition to trigger this particular action call. + TableMatchMap matches; + const auto *hitCondition = computeHit(&matches); + + // Now we iterate over all table actions and create a path per table action. + for (const auto *action : tableActionList) { + // Grab the path from the method call. + const auto *tableAction = action->expression->checkedTo(); + // Try to find the action declaration corresponding to the path reference in the table. + const auto *actionType = state->getP4Action(tableAction); + + auto &nextState = state->clone(); + // We get the control plane name of the action we are calling. + cstring actionName = actionType->controlPlaneName(); + // Copy the previous action profile. + auto *actionProfile = new TofinoActionProfile(*tofinoProperties.actionProfile); + // The entry we are inserting using an index instead of the action name. + cstring actionIndex = std::to_string(actionProfile->getActionMapSize()); + // Synthesize arguments for the call based on the action parameters. + const auto ¶meters = actionType->parameters; + auto *arguments = new IR::Vector(); + std::vector ctrlPlaneArgs; + for (size_t argIdx = 0; argIdx < parameters->size(); ++argIdx) { + const auto *parameter = parameters->getParameter(argIdx); + // Synthesize a symbolic variable here that corresponds to a control plane argument. + // We get the unique name of the table coupled with the unique name of the action. + // Getting the unique name is needed to avoid generating duplicate arguments. + cstring keyName = + properties.tableName + "_param_" + actionName + std::to_string(argIdx); + const auto &actionArg = ToolsVariables::getSymbolicVariable(parameter->type, keyName); + arguments->push_back(new IR::Argument(actionArg)); + // We also track the argument we synthesize for the control plane. + // Note how we use the control plane name for the parameter here. + ctrlPlaneArgs.emplace_back(parameter, actionArg); + } + // Add the chosen action to the profile (it will create a new index.) + // TODO: Should we check if we exceed the maximum number of possible profile entries? + actionProfile->addToActionMap(actionName, ctrlPlaneArgs); + // Update the action profile in the execution state. + nextState.addTestObject("action_profile"_cs, actionProfile->getObjectName(), actionProfile); + + // We add the arguments to our action call, effectively creating a const entry call. + auto *synthesizedAction = tableAction->clone(); + synthesizedAction->arguments = arguments; + + // Finally, add all the new rules to the execution state. + ActionCall ctrlPlaneActionCall(actionIndex, actionType, {}); + auto tableRule = + TableRule(matches, TestSpec::LOW_PRIORITY, ctrlPlaneActionCall, TestSpec::TTL); + auto *tableConfig = new TableConfig(table, {tableRule}); + + // Add the action profile to the table. This signifies a slightly different implementation. + tableConfig->addTableProperty("action_profile"_cs, actionProfile); + nextState.addTestObject("tableconfigs"_cs, table->controlPlaneName(), tableConfig); + + // Update all the tracking variables for tables. + std::vector replacements; + replacements.emplace_back( + new IR::MethodCallStatement(Util::SourceInfo(), synthesizedAction)); + // Some path selection strategies depend on looking ahead and collecting potential + // statements. If that is the case, apply the CoverableNodesScanner visitor. + P4::Coverage::CoverageSet coveredNodes; + if (requiresLookahead(TestgenOptions::get().pathSelectionPolicy)) { + auto collector = CoverableNodesScanner(*state); + collector.updateNodeCoverage(actionType, coveredNodes); + } + + nextState.set(getTableHitVar(table), IR::BoolLiteral::get(true)); + nextState.set(getTableActionVar(table), getTableActionString(tableAction)); + std::stringstream tableStream; + tableStream << "Table Branch: " << properties.tableName; + tableStream << " Chosen action: " << actionName; + nextState.add(*new TraceEvents::Generic(tableStream.str())); + nextState.replaceTopBody(&replacements); + getResult()->emplace_back(hitCondition, *state, nextState, coveredNodes); + } +} + +void TofinoTableStepper::evalTableActionSelector( + const std::vector &tableActionList) { + const auto *keys = table->getKey(); + // If we have no keys, there is nothing to match. + if (keys == nullptr) { + return; + } + const auto *state = getExecutionState(); + + // First, we compute the hit condition to trigger this particular action call. + TableMatchMap matches; + const auto *hitCondition = computeHit(&matches); + + // Now we iterate over all table actions and create a path per table action. + for (const auto *action : tableActionList) { + // Grab the path from the method call. + const auto *tableAction = action->expression->checkedTo(); + // Try to find the action declaration corresponding to the path reference in the table. + const auto *actionType = state->getP4Action(tableAction); + + auto &nextState = state->clone(); + // We get the control plane name of the action we are calling. + cstring actionName = actionType->controlPlaneName(); + + // Copy the previous action profile. + auto *actionProfile = new TofinoActionProfile( + tofinoProperties.actionSelector->getActionProfile()->getProfileDecl()); + // The entry we are inserting using an index instead of the action name. + cstring actionIndex = std::to_string(actionProfile->getActionMapSize()); + // Synthesize arguments for the call based on the action parameters. + const auto ¶meters = actionType->parameters; + auto *arguments = new IR::Vector(); + std::vector ctrlPlaneArgs; + for (size_t argIdx = 0; argIdx < parameters->size(); ++argIdx) { + const auto *parameter = parameters->getParameter(argIdx); + // Synthesize a symbolic variable here that corresponds to a control plane argument. + // We get the unique name of the table coupled with the unique name of the action. + // Getting the unique name is needed to avoid generating duplicate arguments. + cstring keyName = + properties.tableName + "_param_" + actionName + std::to_string(argIdx); + const auto &actionArg = ToolsVariables::getSymbolicVariable(parameter->type, keyName); + arguments->push_back(new IR::Argument(actionArg)); + // We also track the argument we synthesize for the control plane. + // Note how we use the control plane name for the parameter here. + ctrlPlaneArgs.emplace_back(parameter, actionArg); + } + // Add the chosen action to the profile (it will create a new index.) + // TODO: Should we check if we exceed the maximum number of possible profile entries? + actionProfile->addToActionMap(actionName, ctrlPlaneArgs); + + auto *actionSelector = new TofinoActionSelector( + tofinoProperties.actionSelector->getSelectorDecl(), actionProfile); + + // Update the action profile in the execution state. + nextState.addTestObject("action_profile"_cs, actionProfile->getObjectName(), actionProfile); + // Update the action selector in the execution state. + nextState.addTestObject("action_selector"_cs, actionSelector->getObjectName(), + actionSelector); + + // We add the arguments to our action call, effectively creating a const entry call. + auto *synthesizedAction = tableAction->clone(); + synthesizedAction->arguments = arguments; + + // Finally, add all the new rules to the execution state. + ActionCall ctrlPlaneActionCall(actionIndex, actionType, {}); + auto tableRule = + TableRule(matches, TestSpec::LOW_PRIORITY, ctrlPlaneActionCall, TestSpec::TTL); + auto *tableConfig = new TableConfig(table, {tableRule}); + + // Add the action profile to the table. This signifies a slightly different implementation. + tableConfig->addTableProperty("action_profile"_cs, actionProfile); + // Add the action selector to the table. This signifies a slightly different implementation. + tableConfig->addTableProperty("action_selector"_cs, actionSelector); + + nextState.addTestObject("tableconfigs"_cs, table->controlPlaneName(), tableConfig); + + // Update all the tracking variables for tables. + std::vector replacements; + replacements.emplace_back( + new IR::MethodCallStatement(Util::SourceInfo(), synthesizedAction)); + // Some path selection strategies depend on looking ahead and collecting potential + // statements. If that is the case, apply the CoverableNodesScanner visitor. + P4::Coverage::CoverageSet coveredNodes; + if (requiresLookahead(TestgenOptions::get().pathSelectionPolicy)) { + auto collector = CoverableNodesScanner(*state); + collector.updateNodeCoverage(actionType, coveredNodes); + } + + nextState.set(getTableHitVar(table), IR::BoolLiteral::get(true)); + nextState.set(getTableActionVar(table), getTableActionString(tableAction)); + std::stringstream tableStream; + tableStream << "Table Branch: " << properties.tableName; + tableStream << " Chosen action: " << actionName; + nextState.add(*new TraceEvents::Generic(tableStream.str())); + nextState.replaceTopBody(&replacements); + getResult()->emplace_back(hitCondition, *state, nextState); + } +} + +bool TofinoTableStepper::checkForActionProfile() { + const auto *impl = table->properties->getProperty("implementation"); + if (impl == nullptr) { + return false; + } + + const auto *state = getExecutionState(); + + const auto *implExpr = impl->value->checkedTo(); + const auto *implPath = implExpr->expression->checkedTo(); + const auto *implDecl = state->findDecl(implPath)->checkedTo(); + const auto *implDeclType = implDecl->type; + if (const auto *typeName = implDeclType->to()) { + implDeclType = state->resolveType(typeName); + } + const auto *implExtern = implDeclType->checkedTo(); + if (implExtern->name != "ActionProfile") { + return false; + } + + tofinoProperties.implementaton = TableImplementation::profile; + + const auto *testObject = + state->getTestObject("action_profile"_cs, implDecl->controlPlaneName(), false); + if (testObject == nullptr) { + tofinoProperties.addProfileToState = true; + tofinoProperties.actionProfile = new TofinoActionProfile(implDecl); + return true; + } + tofinoProperties.actionProfile = testObject->checkedTo(); + tofinoProperties.addProfileToState = false; + return true; +} + +bool TofinoTableStepper::checkForActionSelector() { + const auto *impl = table->properties->getProperty("implementation"); + if (impl == nullptr) { + return false; + } + + const auto *state = getExecutionState(); + const auto *selectorExpr = impl->value->checkedTo(); + const auto *selectorPath = selectorExpr->expression->checkedTo(); + const auto *selectorDecl = state->findDecl(selectorPath)->checkedTo(); + const auto *selectorDeclType = selectorDecl->type; + if (const auto *typeName = selectorDeclType->to()) { + selectorDeclType = state->resolveType(typeName); + } + const auto *selectorExtern = selectorDeclType->checkedTo(); + if (selectorExtern->name != "ActionSelector") { + return false; + } + + // There are deprecated action selector declarations we do not support. + // These have less than 5 arguments. + const auto *selectorArgs = selectorDecl->arguments; + if (selectorArgs->size() < 5) { + ::warning( + "Only action selectors with at least 5 parameters are supported. " + "Using default action for selector table."); + tofinoProperties.implementaton = TableImplementation::skip; + return false; + } + + const auto *actionProfilePath = + selectorArgs->at(0)->expression->checkedTo(); + const auto *actionProfileDecl = + state->findDecl(actionProfilePath)->checkedTo(); + const auto *testObject = + state->getTestObject("action_profile"_cs, actionProfileDecl->controlPlaneName(), false); + const TofinoActionProfile *actionProfile = nullptr; + + if (testObject == nullptr) { + // This means, for every possible control plane entry (and with that, new execution state) + // add the generated action profile. + tofinoProperties.addProfileToState = true; + actionProfile = new TofinoActionProfile(actionProfileDecl); + } else { + actionProfile = testObject->checkedTo(); + } + CHECK_NULL(actionProfile); + + tofinoProperties.actionSelector = new TofinoActionSelector(selectorDecl, actionProfile); + return true; +} + +void TofinoTableStepper::checkTargetProperties( + const std::vector & /*tableActionList*/) { + // Iterate over the table keys and check whether we can mitigate taint. + for (auto keyProperties : properties.resolvedKeys) { + const auto *keyElement = keyProperties.key; + auto keyIsTainted = + keyProperties.isTainted && + (properties.tableIsImmutable || keyElement->matchType->toString() == "exact"); + properties.tableIsTainted = properties.tableIsTainted || keyIsTainted; + // If the key expression is tainted, do not bother resolving the remaining keys. + if (properties.tableIsTainted) { + ::warning("Key %1% of table %2% is tainted.", keyElement->expression, table); + return; + } + } + + // Check whether the table has an action profile associated with it. + if (checkForActionProfile()) { + tofinoProperties.implementaton = TableImplementation::profile; + return; + } + + // Check whether the table has an action selector associated with it. + if (checkForActionSelector()) { + tofinoProperties.implementaton = TableImplementation::selector; + return; + } +} + +void TofinoTableStepper::evalTargetTable( + const std::vector &tableActionList) { + const auto *keys = table->getKey(); + + const auto *progInfo = getProgramInfo()->checkedTo(); + if (progInfo->isMultiPipe() && TestgenOptions::get().testBackend == "PTF") { + properties.tableName = + getExecutionState()->getProperty("pipe_name"_cs) + "." + properties.tableName; + } + + // If we have no keys, there is nothing to match. + if (keys == nullptr) { + // Either override the default action or fall back to executing it. + // Some test frameworks do not support overriding a default action if there is only one. + // We can not use tableActionList.size() because it does not include the default action. + auto canOverride = !properties.defaultIsImmutable && !tableActionList.empty(); + // There is a special case where we should not override. + // That is when we have only one action and that action is already set as default. + // TODO: Find a simpler way to express this? + canOverride = + canOverride && (tableActionList.size() != 1 || + !tableActionList.at(0)->expression->equiv(*table->getDefaultAction())); + if (canOverride) { + setTableDefaultEntries(tableActionList); + } else { + addDefaultAction(std::nullopt); + } + return; + } + + // If the table is not constant, the default action can always be executed. + // This is because we can simply not enter any table entry. + std::optional tableMissCondition = std::nullopt; + // If the table is not immutable, we synthesize control plane entries and follow the paths. + if (properties.tableIsImmutable) { + tofinoProperties.implementaton = TableImplementation::constant; + } + + switch (tofinoProperties.implementaton) { + case TableImplementation::selector: { + // If an action selector is attached to the table, do not assume normal control plane + // behavior. + if (TestgenOptions::get().testBackend == "PTF") { + evalTableActionSelector(tableActionList); + } else { + // We can only generate profile entries for PTF tests. + ::warning( + "Action selector control plane entries are not implemented. Using default " + "action."); + } + break; + } + case TableImplementation::profile: { + // If an action profile is attached to the table, do not assume normal control plane + // behavior. + if (TestgenOptions::get().testBackend == "PTF") { + evalTableActionProfile(tableActionList); + } else { + // We can only generate profile entries for PTF tests. + ::warning( + "Action profile control plane entries are not implemented. Using default " + "action."); + } + break; + }; + case TableImplementation::skip: { + break; + }; + case TableImplementation::constant: { + // If the entries properties is constant it means the entries are fixed. + // We cannot add or remove table entries. + tableMissCondition = evalTableConstEntries(); + break; + }; + default: { + evalTableControlEntries(tableActionList); + } + } + + // Add the default action. + addDefaultAction(tableMissCondition); +} + +TofinoTableStepper::TofinoTableStepper(SharedTofinoExprStepper *stepper, const IR::P4Table *table) + : TableStepper(stepper, table) {} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.h b/backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.h new file mode 100644 index 00000000000..11c08d230a5 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/shared_table_stepper.h @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_SHARED_TABLE_STEPPER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_SHARED_TABLE_STEPPER_H_ + +#include +#include + +#include "ir/ir.h" +#include "lib/cstring.h" + +#include "backends/p4tools/modules/testgen/core/small_step/table_stepper.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/lib/test_spec.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class TofinoTableStepper : public TableStepper { + private: + /// Specifies the type of the table implementation: + /// standard: standard implementation - use normal control plane entries. + /// selector: ActionSelector implementation - also uses an action profile. + /// profile: ACtionProfile implementation - normal entries are not valid + /// constant: The table is constant - no control entries are possible. + /// skip: Skip the implementation and just use the default entry (no entry at all). + enum class TableImplementation { standard, selector, profile, constant, skip }; + + /// Tofino specific table properties. + struct TofinoProperties { + /// The table has an action profile associated with it. + const TofinoActionProfile *actionProfile = nullptr; + + /// The table has an action selector associated with it. + TofinoActionSelector *actionSelector = nullptr; + + /// The selector keys that are part of the selector hash that is calculated. + std::vector actionSelectorKeys; + + /// The current execution state does not have this profile added to it yet. + bool addProfileToState = false; + + /// The type of the table implementation. + TableImplementation implementaton; + } tofinoProperties; + + /// Check whether the table has an action profile implementation. + bool checkForActionProfile(); + + /// Check whether the table has an action selector implementation. + bool checkForActionSelector(); + + /// If the table has an action profile implementation, evaluate the match-action list + /// accordingly. Entries will use indices to refer to actions instead of their labels. + void evalTableActionProfile(const std::vector &tableActionList); + + /// If the table has an action selector implementation, evaluate the match-action list + /// accordingly. Entries will use indices to refer to actions instead of their labels. + void evalTableActionSelector(const std::vector &tableActionList); + + protected: + const IR::Expression *computeTargetMatchType(const TableUtils::KeyProperties &keyProperties, + TableMatchMap *matches, + const IR::Expression *hitCondition) override; + + void checkTargetProperties( + const std::vector &tableActionList) override; + + void evalTargetTable( + const std::vector &tableActionList) override; + + public: + explicit TofinoTableStepper(SharedTofinoExprStepper *stepper, const IR::P4Table *table); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_SHARED_TABLE_STEPPER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/target.cpp b/backends/p4tools/modules/testgen/targets/tofino/target.cpp new file mode 100644 index 00000000000..6cf8d202298 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/target.cpp @@ -0,0 +1,306 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/target.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/namespace_context.h" +#include "backends/p4tools/common/lib/util.h" +#include "ir/ir.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" +#include "lib/ordered_map.h" + +#include "backends/p4tools/modules/testgen/core/target.h" +#include "backends/p4tools/modules/testgen/targets/tofino/compiler_result.h" +#include "backends/p4tools/modules/testgen/targets/tofino/map_direct_externs.h" +#include "backends/p4tools/modules/testgen/targets/tofino/rename_keys.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/* ============================================================================================= + * AbstractTofinoTestgenTarget implementation + * ============================================================================================= */ + +AbstractTofinoTestgenTarget::AbstractTofinoTestgenTarget(std::string deviceName, + std::string archName) + : TestgenTarget(std::move(deviceName), std::move(archName)) {} + +TofinoTestBackend *AbstractTofinoTestgenTarget::getTestBackendImpl( + const ProgramInfo &programInfo, const TestBackendConfiguration &testBackendConfiguration, + SymbolicExecutor &symbex) const { + return new TofinoTestBackend(*programInfo.checkedTo(), + testBackendConfiguration, symbex); +} + +P4::FrontEnd AbstractTofinoTestgenTarget::mkFrontEnd() const { + // FIXME: Reenable this. + // return P4::FrontEnd(BFN::ParseAnnotations()); + return {}; +} + +MidEnd AbstractTofinoTestgenTarget::mkMidEnd(const CompilerOptions &options) const { + // We need to initialize the device to be able to use Tofino compiler passes + // FIXME: Reenable this? + // Device::init(spec.deviceName); + auto midEnd = CompilerTarget::mkMidEnd(options); + // auto *refMap = midEnd.getRefMap(); + // auto *typeMap = midEnd.getTypeMap(); + midEnd.addPasses({ + // Remove all unused fields in the egress intrinsic metadata + // FIXME: Reenable this. + // new BFN::RewriteEgressIntrinsicMetadataHeader(refMap, typeMap), + // Remove trailing '$' in '$valid$' key matches and + // replace subscript operator in header stack indices + // with '$' + new RenameKeys(), + }); + return midEnd; +} + +CompilerResultOrError AbstractTofinoTestgenTarget::runCompilerImpl( + const CompilerOptions &options, const IR::P4Program *program) const { + program = runFrontend(options, program); + if (program == nullptr) { + return std::nullopt; + } + + if (errorCount() > 0) { + return std::nullopt; + } + + program = runMidEnd(options, program); + if (program == nullptr) { + return std::nullopt; + } + + // Create DCG. + NodesCallGraph *dcg = nullptr; + if (TestgenOptions::get().dcg || !TestgenOptions::get().pattern.empty()) { + dcg = new NodesCallGraph("NodesCallGraph"); + P4ProgramDCGCreator dcgCreator(dcg); + program->apply(dcgCreator); + } + if (errorCount() > 0) { + return std::nullopt; + } + /// Collect coverage information about the program. + auto coverage = P4::Coverage::CollectNodes(TestgenOptions::get().coverageOptions); + program->apply(coverage); + if (errorCount() > 0) { + return std::nullopt; + } + + // Try to map all instances of direct externs to the table they are attached to. + // Save the map in @var directExternMap. + auto directExternMapper = MapDirectExterns(); + program->apply(directExternMapper); + if (errorCount() > 0) { + return std::nullopt; + } + + return {*new TofinoCompilerResult( + TestgenCompilerResult(CompilerResult(*program), coverage.getCoverableNodes(), dcg), + directExternMapper.getDirectExternMap())}; +} + +/* ============================================================================================= + * Tofino_TnaTestgenTarget implementation + * ============================================================================================= */ + +Tofino_TnaTestgenTarget::Tofino_TnaTestgenTarget() : AbstractTofinoTestgenTarget("tofino", "tna") {} + +void Tofino_TnaTestgenTarget::make() { + static Tofino_TnaTestgenTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new Tofino_TnaTestgenTarget(); + } +} + +const TofinoProgramInfo *Tofino_TnaTestgenTarget::produceProgramInfoImpl( + const CompilerResult &compilerResult, const IR::Declaration_Instance *mainDecl) const { + // Ensure that main instantiates Switch. + // TODO: Handle MultiParserSwitch. + const auto *mainType = mainDecl->type->to(); + if ((mainType == nullptr) || mainType->baseType->path->name != "Switch") { + return nullptr; + } + + // The pipes in the main declaration are just the arguments in the constructor call. + // Convert mainDecl->arguments into a vector of pipes, represented as constructor-call + // expressions. + const auto *mainDeclArgs = mainDecl->arguments; + const auto *ns = NamespaceContext::Empty->push(&compilerResult.getProgram()); + std::vector pipeInfos; + std::map declIdToGress; + std::map declIdToPipe; + + // Expand each pipe into its constituent parser, MAU, and deparser, each represented as a + // constructor-call expression. Also identify the gress for each parser, MAU control, and + // deparser. + // TODO: Handle ghost threads. + std::vector blocks; + for (size_t pipeIdx = 0; pipeIdx < mainDeclArgs->size(); ++pipeIdx) { + const auto *pipe = mainDeclArgs->at(pipeIdx); + + std::vector blocks; + const auto *const pathExpr = pipe->expression->checkedTo(); + const auto *declInstance = + ns->findDecl(pathExpr->path)->checkedTo(); + auto subBlocks = + argumentsToTypeDeclarations(&compilerResult.getProgram(), declInstance->arguments); + blocks.insert(blocks.end(), subBlocks.begin(), subBlocks.end()); + ordered_map programmableBlocks; + + // We should have six arguments. + BUG_CHECK(blocks.size() == 6, "%1%: Not a valid pipeline instantiation.", pipe); + + // Add to parserDeclIdToGress, mauDeclIdToGress, and deparserDeclIdToGress. + for (size_t idx = 0; idx < blocks.size(); ++idx) { + const auto *declType = blocks.at(idx); + + auto canonicalName = TofinoProgramInfo::ARCH_SPEC.getArchMember(idx)->blockName; + programmableBlocks.emplace(canonicalName, declType); + + if (idx < 3) { + declIdToGress[declType->declid] = INGRESS; + } else { + declIdToGress[declType->declid] = EGRESS; + } + declIdToPipe[declType->declid] = pipeIdx; + } + pipeInfos.push_back({declInstance->controlPlaneName(), programmableBlocks}); + } + return new TofinoProgramInfo(*compilerResult.checkedTo(), pipeInfos, + declIdToGress, declIdToPipe); +} + +TofinoCmdStepper *Tofino_TnaTestgenTarget::getCmdStepperImpl(ExecutionState &state, + AbstractSolver &solver, + const ProgramInfo &programInfo) const { + return new TofinoCmdStepper(state, solver, programInfo); +} + +Tofino1ExprStepper *Tofino_TnaTestgenTarget::getExprStepperImpl( + ExecutionState &state, AbstractSolver &solver, const ProgramInfo &programInfo) const { + return new Tofino1ExprStepper(state, solver, programInfo); +} + +/* ============================================================================================= + * JBay_T2naTestgenTarget implementation + * ============================================================================================= */ + +JBay_T2naTestgenTarget::JBay_T2naTestgenTarget() : AbstractTofinoTestgenTarget("tofino2", "t2na") {} + +void JBay_T2naTestgenTarget::make() { + static JBay_T2naTestgenTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new JBay_T2naTestgenTarget(); + } +} + +const JBayProgramInfo *JBay_T2naTestgenTarget::produceProgramInfoImpl( + const CompilerResult &compilerResult, const IR::Declaration_Instance *mainDecl) const { + // Ensure that main instantiates Switch. + // TODO: Handle MultiParserSwitch. + const auto *mainType = mainDecl->type->to(); + if ((mainType == nullptr) || mainType->baseType->path->name != "Switch") { + return nullptr; + } + + // The pipes in the main declaration are just the arguments in the constructor call. + // Convert mainDecl->arguments into a vector of pipes, represented as constructor-call + // expressions. + const auto *mainDeclArgs = mainDecl->arguments; + const auto *ns = NamespaceContext::Empty->push(&compilerResult.getProgram()); + std::vector pipeInfos; + std::map declIdToGress; + std::map declIdToPipe; + + // Expand each pipe into its constituent parser, MAU, and deparser, each represented as a + // constructor-call expression. Also identify the gress for each parser, MAU control, and + // deparser. + // TODO: Handle ghost threads. + std::vector blocks; + for (size_t pipeIdx = 0; pipeIdx < mainDeclArgs->size(); ++pipeIdx) { + const auto *pipe = mainDeclArgs->at(pipeIdx); + + std::vector blocks; + const auto *const pathExpr = pipe->expression->checkedTo(); + const auto *declInstance = + ns->findDecl(pathExpr->path)->checkedTo(); + auto subBlocks = + argumentsToTypeDeclarations(&compilerResult.getProgram(), declInstance->arguments); + + ordered_map programmableBlocks; + + // We should have six or seven (in case of the optional ghost) arguments per pipeline. + BUG_CHECK(blocks.size() == 6 || blocks.size() == 7, + "%1%: Not a valid pipeline instantiation.", pipe); + + // Add to parserDeclIdToGress, mauDeclIdToGress, and deparserDeclIdToGress. + for (size_t idx = 0; idx < blocks.size(); ++idx) { + const auto *declType = blocks.at(idx); + + auto canonicalName = JBayProgramInfo::ARCH_SPEC.getArchMember(idx)->blockName; + programmableBlocks.emplace(canonicalName, declType); + + if (idx < 3) { + declIdToGress[declType->declid] = INGRESS; + } else if (idx < 6) { + declIdToGress[declType->declid] = EGRESS; + } else if (idx == 6) { + declIdToGress[declType->declid] = GHOST; + } else { + BUG("Unexpected number of blocks %1%", idx); + } + declIdToPipe[declType->declid] = pipeIdx; + } + pipeInfos.push_back({declInstance->controlPlaneName(), programmableBlocks}); + } + + return new JBayProgramInfo(*compilerResult.checkedTo(), pipeInfos, + declIdToGress, declIdToPipe); +} + +JBayCmdStepper *JBay_T2naTestgenTarget::getCmdStepperImpl(ExecutionState &state, + AbstractSolver &solver, + const ProgramInfo &programInfo) const { + return new JBayCmdStepper(state, solver, programInfo); +} + +JBayExprStepper *JBay_T2naTestgenTarget::getExprStepperImpl(ExecutionState &state, + AbstractSolver &solver, + const ProgramInfo &programInfo) const { + return new JBayExprStepper(state, solver, programInfo); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/target.h b/backends/p4tools/modules/testgen/targets/tofino/target.h new file mode 100644 index 00000000000..48ecee15339 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/target.h @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TARGET_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TARGET_H_ + +#include + +#include + +#include "ir/ir.h" +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/core/symbolic_executor/symbolic_executor.h" +#include "backends/p4tools/modules/testgen/core/target.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h" +#if HAVE_FLATROCK_TARGET +#include "backends/p4tools/modules/testgen/targets/tofino/tofino5/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino5/expr_stepper.h" +#endif + +namespace P4::P4Tools::P4Testgen::Tofino { + +class AbstractTofinoProgramInfo : public ProgramInfo { + public: + ~AbstractTofinoProgramInfo() override = default; +}; + +class AbstractTofinoTestgenTarget : public TestgenTarget { + protected: + TofinoTestBackend *getTestBackendImpl(const ProgramInfo &programInfo, + const TestBackendConfiguration &testBackendConfiguration, + SymbolicExecutor &symbex) const override; + + explicit AbstractTofinoTestgenTarget(std::string deviceName, std::string archName); + + [[nodiscard]] MidEnd mkMidEnd(const CompilerOptions &options) const override; + + [[nodiscard]] P4::FrontEnd mkFrontEnd() const override; + + CompilerResultOrError runCompilerImpl(const CompilerOptions &options, + const IR::P4Program *program) const override; +}; + +class Tofino_TnaTestgenTarget : public AbstractTofinoTestgenTarget { + public: + /// Registers this target. + static void make(); + + protected: + const TofinoProgramInfo *produceProgramInfoImpl( + const CompilerResult &compilerResult, + const IR::Declaration_Instance *mainDecl) const override; + + TofinoCmdStepper *getCmdStepperImpl(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) const override; + + Tofino1ExprStepper *getExprStepperImpl(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) const override; + + private: + Tofino_TnaTestgenTarget(); +}; + +class JBay_T2naTestgenTarget : public AbstractTofinoTestgenTarget { + public: + /// Registers this target. + static void make(); + + protected: + const JBayProgramInfo *produceProgramInfoImpl( + const CompilerResult &compilerResult, + const IR::Declaration_Instance *mainDecl) const override; + + JBayCmdStepper *getCmdStepperImpl(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) const override; + + JBayExprStepper *getExprStepperImpl(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) const override; + + private: + JBay_T2naTestgenTarget(); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TARGET_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/P4Tests.cmake b/backends/p4tools/modules/testgen/targets/tofino/test/P4Tests.cmake new file mode 100644 index 00000000000..3ab4231817d --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/P4Tests.cmake @@ -0,0 +1,242 @@ +# General test utilities. +include(${P4TOOLS_SOURCE_DIR}/cmake/TestUtils.cmake) +# This file defines how we write the tests we generate. +include(${CMAKE_CURRENT_LIST_DIR}/TestTemplate.cmake) + +set (TOFINO_SOURCE_DIR ${P4C_SOURCE_DIR}/backends/tofino) + +# # # ########################################################################## +# TEST SUITES +# # # ########################################################################## +# Check for PTF +find_program(PTF ptf PATHS ${CMAKE_INSTALL_PREFIX}/bin) + +if(PTF) + message(STATUS "ptf for ${device} found at ${PTF}.") +else() + message(WARNING "PTF tests need ptf for ${device}.\nLooked in ${CMAKE_INSTALL_PREFIX}/bin.") +endif() + +set( + BF_SWITCHD_SEARCH_PATHS + ${BFN_P4C_SOURCE_DIR}/../install/bin + ${CMAKE_INSTALL_PREFIX}/bin +) + + +set( + BF_SWITCH_INC_PATH + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/switch_16/p4src/shared/ +) + +# check for bf_switchd +find_program(BF_SWITCHD bf_switchd PATHS ${BF_SWITCHD_SEARCH_PATHS} NO_DEFAULT_PATH) + +if(BF_SWITCHD) + message(STATUS "bf-switchd for ${device} found at ${BF_SWITCHD}.") +else() + message(WARNING "PTF tests need bf-switchd for ${device}.\nLooked in ${BF_SWITCHD_SEARCH_PATHS}.") +endif() + +# check for tofino-model +set( + HARLYN_MODEL_SEARCH_PATHS + ${BFN_P4C_SOURCE_DIR}/../install/bin + ${CMAKE_INSTALL_PREFIX}/bin +) + +find_program(HARLYN_MODEL tofino-model PATHS ${HARLYN_MODEL_SEARCH_PATHS} NO_DEFAULT_PATH) + +if(HARLYN_MODEL) + message(STATUS "tofino-model for ${device} found at ${HARLYN_MODEL}.") +else() + message(WARNING "PTF tests need tofino-model for ${device}.\nLooked in ${HARLYN_MODEL_SEARCH_PATHS}.") +endif() + +# # # ########################################################################## +# BF-P4C TNA P4-PROGRAMS TESTS +# # # ########################################################################## +file( + GLOB + P4TOOLS_TESTGEN_TOFINO + # Compiler tests. + ${TOFINO_SOURCE_DIR}/p4-tests/p4-programs/p4_16_programs/*/*.p4 + ${TOFINO_SOURCE_DIR}/p4-tests/p4-programs/internal_p4_16/*/*.p4 + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/*.p4 + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/switch_16/p4src/switch-tofino/*.p4 + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/switch_16/p4src/switch-tofino2/*.p4 + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/*.p4 +) + +# Some p4s are compiler Xfails - excluding them here. +set( + P4TOOLS_TESTGEN_TOFINO_EXCLUDES + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/atcam_match_wide1-neg.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/dkm_invalid.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/multi-constraint.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/neg_test_1_lpf_constant_param.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/too_many_ternary_match_key_bits.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/checksum_neg_test1.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/checksum_neg_test2.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/compile_only/checksum_neg_test3.p4 + # # Set as Tofino Errors. + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/parser_multi_write_2.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/parser_multi_write_8.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/parser_multi_write_checksum_deposit_2.p4 + # ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/parser_multi_write_checksum_verify_2.p4 + # Infinite parser loop. TODO: Fix handling of infinite parser loops. + # This test takes extremely long to compile. + # This test has very complex conditions, causing the solver to time out. +) +# Some p4s are included in earlier folders (even though they are different) - excluding them here. +# If they are needed, we have to add them explicitly with a different test name. +set( + P4TOOLS_TESTGEN_TOFINO_DUPLICATES + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/match_key_slices.p4 +) +list(REMOVE_ITEM P4TOOLS_TESTGEN_TOFINO ${P4TOOLS_TESTGEN_TOFINO_DUPLICATES} ${P4TOOLS_TESTGEN_TOFINO_EXCLUDES}) + +set(TNA_SEARCH_PATTERNS "include.*tna.p4" "main") +p4c_find_tests("${P4TOOLS_TESTGEN_TOFINO}" P4TOOLS_TESTGEN_TOFINO_TNA INCLUDE "${TNA_SEARCH_PATTERNS}" EXCLUDE "${P4TOOLS_TESTGEN_TOFINO_EXCLUDES}") + +# Custom tests, not subject to deduplication. +file( + GLOB + P4TOOLS_TESTGEN_TOFINO_CUSTOM + ${CMAKE_CURRENT_LIST_DIR}/p4-programs/*.p4 + ${CMAKE_CURRENT_LIST_DIR}/p4-programs/tna_simple_switch/tna_simple_switch.p4 + ${CMAKE_CURRENT_LIST_DIR}/p4-programs/tofino1_only/*.p4 +) + +set( + P4TOOLS_TESTGEN_TOFINO_TNA + ${P4TOOLS_TESTGEN_TOFINO_TNA} + ${P4TOOLS_TESTGEN_TOFINO_CUSTOM} +) + +set(T2NA_SEARCH_PATTERNS "include.*t2na.p4" "main") +p4c_find_tests("${P4TOOLS_TESTGEN_TOFINO}" P4TOOLS_TESTGEN_TOFINO_T2NA INCLUDE "${T2NA_SEARCH_PATTERNS}" EXCLUDE "${P4TOOLS_TESTGEN_TOFINO_EXCLUDES}") + +# Custom tests, not subject to deduplication. +file( + GLOB + P4TOOLS_TESTGEN_TOFINO_T2NA_CUSTOM + ${CMAKE_CURRENT_LIST_DIR}/p4-programs/*.p4 + ${CMAKE_CURRENT_LIST_DIR}/p4-programs/tna_simple_switch/tna_simple_switch.p4 + ${CMAKE_CURRENT_LIST_DIR}/p4-programs/tofino2_only/*.p4 +) + +set( + P4TOOLS_TESTGEN_TOFINO_T2NA + ${P4TOOLS_TESTGEN_TOFINO_T2NA} + ${P4TOOLS_TESTGEN_TOFINO_T2NA_CUSTOM} +) + +# # # ########################################################################## +# STF TESTS +# # # ########################################################################## +set( + P4TOOLS_TESTGEN_TOFINO_INC_PATH + ${TOFINO_SOURCE_DIR}/p4-tests/p4-programs/p4_16_programs/ +) +set( + P4TOOLS_TESTGEN_TOFINO_P416_INC_PATH + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/includes +) + +set( + P4TOOLS_TESTGEN_TOFINO_INTERNAL_TESTS_INC_PATH + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/stf/include/ +) +set( + P4TOOLS_TESTGEN_TOFINO_SWITCH_INC_PATH + ${TOFINO_SOURCE_DIR}/p4-tests/p4_16/switch_16/p4src/shared +) + +# Test settings. +set(P416INCLUDES "-I${P4C_SOURCE_DIR}/backends/tofino/bf-p4c/p4include -I${P4C_SOURCE_DIR}/p4include -I${P4TOOLS_TESTGEN_TOFINO_INC_PATH} -I${P4TOOLS_TESTGEN_TOFINO_P416_INC_PATH} -I${P4TOOLS_TESTGEN_TOFINO_INTERNAL_TESTS_INC_PATH} -I${P4TOOLS_TESTGEN_TOFINO_SWITCH_INC_PATH}") +if(NOT NIGHTLY) + set(EXTRA_OPTS "${P416INCLUDES} --print-traces --strict --seed 1000 --max-tests 100") +else() + set(EXTRA_OPTS "${P416INCLUDES} --print-traces --strict --max-tests 500") +endif() + +# # # ########################################################################## +# STF TESTS +# # # ########################################################################## +p4tools_add_tests( + TESTS "${P4TOOLS_TESTGEN_TOFINO_TNA}" + TAG "testgen-tofino" DRIVER ${P4TESTGEN_DRIVER} + TARGET "tofino" ARCH "tna" CTEST_P4C_ARGS "${P416INCLUDES} --disable-power-check --disable-parse-depth-limit" RUN_STF TEST_ARGS "-D__TARGET_TOFINO__=1 --test-backend STF --port-ranges 0:63 ${EXTRA_OPTS}" +) + +# include(${CMAKE_CURRENT_LIST_DIR}/TofinoXfail.cmake) + +set (P4TOOLS_TESTGEN_TOFINO_T2NA_STF ${P4TOOLS_TESTGEN_TOFINO_T2NA}) +list(REMOVE_ITEM P4TOOLS_TESTGEN_TOFINO_T2NA_STF + # This test times out when running on STF. +) +p4tools_add_tests( + TESTS "${P4TOOLS_TESTGEN_TOFINO_T2NA_STF}" + TAG "testgen-tofino2" DRIVER ${P4TESTGEN_DRIVER} + TARGET "tofino2" ARCH "t2na" CTEST_P4C_ARGS "${P416INCLUDES} --disable-power-check --disable-parse-depth-limit" RUN_STF TEST_ARGS "-D__TARGET_TOFINO__=2 --test-backend STF --port-ranges 8:71 ${EXTRA_OPTS}" +) +# include(${CMAKE_CURRENT_LIST_DIR}/Tofino2Xfail.cmake) + +# # # ########################################################################## +# PTF TESTS +# # # ########################################################################## + +p4tools_add_tests( + TESTS "${P4TOOLS_TESTGEN_TOFINO_TNA}" + TAG "testgen-tofino-ptf" DRIVER ${P4TESTGEN_DRIVER} + TARGET "tofino" ARCH "tna" CTEST_P4C_ARGS "${P416INCLUDES} --disable-power-check --disable-parse-depth-limit" RUN_PTF TEST_ARGS "-D__TARGET_TOFINO__=1 -I${P4C_SOURCE_DIR}/p4_16/includes --test-backend PTF --port-ranges 0:15 ${EXTRA_OPTS}" +) +# include(${CMAKE_CURRENT_LIST_DIR}/TofinoPTFXfail.cmake) + +p4tools_add_tests( + TESTS "${P4TOOLS_TESTGEN_TOFINO_T2NA}" + TAG "testgen-tofino2-ptf" DRIVER ${P4TESTGEN_DRIVER} + TARGET "tofino2" ARCH "t2na" CTEST_P4C_ARGS "${P416INCLUDES} --disable-power-check --disable-parse-depth-limit" RUN_PTF TEST_ARGS "-D__TARGET_TOFINO__=2 --test-backend PTF --port-ranges 8:15 ${EXTRA_OPTS}" +) +# include(${CMAKE_CURRENT_LIST_DIR}/Tofino2PTFXfail.cmake) + + +# # # ########################################################################## +# TEST PROPERTIES +# # # ########################################################################## + +# These are pathologically slow tests that need extra time. +# set_tests_properties("testgen-tofino2-ptf/switch_tofino2_y2.p4" PROPERTIES TIMEOUT 6000) +# set_tests_properties("testgen-tofino2/switch_tofino2_y2.p4" PROPERTIES TIMEOUT 6000) +# set_tests_properties("testgen-tofino2-ptf/switch_tofino2_y1_mod.p4" PROPERTIES TIMEOUT 6000) +# set_tests_properties("testgen-tofino2/switch_tofino2_y1_mod.p4" PROPERTIES TIMEOUT 6000) + + +# set_tests_properties("testgen-tofino-ptf/switch_tofino_x2.p4" PROPERTIES TIMEOUT 6000) +# set_tests_properties("testgen-tofino/switch_tofino_x2.p4" PROPERTIES TIMEOUT 6000) +# set_tests_properties("testgen-tofino/switch_tofino_x1_mod.p4" PROPERTIES TIMEOUT 6000) + +# set_tests_properties("testgen-tofino2/tna_alpmV2.p4" PROPERTIES TIMEOUT 3000) +# set_tests_properties("testgen-tofino2/tna_ipv4_alpm.p4" PROPERTIES TIMEOUT 3000) + + +# # # ########################################################################## +# UNSTABLE TESTS +# # # ########################################################################## +# p4c_add_test_label("testgen-tofino2-ptf" "UNSTABLE" "switch_tofino2_y1_mod.p4") + + +# # # ########################################################################## +# ADD NIGHTLY LABEL TO GROUP OF TESTS +# # # ########################################################################## +# macro(p4tools_add_to_nightly p4ProgramPaths testLabel) +# message("P4 programs added to Nightly:") +# foreach(programPath ${p4ProgramPaths}) +# get_filename_component(programFileName ${programPath} NAME) +# p4c_add_test_label("${testLabel}" "NIGHTLY" "${programFileName}") +# message(">>>NIGHTLY: ${testLabel}/${programFileName}") +# endforeach() +# endmacro(p4tools_add_to_nightly) + +# p4tools_add_to_nightly("${P4TOOLS_TESTGEN_TOFINO_TNA}" "testgen-tofino") diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/TestTemplate.cmake b/backends/p4tools/modules/testgen/targets/tofino/test/TestTemplate.cmake new file mode 100644 index 00000000000..e47a8cf7c7c --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/TestTemplate.cmake @@ -0,0 +1,131 @@ +# This file defines how a test should be written for a particular target. This is used by testutils + +# Write the script to check Tofino STF tests to the designated test file. +# Arguments: +# - testfile is the testing script that this script is written to. +# - testfolder is target folder of the test. +# - testharness is binary to use for execution. +# - aliasname is name of the test. +function(check_with_test_harness testfile testfolder testharness aliasname) + if(NOT testharness) + message(WARNING "STF tests need the test harness for ${target}. No entry was found.\n") + return() + endif() + # Find all the stf tests generated for this p4 and run the test harness + file(APPEND ${testfile} "stffiles=($(cd ${testfolder} && find * -name \"*.stf\"))\n") + file(APPEND ${testfile} "for item in \${stffiles[@]}\n") + file(APPEND ${testfile} "do\n") + file(APPEND ${testfile} "\techo \"Found \${item}\"\n") + file(APPEND ${testfile} "\t${testharness} -l ${testfolder}/${aliasname}.conf ${testfolder}/\${item}\n") + file(APPEND ${testfile} "done\n") +endfunction(check_with_test_harness) + +# Write the script to check Tofino STF tests to the designated test file. +# Arguments: +# - testfile is the testing script that this script is written to. +# - testfolder is target folder of the test. +# - p4test is the file that is to be tested. +function(check_with_model testfile testfolder target aliasname) + set(__cmakecache "${CMAKE_BINARY_DIR}/CMakeCache.txt") + set(__ptfrunner "${CMAKE_SOURCE_DIR}/p4-tests/ptf_runner.py") + file(APPEND ${testfile} "if test -f \"${testfolder}/${aliasname}.py\"; then") + file(APPEND ${testfile} "\techo \"Starting PTF: ${CMAKE_SOURCE_DIR}/p4-tests/run-isolated python3 ${__ptfrunner} --testdir ${testfolder} --name ${aliasname} --ptfdir ${testfolder} --top-builddir ${__cmakecache} --device ${target} --bfrt-test ${testfolder}/${aliasname}.conf\"\n") + file(APPEND ${testfile} "\t${CMAKE_SOURCE_DIR}/p4-tests/run-isolated python3 ${__ptfrunner} --testdir ${testfolder} --name ${aliasname} --ptfdir ${testfolder} --top-builddir ${__cmakecache} --device ${target} --bfrt-test ${testfolder}/${aliasname}.conf\n") + file(APPEND ${testfile} "fi") +endfunction(check_with_model) + + +# Add a single test to the testsuite. +# Arguments: +# - TAG is a label for the set of test suite where this test belongs +# (for example, p4ctest) +# - DRIVER is the script that is used to run the test and compare the results +# - ALIAS is a possibly different name for the test such that the +# same p4 program can be used in different test configurations. +# Must be unique across the test suite. +# - P4TEST is the name of the p4 program to test (path relative to the p4c directory) +# - TARGET is the target to test against +# - ARCH is the p4 architecture +# - RUN_STF is the flag to execute the Tofino test harness on the generated tests. +# - RUN_PTF is the flag to execute the Tofino model on the generated tests. +# - TEST_ARGS is a list of arguments to pass to the test +# - CMAKE_ARGS are additional arguments to pass to the test +# - COMPILE_ARGS are additional compilation arguments to pass to p4c-barefoot +# +# It generates a ${p4test}.test file invoking ${driver} on the p4 +# program with command line arguments ${args} +# Sets the timeout on tests at 1500. For the slow CI machines. +macro(p4tools_add_test_with_args) + # Parse arguments. + set(options RUN_STF RUN_PTF) + set(oneValueArgs TAG DRIVER ALIAS P4TEST TARGET ARCH) + set(multiValueArgs TEST_ARGS CMAKE_ARGS CTEST_P4C_ARGS) + cmake_parse_arguments( + TOOLS_TOFINO_TESTS "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN} + ) + # Set some lowercase variables for convenience. + set(tag ${TOOLS_TOFINO_TESTS_TAG}) + set(driver ${TOOLS_TOFINO_TESTS_DRIVER}) + set(alias ${TOOLS_TOFINO_TESTS_ALIAS}) + set(p4test ${TOOLS_TOFINO_TESTS_P4TEST}) + set(target ${TOOLS_TOFINO_TESTS_TARGET}) + set(arch ${TOOLS_TOFINO_TESTS_ARCH}) + set(test_args ${TOOLS_TOFINO_TESTS_TEST_ARGS}) + set(ctest_p4c_args ${TOOLS_TOFINO_TESTS_CTEST_P4C_ARGS}) + set(cmake_args ${TOOLS_TOFINO_TESTS_CMAKE_ARGS}) + + # This is the actual test processing. + p4c_test_set_name(__testname ${tag} ${alias}) + string(REPLACE ".p4" "" aliasname ${alias}) + set(__testfile "${P4TESTGEN_DIR}/${tag}/${alias}.test") + set(__testfolder "${P4TESTGEN_DIR}/${tag}/${aliasname}.out") + get_filename_component(__testdir ${p4test} DIRECTORY) + file(WRITE ${__testfile} "#! /usr/bin/env bash\n") + file(APPEND ${__testfile} "# Generated file, modify with care\n\n") + file(APPEND ${__testfile} "set -e\n") + file(APPEND ${__testfile} "cd ${P4C_BINARY_DIR}\n") + file( + APPEND ${__testfile} "${driver} --target ${target} --arch ${arch} " + "--std p4-16 ${test_args} --out-dir ${__testfolder} \"$@\" ${p4test}\n" + ) + # Compile the p4 file. + file(APPEND ${__testfile} "\techo \"Compiling ${p4test}...\"\n") + file( + APPEND ${__testfile} "${CMAKE_BINARY_DIR}/p4c-barefoot --target ${target} --arch ${arch} " + "--std p4-16 ${p4test} -I${CMAKE_SOURCE_DIR}/p4-tests/p4_16/includes " + "-o ${__testfolder} ${ctest_p4c_args} || (echo Compiler failed && false)\n" + ) + + if(${TOOLS_TOFINO_TESTS_RUN_STF}) + # RUN_STF + # Manually overwrite the tofino2 target as jbay, since tofino2 is not listed as variable. + if(${target} STREQUAL "tofino2") + get_property(testharness CACHE HARLYN_STF_jbay PROPERTY VALUE) + elseif(${target} STREQUAL "tofino5") + get_property(testharness CACHE HARLYN_STF_ftr PROPERTY VALUE) + else() + get_property(testharness CACHE HARLYN_STF_${target} PROPERTY VALUE) + endif() + if (testharness) + check_with_test_harness(${__testfile} ${__testfolder} ${testharness} ${aliasname}) + else() + message(WARNING "STF tests need the test harness for ${target}. No entry was found.\n") + endif() + elseif(${TOOLS_TOFINO_TESTS_RUN_PTF}) + # Run PTF + check_with_model(${__testfile} ${__testfolder} ${target} ${aliasname}) + endif() + + execute_process(COMMAND chmod +x ${__testfile}) + separate_arguments(__args UNIX_COMMAND ${cmake_args}) + add_test( + NAME ${__testname} + COMMAND ${tag}/${alias}.test ${__args} + WORKING_DIRECTORY ${P4TESTGEN_DIR} + ) + if(NOT DEFINED ${tag}_timeout) + set(${tag}_timeout 1500) + endif() + set_tests_properties(${__testname} PROPERTIES LABELS ${tag} TIMEOUT ${${tag}_timeout}) +endmacro(p4tools_add_test_with_args) diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/Tofino2PTFXfail.cmake b/backends/p4tools/modules/testgen/targets/tofino/test/Tofino2PTFXfail.cmake new file mode 100644 index 00000000000..e3e39404406 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/Tofino2PTFXfail.cmake @@ -0,0 +1,132 @@ +# XFAILS: tests that *temporarily* fail +# ===================================== +# +# Xfails are _temporary_ failures: the tests should work but we haven't fixed +# the tools yet. + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method:" + mirror.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method: emit" + # Resubmit.emit + tna_resubmit.p4 + empty_header_stack.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method: set" + parser_counter_10.p4 + parser_counter_7.p4 + parser_counter_stack_1.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method: enqueue" + # RegisterAction.enqueue + t2na_fifo.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method: execute" + # MinMaxAction.execute + t2na_ghost.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method: clear" +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Unknown or unimplemented extern method: write" + # RegisterAction.write +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "writing to a structure is not supported" +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "error: Power worst case estimated budget" +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "is trying to match on a tainted key set" + parser_multi_write_checksum_verify_5.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Expected packet was not received on device .*" + # Our packet model is incomplete. + # We do not model the case where we append metadata but do not parse it. + # tna_register.p4 + tna_alpmV2.p4 + # Most likely a compiler bug + tna_extract_emit_3.p4 + tna_action_profile_1.p4 + # Meter is not fully implemented. + large_counter_meter.p4 + # TODO: + tna_multicast.p4 + tna_simple_switch.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "exceeded the maximum number of permitted guard violations for this run" + t2na_emulation.p4 + too_many_pov_bits.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "The validity bit of .* is tainted" +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "A packet was received on device .*" +) + +# This p4 is supposed to compile with --no-bf-rt-schema option, not sure how to run it though. +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "is used for multiple Register objects in the P4Info message" +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Match type dleft_hash not implemented for table keys" + hwlrn.p4 +) + +# This is out of our control. +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Table Add failed .* Not enough space" +) + +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Failed to subscribe to server at %s" + varbit_four_options.p4 +) + +# These are compilation bugs we have no control over. +p4tools_add_xfail_reason( + "testgen-tofino2-ptf" + "Compiler failed" +) diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/Tofino2Xfail.cmake b/backends/p4tools/modules/testgen/targets/tofino/test/Tofino2Xfail.cmake new file mode 100644 index 00000000000..e3950b0ba94 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/Tofino2Xfail.cmake @@ -0,0 +1,195 @@ +# XFAILS: tests that *temporarily* fail +# ===================================== +# +# Xfails are _temporary_ failures: the tests should work but we haven't fixed +# the tools yet. + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Match type range not implemented" + tna_range_match.p4 + tna_field_slice.p4 + t2na_uni_dim_scale_base.p4 + varbit_four_options.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Match type dleft_hash not implemented" + hwlrn.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method:" + mirror.p4 + t2na_ghost.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method: emit" + # Resubmit.emit + tna_resubmit.p4 + empty_header_stack.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method: set" + parser_counter_10.p4 + parser_counter_7.p4 + parser_counter_stack_1.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method: execute" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method: clear" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method: write" + # RegisterAction.write +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Unknown or unimplemented extern method: enqueue" + # RegisterAction.enqueue + t2na_fifo.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "warning: .*: Table key name not supported. Replacing .* with .*." +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Not a valid state variable" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "writing to a structure is not supported" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "unhandled stage table type" + tna_proxy_hash.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "Table .* has no stage tables" + bfrt_alpm_perf.p4 + tna_lpm_match.p4 + tna_alpm.p4 + tna_alpmV2.p4 + tna_ipv4_alpm.p4 + tna_ipv6_alpm.p4 + tna_ternary_match.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "error -2 thrown" + tna_meter_bytecount_adjust.p4 + tna_meter_lpf_wred.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "terminate called after throwing an instance of" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "is trying to match on a tainted key set" + switch_tofino2_y1_mod.p4 + parser_multi_write_checksum_verify_5.p4 +) + +# Looks like a compiler/test_harness bug +p4tools_add_xfail_reason( + "testgen-tofino2" + "Clot slices must be used in order" + tna_extract_emit_3.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "default_action_handle .* not found in table .*" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "must specify match fields before action" + tna_checksum.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "exceeded the maximum number of permitted guard violations for this run" + t2na_emulation.p4 + too_many_pov_bits.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "shorter than expected" + # Strange behavior with packets that are too short when using the STF model. + tna_extract_emit_8.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "1 expected packet on port .* not seen" + tna_multicast.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "invalid vpn .* in write_sram for logical table .*" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "no field .* related to table .*" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "mismatch from expected" + # Our packet model is incomplete. + # We do not model the case where we append metadata but do not parse it. + # tna_register.p4 + # Meter is not fully implemented. + large_counter_meter.p4 + # TODO +) + +# This p4 is supposed to compile with --no-bf-rt-schema option, not sure how to run it though. +p4tools_add_xfail_reason( + "testgen-tofino2" + "is used for multiple Register objects in the P4Info message" +) + +p4tools_add_xfail_reason( + "testgen-tofino2" + "duplicate value for .*" + misc1.p4 +) + +# These are tests that currently fail. +p4tools_add_xfail_reason( + "testgen-tofino2" + "Compiler failed" +) diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/TofinoPTFXfail.cmake b/backends/p4tools/modules/testgen/targets/tofino/test/TofinoPTFXfail.cmake new file mode 100644 index 00000000000..4c5842b48eb --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/TofinoPTFXfail.cmake @@ -0,0 +1,313 @@ +# XFAILS: tests that *temporarily* fail +# ===================================== +# +# Xfails are _temporary_ failures: the tests should work but we haven't fixed +# the tools yet. + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "cast" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Not a valid state variable" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unable to resolve extraction source" + simple_l3_mcast.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "not padded to be byte-aligned in" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "writing to a structure" + # These tests fail because the nestedStructs pass does not unroll assignments + # to a structure that has been returned from an extern +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Table key name not supported" + static_entries2.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unable to find var" + # Need to convert a path expression reference into a struct expression. +) + +# Valid tna program fails in MidEnd_32_BFN::RewriteEgressIntrinsicMetadataHeader pass +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Program not supported by target device and architecture" + test_config_2_multiple_parsers.p4 + test_config_3_unused_parsers.p4 + test_config_11_multi_pipe_multi_parsers.p4 + tna_multi_prsr_programs_multi_pipes.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "StackOutOfBounds will be triggered" + parser_loop_3.p4 + parser_loop_4.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "error: This program violates action constraints imposed by Tofino" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Saturating arithmetic operators" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unknown or unimplemented extern method: is_zero" + tcp_option_mss.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unknown or unimplemented extern method: write" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Cannot cast implicitly type" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unknown or unimplemented extern method: emit" + tna_resubmit.p4 + mirror.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unknown or unimplemented extern method: execute" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unknown or unimplemented extern method: set" + parser_counter_6.p4 + parser_counter_7.p4 + parser_counter_8.p4 + parser_counter_9.p4 + parser_counter_10.p4 + parser_match_17.p4 + parser_counter_12.p4 + parser_counter_11.p4 + ipv6_tlv.p4 + parser_loop_1.p4 + parser_loop_2.p4 + tcp_option_mss_4_byte_chunks.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Found 2 duplicate name" + multiple_apply2.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "A packet was received on device" + # Potentially a bug in bf-p4c. + tna_action_profile_1.p4 + # We are not modelling short packets correctly. + # TODO +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Expected packet was not received" + # Our packet model is incomplete. + # We do not model the case where we append metadata but do not parse it. + # tna_register.p4 + parse_srv6_fast.p4 + # This is likely a bug in bf-p4c + header_stack_strided_alloc2.p4 + action_profile.p4 + multithread_pipe_lock.p4 + snapshot.p4 + # Multicast is not implemented + tna_multicast.p4 + # These tests currently fail because of a bug in bf-p4c. + # symmetric_hash.p4 + # This looks like a problem with the @flexible annotation. + meter_dest_16_32_flexible.p4 + # Missing implementation for pa_alias + # mau-meter.cpp:233 (calculate_output): error -2 thrown. + large_counter_meter.p4 + # TODO + tna_simple_switch.p4 + tna_alpmV2.p4 + varbit_four_options.p4 + +) + +# New tests added +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Unimplemented compiler support" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Please rewrite the action to be a single stage action" +) + +# These are tests that currently fail. +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Compiler failed" + mirror_5.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "failed command verifier" + simple_l3_nexthop_ipv6_options.p4 +) + +# Trying to compile a t2na program. +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "error: AssignmentStatement" + t2na_tm_stress.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Compiler Bug: : Operand .* of instruction .* operating on container .* must be a PHV." + deparse-zero-clustering.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Compiler Bug: For Tofino, Index of the header stack ." +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Compiler Bug: Flexible packing bug found" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Compiler Bug: Could not find field" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "error: mirror.emit" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Compiler Bug: Inconsistent parser write semantic on .*" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "error: Power worst case estimated budget" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "is trying to match on a tainted key set" + lookahead1.p4 + switchml.p4 + parser_multi_write_checksum_verify_5.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "error: Size of learning quanta is .* bytes, greater than the maximum allowed .* bytes." +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "longest path through parser (.*) exceeds maximum parse depth (.*)" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Tofino requires byte-aligned headers" + tagalong_mdinit_switch.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Ensure that the atcam_partition_index and exact match key name match" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "exceeded the maximum number of permitted guard violations for this run" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "table .* should not have empty const entries list" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Internal error: Attempt to assign a negative value to an unsigned type." + # The parser of the interpreter hits an infinite loop. +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "PTF runner:Error when starting model & switchd" + # Switchd fails to start with context json processing error. + forensics.p4 +) + +# Most likely a model bug +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "BfruntimeReadWriteRpcException" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "The validity bit of .* is tainted" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "The varbit expression of .* is tainted" +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "We can not predict how much this call will advance the parser cursor" + varbit_in_middle.p4 + simple_l3_csum_varbit_v2.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Failed to subscribe to server at %s" + # (check_extract_constraints): error -2 thrown +) + +p4tools_add_xfail_reason( + "testgen-tofino-ptf" + "Table Add failed .* Not enough space" +) diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/TofinoXfail.cmake b/backends/p4tools/modules/testgen/targets/tofino/test/TofinoXfail.cmake new file mode 100644 index 00000000000..224d581137f --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/TofinoXfail.cmake @@ -0,0 +1,406 @@ +# XFAILS: tests that *temporarily* fail +# ===================================== +# +# Xfails are _temporary_ failures: the tests should work but we haven't fixed +# the tools yet. + +p4tools_add_xfail_reason( + "testgen-tofino" + "cast" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Not a valid state variable" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unimplemented for LPM FieldMatch" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unable to resolve extraction source" + simple_l3_mcast.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "not padded to be byte-aligned in" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "writing to a structure" + # These tests fail because the nestedStructs pass does not unroll assignments + # to a structure that has been returned from an extern +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Table key name not supported" + static_entries2.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unable to find var" + # Need to convert a path expression reference into a struct expression. +) + +# Valid tna program fails in MidEnd_32_BFN::RewriteEgressIntrinsicMetadataHeader pass +p4tools_add_xfail_reason( + "testgen-tofino" + "Program not supported by target device and architecture" + test_config_2_multiple_parsers.p4 + test_config_3_unused_parsers.p4 + test_config_11_multi_pipe_multi_parsers.p4 + tna_multi_prsr_programs_multi_pipes.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "StackOutOfBounds will be triggered" + parser_loop_3.p4 + parser_loop_4.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "error: This program violates action constraints imposed by Tofino" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Saturating arithmetic operators" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unknown or unimplemented extern method: is_zero" + tcp_option_mss.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unknown or unimplemented extern method: write" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Cannot cast implicitly type" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unknown or unimplemented extern method: emit" + tna_resubmit.p4 + mirror.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unknown or unimplemented extern method: execute" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Cast failed" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Unknown or unimplemented extern method: set" + parser_counter_6.p4 + parser_counter_7.p4 + parser_counter_8.p4 + parser_counter_9.p4 + parser_counter_10.p4 + parser_match_17.p4 + parser_counter_12.p4 + parser_counter_11.p4 + ipv6_tlv.p4 + parser_loop_1.p4 + parser_loop_2.p4 + tcp_option_mss_4_byte_chunks.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Found 2 duplicate name" + multiple_apply2.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "mismatch from expected" + # Our packet model is incomplete. + # We do not model the case where we append metadata but do not parse it. + # tna_register.p4 + # Known issue + # These tests currently fail because of a bug in bf-p4c. + snapshot.p4 + # symmetric_hash.p4 + # This looks like a problem with the @flexible annotation. + # mau-meter.cpp:233 (calculate_output): error -2 thrown. + large_counter_meter.p4 + # pa_alias not implemented. + # TODO + meter_dest_16_32_flexible.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Malformed packet data" + # This looks like a problem with the @flexible annotation. +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "1 expected packet on port .* not seen" + header_stack_strided_alloc2.p4 + parse_srv6_fast.p4 + # STF seems to handle these wild tests incorrectly and fails on every generated testcase. + # Might be a bug in the tofino STF harness... + static_entries_over_multiple_stages.p4 + # Multicast is not implemented + tna_multicast.p4 + # Missing implementation for pa_alias + # TODO: +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "longer than expected" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "shorter than expected" + # Strange behavior with packets that are too short when using the STF model. + tna_extract_emit_8.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "no argument .* for .*" + # test_harness does not process multiple arguments for overriding default actions. +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Match type range not implemented" + color_aware_meter.p4 + tna_range_match.p4 + tna_field_slice.p4 + tagalong_mdinit_switch.p4 + varbit_four_options.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "warning: .*: Table key name not supported. Replacing .* with .*." +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Table .* has no stage tables" + atcam_match_wide1.p4 + atcam_match1.p4 + atcam_match2.p4 + atcam_match3.p4 + atcam_match4.p4 + atcam_match5.p4 + bfrt_alpm_perf.p4 + tna_alpm.p4 + tna_alpmV2.p4 + tna_lpm_match.p4 + alpm.p4 + alpm_2.p4 + alpm_4.p4 + atcam_match_extern.p4 + tna_ipv4_alpm.p4 + tna_ipv6_alpm.p4 + tna_ternary_match.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "no field .* related to table .*" +) + +# New tests added +p4tools_add_xfail_reason( + "testgen-tofino" + "Unimplemented compiler support" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "unhandled stage table type" + proxy_hash.p4 + tna_proxy_hash.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Please rewrite the action to be a single stage action" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "error -2 thrown" + # https://jira.devtools.intel.com/browse/MODEL-1151 + meters_adjust_byte_count.p4 + tna_meter_bytecount_adjust.p4 + # TODO - Check if this is a new issue - test harness crashed. + switch_tofino_x2.p4 + counter_meter_test.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "terminate called after throwing an instance of" + test_compiler_macro_defs.p4 +) + +# These are tests that currently fail. +p4tools_add_xfail_reason( + "testgen-tofino" + "Compiler failed" + mirror_5.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "failed command verifier" + simple_l3_nexthop_ipv6_options.p4 +) + +# Trying to compile a t2na program. +p4tools_add_xfail_reason( + "testgen-tofino" + "error: AssignmentStatement" + t2na_tm_stress.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Compiler Bug: : Operand .* of instruction .* operating on container .* must be a PHV." + deparse-zero-clustering.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Compiler Bug: For Tofino, Index of the header stack ." +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "error: mirror.emit" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "error: Power worst case estimated budget" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "is trying to match on a tainted key set" + lookahead1.p4 + switchml.p4 + parser_multi_write_checksum_verify_5.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "error: Size of learning quanta is .* bytes, greater than the maximum allowed .* bytes." +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "invalid vpn .* in write_sram for logical table .*" + snapshot_all_stages.p4 + snapshot_all_stages_egress.p4 + forensics.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "longest path through parser (.*) exceeds maximum parse depth (.*)" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Compiler Bug: Flexible packing bug found" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "exceeded the maximum number of permitted guard violations for this run" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "table .* should not have empty const entries list" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "default_action_handle .* not found in table ipv4_lpm" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Internal error: Attempt to assign a negative value to an unsigned type." + # The parser of the interpreter hits an infinite loop. +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "must specify match fields before action" + tna_checksum.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "Tofino requires byte-aligned headers" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "The validity bit of .* is tainted. Tainted emit calls can not be mitigated" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "The varbit expression of .* is tainted" +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "We can not predict how much this call will advance the parser cursor" + varbit_in_middle.p4 + simple_l3_csum_varbit_v2.p4 +) + +# Issue with the Tofino test harness. +p4tools_add_xfail_reason( + "testgen-tofino" + "duplicate value for .*" + tcam_no_versioning.p4 + misc1.p4 +) + +p4tools_add_xfail_reason( + "testgen-tofino" + "invalid vpn .* in write_sram for logical table" +) + +# TODO - New bug, need to debug. +p4tools_add_xfail_reason( + "testgen-tofino" + "no argument .*" +) diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/headers.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/headers.p4 new file mode 100644 index 00000000000..56b35eb12d6 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/headers.p4 @@ -0,0 +1,160 @@ +/* -*- P4_16 -*- */ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) Intel Corporation + * SPDX-License-Identifier: CC-BY-ND-4.0 + */ + + + +#ifndef _HEADERS_ +#define _HEADERS_ + +typedef bit<48> mac_addr_t; +typedef bit<32> ipv4_addr_t; +typedef bit<128> ipv6_addr_t; +typedef bit<12> vlan_id_t; + +typedef bit<16> ether_type_t; +const ether_type_t ETHERTYPE_IPV4 = 16w0x0800; +const ether_type_t ETHERTYPE_ARP = 16w0x0806; +const ether_type_t ETHERTYPE_IPV6 = 16w0x86dd; +const ether_type_t ETHERTYPE_VLAN = 16w0x8100; + +typedef bit<8> ip_protocol_t; +const ip_protocol_t IP_PROTOCOLS_ICMP = 1; +const ip_protocol_t IP_PROTOCOLS_TCP = 6; +const ip_protocol_t IP_PROTOCOLS_UDP = 17; + +header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +header vlan_tag_h { + bit<3> pcp; + bit<1> cfi; + vlan_id_t vid; + bit<16> ether_type; +} + +header mpls_h { + bit<20> label; + bit<3> exp; + bit<1> bos; + bit<8> ttl; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header ipv6_h { + bit<4> version; + bit<8> traffic_class; + bit<20> flow_label; + bit<16> payload_len; + bit<8> next_hdr; + bit<8> hop_limit; + ipv6_addr_t src_addr; + ipv6_addr_t dst_addr; +} + +header tcp_h { + bit<16> src_port; + bit<16> dst_port; + bit<32> seq_no; + bit<32> ack_no; + bit<4> data_offset; + bit<4> res; + bit<8> flags; + bit<16> window; + bit<16> checksum; + bit<16> urgent_ptr; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> hdr_length; + bit<16> checksum; +} + +header icmp_h { + bit<8> type_; + bit<8> code; + bit<16> hdr_checksum; +} + +// Address Resolution Protocol -- RFC 6747 +header arp_h { + bit<16> hw_type; + bit<16> proto_type; + bit<8> hw_addr_len; + bit<8> proto_addr_len; + bit<16> opcode; + // ... +} + +// Segment Routing Extension (SRH) -- IETFv7 +header ipv6_srh_h { + bit<8> next_hdr; + bit<8> hdr_ext_len; + bit<8> routing_type; + bit<8> seg_left; + bit<8> last_entry; + bit<8> flags; + bit<16> tag; +} + +// VXLAN -- RFC 7348 +header vxlan_h { + bit<8> flags; + bit<24> reserved; + bit<24> vni; + bit<8> reserved2; +} + +// Generic Routing Encapsulation (GRE) -- RFC 1701 +header gre_h { + bit<1> C; + bit<1> R; + bit<1> K; + bit<1> S; + bit<1> s; + bit<3> recurse; + bit<5> flags; + bit<3> version; + bit<16> proto; +} + +struct header_t { + ethernet_h ethernet; + vlan_tag_h vlan_tag; + ipv4_h ipv4; + ipv6_h ipv6; + tcp_h tcp; + udp_h udp; + + // Add more headers here. +} + +struct empty_header_t {} + +struct empty_metadata_t {} + +#endif /* _HEADERS_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/util.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/util.p4 new file mode 100644 index 00000000000..0a684e45539 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/common/util.p4 @@ -0,0 +1,94 @@ +/* -*- P4_16 -*- */ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) Intel Corporation + * SPDX-License-Identifier: CC-BY-ND-4.0 + */ + + + +#ifndef _UTIL_ +#define _UTIL_ + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +parser TofinoEgressParser( + packet_in pkt, + out egress_intrinsic_metadata_t eg_intr_md) { + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} + +// Skip egress +control BypassEgress(inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action set_bypass_egress() { + ig_tm_md.bypass_egress = 1w1; + } + + table bypass_egress { + actions = { + set_bypass_egress(); + } + const default_action = set_bypass_egress; + } + + apply { + bypass_egress.apply(); + } +} + +// Empty egress parser/control blocks +parser EmptyEgressParser( + packet_in pkt, + out empty_header_t hdr, + out empty_metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + state start { + transition accept; + } +} + +control EmptyEgressDeparser( + packet_out pkt, + inout empty_header_t hdr, + in empty_metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply {} +} + +control EmptyEgress( + inout empty_header_t hdr, + inout empty_metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply {} +} + +#endif /* _UTIL */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/multipipe_simple.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/multipipe_simple.p4 new file mode 100644 index 00000000000..7129ae53f35 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/multipipe_simple.p4 @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include +#include + +header payload_t { + bit<16> x; +} +struct header_t { + payload_t payload; +} +struct metadata_t {} + +parser IngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + pkt.advance(PORT_METADATA_SIZE); + pkt.extract(hdr.payload); + transition accept; + } +} + +control IngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + + apply { + pkt.emit(hdr); + } +} + +control Ingress( + inout header_t hdr, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) (bit<16> val) { + + action a1() { + hdr.payload.x = hdr.payload.x | val; + } + + table t1 { + key = { hdr.payload.x : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + ig_tm_md.ucast_egress_port = 8; + } +} + +parser EmptyEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} +control EmptyEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply {} +} + +control EmptyEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply {} +} + +Pipeline(IngressParser(), Ingress(1), IngressDeparser(), + EmptyEgressParser(), EmptyEgress(), EmptyEgressDeparser()) pipe1; +Pipeline(IngressParser(), Ingress(2), IngressDeparser(), + EmptyEgressParser(), EmptyEgress(), EmptyEgressDeparser()) pipe2; + +Switch(pipe1, pipe2) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/parser_reject.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/parser_reject.p4 new file mode 100644 index 00000000000..b41c16d69e6 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/parser_reject.p4 @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#include + +struct metadata_t { +} + +#include "common/headers.p4" +#include "common/util.p4" + + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type) { + ETHERTYPE_IPV4 : parse_ipv4; + default : reject; + } + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser(packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t + ig_intr_dprsr_md + ) { + + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t hdr, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + BypassEgress() bypass_egress; + apply { + // forward the pkt back to the incoming port + ig_tm_md.ucast_egress_port = ig_intr_md.ingress_port; + bypass_egress.apply(ig_tm_md); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + EmptyEgressParser(), + EmptyEgress(), + EmptyEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/port_metadata_unpack.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/port_metadata_unpack.p4 new file mode 100644 index 00000000000..56034e16688 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/port_metadata_unpack.p4 @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +typedef bit<4> switch_port_type_t; + +struct switch_port_metadata_frontpipe_t { + switch_port_type_t port_type; +} + +struct switch_ingress_common_metadata_t { + switch_port_type_t port_type; /* ingress port_type */ + bit<32> timestamp; +} + +struct metadata_t { + switch_ingress_common_metadata_t common; +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + // Parse port metadata produced by ibuf + switch_port_metadata_frontpipe_t port_md = port_metadata_unpack(pkt); + ig_md.common.port_type = port_md.port_type; + ig_md.common.timestamp = ig_intr_md.ingress_mac_tstamp[31:0]; + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 3; + h.ethernet.src_addr = 1; + h.ethernet.ether_type = 1; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + // hdr.ethernet.dst_addr = 2; + hdr.ethernet.src_addr = 2; + hdr.ethernet.ether_type = 2; + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_declaration.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_declaration.p4 new file mode 100644 index 00000000000..168ad609997 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_declaration.p4 @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action MyAction1(bit<48> dst_input) { + h.ethernet.dst_addr = dst_input; + } + + action MyAction2() { + h.ethernet.dst_addr = 2; + } + + table exact_table { + key = { + h.ethernet.ether_type : exact; + } + + actions = { + NoAction; + MyAction1; + MyAction2; + } + + const entries = { + 16w1 : MyAction1(1); + 16w2 : MyAction2(); + } + + size = 1024; + default_action = NoAction(); + } + + apply { + exact_table.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_1.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_1.p4 new file mode 100644 index 00000000000..2abc8b72f58 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_1.p4 @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +header data_t { + bit<32> f1; + bit<32> f2; + bit<16> h1; + bit<8> b1; + bit<8> b2; +} + +struct metadata { +} + +struct headers { + data_t data; +} + +header ingress_skip_t { + bit<64> pad; +} + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> f1; + bit<32> f2; + bit<16> h1; + bit<8> b1; + bit<8> b2; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchIngress( + inout header_t hdr, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + ActionProfile(288) bd_action_profile; + action act(bit<9> port) { + ig_tm_md.ucast_egress_port = port; + } + action change(bit<8> c) { + hdr.h.b2 = c; + } + + table test1 { + actions = { NoAction; act;change;} + key = { hdr.h.f1: exact; } + default_action = NoAction; + implementation = bd_action_profile; + } + + table test2 { + actions = { NoAction;act;change;} + key = { hdr.h.f2: exact; } + default_action = NoAction; + implementation = bd_action_profile; + } + apply { + switch(test1.apply().action_run) { + NoAction: {test2.apply();} + default : {} + } + } + +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_default.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_default.p4 new file mode 100644 index 00000000000..dac58a7f30f --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_action_profile_default.p4 @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + + @name(".set_lag_port") action set_lag_port(bit<9> port) { + ig_tm_md.ucast_egress_port = port; + } + @name(".lag_miss") action lag_miss() { + } + @name(".lag_table") table lag { + key = { + h.ethernet.dst_addr : ternary @name("port_lag_index") ; + } + actions = { + lag_miss; + set_lag_port; + } + const default_action = set_lag_port(8); + size = 1024; + } + apply { + lag.apply(); + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_advance.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_advance.p4 new file mode 100644 index 00000000000..def8b9101fe --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_advance.p4 @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition advance; + } + + state advance { + pkt.advance(32); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.advance(32); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_cast.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_cast.p4 new file mode 100644 index 00000000000..0b98ec006fb --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_cast.p4 @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = (bit<48>) h.ethernet.ether_type; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + // hdr.ethernet.dst_addr = 2; + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_drop.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_drop.p4 new file mode 100644 index 00000000000..b803be9603b --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_drop.p4 @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; ig_dprsr_md.drop_ctl = 1; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_emit_extern.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_emit_extern.p4 new file mode 100644 index 00000000000..b895aa0e4f5 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_emit_extern.p4 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + // transition select(hdr.ethernet.ether_type) { + // 1 : parse_ipv4; + // 0 : parse_ipv4; + // } + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_empty.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_empty.p4 new file mode 100644 index 00000000000..870bbd62c44 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_empty.p4 @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply {} +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + apply { + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply {} +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply {} +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_exit.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_exit.p4 new file mode 100644 index 00000000000..b604778e965 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_exit.p4 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; h.ethernet.dst_addr = 2; + exit; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + hdr.ethernet.dst_addr = 5; + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_1.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_1.p4 new file mode 100644 index 00000000000..67fb9385eda --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_1.p4 @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_2.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_2.p4 new file mode 100644 index 00000000000..5538c07fb81 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_2.p4 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + transition parse_ipv4; + } + + state parse_ipv4 { + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_3.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_3.p4 new file mode 100644 index 00000000000..8d9451acc35 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_3.p4 @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ethernet); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_4.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_4.p4 new file mode 100644 index 00000000000..9627b578374 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_4.p4 @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ethernet); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_5.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_5.p4 new file mode 100644 index 00000000000..3372d09f73e --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_5.p4 @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + if (h.ipv4.dst_addr == 3) { + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; } else { + ig_tm_md.ucast_egress_port = 8; + } + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_6.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_6.p4 new file mode 100644 index 00000000000..148a337fbd2 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_6.p4 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_7.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_7.p4 new file mode 100644 index 00000000000..982205c2eb7 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_7.p4 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_8.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_8.p4 new file mode 100644 index 00000000000..67ad15e39f4 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_extract_emit_8.p4 @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 0xFFFFFFFFFFFF; + h.ethernet.src_addr = 0xFFFFFFFFFFFF; + h.ethernet.ether_type = 0xFFFF; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_1.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_1.p4 new file mode 100644 index 00000000000..73c53065557 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_1.p4 @@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC16) hash1; + + action a1() { + h.h.a = 0xEEEE; + h.h.a = hash1.get({h.eth_hdr.dst_addr, h.eth_hdr.src_addr, h.eth_hdr.eth_type}); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_10.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_10.p4 new file mode 100644 index 00000000000..964d873b927 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_10.p4 @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +header data_t { + bit<32> read; + bit<32> f1; + bit<32> f2; + bit<32> f3; + bit<16> h1; + bit<8> b1; + bit<8> b2; +} + +struct metadata { +} + +header ingress_skip_t { + bit<64> pad; +} + +struct headers { + data_t data; +} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser ParserI( + packet_in pkt, + out headers hdr, + out metadata ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + transition h; + } + + state h { + pkt.extract(hdr.data); + transition accept; + } +} + +control IngressP( + inout headers hdr, + inout metadata meta, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_intr_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout ingress_intrinsic_metadata_for_tm_t ig_intr_tm_md) { + + Hash>(HashAlgorithm_t.IDENTITY) hash5; + action E_act(bit<9> port) { + ig_intr_tm_md.ucast_egress_port = port; + hdr.data.h1 = hash5.get({hdr.data.f3, 8w255}); + } + + table E_tbl { + actions = { E_act; NoAction; } + key = { hdr.data.read: exact; } + const default_action = NoAction; + } + + apply { + E_tbl.apply(); + } + +} + +control DeparserI( + packet_out b, + inout headers hdr, + in metadata meta, + in ingress_intrinsic_metadata_for_deparser_t ig_intr_dprsr_md) { + apply { b.emit(hdr.data); } +} + +parser ParserE(packet_in b, + out headers hdr, + out metadata meta, + out egress_intrinsic_metadata_t eg_intr_md) { + state start { + b.extract(eg_intr_md); + b.extract(hdr.data); + transition accept; + } +} + +control EgressP( + inout headers hdr, + inout metadata meta, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_prsr_md, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { } +} + +control DeparserE(packet_out b, + inout headers hdr, + in metadata meta, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { b.emit(hdr.data); } +} + +Pipeline(ParserI(), IngressP(), DeparserI(), ParserE(), EgressP(), DeparserE()) pipe0; +Switch(pipe0) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_2.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_2.p4 new file mode 100644 index 00000000000..dda80d375c9 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_2.p4 @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC16) hash1; + + action a1() { + h.eth_hdr.src_addr = 0xAAAAAAAAAAAA; + h.eth_hdr.dst_addr = 0xFFFFFFFFFFFF; + h.eth_hdr.eth_type = 0xBBBB; + h.h.a = 0xEEEE; + h.h.a = hash1.get({h.eth_hdr.dst_addr, h.eth_hdr.src_addr, h.eth_hdr.eth_type}); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_3.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_3.p4 new file mode 100644 index 00000000000..a06a12f4d66 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_3.p4 @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC16) hash1; + + action a1() { + h.h.a = 0xEEEE; + h.h.a = hash1.get({h.eth_hdr.dst_addr, h.eth_hdr.src_addr, h.eth_hdr.eth_type}); + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + if (h.h.a == 0x00004444) { + h.eth_hdr.eth_type = 0xBBBB; + } + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } + +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_4.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_4.p4 new file mode 100644 index 00000000000..dc1de74cdea --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_4.p4 @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC16) hash1; + + action a1() { + h.h.a = 0xEEEE; + h.eth_hdr.src_addr = 0xAAAAAAAAAAAA; + h.eth_hdr.dst_addr = 0xFFFFFFFFFFFF; + h.eth_hdr.eth_type = 0xBBBB; + h.h.a = 0xEEEE; + h.h.a = hash1.get({h.eth_hdr.dst_addr, h.eth_hdr.src_addr, h.eth_hdr.eth_type}); + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + if (h.h.a == 0x00004444) { + h.eth_hdr.eth_type = 0xBBBB; + } + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_5.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_5.p4 new file mode 100644 index 00000000000..21801d66aba --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_5.p4 @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; + bit<32> b; + bit<32> c; + bit<32> d; + bit<32> e; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC16) hash1; + + action a1() { + h.h.a = hash1.get({8w0x45, 8w0x00, 8w0x00, 8w0x73, 8w0x00, 8w0x00, + 8w0x40, 8w0x00, 8w0x40, 8w0x11, 8w0x00, 8w0x00, + 8w0xc0, 8w0xa8, 8w0x00, 8w0x01, 8w0xc0, 8w0xa8, + 8w0x00, 8w0xc7}); + h.h.b = hash1.get({8w1, 8w0, 8w1, 8w0, 8w1, 8w0}); + h.h.c = hash1.get({8w0, 8w1, 8w0, 8w1, 8w0, 8w1}); + h.h.d = hash1.get({16w1, 16w1, 16w1}); + h.h.e = hash1.get({17w1, 15w1, 14w1}); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_6.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_6.p4 new file mode 100644 index 00000000000..22c011aefec --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_6.p4 @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; + bit<32> b; + bit<32> c; + bit<32> d; + bit<32> e; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.IDENTITY) hash1; + + action a1() { + h.h.a = hash1.get({h.h.b ++ h.h.c ++ h.h.d}); + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + if (h.h.b == 0xDEADBEEF) { + h.h.c = 0xAAAAAAAA; + } + t1.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } + +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_7.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_7.p4 new file mode 100644 index 00000000000..857d62cff1b --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_7.p4 @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; + bit<32> b; + bit<32> c; + bit<32> d; + bit<32> e; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC32) hash1; + + action a1() { + h.h.a = hash1.get({h.h.b ++ h.h.c ++ h.h.d}); + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + if (h.h.b == 0xDEADBEEF) { + h.h.c = 0xAAAAAAAA; + } + + t1.apply(); + + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_8.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_8.p4 new file mode 100644 index 00000000000..6bf0ed511e0 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_8.p4 @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; + bit<32> b; + bit<32> c; + bit<32> d; + bit<32> e; + bit<128> f; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC32) hash1; + + action a1() { + h.h.c = hash1.get({h.h.b, h.h.c, h.h.f}); + } + + table t1 { + key = { h.h.a : exact; } + actions = { a1; } + size = 1024; + } + + apply { + if (h.h.b == 0xDEADBEEF) { + h.h.c = 0xAAAAAAAA; + } + + t1.apply(); + + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } + +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_9.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_9.p4 new file mode 100644 index 00000000000..a9090258059 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_hash_9.p4 @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +header H { + bit<32> a; +} + +struct header_t { + ethernet_t eth_hdr; + H h; +} + +struct metadata_t {} + +parser TofinoIngressParser( + packet_in pkt, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1 : parse_resubmit; + 0 : parse_port_metadata; + } + } + + state parse_resubmit { + // Parse resubmitted packet here. + transition reject; + } + + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.eth_hdr); + transition h; + } + + state h { + pkt.extract(hdr.h); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Hash>(HashAlgorithm_t.CRC16) hash1; + action E_act(bit<9> port) { + h.h.a = hash1.get({h.eth_hdr.dst_addr, h.eth_hdr.src_addr, h.eth_hdr.eth_type}); + } + + table E_tbl { + actions = { E_act; NoAction; } + key = { h.eth_hdr.dst_addr: exact; } + const default_action = NoAction; + } + + apply { + h.eth_hdr.src_addr = 0xAAAAAAAAAAAA; + h.eth_hdr.dst_addr = 0xFFFFFFFFFFFF; + h.eth_hdr.eth_type = 0xBBBB; + h.h.a = 0xEEEE; + E_tbl.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.eth_hdr); + pkt.extract(hdr.h); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_if_stmt.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_if_stmt.p4 new file mode 100644 index 00000000000..e4c8dc92ca8 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_if_stmt.p4 @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + if (h.ethernet.ether_type == 0xDE) { + h.ethernet.dst_addr = 1; + } + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_mirror_2.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_mirror_2.p4 new file mode 100644 index 00000000000..25a71d89584 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_mirror_2.p4 @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#include + +header payload_t { + bit<16> x; +} +struct header_t { + payload_t payload; +} +struct metadata_t { + MirrorId_t session_id; +} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + pkt.advance(PORT_METADATA_SIZE); + pkt.extract(hdr.payload); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + + Mirror() mirror; + apply { + if (ig_dprsr_md.mirror_type == 0) { + mirror.emit(ig_md.session_id); + } + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t hdr, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_pvs_fixed.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_pvs_fixed.p4 new file mode 100644 index 00000000000..2a69eda823c --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_pvs_fixed.p4 @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 3 +#include +#elif __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +header data_t { + bit<16> f16; + bit<8> f8; +} + +struct headers { + data_t data; +} + +struct pvs_data { + bit<16> f16; + bit<8> f8; +} + +struct metadata { } + +parser ParserI(packet_in b, + out headers hdr, + out metadata meta, + out ingress_intrinsic_metadata_t ig_intr_md) { + value_set(4) vs; + TofinoIngressParser() tofino_parser; + state start { + tofino_parser.apply(b, ig_intr_md); + transition parse_ethernet; + } + state parse_ethernet { + b.extract(hdr.data); + transition select(hdr.data.f16, hdr.data.f8) { + vs : pvs_state; + _ : reject; + } + } + state pvs_state { + transition accept; + } +} +control IngressP( + inout headers hdr, + inout metadata meta, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_intr_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout ingress_intrinsic_metadata_for_tm_t ig_intr_tm_md) { + apply { + ig_intr_tm_md.ucast_egress_port = ig_intr_md.ingress_port; + hdr.data.f16 = 2; + } +} + +control DeparserI( + packet_out b, + inout headers hdr, + in metadata meta, + in ingress_intrinsic_metadata_for_deparser_t ig_intr_dprsr_md) { + apply { b.emit(hdr.data); } +} + +parser ParserE(packet_in b, + out headers hdr, + out metadata meta, + out egress_intrinsic_metadata_t eg_intr_md) { + state start { + b.extract(eg_intr_md); + b.extract(hdr.data); + transition accept; + } +} +control EgressP( + inout headers hdr, + inout metadata meta, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_prsr_md, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { } +} + +control DeparserE(packet_out b, + inout headers hdr, + in metadata meta, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { b.emit(hdr.data); } +} + +Pipeline(ParserI(), IngressP(), DeparserI(), ParserE(), EgressP(), DeparserE()) pipe; +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_random_extern.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_random_extern.p4 new file mode 100644 index 00000000000..01254f0c60d --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_random_extern.p4 @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#include + +header payload_t { + bit<16> x; +} +struct header_t { + payload_t payload; +} +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + pkt.advance(PORT_METADATA_SIZE); + pkt.extract(hdr.payload); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t hdr, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + Random>() rand; + + action a1() { + hdr.payload.x = rand.get(); + } + + table t1 { + key = { hdr.payload.x : exact; } + actions = { a1; } + size = 1024; + } + + apply { + t1.apply(); + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_1.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_1.p4 new file mode 100644 index 00000000000..c3581384272 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_1.p4 @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + +struct metadata_t {} + +Register, bit<11>>(2048) ping_reg; + + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + RegisterAction, bit<11>, bit<32>>(ping_reg) ping_update = { + void apply(inout bit<32> value) { value = 0xFFFFFFFF; } }; + + RegisterAction, bit<11>, bit<32>>(ping_reg) ping_get = { + void apply(inout bit<32> value, out bit<32> rv) { rv = value; } + }; + apply { + ping_update.execute(1); + + h.ipv4.src_addr = ping_get.execute(1); + if (h.ipv4.src_addr == 0) { + h.ipv4.src_addr = 1; + } + h.ethernet.dst_addr = 0xAAAAAAAAAAAA; + h.ethernet.src_addr = 0xBBBBBBBBBBBB; + h.ethernet.ether_type = 0xCCCC; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_2.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_2.p4 new file mode 100644 index 00000000000..fbc535c3114 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_register_action_2.p4 @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +DirectRegister>(2048) ping_reg1; +DirectRegister>(2048) ping_reg2; + + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + DirectRegisterAction,bit<32>>(ping_reg1) ping_update = { + void apply(inout bit<32> value) { value = 0xFFFFFFFF; } }; + + DirectRegisterAction, bit<32>>(ping_reg2) ping_get = { + void apply(inout bit<32> value, out bit<32> rv) { rv = value; } + }; + + apply { + ping_update.execute(); + + h.ipv4.src_addr = ping_get.execute(); + h.ethernet.dst_addr = 0xAAAAAAAAAAAA; + h.ethernet.src_addr = 0xBBBBBBBBBBBB; + h.ethernet.ether_type = 0xCCCC; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_1.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_1.p4 new file mode 100644 index 00000000000..35519459b40 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_1.p4 @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 3; + h.ethernet.src_addr = 1; + h.ethernet.ether_type = 1; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + // hdr.ethernet.dst_addr = 2; + hdr.ethernet.src_addr = 2; + hdr.ethernet.ether_type = 2; + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_2.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_2.p4 new file mode 100644 index 00000000000..b8f73e3ac96 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_assign_2.p4 @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + apply { + h.ethernet.dst_addr = 3; + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_switch.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_switch.p4 new file mode 100644 index 00000000000..bce7d46010d --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_simple_switch.p4 @@ -0,0 +1,1555 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/* -*- P4_16 -*- */ + + + + + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +@command_line("--disable-parse-max-depth-limit") + +typedef bit<48> mac_addr_t; +typedef bit<32> ipv4_addr_t; +typedef bit<128> ipv6_addr_t; +typedef bit<12> vlan_id_t; +typedef bit<16> ether_type_t; +const ether_type_t ETHERTYPE_IPV4 = 16w0x800; +const ether_type_t ETHERTYPE_ARP = 16w0x806; +const ether_type_t ETHERTYPE_VLAN = 16w0x8100; +const ether_type_t ETHERTYPE_IPV6 = 16w0x86dd; +typedef bit<8> ip_protocol_t; +const ip_protocol_t IP_PROTOCOLS_ICMP = 1; +const ip_protocol_t IP_PROTOCOLS_IPV4 = 4; +const ip_protocol_t IP_PROTOCOLS_TCP = 6; +const ip_protocol_t IP_PROTOCOLS_UDP = 17; +const ip_protocol_t IP_PROTOCOLS_IPV6 = 41; +const ip_protocol_t IP_PROTOCOLS_SRV6 = 43; +const ip_protocol_t IP_PROTOCOLS_NONXT = 59; +typedef bit<16> udp_port_t; +const udp_port_t UDP_PORT_GTPC = 2123; +const udp_port_t PORT_GTPU = 2152; +const udp_port_t UDP_PORT_VXLAN = 4789; +header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +header vlan_tag_h { + bit<3> pcp; + bit<1> cfi; + vlan_id_t vid; + bit<16> ether_type; +} + +header mpls_h { + bit<20> label; + bit<3> exp; + bit<1> bos; + bit<8> ttl; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header ipv6_h { + bit<4> version; + bit<8> traffic_class; + bit<20> flow_label; + bit<16> payload_len; + bit<8> next_hdr; + bit<8> hop_limit; + ipv6_addr_t src_addr; + ipv6_addr_t dst_addr; +} + +header tcp_h { + bit<16> src_port; + bit<16> dst_port; + bit<32> seq_no; + bit<32> ack_no; + bit<4> data_offset; + bit<4> res; + bit<8> flags; + bit<16> window; + bit<16> checksum; + bit<16> urgent_ptr; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> hdr_length; + bit<16> checksum; +} + +header icmp_h { + bit<8> type_; + bit<8> code; + bit<16> hdr_checksum; +} + +header srh_h { + bit<8> next_hdr; + bit<8> hdr_ext_len; + bit<8> routing_type; + bit<8> segment_left; + bit<8> last_entry; + bit<8> flags; + bit<16> tag; +} + +header srh_segment_list_t { + bit<128> sid; +} + +header gtpu_h { + bit<3> version; + bit<1> pt; + bit<1> reserved; + bit<1> e; + bit<1> s; + bit<1> pn; + bit<8> message_type; + bit<16> message_len; + bit<32> teid; +} + +typedef bit<16> bd_t; +typedef bit<16> vrf_t; +typedef bit<16> nexthop_t; +typedef bit<16> ifindex_t; +typedef bit<8> bypass_t; +const bypass_t BYPASS_L2 = 8w0x1; +const bypass_t BYPASS_L3 = 8w0x2; +const bypass_t BYPASS_ACL = 8w0x4; +const bypass_t BYPASS_ALL = 8w0xff; +typedef bit<128> srv6_sid_t; +struct srv6_metadata_t { + srv6_sid_t sid; + bit<16> rewrite; + bool psp; + bool usp; + bool decap; + bool encap; +} + +struct ingress_metadata_t { + bool checksum_err; + bd_t bd; + vrf_t vrf; + nexthop_t nexthop; + ifindex_t ifindex; + ifindex_t egress_ifindex; + bypass_t bypass; + srv6_metadata_t srv6; +} + +struct egress_metadata_t { + srv6_metadata_t srv6; +} + +header bridged_metadata_t { + bit<16> rewrite; + bit<1> psp; + bit<1> usp; + bit<1> decap; + bit<1> encap; + bit<4> pad; +} + +struct lookup_fields_t { + mac_addr_t mac_src_addr; + mac_addr_t mac_dst_addr; + bit<16> mac_type; + bit<4> ip_version; + bit<8> ip_proto; + bit<8> ip_ttl; + bit<8> ip_dscp; + bit<20> ipv6_flow_label; + ipv4_addr_t ipv4_src_addr; + ipv4_addr_t ipv4_dst_addr; + ipv6_addr_t ipv6_src_addr; + ipv6_addr_t ipv6_dst_addr; +} + +struct header_t { + bridged_metadata_t bridged_md; + ethernet_h ethernet; + vlan_tag_h vlan_tag; + ipv4_h ipv4; + ipv6_h ipv6; + srh_h srh; + srh_segment_list_t[5] srh_segment_list; + tcp_h tcp; + udp_h udp; + gtpu_h gtpu; + ethernet_h inner_ethernet; + ipv6_h inner_ipv6; + srh_h inner_srh; + srh_segment_list_t[5] inner_srh_segment_list; + ipv4_h inner_ipv4; + tcp_h inner_tcp; + udp_h inner_udp; +} + +parser TofinoIngressParser(packet_in pkt, out ingress_intrinsic_metadata_t ig_intr_md) { + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1: parse_resubmit; + 0: parse_port_metadata; + } + } + state parse_resubmit { + transition reject; + } + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition accept; + } +} + +parser TofinoEgressParser(packet_in pkt, out egress_intrinsic_metadata_t eg_intr_md) { + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} + +parser SwitchIngressParser(packet_in pkt, out header_t hdr, out ingress_metadata_t ig_md, out ingress_intrinsic_metadata_t ig_intr_md) { + Checksum() ipv4_checksum; + TofinoIngressParser() tofino_parser; + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type) { + ETHERTYPE_IPV4: parse_ipv4; + ETHERTYPE_IPV6: parse_ipv6; + ETHERTYPE_VLAN: parse_vlan; + default: accept; + } + } + state parse_vlan { + pkt.extract(hdr.vlan_tag); + transition select(hdr.vlan_tag.ether_type) { + ETHERTYPE_IPV4: parse_ipv4; + ETHERTYPE_IPV6: parse_ipv6; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + ipv4_checksum.add(hdr.ipv4); + ig_md.checksum_err = ipv4_checksum.verify(); + transition select(hdr.ipv4.protocol) { + IP_PROTOCOLS_TCP: parse_tcp; + IP_PROTOCOLS_UDP: parse_udp; + default: accept; + } + } + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition select(hdr.ipv6.next_hdr) { + IP_PROTOCOLS_TCP: parse_tcp; + IP_PROTOCOLS_UDP: parse_udp; + IP_PROTOCOLS_SRV6: parse_srh; + IP_PROTOCOLS_IPV6: parse_inner_ipv6; + IP_PROTOCOLS_IPV4: parse_inner_ipv4; + default: accept; + } + } + state parse_srh { + pkt.extract(hdr.srh); + transition parse_srh_segment_0; + } + state parse_srh_segment_0 { + pkt.extract(hdr.srh_segment_list[0]); + transition select(hdr.srh.last_entry) { + 0: parse_srh_next_header; + default: parse_srh_segment_1; + } + } + state set_active_segment_0 { + transition parse_srh_segment_1; + } + state parse_srh_segment_1 { + pkt.extract(hdr.srh_segment_list[1]); + transition select(hdr.srh.last_entry) { + 1: parse_srh_next_header; + default: parse_srh_segment_2; + } + } + state set_active_segment_1 { + transition parse_srh_segment_2; + } + state parse_srh_segment_2 { + pkt.extract(hdr.srh_segment_list[2]); + transition select(hdr.srh.last_entry) { + 2: parse_srh_next_header; + default: parse_srh_segment_3; + } + } + state set_active_segment_2 { + transition parse_srh_segment_3; + } + state parse_srh_segment_3 { + pkt.extract(hdr.srh_segment_list[3]); + transition select(hdr.srh.last_entry) { + 3: parse_srh_next_header; + default: parse_srh_segment_4; + } + } + state set_active_segment_3 { + transition parse_srh_segment_4; + } + state parse_srh_segment_4 { + pkt.extract(hdr.srh_segment_list[4]); + transition parse_srh_next_header; + } + state set_active_segment_4 { + ig_md.srv6.sid = hdr.srh_segment_list[4].sid; + transition parse_srh_next_header; + } + state parse_srh_next_header { + transition select(hdr.srh.next_hdr) { + IP_PROTOCOLS_IPV6: parse_inner_ipv6; + IP_PROTOCOLS_IPV4: parse_inner_ipv4; + IP_PROTOCOLS_SRV6: parse_inner_srh; + IP_PROTOCOLS_NONXT: accept; + default: reject; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + PORT_GTPU: parse_gtpu; + default: accept; + } + } + state parse_tcp { + pkt.extract(hdr.tcp); + transition accept; + } + state parse_gtpu { + pkt.extract(hdr.gtpu); + bit<4> version = pkt.lookahead>(); + transition select(version) { + 4w4: parse_inner_ipv4; + 4w6: parse_inner_ipv6; + } + } + state parse_inner_ipv4 { + pkt.extract(hdr.inner_ipv4); + transition select(hdr.inner_ipv4.protocol) { + IP_PROTOCOLS_TCP: parse_inner_tcp; + IP_PROTOCOLS_UDP: parse_inner_udp; + default: accept; + } + } + state parse_inner_ipv6 { + pkt.extract(hdr.inner_ipv6); + transition select(hdr.inner_ipv6.next_hdr) { + IP_PROTOCOLS_TCP: parse_inner_tcp; + IP_PROTOCOLS_UDP: parse_inner_udp; + IP_PROTOCOLS_SRV6: parse_inner_srh; + default: accept; + } + } + state parse_inner_srh { + pkt.extract(hdr.inner_srh); + transition accept; + } + state parse_inner_udp { + pkt.extract(hdr.inner_udp); + transition select(hdr.inner_udp.dst_port) { + default: accept; + } + } + state parse_inner_tcp { + pkt.extract(hdr.inner_tcp); + transition accept; + } +} + +control SwitchIngressDeparser(packet_out pkt, inout header_t hdr, in ingress_metadata_t ig_md, in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.bridged_md); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + pkt.emit(hdr.ipv6); + pkt.emit(hdr.srh); + pkt.emit(hdr.srh_segment_list); + pkt.emit(hdr.udp); + pkt.emit(hdr.tcp); + pkt.emit(hdr.gtpu); + pkt.emit(hdr.inner_ipv4); + pkt.emit(hdr.inner_ipv6); + pkt.emit(hdr.inner_srh); + pkt.emit(hdr.inner_srh_segment_list); + pkt.emit(hdr.inner_udp); + pkt.emit(hdr.inner_tcp); + } +} + +parser SwitchEgressParser(packet_in pkt, out header_t hdr, out egress_metadata_t eg_md, out egress_intrinsic_metadata_t eg_intr_md) { + TofinoEgressParser() tofino_parser; + state start { + tofino_parser.apply(pkt, eg_intr_md); + transition parse_bridged_metadata; + } + state parse_bridged_metadata { + pkt.extract(hdr.bridged_md); + eg_md.srv6.rewrite = hdr.bridged_md.rewrite; + eg_md.srv6.psp = (bool)hdr.bridged_md.psp; + eg_md.srv6.usp = (bool)hdr.bridged_md.psp; + eg_md.srv6.encap = (bool)hdr.bridged_md.encap; + eg_md.srv6.decap = (bool)hdr.bridged_md.decap; + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type) { + ETHERTYPE_IPV4: parse_ipv4; + ETHERTYPE_IPV6: parse_ipv6; + ETHERTYPE_VLAN: parse_vlan; + default: accept; + } + } + state parse_vlan { + pkt.extract(hdr.vlan_tag); + transition select(hdr.vlan_tag.ether_type) { + ETHERTYPE_IPV4: parse_ipv4; + ETHERTYPE_IPV6: parse_ipv6; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition select(hdr.ipv4.protocol) { + IP_PROTOCOLS_UDP: parse_udp; + IP_PROTOCOLS_TCP: parse_tcp; + default: accept; + } + } + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition select(hdr.ipv6.next_hdr) { + IP_PROTOCOLS_UDP: parse_udp; + IP_PROTOCOLS_TCP: parse_tcp; + IP_PROTOCOLS_SRV6: parse_srh; + IP_PROTOCOLS_IPV6: parse_inner_ipv6; + IP_PROTOCOLS_IPV4: parse_inner_ipv4; + default: accept; + } + } + state parse_srh { + pkt.extract(hdr.srh); + transition parse_srh_segment_0; + } + state parse_srh_segment_0 { + pkt.extract(hdr.srh_segment_list[0]); + transition select(hdr.srh.last_entry) { + 0: parse_srh_next_header; + default: parse_srh_segment_1; + } + } + state parse_srh_segment_1 { + pkt.extract(hdr.srh_segment_list[1]); + transition select(hdr.srh.last_entry) { + 1: parse_srh_next_header; + default: parse_srh_segment_2; + } + } + state parse_srh_segment_2 { + pkt.extract(hdr.srh_segment_list[2]); + transition select(hdr.srh.last_entry) { + 2: parse_srh_next_header; + default: parse_srh_segment_3; + } + } + state parse_srh_segment_3 { + pkt.extract(hdr.srh_segment_list[3]); + transition select(hdr.srh.last_entry) { + 3: parse_srh_next_header; + default: parse_srh_segment_4; + } + } + state parse_srh_segment_4 { + pkt.extract(hdr.srh_segment_list[4]); + transition parse_srh_next_header; + } + state parse_srh_next_header { + transition select(hdr.srh.next_hdr) { + IP_PROTOCOLS_IPV6: parse_inner_ipv6; + IP_PROTOCOLS_IPV4: parse_inner_ipv4; + IP_PROTOCOLS_SRV6: parse_inner_srh; + IP_PROTOCOLS_NONXT: accept; + default: reject; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + PORT_GTPU: parse_gtpu; + default: accept; + } + } + state parse_tcp { + pkt.extract(hdr.tcp); + transition select(hdr.tcp.dst_port) { + PORT_GTPU: parse_gtpu; + default: accept; + } + } + state parse_gtpu { + pkt.extract(hdr.gtpu); + bit<4> version = pkt.lookahead>(); + transition select(version) { + 4w4: parse_inner_ipv4; + 4w6: parse_inner_ipv6; + } + } + state parse_inner_ipv4 { + pkt.extract(hdr.inner_ipv4); + transition select(hdr.inner_ipv4.protocol) { + IP_PROTOCOLS_UDP: parse_inner_udp; + IP_PROTOCOLS_TCP: parse_inner_tcp; + default: accept; + } + } + state parse_inner_ipv6 { + pkt.extract(hdr.inner_ipv6); + transition select(hdr.inner_ipv6.next_hdr) { + IP_PROTOCOLS_UDP: parse_inner_udp; + IP_PROTOCOLS_TCP: parse_inner_tcp; + IP_PROTOCOLS_SRV6: parse_inner_srh; + default: accept; + } + } + state parse_inner_srh { + pkt.extract(hdr.inner_srh); + transition accept; + } + state parse_inner_udp { + pkt.extract(hdr.inner_udp); + transition accept; + } + state parse_inner_tcp { + pkt.extract(hdr.inner_tcp); + transition accept; + } +} + +control SwitchEgressDeparser(packet_out pkt, inout header_t hdr, in egress_metadata_t eg_md, in egress_intrinsic_metadata_for_deparser_t eg_dprsr_md) { + Checksum() ipv4_checksum; + apply { + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.total_len, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.src_addr, hdr.ipv4.dst_addr }); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + pkt.emit(hdr.ipv6); + pkt.emit(hdr.srh); + pkt.emit(hdr.srh_segment_list); + pkt.emit(hdr.udp); + pkt.emit(hdr.gtpu); + pkt.emit(hdr.inner_ipv4); + pkt.emit(hdr.inner_ipv6); + pkt.emit(hdr.inner_srh); + pkt.emit(hdr.inner_srh_segment_list); + pkt.emit(hdr.inner_udp); + } +} + +control SRv6(inout header_t hdr, inout ingress_metadata_t ig_md, inout lookup_fields_t lkp) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) cnt1; + action t() { + } + action t_insert(srv6_sid_t s1, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + hdr.ipv6.dst_addr = s1; + ig_md.srv6.rewrite = rewrite_index; + } + action t_insert_red(srv6_sid_t s1, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + hdr.ipv6.dst_addr = s1; + ig_md.srv6.rewrite = rewrite_index; + } + action t_encaps(srv6_sid_t s1, bit<20> flow_label, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + lkp.ipv6_flow_label = flow_label; + ig_md.srv6.encap = true; + ig_md.srv6.rewrite = rewrite_index; + } + action t_encaps_red(srv6_sid_t s1, bit<20> flow_label, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + lkp.ipv6_flow_label = flow_label; + ig_md.srv6.encap = true; + ig_md.srv6.rewrite = rewrite_index; + } + action t_encaps_l2() { + } + action t_encaps_l2_red() { + } + action drop() { + cnt1.count(); + } + action end() { + lkp.ipv6_dst_addr = ig_md.srv6.sid; + hdr.srh.segment_left = hdr.srh.segment_left - 1; + hdr.ipv6.dst_addr = ig_md.srv6.sid; + cnt1.count(); + } + action end_x(nexthop_t nexthop) { + ig_md.nexthop = nexthop; + ig_md.bypass = BYPASS_L3; + hdr.srh.segment_left = hdr.srh.segment_left - 1; + hdr.ipv6.dst_addr = ig_md.srv6.sid; + cnt1.count(); + } + action end_t() { + cnt1.count(); + } + action end_dx2(ifindex_t ifindex) { + ig_md.egress_ifindex = ifindex; + ig_md.bypass = BYPASS_ALL; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_dx2v() { + cnt1.count(); + } + action end_dt2u(bd_t bd) { + lkp.mac_src_addr = hdr.inner_ethernet.src_addr; + lkp.mac_dst_addr = hdr.inner_ethernet.dst_addr; + ig_md.srv6.decap = true; + ig_md.bd = bd; + cnt1.count(); + } + action end_dt2m() { + lkp.mac_src_addr = hdr.inner_ethernet.src_addr; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_dx6(nexthop_t nexthop) { + ig_md.nexthop = nexthop; + ig_md.bypass = BYPASS_L3; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_dx4(nexthop_t nexthop) { + ig_md.nexthop = nexthop; + ig_md.bypass = BYPASS_L3; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_dt4(vrf_t vrf) { + ig_md.vrf = vrf; + lkp.ipv4_src_addr = hdr.inner_ipv4.src_addr; + lkp.ipv4_dst_addr = hdr.inner_ipv4.dst_addr; + lkp.ip_proto = hdr.inner_ipv4.protocol; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_dt6(vrf_t vrf) { + ig_md.vrf = vrf; + lkp.ipv6_src_addr = hdr.inner_ipv6.src_addr; + lkp.ipv6_dst_addr = hdr.inner_ipv6.dst_addr; + lkp.ip_proto = hdr.inner_ipv6.next_hdr; + lkp.ipv6_flow_label = hdr.inner_ipv6.flow_label; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_dt46() { + } + action end_b6(srv6_sid_t s1, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + hdr.ipv6.dst_addr = s1; + ig_md.srv6.rewrite = rewrite_index; + cnt1.count(); + } + action end_b6_red(srv6_sid_t s1, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + hdr.ipv6.dst_addr = s1; + ig_md.srv6.rewrite = rewrite_index; + cnt1.count(); + } + action end_b6_encaps(bit<16> rewrite_index) { + lkp.ipv6_dst_addr = ig_md.srv6.sid; + hdr.srh.segment_left = hdr.srh.segment_left - 1; + hdr.ipv6.dst_addr = ig_md.srv6.sid; + ig_md.srv6.rewrite = rewrite_index; + ig_md.srv6.encap = true; + cnt1.count(); + } + action end_b6_encaps_red(bit<16> rewrite_index) { + lkp.ipv6_dst_addr = ig_md.srv6.sid; + hdr.srh.segment_left = hdr.srh.segment_left - 1; + hdr.ipv6.dst_addr = ig_md.srv6.sid; + ig_md.srv6.rewrite = rewrite_index; + ig_md.srv6.encap = true; + cnt1.count(); + } + action end_bm() { + } + action end_s() { + } + action end_map(srv6_sid_t sid) { + lkp.ipv6_dst_addr = sid; + hdr.ipv6.dst_addr = sid; + cnt1.count(); + } + action end_m_gtp6_d(srv6_sid_t s1, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + ig_md.srv6.rewrite = rewrite_index; + ig_md.srv6.decap = true; + ig_md.srv6.encap = true; + cnt1.count(); + } + action end_m_gtp6_e(bit<16> rewrite_index) { + hdr.srh.segment_left = hdr.srh.segment_left - 1; + lkp.ipv6_dst_addr = ig_md.srv6.sid; + ig_md.srv6.rewrite = rewrite_index; + ig_md.srv6.decap = true; + ig_md.srv6.encap = true; + cnt1.count(); + } + action end_m_gtp4_d() { + cnt1.count(); + } + action end_limit() { + cnt1.count(); + } + action t_map() { + } + action end_as(nexthop_t nexthop) { + ig_md.nexthop = nexthop; + ig_md.bypass = BYPASS_L3; + ig_md.srv6.decap = true; + cnt1.count(); + } + action end_ad(nexthop_t nexthop) { + ig_md.nexthop = nexthop; + ig_md.bypass = BYPASS_L3; + ig_md.srv6.decap = true; + cnt1.count(); + } + action static_proxy(srv6_sid_t s1, bit<16> rewrite_index) { + lkp.ipv6_dst_addr = s1; + ig_md.srv6.rewrite = rewrite_index; + ig_md.srv6.encap = true; + cnt1.count(); + } + action dynamic_proxy() { + cnt1.count(); + } + table local_sid { + key = { + hdr.ipv6.dst_addr : ternary; + hdr.ipv6.next_hdr : ternary; + hdr.srh.isValid() : ternary; + hdr.srh.segment_left: ternary; + hdr.srh.next_hdr : ternary; + ig_md.ifindex : ternary; + } + actions = { + @defaultonly NoAction; + drop; + end; + end_x; + end_t; + end_dx2; + end_dx2v; + end_dt2u; + end_dt2m; + end_dx4; + end_dx6; + end_dt4; + end_dt6; + end_b6; + end_b6_red; + end_b6_encaps; + end_b6_encaps_red; + end_map; + end_m_gtp6_d; + end_m_gtp6_e; + end_m_gtp4_d; + end_limit; + end_as; + end_ad; + static_proxy; + dynamic_proxy; + } + const default_action = NoAction; + counters = cnt1; + } + table transit_ { + key = { + hdr.ipv6.dst_addr: lpm; + } + actions = { + t; + t_insert; + t_insert_red; + t_encaps; + t_encaps_red; + t_encaps_l2; + t_encaps_l2_red; + } + } + apply { + if (hdr.ipv6.isValid()) { + if (!local_sid.apply().hit) { + transit_.apply(); + } + } + } +} + +control SRv6Decap(in srv6_metadata_t srv6_md, inout header_t hdr) { + action decap_inner_udp() { + hdr.udp = hdr.inner_udp; + hdr.inner_udp.setInvalid(); + hdr.gtpu.setInvalid(); + } + action decap_inner_tcp() { + hdr.tcp = hdr.inner_tcp; + hdr.inner_tcp.setInvalid(); + hdr.gtpu.setInvalid(); + } + action decap_inner_unknown() { + hdr.gtpu.setInvalid(); + } + table decap_inner_l4 { + key = { + hdr.inner_udp.isValid(): exact; + hdr.inner_tcp.isValid(): exact; + } + actions = { + decap_inner_udp; + decap_inner_tcp; + decap_inner_unknown; + } + const default_action = decap_inner_unknown; + const entries = { + (true, false) : decap_inner_udp(); + (false, true) : decap_inner_tcp(); + } + } + action remove_srh_header() { + hdr.srh.setInvalid(); + hdr.srh_segment_list[0].setInvalid(); + hdr.srh_segment_list[1].setInvalid(); + hdr.srh_segment_list[2].setInvalid(); + hdr.srh_segment_list[3].setInvalid(); + hdr.srh_segment_list[4].setInvalid(); + } + action remove_inner_srh_header() { + hdr.inner_srh.setInvalid(); + hdr.inner_srh_segment_list[0].setInvalid(); + hdr.inner_srh_segment_list[1].setInvalid(); + hdr.inner_srh_segment_list[2].setInvalid(); + hdr.inner_srh_segment_list[3].setInvalid(); + hdr.inner_srh_segment_list[4].setInvalid(); + } + action copy_srh_header() { + hdr.srh = hdr.inner_srh; + hdr.srh_segment_list[0] = hdr.inner_srh_segment_list[0]; + hdr.srh_segment_list[1] = hdr.inner_srh_segment_list[1]; + hdr.srh_segment_list[2] = hdr.inner_srh_segment_list[2]; + hdr.srh_segment_list[3] = hdr.inner_srh_segment_list[3]; + } + action decap_inner_non_ip() { + hdr.ethernet = hdr.inner_ethernet; + hdr.inner_ethernet.setInvalid(); + hdr.ipv6.setInvalid(); + remove_srh_header(); + } + action decap_inner_ipv6() { + hdr.ethernet.ether_type = ETHERTYPE_IPV6; + hdr.ipv6 = hdr.inner_ipv6; + hdr.inner_ipv6.setInvalid(); + remove_srh_header(); + } + action decap_inner_ipv6_srh() { + hdr.ethernet.ether_type = ETHERTYPE_IPV6; + hdr.ipv6 = hdr.inner_ipv6; + hdr.inner_ipv6.setInvalid(); + copy_srh_header(); + remove_inner_srh_header(); + } + table decap_inner_ip { + key = { + hdr.inner_ipv6.isValid(): exact; + hdr.inner_srh.isValid() : exact; + } + actions = { + NoAction; + decap_inner_non_ip; + decap_inner_ipv6; + decap_inner_ipv6_srh; + } + const default_action = NoAction; + const entries = { + (false, false) : decap_inner_non_ip(); + (true, false) : decap_inner_ipv6(); + (true, true) : decap_inner_ipv6_srh(); + } + } + apply { + if (srv6_md.decap) { + decap_inner_l4.apply(); + decap_inner_ip.apply(); + } + } +} + +control SRv6Encap(in srv6_metadata_t srv6_md, inout header_t hdr) { + bit<8> proto; + bit<16> len; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) cnt2; + action encap_outer_udp() { + hdr.inner_udp = hdr.udp; + hdr.udp.setInvalid(); + } + action encap_outer_tcp() { + hdr.inner_tcp = hdr.tcp; + hdr.tcp.setInvalid(); + } + action encap_outer_unknown() { + } + table encap_outer_l4 { + key = { + hdr.udp.isValid(): exact; + hdr.tcp.isValid(): exact; + } + actions = { + encap_outer_tcp; + encap_outer_udp; + encap_outer_unknown; + } + const default_action = encap_outer_unknown; + const entries = { + (true, false) : encap_outer_udp(); + (false, true) : encap_outer_tcp(); + } + } + action remove_srh_header() { + hdr.srh.setInvalid(); + hdr.srh_segment_list[0].setInvalid(); + hdr.srh_segment_list[1].setInvalid(); + hdr.srh_segment_list[2].setInvalid(); + hdr.srh_segment_list[3].setInvalid(); + } + action copy_srh_header() { + hdr.inner_srh = hdr.srh; + hdr.inner_srh_segment_list[0] = hdr.srh_segment_list[0]; + hdr.inner_srh_segment_list[1] = hdr.srh_segment_list[1]; + hdr.inner_srh_segment_list[2] = hdr.srh_segment_list[2]; + hdr.inner_srh_segment_list[3] = hdr.srh_segment_list[3]; + } + action encap_outer_ipv6() { + hdr.inner_ipv6 = hdr.ipv6; + hdr.ipv6.setInvalid(); + len = hdr.ipv6.payload_len + 16w40; + proto = IP_PROTOCOLS_IPV6; + } + action encap_outer_ipv6_srh() { + hdr.inner_ipv6 = hdr.ipv6; + copy_srh_header(); + hdr.ipv6.setInvalid(); + len = hdr.ipv6.payload_len + 16w40; + proto = IP_PROTOCOLS_IPV6; + } + action push_outer_srh() { + copy_srh_header(); + remove_srh_header(); + proto = IP_PROTOCOLS_SRV6; + } + action no_encap() { + proto = hdr.ipv6.next_hdr; + } + table encap_outer_ip { + key = { + srv6_md.encap : exact; + hdr.ipv6.isValid(): exact; + hdr.srh.isValid() : exact; + } + actions = { + no_encap; + encap_outer_ipv6; + encap_outer_ipv6_srh; + push_outer_srh; + } + const entries = { + (false, true, true) : push_outer_srh(); + (true, true, false) : encap_outer_ipv6(); + (true, true, true) : encap_outer_ipv6_srh(); + } + const default_action = no_encap; + } + action insert_srh_header(in bit<8> next_hdr, in bit<8> hdr_ext_len, in bit<8> last_entry, in bit<8> segment_left) { + hdr.srh.setValid(); + hdr.srh.next_hdr = next_hdr; + hdr.srh.hdr_ext_len = hdr_ext_len; + hdr.srh.routing_type = 0x4; + hdr.srh.segment_left = segment_left; + hdr.srh.last_entry = last_entry; + hdr.srh.flags = 0; + hdr.srh.tag = 0; + } + action rewrite_ipv6(ipv6_addr_t src_addr, ipv6_addr_t dst_addr, bit<8> next_hdr) { + hdr.ethernet.ether_type = ETHERTYPE_IPV6; + hdr.ipv6.setValid(); + hdr.ipv6.version = 4w6; + hdr.ipv6.traffic_class = 0; + hdr.ipv6.flow_label = 0; + hdr.ipv6.payload_len = len; + hdr.ipv6.next_hdr = next_hdr; + hdr.ipv6.hop_limit = 8w64; + hdr.ipv6.src_addr = src_addr; + hdr.ipv6.dst_addr = dst_addr; + } + table ipv6_rewrite { + key = { + srv6_md.rewrite: exact; + } + actions = { + NoAction; + rewrite_ipv6; + } + const default_action = NoAction; + } + action rewrite_srh_0() { + hdr.ipv6.next_hdr = proto; + cnt2.count(); + } + action rewrite_srh_1(bit<8> segment_left, srv6_sid_t s1) { + hdr.ipv6.payload_len = hdr.ipv6.payload_len + 16w24; + hdr.ipv6.next_hdr = IP_PROTOCOLS_SRV6; + insert_srh_header(proto, 8w2, 8w0, segment_left); + hdr.srh_segment_list[0].setValid(); + hdr.srh_segment_list[0].sid = s1; + cnt2.count(); + } + action rewrite_srh_2(bit<8> segment_left, srv6_sid_t s1, srv6_sid_t s2) { + hdr.ipv6.payload_len = hdr.ipv6.payload_len + 16w40; + hdr.ipv6.next_hdr = IP_PROTOCOLS_SRV6; + insert_srh_header(proto, 8w4, 8w1, segment_left); + hdr.srh_segment_list[0].setValid(); + hdr.srh_segment_list[1].setValid(); + hdr.srh_segment_list[0].sid = s2; + hdr.srh_segment_list[1].sid = s1; + cnt2.count(); + } + action rewrite_srh_3(bit<8> segment_left, srv6_sid_t s1, srv6_sid_t s2, srv6_sid_t s3) { + hdr.ipv6.payload_len = hdr.ipv6.payload_len + 16w56; + hdr.ipv6.next_hdr = IP_PROTOCOLS_SRV6; + insert_srh_header(proto, 8w6, 8w2, segment_left); + hdr.srh_segment_list[0].setValid(); + hdr.srh_segment_list[1].setValid(); + hdr.srh_segment_list[2].setValid(); + hdr.srh_segment_list[0].sid = s3; + hdr.srh_segment_list[1].sid = s2; + hdr.srh_segment_list[2].sid = s1; + cnt2.count(); + } + action rewrite_srh_4(bit<8> segment_left, srv6_sid_t s1, srv6_sid_t s2, srv6_sid_t s3, srv6_sid_t s4) { + hdr.ipv6.payload_len = hdr.ipv6.payload_len + 16w72; + hdr.ipv6.next_hdr = IP_PROTOCOLS_SRV6; + insert_srh_header(proto, 8w8, 8w3, segment_left); + hdr.srh_segment_list[0].setValid(); + hdr.srh_segment_list[1].setValid(); + hdr.srh_segment_list[2].setValid(); + hdr.srh_segment_list[3].setValid(); + hdr.srh_segment_list[0].sid = s4; + hdr.srh_segment_list[1].sid = s3; + hdr.srh_segment_list[2].sid = s2; + hdr.srh_segment_list[3].sid = s1; + cnt2.count(); + } + action rewrite_srh_5(bit<8> segment_left, srv6_sid_t s1, srv6_sid_t s2, srv6_sid_t s3, srv6_sid_t s4, srv6_sid_t s5) { + hdr.ipv6.payload_len = hdr.ipv6.payload_len + 16w88; + hdr.ipv6.next_hdr = IP_PROTOCOLS_SRV6; + insert_srh_header(proto, 8w10, 8w4, segment_left); + hdr.srh_segment_list[0].setValid(); + hdr.srh_segment_list[1].setValid(); + hdr.srh_segment_list[2].setValid(); + hdr.srh_segment_list[3].setValid(); + hdr.srh_segment_list[4].setValid(); + hdr.srh_segment_list[0].sid = s5; + hdr.srh_segment_list[1].sid = s4; + hdr.srh_segment_list[2].sid = s3; + hdr.srh_segment_list[3].sid = s2; + hdr.srh_segment_list[4].sid = s1; + cnt2.count(); + } + table srh_rewrite { + key = { + srv6_md.rewrite: exact; + } + actions = { + @defaultonly NoAction; + rewrite_srh_0; + rewrite_srh_1; + rewrite_srh_2; + rewrite_srh_3; + } + const default_action = NoAction; + counters = cnt2; + } + apply { + if (srv6_md.rewrite != 0) { + encap_outer_l4.apply(); + encap_outer_ip.apply(); + } + ipv6_rewrite.apply(); + srh_rewrite.apply(); + } +} + +control PktValidation(in header_t hdr, out lookup_fields_t lkp) { + const bit<32> table_size = 512; + action malformed_pkt() { + } + action valid_pkt_untagged() { + lkp.mac_src_addr = hdr.ethernet.src_addr; + lkp.mac_dst_addr = hdr.ethernet.dst_addr; + lkp.mac_type = hdr.ethernet.ether_type; + } + action valid_pkt_tagged() { + lkp.mac_src_addr = hdr.ethernet.src_addr; + lkp.mac_dst_addr = hdr.ethernet.dst_addr; + lkp.mac_type = hdr.vlan_tag.ether_type; + } + table validate_ethernet { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + hdr.vlan_tag.isValid(): ternary; + } + actions = { + malformed_pkt; + valid_pkt_untagged; + valid_pkt_tagged; + } + size = table_size; + } + action valid_ipv4_pkt() { + lkp.ip_version = 4w4; + lkp.ip_dscp = hdr.ipv4.diffserv; + lkp.ip_proto = hdr.ipv4.protocol; + lkp.ip_ttl = hdr.ipv4.ttl; + lkp.ipv4_src_addr = hdr.ipv4.src_addr; + lkp.ipv4_dst_addr = hdr.ipv4.dst_addr; + } + table validate_ipv4 { + key = { + hdr.ipv4.version: ternary; + hdr.ipv4.ihl : ternary; + hdr.ipv4.ttl : ternary; + } + actions = { + valid_ipv4_pkt; + malformed_pkt; + } + size = table_size; + } + action valid_ipv6_pkt() { + lkp.ip_version = 4w6; + lkp.ip_dscp = hdr.ipv6.traffic_class; + lkp.ip_proto = hdr.ipv6.next_hdr; + lkp.ip_ttl = hdr.ipv6.hop_limit; + lkp.ipv6_src_addr = hdr.ipv6.src_addr; + lkp.ipv6_dst_addr = hdr.ipv6.dst_addr; + } + table validate_ipv6 { + key = { + hdr.ipv6.version : ternary; + hdr.ipv6.hop_limit: ternary; + } + actions = { + valid_ipv6_pkt; + malformed_pkt; + } + size = table_size; + } + apply { + validate_ethernet.apply(); + if (hdr.ipv4.isValid()) { + validate_ipv4.apply(); + } else if (hdr.ipv6.isValid()) { + validate_ipv6.apply(); + } + } +} + +control PortMapping(in PortId_t port, in vlan_tag_h vlan_tag, inout ingress_metadata_t ig_md)(bit<32> port_vlan_table_size, bit<32> bd_table_size) { + ActionProfile(bd_table_size) bd_action_profile; + action set_port_attributes(ifindex_t ifindex) { + ig_md.ifindex = ifindex; + } + table port_mapping { + key = { + port: exact; + } + actions = { + set_port_attributes; + } + } + action set_bd_attributes(bd_t bd, vrf_t vrf) { + ig_md.bd = bd; + ig_md.vrf = vrf; + } + table port_vlan_to_bd_mapping { + key = { + ig_md.ifindex : exact; + vlan_tag.isValid(): ternary; + vlan_tag.vid : ternary; + } + actions = { + NoAction; + set_bd_attributes; + } + const default_action = NoAction; + implementation = bd_action_profile; + size = port_vlan_table_size; + } + apply { + port_mapping.apply(); + port_vlan_to_bd_mapping.apply(); + } +} + +control MAC(in mac_addr_t dst_addr, in bd_t bd, out ifindex_t egress_ifindex)(bit<32> mac_table_size) { + action dmac_miss() { + egress_ifindex = 16w0xffff; + } + action dmac_hit(ifindex_t ifindex) { + egress_ifindex = ifindex; + } + table dmac { + key = { + bd : exact; + dst_addr: exact; + } + actions = { + dmac_miss; + dmac_hit; + } + const default_action = dmac_miss; + size = mac_table_size; + } + apply { + dmac.apply(); + } +} + +control FIB(in ipv4_addr_t dst_addr, in vrf_t vrf, out nexthop_t nexthop)(bit<32> host_table_size, bit<32> lpm_table_size) { + action fib_hit(nexthop_t nexthop_index) { + nexthop = nexthop_index; + } + action fib_miss() { + } + table fib { + key = { + vrf : exact; + dst_addr: exact; + } + actions = { + fib_miss; + fib_hit; + } + const default_action = fib_miss; + size = host_table_size; + } + table fib_lpm { + key = { + vrf : exact; + dst_addr: lpm; + } + actions = { + fib_miss; + fib_hit; + } + const default_action = fib_miss; + size = lpm_table_size; + } + apply { + if (!fib.apply().hit) { + fib_lpm.apply(); + } + } +} + +control FIBv6(in ipv6_addr_t dst_addr, in vrf_t vrf, out nexthop_t nexthop)(bit<32> host_table_size, bit<32> lpm_table_size) { + action fib_hit(nexthop_t nexthop_index) { + nexthop = nexthop_index; + } + action fib_miss() { + } + table fib { + key = { + vrf : exact; + dst_addr: exact; + } + actions = { + fib_miss; + fib_hit; + } + const default_action = fib_miss; + size = host_table_size; + } + table fib_lpm { + key = { + vrf : exact; + dst_addr: lpm; + } + actions = { + fib_miss; + fib_hit; + } + const default_action = fib_miss; + size = lpm_table_size; + } + apply { + if (!fib.apply().hit) { + fib_lpm.apply(); + } + } +} + +control Nexthop(in lookup_fields_t lkp, inout header_t hdr, inout ingress_metadata_t ig_md)(bit<32> table_size) { + bool routed; + Hash>(HashAlgorithm_t.CRC32) sel_hash; + ActionProfile(2048) ecmp_selector_ap; + + ActionSelector(ecmp_selector_ap, // action profile + sel_hash, // hash extern + SelectorMode_t.FAIR, // Selector algorithm + 200, // max group size + 100 // max number of groups + ) ecmp_selector; + action set_nexthop_attributes(bd_t bd, mac_addr_t dmac) { + routed = true; + ig_md.bd = bd; + hdr.ethernet.dst_addr = dmac; + } + table ecmp { + key = { + ig_md.nexthop : exact; + lkp.ipv6_src_addr : selector; + lkp.ipv6_dst_addr : selector; + lkp.ipv6_flow_label: selector; + lkp.ipv4_src_addr : selector; + lkp.ipv4_dst_addr : selector; + } + actions = { + NoAction; + set_nexthop_attributes; + } + const default_action = NoAction; + size = table_size; + implementation = ecmp_selector; + } + table nexthop { + key = { + ig_md.nexthop: exact; + } + actions = { + NoAction; + set_nexthop_attributes; + } + const default_action = NoAction; + size = table_size; + } + action rewrite_ipv4() { + hdr.ipv4.ttl = hdr.ipv4.ttl - 1; + } + action rewrite_ipv6() { + hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1; + } + table ip_rewrite { + key = { + hdr.ipv4.isValid(): exact; + hdr.ipv6.isValid(): exact; + } + actions = { + rewrite_ipv4; + rewrite_ipv6; + } + const entries = { + (true, false) : rewrite_ipv4(); + (false, true) : rewrite_ipv6(); + } + } + action rewrite_smac(mac_addr_t smac) { + hdr.ethernet.src_addr = smac; + } + table smac_rewrite { + key = { + ig_md.bd: exact; + } + actions = { + NoAction; + rewrite_smac; + } + const default_action = NoAction; + } + apply { + routed = false; + switch (nexthop.apply().action_run) { + NoAction: { + ecmp.apply(); + } + } + if (routed) { + ip_rewrite.apply(); + smac_rewrite.apply(); + } + } +} + +control LAG(in lookup_fields_t lkp, in ifindex_t ifindex, out PortId_t egress_port) { + Hash>(HashAlgorithm_t.CRC32) sel_hash; + ActionProfile(2048) lag_selector_ap; + + ActionSelector(lag_selector_ap, // action profile + sel_hash, // hash extern + SelectorMode_t.FAIR, // Selector algorithm + 200, // max group size + 100 // max number of groups + ) lag_selector; + action set_port(PortId_t port) { + egress_port = port; + } + action lag_miss() { + } + table lag { + key = { + ifindex : exact; + lkp.ipv6_src_addr : selector; + lkp.ipv6_dst_addr : selector; + lkp.ipv6_flow_label: selector; + lkp.ipv4_src_addr : selector; + lkp.ipv4_dst_addr : selector; + } + actions = { + lag_miss; + set_port; + } + const default_action = lag_miss; + size = 1024; + implementation = lag_selector; + } + apply { + lag.apply(); + } +} + +control SwitchIngress(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + PortMapping(1024, 1024) port_mapping; + PktValidation() pkt_validation; + SRv6() srv6; + MAC(1024) mac; + FIB(1024, 1024) fib; + FIBv6(1024, 1024) fibv6; + Nexthop(1024) nexthop; + LAG() lag; + lookup_fields_t lkp; + action add_bridged_md() { + hdr.bridged_md.setValid(); + hdr.bridged_md.rewrite = ig_md.srv6.rewrite; + hdr.bridged_md.psp = (bit<1>)ig_md.srv6.psp; + hdr.bridged_md.usp = (bit<1>)ig_md.srv6.usp; + hdr.bridged_md.decap = (bit<1>)ig_md.srv6.decap; + hdr.bridged_md.encap = (bit<1>)ig_md.srv6.encap; + } + action rmac_hit() { + } + table rmac { + key = { + lkp.mac_dst_addr: exact; + } + actions = { + NoAction; + rmac_hit; + } + const default_action = NoAction; + size = 1024; + } + apply { + port_mapping.apply(ig_intr_md.ingress_port, hdr.vlan_tag, ig_md); + pkt_validation.apply(hdr, lkp); + switch (rmac.apply().action_run) { + rmac_hit: { + ig_md.bypass = 0; + srv6.apply(hdr, ig_md, lkp); + if (!(ig_md.bypass & BYPASS_L3 != 0)) { + if (lkp.ip_version == 4w4) { + fib.apply(lkp.ipv4_dst_addr, ig_md.vrf, ig_md.nexthop); + } else if (lkp.ip_version == 4w6) { + fibv6.apply(lkp.ipv6_dst_addr, ig_md.vrf, ig_md.nexthop); + } + } + } + } + nexthop.apply(lkp, hdr, ig_md); + if (!(ig_md.bypass & BYPASS_L2 != 0)) { + mac.apply(hdr.ethernet.dst_addr, ig_md.bd, ig_md.egress_ifindex); + } + lag.apply(lkp, ig_md.egress_ifindex, ig_tm_md.ucast_egress_port); + add_bridged_md(); + } +} + +control SwitchEgress(inout header_t hdr, inout egress_metadata_t eg_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_from_prsr, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, inout egress_intrinsic_metadata_for_output_port_t eg_intr_md_for_oport) { + SRv6Decap() decap; + SRv6Encap() encap; + apply { + decap.apply(eg_md.srv6, hdr); + encap.apply(eg_md.srv6, hdr); + } +} + +Pipeline(SwitchIngressParser(), SwitchIngress(), SwitchIngressDeparser(), SwitchEgressParser(), SwitchEgress(), SwitchEgressDeparser()) pipe; + +Switch(pipe) main; \ No newline at end of file diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_exact.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_exact.p4 new file mode 100644 index 00000000000..9fb01b932b3 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_exact.p4 @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action a() { h.ethernet.dst_addr = 0xFFFFFF; } + + table t { + key = { + h.ethernet.ether_type : exact; + } + actions = { + a; + NoAction; + } + default_action = a; + } + + apply { + t.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_lpm.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_lpm.p4 new file mode 100644 index 00000000000..cdf816f3b21 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_lpm.p4 @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action a() { h.ethernet.dst_addr = 0xFFFFFF; } + + table t { + key = { + h.ethernet.ether_type : lpm; + } + actions = { + a; + NoAction; + } + default_action = a; + } + + apply { + t.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary.p4 new file mode 100644 index 00000000000..df18d2bdb2e --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary.p4 @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action a() { h.ethernet.dst_addr = 0xFFFFFF; } + + table t { + key = { + h.ethernet.ether_type : ternary; + } + actions = { + a; + NoAction; + } + default_action = a; + } + + apply { + t.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary_anno.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary_anno.p4 new file mode 100644 index 00000000000..3833c2db691 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_control_ternary_anno.p4 @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action a() { h.ethernet.dst_addr = 0xFFFFFF; } + + @ternary(1) + table t { + key = { + h.ethernet.ether_type : exact; + } + actions = { + a; + NoAction; + } + default_action = a; + } + + apply { + t.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_entries_range.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_entries_range.p4 new file mode 100644 index 00000000000..f8e649ace51 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_entries_range.p4 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 3 +#include +#elif __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select (hdr.ethernet.ether_type) { + ETHERTYPE_IPV4 : parse_ipv4; + default : accept; + } + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_intr_dprsr_md) { + + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t hdr, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_intr_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_intr_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_intr_tm_md) { + + action hit(PortId_t port) { + ig_intr_tm_md.ucast_egress_port = 0x8; + } + + action miss() { + ig_intr_dprsr_md.drop_ctl = 0x1; // Drop packet. + } + + table forward { + key = { + hdr.ethernet.src_addr : exact; + hdr.ipv4.total_len : range; + } + + actions = { + hit; + miss; + } + + const default_action = miss; + const entries = { + (0x0, 0x0000 .. 0xFFFF) : hit(8); + } + size = 1024; + } + + apply { + forward.apply(); + + // No need for egress processing, skip it and use empty controls for egress. + ig_intr_tm_md.bypass_egress = 1w1; + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + EmptyEgressParser(), + EmptyEgress(), + EmptyEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_no_key.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_no_key.p4 new file mode 100644 index 00000000000..c41d2b0ae7b --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_table_no_key.p4 @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + action a() { h.ethernet.dst_addr = 0xFFFFFF; } + + table t { + actions = { + a; + NoAction; + } + default_action = NoAction; + } + + apply { + t.apply(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_tainted_table_key.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_tainted_table_key.p4 new file mode 100644 index 00000000000..ec830c5203a --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_tainted_table_key.p4 @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition accept; + } + +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + + // Hash>(HashAlgorithm_t.CRC16) portHash; + + action set_output_port(bit<9> egress_port) { + ig_tm_md.ucast_egress_port = egress_port; + // ig_tm_md.ucast_egress_port = portHash.get({h.ethernet.dst_addr}); + } + + table forward_table { + key = { + h.ethernet.src_addr : exact; + } + actions = { + set_output_port; + NoAction; + } + default_action = NoAction; + } + + apply { + h.ethernet.src_addr = 48w1; + forward_table.apply(); + // Let's skip the egress pipeline, we do not need it. + ig_tm_md.bypass_egress = 1; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_undefined.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_undefined.p4 new file mode 100644 index 00000000000..e2102af05a6 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tna_undefined.p4 @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +/******************************************************************************* + * BAREFOOT NETWORKS CONFIDENTIAL & PROPRIETARY + * + * Copyright (c) 2019-present Barefoot Networks, Inc. + * + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of + * Barefoot Networks, Inc. and its suppliers, if any. The intellectual and + * technical concepts contained herein are proprietary to Barefoot Networks, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, patents in + * process, and are protected by trade secret or copyright law. Dissemination of + * this information or reproduction of this material is strictly forbidden unless + * prior written permission is obtained from Barefoot Networks, Inc. + * + * No warranty, explicit or implicit is provided, unless granted under a written + * agreement with Barefoot Networks, Inc. + * + ******************************************************************************/ + +#include +#if __TARGET_TOFINO__ == 2 +#include +#else +#include +#endif + +#include "common/headers.p4" +#include "common/util.p4" + + +struct metadata_t {} + +// --------------------------------------------------------------------------- +// Ingress parser +// --------------------------------------------------------------------------- +parser SwitchIngressParser( + packet_in pkt, + out header_t hdr, + out metadata_t ig_md, + out ingress_intrinsic_metadata_t ig_intr_md) { + TofinoIngressParser() tofino_parser; + + state start { + tofino_parser.apply(pkt, ig_intr_md); + transition parse_ethernet; + } + + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition parse_ipv4; + } + + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition accept; + } +} + +// --------------------------------------------------------------------------- +// Ingress Deparser +// --------------------------------------------------------------------------- +control SwitchIngressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t ig_md, + in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +control SwitchIngress( + inout header_t h, + inout metadata_t ig_md, + in ingress_intrinsic_metadata_t ig_intr_md, + in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, + inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, + inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + bit<16> tmp; + apply { + h.ethernet.ether_type = 1; + // This should reset the header. + h.ethernet.setInvalid(); + h.ethernet.setValid(); + // We also need to explicitly set the output port. + ig_tm_md.ucast_egress_port = 8; + } +} + +parser SwitchEgressParser( + packet_in pkt, + out header_t hdr, + out metadata_t eg_md, + out egress_intrinsic_metadata_t eg_intr_md) { + + state start { + pkt.extract(eg_intr_md); + pkt.extract(hdr.ethernet); + pkt.extract(hdr.ipv4); + transition accept; + } +} + +control SwitchEgress( + inout header_t hdr, + inout metadata_t eg_md, + in egress_intrinsic_metadata_t eg_intr_md, + in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + inout egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md, + inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + apply { + } +} + +control SwitchEgressDeparser( + packet_out pkt, + inout header_t hdr, + in metadata_t eg_md, + in egress_intrinsic_metadata_for_deparser_t ig_intr_dprs_md) { + apply { + pkt.emit(hdr.ethernet); + pkt.emit(hdr.ipv4); + } +} + +Pipeline(SwitchIngressParser(), + SwitchIngress(), + SwitchIngressDeparser(), + SwitchEgressParser(), + SwitchEgress(), + SwitchEgressDeparser()) pipe; + +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/nasa_bundle_translator.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/nasa_bundle_translator.p4 new file mode 100644 index 00000000000..2fa32a83ac8 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/nasa_bundle_translator.p4 @@ -0,0 +1,810 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include +#include + +header bpv6_primary_cbhe_h { + bit<8> version; + bit<16> bundle_flags; + bit<8> block_length; + bit<8> dst_node_num; + bit<8> dst_serv_num; + bit<8> src_node_num; + bit<8> src_serv_num; + bit<8> rep_node_num; + bit<8> rep_serv_num; + bit<8> cust_node_num; + bit<8> cust_serv_num; + bit<40> creation_ts; + bit<8> creation_ts_seq_num; + bit<24> lifetime; + bit<8> dict_len; +} + +header bpv6_extension_phib_h { + bit<8> block_type_code; + bit<8> block_flags; + bit<8> block_data_len; + bit<32> prev_hop_scheme_name; + bit<32> prev_hop; +} + +header bpv6_extension_age_h { + bit<8> block_type_code; + bit<8> block_flags; + bit<8> block_data_len; + bit<8> bundle_age; +} + +header bpv6_payload_h { + bit<8> block_type_code; + bit<8> block_flags; + bit<8> payload_length; +} + +const bit<8> CBOR_INDEF_LEN_ARRAY_START_CODE = 8w0x9f; +const bit<8> CBOR_INDEF_LEN_ARRAY_STOP_CODE = 8w0xff; +const bit<8> CBOR_MASK_MT = 8w0b11100000; +const bit<8> CBOR_MASK_AI = 8w0b11111; +header bpv7_start_code_h { + bit<8> start_code; +} + +header bpv7_primary_1_h { + bit<8> prim_initial_byte; + bit<8> version_num; + bit<16> bundle_flags; + bit<8> crc_type; + bit<40> dest_eid; + bit<40> src_eid; + bit<40> report_eid; + bit<8> creation_timestamp_time_initial_byte; + bit<72> creation_timestamp_time; + bit<8> creation_timestamp_seq_num_initial_byte; +} + +header bpv7_primary_2_1_h { + bit<8> creation_timestamp_seq_num; +} + +header bpv7_primary_2_2_h { + bit<16> creation_timestamp_seq_num; +} + +header bpv7_primary_2_4_h { + bit<32> creation_timestamp_seq_num; +} + +header bpv7_primary_2_8_h { + bit<64> creation_timestamp_seq_num; +} + +header bpv7_primary_3_h { + bit<40> lifetime; + bit<24> crc_field_integer; +} + +header bpv7_extension_prev_node_h { + bit<8> initial_byte; + bit<8> block_type; + bit<8> block_num; + bit<8> block_flags; + bit<8> crc_type; + bit<8> block_data_initial_byte; + bit<8> prev_node_array_initial_byte; + bit<8> uri_scheme; + bit<8> prev_node_eid_initial_byte; + bit<8> node_num; + bit<8> serv_num; +} + +header bpv7_extension_ecos_h { + bit<8> initial_byte; + bit<16> block_type; + bit<8> block_num; + bit<8> block_flags; + bit<8> crc_type; + bit<8> block_data_initial_byte; + bit<8> ecos_array_start; + bit<32> ecos_data; +} + +header bpv7_extension_age_h { + bit<8> initial_byte; + bit<8> block_type; + bit<8> block_num; + bit<8> block_flags; + bit<8> crc_type; + bit<8> block_data_initial_byte; + bit<8> bundle_age; +} + +header bpv7_payload_h { + bit<8> initial_byte; + bit<8> block_type; + bit<8> block_num; + bit<8> block_flags; + bit<8> crc_type; + bit<8> adu_initial_byte; +} + +header bpv7_stop_code_h { + bit<8> stop_code; +} + +header adu_1_h { + bit<8> adu; +} + +header adu_2_h { + bit<16> adu; +} + +header adu_3_h { + bit<24> adu; +} + +header adu_4_h { + bit<32> adu; +} + +header adu_5_h { + bit<40> adu; +} + +header adu_6_h { + bit<48> adu; +} + +header adu_7_h { + bit<56> adu; +} + +header adu_8_h { + bit<64> adu; +} + +header adu_9_h { + bit<72> adu; +} + +header adu_10_h { + bit<80> adu; +} + +header adu_11_h { + bit<88> adu; +} + +header adu_12_h { + bit<96> adu; +} + +header adu_13_h { + bit<104> adu; +} + +typedef bit<48> mac_addr_t; +typedef bit<32> ipv4_addr_t; +typedef bit<16> ether_type_t; +const ether_type_t ETHERTYPE_IPV4 = 16w0x800; +const bit<3> DIGEST_TYPE_DEBUG = 0x1; +header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> hdr_length; + bit<16> checksum; +} + +struct headers_t { + ethernet_h ethernet; + ipv4_h ipv4; + udp_h udp; + bpv6_primary_cbhe_h bpv6_primary_cbhe; + bpv6_extension_phib_h bpv6_extension_phib; + bpv6_extension_age_h bpv6_extension_age; + bpv6_payload_h bpv6_payload; + bpv7_start_code_h bpv7_start_code; + bpv7_primary_1_h bpv7_primary_1; + bpv7_primary_2_1_h bpv7_primary_2_1; + bpv7_primary_2_2_h bpv7_primary_2_2; + bpv7_primary_2_4_h bpv7_primary_2_4; + bpv7_primary_2_8_h bpv7_primary_2_8; + bpv7_primary_3_h bpv7_primary_3; + bpv7_extension_prev_node_h bpv7_extension_prev_node; + bpv7_extension_ecos_h bpv7_extension_ecos; + bpv7_extension_age_h bpv7_extension_age; + bpv7_payload_h bpv7_payload; + adu_1_h adu_1; + adu_2_h adu_2; + adu_3_h adu_3; + adu_4_h adu_4; + adu_5_h adu_5; + adu_6_h adu_6; + adu_7_h adu_7; + adu_8_h adu_8; + adu_9_h adu_9; + adu_10_h adu_10; + adu_11_h adu_11; + adu_12_h adu_12; + adu_13_h adu_13; + bpv7_stop_code_h bpv7_stop_code; +} + +struct debug_digest_t { + bit<8> hdr_version_num; + bit<8> initial_byte; + bit<8> block_type; + bit<8> block_num; + bit<8> block_flags; + bit<8> crc_type; + bit<8> block_data_initial_byte; + bit<8> bundle_age; +} + +struct metadata_headers_t { + bool checksum_upd_ipv4; + bool checksum_upd_udp; + bool checksum_err_ipv4_igprs; + bool incomingV7; + bool incomingV6; + debug_digest_t debug_metadata; +} + +parser IngressParser(packet_in pkt, out headers_t hdr, out metadata_headers_t meta, out ingress_intrinsic_metadata_t ig_intr_md) { + Checksum() ipv4_checksum; + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1: parse_resubmit; + 0: parse_port_metadata; + } + } + state parse_resubmit { + transition reject; + } + state parse_port_metadata { + pkt.advance(PORT_METADATA_SIZE); + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type) { + ETHERTYPE_IPV4: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + ipv4_checksum.add(hdr.ipv4); + meta.checksum_err_ipv4_igprs = ipv4_checksum.verify(); + transition select(hdr.ipv4.protocol) { + 8w0x11: parse_udp; + default: accept; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition parse_version; + } + state parse_version { + bit<8> start_byte = pkt.lookahead>(); + transition select(start_byte) { + 0x6: parse_v6; + 0x9f: parse_v7; + default: accept; + } + } + state parse_v6 { + meta.incomingV6 = true; + pkt.extract(hdr.bpv6_primary_cbhe); + pkt.extract(hdr.bpv6_extension_phib); + pkt.extract(hdr.bpv6_extension_age); + pkt.extract(hdr.bpv6_payload); + transition select(hdr.bpv6_payload.payload_length) { + 1: parse_v6_adu_1; + 2: parse_v6_adu_2; + 3: parse_v6_adu_3; + 4: parse_v6_adu_4; + 5: parse_v6_adu_5; + 6: parse_v6_adu_6; + 7: parse_v6_adu_7; + 8: parse_v6_adu_8; + 9: parse_v6_adu_9; + 10: parse_v6_adu_10; + 11: parse_v6_adu_11; + 12: parse_v6_adu_12; + 13: parse_v6_adu_13; + default: accept; + } + } + state parse_v6_adu_1 { + pkt.extract(hdr.adu_1); + transition accept; + } + state parse_v6_adu_2 { + pkt.extract(hdr.adu_2); + transition accept; + } + state parse_v6_adu_3 { + pkt.extract(hdr.adu_3); + transition accept; + } + state parse_v6_adu_4 { + pkt.extract(hdr.adu_4); + transition accept; + } + state parse_v6_adu_5 { + pkt.extract(hdr.adu_5); + transition accept; + } + state parse_v6_adu_6 { + pkt.extract(hdr.adu_6); + transition accept; + } + state parse_v6_adu_7 { + pkt.extract(hdr.adu_7); + transition accept; + } + state parse_v6_adu_8 { + pkt.extract(hdr.adu_8); + transition accept; + } + state parse_v6_adu_9 { + pkt.extract(hdr.adu_9); + transition accept; + } + state parse_v6_adu_10 { + pkt.extract(hdr.adu_10); + transition accept; + } + state parse_v6_adu_11 { + pkt.extract(hdr.adu_11); + transition accept; + } + state parse_v6_adu_12 { + pkt.extract(hdr.adu_12); + transition accept; + } + state parse_v6_adu_13 { + pkt.extract(hdr.adu_13); + transition accept; + } + state parse_v7 { + meta.incomingV7 = true; + pkt.extract(hdr.bpv7_start_code); + transition parse_v7_prim_block; + } + state parse_v7_prim_block { + pkt.extract(hdr.bpv7_primary_1); + transition select(hdr.bpv7_primary_1.creation_timestamp_seq_num_initial_byte) { + 24 &&& CBOR_MASK_AI: parse_v7_prim_block_2_1; + 25 &&& CBOR_MASK_AI: parse_v7_prim_block_2_2; + 26 &&& CBOR_MASK_AI: parse_v7_prim_block_2_4; + 27 &&& CBOR_MASK_AI: parse_v7_prim_block_2_8; + default: parse_v7_prim_block_3; + } + } + state parse_v7_prim_block_2_1 { + pkt.extract(hdr.bpv7_primary_2_1); + transition parse_v7_prim_block_3; + } + state parse_v7_prim_block_2_2 { + pkt.extract(hdr.bpv7_primary_2_2); + transition parse_v7_prim_block_3; + } + state parse_v7_prim_block_2_4 { + pkt.extract(hdr.bpv7_primary_2_4); + transition parse_v7_prim_block_3; + } + state parse_v7_prim_block_2_8 { + pkt.extract(hdr.bpv7_primary_2_8); + transition parse_v7_prim_block_3; + } + state parse_v7_prim_block_3 { + pkt.extract(hdr.bpv7_primary_3); + transition parse_v7_prev_node_block; + } + state parse_v7_prev_node_block { + pkt.extract(hdr.bpv7_extension_prev_node); + transition parse_v7_ecos_block; + } + state parse_v7_ecos_block { + pkt.extract(hdr.bpv7_extension_ecos); + transition parse_v7_age_block; + } + state parse_v7_age_block { + pkt.extract(hdr.bpv7_extension_age); + transition parse_v7_payload_header; + } + state parse_v7_payload_header { + pkt.extract(hdr.bpv7_payload); + transition select(hdr.bpv7_payload.adu_initial_byte) { + 1 &&& CBOR_MASK_AI: parse_v7_adu_1; + 2 &&& CBOR_MASK_AI: parse_v7_adu_2; + 3 &&& CBOR_MASK_AI: parse_v7_adu_3; + 4 &&& CBOR_MASK_AI: parse_v7_adu_4; + 5 &&& CBOR_MASK_AI: parse_v7_adu_5; + 6 &&& CBOR_MASK_AI: parse_v7_adu_6; + 7 &&& CBOR_MASK_AI: parse_v7_adu_7; + 8 &&& CBOR_MASK_AI: parse_v7_adu_8; + 9 &&& CBOR_MASK_AI: parse_v7_adu_9; + 10 &&& CBOR_MASK_AI: parse_v7_adu_10; + 11 &&& CBOR_MASK_AI: parse_v7_adu_11; + 12 &&& CBOR_MASK_AI: parse_v7_adu_12; + 13 &&& CBOR_MASK_AI: parse_v7_adu_13; + default: accept; + } + } + state parse_v7_adu_1 { + pkt.extract(hdr.adu_1); + transition parse_v7_stop_code; + } + state parse_v7_adu_2 { + pkt.extract(hdr.adu_2); + transition parse_v7_stop_code; + } + state parse_v7_adu_3 { + pkt.extract(hdr.adu_3); + transition parse_v7_stop_code; + } + state parse_v7_adu_4 { + pkt.extract(hdr.adu_4); + transition parse_v7_stop_code; + } + state parse_v7_adu_5 { + pkt.extract(hdr.adu_5); + transition parse_v7_stop_code; + } + state parse_v7_adu_6 { + pkt.extract(hdr.adu_6); + transition parse_v7_stop_code; + } + state parse_v7_adu_7 { + pkt.extract(hdr.adu_7); + transition parse_v7_stop_code; + } + state parse_v7_adu_8 { + pkt.extract(hdr.adu_8); + transition parse_v7_stop_code; + } + state parse_v7_adu_9 { + pkt.extract(hdr.adu_9); + transition parse_v7_stop_code; + } + state parse_v7_adu_10 { + pkt.extract(hdr.adu_10); + transition parse_v7_stop_code; + } + state parse_v7_adu_11 { + pkt.extract(hdr.adu_11); + transition parse_v7_stop_code; + } + state parse_v7_adu_12 { + pkt.extract(hdr.adu_12); + transition parse_v7_stop_code; + } + state parse_v7_adu_13 { + pkt.extract(hdr.adu_13); + transition parse_v7_stop_code; + } + state parse_v7_stop_code { + pkt.extract(hdr.bpv7_stop_code); + transition accept; + } +} + +control Ingress(inout headers_t hdr, inout metadata_headers_t meta, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + action checksum_upd_ipv4(bool update) { + meta.checksum_upd_ipv4 = update; + } + action checksum_upd_udp(bool update) { + meta.checksum_upd_udp = update; + } + action send(PortId_t port) { + ig_tm_md.ucast_egress_port = port; + } + action drop() { + ig_dprsr_md.drop_ctl = 1; + } + table ipv4_host { + key = { + hdr.ipv4.dst_addr: exact; + } + actions = { + send; + drop; + } + size = 65536; + } + apply { + ig_dprsr_md.digest_type = DIGEST_TYPE_DEBUG; + hdr.ipv4.ttl = 1; + if (hdr.ipv4.isValid()) { + hdr.ipv4.ttl = 2; + if (hdr.udp.isValid()) { + hdr.ipv4.ttl = 3; + if (meta.incomingV7) { + if (hdr.bpv7_start_code.isValid()) { + hdr.ipv4.ttl = 4; + if (hdr.bpv7_primary_1.isValid()) { + hdr.ipv4.ttl = 5; + meta.debug_metadata.hdr_version_num = hdr.bpv7_primary_1.version_num; + if (hdr.bpv7_primary_2_1.isValid()) { + hdr.ipv4.ttl = 6; + } else if (hdr.bpv7_primary_2_2.isValid()) { + hdr.ipv4.ttl = 7; + } else if (hdr.bpv7_primary_2_4.isValid()) { + hdr.ipv4.ttl = 8; + } else if (hdr.bpv7_primary_2_8.isValid()) { + hdr.ipv4.ttl = 9; + } + if (hdr.bpv7_payload.isValid()) { + hdr.ipv4.ttl = 14; + hdr.ipv4.identification = hdr.bpv7_extension_ecos.block_data_initial_byte ++ hdr.bpv7_extension_ecos.ecos_array_start; + meta.debug_metadata.initial_byte = hdr.bpv7_extension_age.initial_byte; + meta.debug_metadata.block_type = hdr.bpv7_extension_age.block_type; + meta.debug_metadata.block_num = hdr.bpv7_extension_age.block_num; + meta.debug_metadata.block_flags = hdr.bpv7_extension_age.block_flags; + meta.debug_metadata.crc_type = hdr.bpv7_extension_age.crc_type; + meta.debug_metadata.block_data_initial_byte = hdr.bpv7_extension_age.block_data_initial_byte; + meta.debug_metadata.bundle_age = hdr.bpv7_extension_age.bundle_age; + if (hdr.adu_1.isValid()) { + hdr.ipv4.ttl = 15; + } else if (hdr.adu_2.isValid()) { + hdr.ipv4.ttl = 16; + } else if (hdr.adu_3.isValid()) { + hdr.ipv4.ttl = 17; + } else if (hdr.adu_4.isValid()) { + hdr.ipv4.ttl = 18; + } else if (hdr.adu_5.isValid()) { + hdr.ipv4.ttl = 19; + } else if (hdr.adu_6.isValid()) { + hdr.ipv4.ttl = 20; + } else if (hdr.adu_7.isValid()) { + hdr.ipv4.ttl = 21; + } else if (hdr.adu_8.isValid()) { + hdr.ipv4.ttl = 22; + } else if (hdr.adu_9.isValid()) { + hdr.ipv4.ttl = 23; + } else if (hdr.adu_10.isValid()) { + hdr.ipv4.ttl = 24; + } else if (hdr.adu_11.isValid()) { + hdr.ipv4.ttl = 25; + } else if (hdr.adu_12.isValid()) { + hdr.ipv4.ttl = 26; + } else if (hdr.adu_13.isValid()) { + hdr.ipv4.ttl = 27; + } + hdr.bpv6_primary_cbhe.version = 6; + hdr.bpv6_primary_cbhe.bundle_flags = 0x8110; + hdr.bpv6_primary_cbhe.block_length = 18; + hdr.bpv6_primary_cbhe.dst_node_num = hdr.bpv7_primary_1.dest_eid[15:8]; + hdr.bpv6_primary_cbhe.dst_serv_num = hdr.bpv7_primary_1.dest_eid[7:0]; + hdr.bpv6_primary_cbhe.src_node_num = hdr.bpv7_primary_1.src_eid[15:8]; + hdr.bpv6_primary_cbhe.src_serv_num = hdr.bpv7_primary_1.src_eid[7:0]; + hdr.bpv6_primary_cbhe.rep_node_num = hdr.bpv7_primary_1.report_eid[15:8]; + hdr.bpv6_primary_cbhe.rep_serv_num = hdr.bpv7_primary_1.report_eid[7:0]; + hdr.bpv6_primary_cbhe.cust_node_num = 0; + hdr.bpv6_primary_cbhe.cust_serv_num = 0; + hdr.bpv6_primary_cbhe.creation_ts = 0x82d2e2cc6e; + hdr.bpv6_primary_cbhe.creation_ts_seq_num = 1; + hdr.bpv6_primary_cbhe.lifetime = 0x85a300; + hdr.bpv6_primary_cbhe.dict_len = 0; + hdr.bpv6_primary_cbhe.setValid(); + hdr.bpv6_payload.block_type_code = 1; + hdr.bpv6_payload.block_flags = 0x9; + if (hdr.adu_1.isValid()) { + hdr.bpv6_payload.payload_length = 1; + } else if (hdr.adu_2.isValid()) { + hdr.bpv6_payload.payload_length = 2; + } else if (hdr.adu_3.isValid()) { + hdr.bpv6_payload.payload_length = 3; + } else if (hdr.adu_4.isValid()) { + hdr.bpv6_payload.payload_length = 4; + } else if (hdr.adu_5.isValid()) { + hdr.bpv6_payload.payload_length = 5; + } else if (hdr.adu_6.isValid()) { + hdr.bpv6_payload.payload_length = 6; + } else if (hdr.adu_7.isValid()) { + hdr.bpv6_payload.payload_length = 7; + } else if (hdr.adu_8.isValid()) { + hdr.bpv6_payload.payload_length = 8; + } else if (hdr.adu_9.isValid()) { + hdr.bpv6_payload.payload_length = 9; + } else if (hdr.adu_10.isValid()) { + hdr.bpv6_payload.payload_length = 10; + } else if (hdr.adu_11.isValid()) { + hdr.bpv6_payload.payload_length = 11; + } else if (hdr.adu_12.isValid()) { + hdr.bpv6_payload.payload_length = 12; + } else if (hdr.adu_13.isValid()) { + hdr.bpv6_payload.payload_length = 13; + } + hdr.bpv6_payload.setValid(); + bit<8> udp_len = 8 + 22 + 3 + hdr.bpv6_payload.payload_length; + hdr.udp.hdr_length = (bit<16>)udp_len; + hdr.ipv4.total_len = (bit<16>)(20 + 8 + 22 + 3 + hdr.bpv6_payload.payload_length); + hdr.bpv7_start_code.setInvalid(); + hdr.bpv7_primary_1.setInvalid(); + hdr.bpv7_primary_2_1.setInvalid(); + hdr.bpv7_primary_2_2.setInvalid(); + hdr.bpv7_primary_2_4.setInvalid(); + hdr.bpv7_primary_2_8.setInvalid(); + hdr.bpv7_primary_3.setInvalid(); + hdr.bpv7_extension_prev_node.setInvalid(); + hdr.bpv7_extension_ecos.setInvalid(); + hdr.bpv7_extension_age.setInvalid(); + hdr.bpv7_payload.setInvalid(); + hdr.bpv7_stop_code.setInvalid(); + } + } + } + } else if (meta.incomingV6) { + hdr.ipv4.ttl = 32; + if (hdr.bpv6_primary_cbhe.isValid()) { + hdr.ipv4.ttl = 33; + meta.debug_metadata.hdr_version_num = hdr.bpv6_primary_cbhe.version; + if (hdr.bpv6_extension_phib.isValid()) { + hdr.ipv4.ttl = 34; + if (hdr.bpv6_extension_age.isValid()) { + hdr.ipv4.ttl = 35; + if (hdr.bpv6_payload.isValid()) { + if (hdr.adu_1.isValid()) { + hdr.ipv4.ttl = 36; + } else if (hdr.adu_2.isValid()) { + hdr.ipv4.ttl = 37; + } else if (hdr.adu_3.isValid()) { + hdr.ipv4.ttl = 38; + } else if (hdr.adu_4.isValid()) { + hdr.ipv4.ttl = 39; + } else if (hdr.adu_5.isValid()) { + hdr.ipv4.ttl = 40; + } else if (hdr.adu_6.isValid()) { + hdr.ipv4.ttl = 41; + } else if (hdr.adu_7.isValid()) { + hdr.ipv4.ttl = 42; + } else if (hdr.adu_8.isValid()) { + hdr.ipv4.ttl = 43; + } else if (hdr.adu_9.isValid()) { + hdr.ipv4.ttl = 44; + } else if (hdr.adu_10.isValid()) { + hdr.ipv4.ttl = 45; + } else if (hdr.adu_11.isValid()) { + hdr.ipv4.ttl = 46; + } else if (hdr.adu_12.isValid()) { + hdr.ipv4.ttl = 47; + } else if (hdr.adu_13.isValid()) { + hdr.ipv4.ttl = 48; + } + hdr.bpv7_start_code.start_code = CBOR_INDEF_LEN_ARRAY_START_CODE; + hdr.bpv7_start_code.setValid(); + hdr.bpv7_primary_1.prim_initial_byte = 0x89; + hdr.bpv7_primary_1.version_num = 0x7; + hdr.bpv7_primary_1.bundle_flags = 0x1840; + hdr.bpv7_primary_1.crc_type = 0x1; + hdr.bpv7_primary_1.dest_eid = 24w0x820282 ++ hdr.bpv6_primary_cbhe.dst_node_num ++ hdr.bpv6_primary_cbhe.dst_serv_num; + hdr.bpv7_primary_1.src_eid = 24w0x820282 ++ hdr.bpv6_primary_cbhe.src_node_num ++ hdr.bpv6_primary_cbhe.src_serv_num; + hdr.bpv7_primary_1.report_eid = 24w0x820282 ++ hdr.bpv6_primary_cbhe.rep_node_num ++ hdr.bpv6_primary_cbhe.rep_serv_num; + hdr.bpv7_primary_1.creation_timestamp_time_initial_byte = 0x82; + hdr.bpv7_primary_1.creation_timestamp_time = 0x1b000000a56f9117c4; + hdr.bpv7_primary_1.creation_timestamp_seq_num_initial_byte = 0x1; + hdr.bpv7_primary_1.setValid(); + hdr.bpv7_primary_3.lifetime = 0x1a05265c00; + hdr.bpv7_primary_3.crc_field_integer = 24w0x420d79; + hdr.bpv7_primary_3.setValid(); + hdr.bpv7_payload.initial_byte = 0x85; + hdr.bpv7_payload.block_type = 0x1; + hdr.bpv7_payload.block_num = 0x1; + hdr.bpv7_payload.block_flags = 0x1; + hdr.bpv7_payload.crc_type = 0x0; + hdr.bpv7_payload.adu_initial_byte = 4w0x4 ++ hdr.bpv6_payload.payload_length[3:0]; + hdr.bpv7_payload.setValid(); + hdr.bpv7_stop_code.stop_code = CBOR_INDEF_LEN_ARRAY_STOP_CODE; + hdr.bpv7_stop_code.setValid(); + bit<8> udp_len = 54 + hdr.bpv6_payload.payload_length; + hdr.udp.hdr_length = (bit<16>)udp_len; + hdr.ipv4.total_len = (bit<16>)(20 + 54 + hdr.bpv6_payload.payload_length); + hdr.bpv6_primary_cbhe.setInvalid(); + hdr.bpv6_extension_phib.setInvalid(); + hdr.bpv6_extension_age.setInvalid(); + hdr.bpv6_payload.setInvalid(); + } + } + } + } + } + checksum_upd_udp(true); + } + checksum_upd_ipv4(true); + ipv4_host.apply(); + } + } +} + +control IngressDeparser(packet_out pkt, inout headers_t hdr, in metadata_headers_t meta, in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + Checksum() ipv4_checksum; + Checksum() udp_checksum; + Digest() debug_digest; + apply { + if (ig_dprsr_md.digest_type == DIGEST_TYPE_DEBUG) { + debug_digest.pack(meta.debug_metadata); + } + if (meta.checksum_upd_ipv4) { + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.total_len, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.src_addr, hdr.ipv4.dst_addr }); + } + if (meta.checksum_upd_udp) { + if (meta.incomingV7) { + hdr.udp.checksum = udp_checksum.update(data = { hdr.ipv4.src_addr, hdr.ipv4.dst_addr, 8w0, hdr.ipv4.protocol, hdr.udp.hdr_length, hdr.udp.src_port, hdr.udp.dst_port, hdr.udp.hdr_length, hdr.bpv6_primary_cbhe.version, hdr.bpv6_primary_cbhe.bundle_flags, hdr.bpv6_primary_cbhe.block_length, hdr.bpv6_primary_cbhe.dst_node_num, hdr.bpv6_primary_cbhe.dst_serv_num, hdr.bpv6_primary_cbhe.src_node_num, hdr.bpv6_primary_cbhe.src_serv_num, hdr.bpv6_primary_cbhe.rep_node_num, hdr.bpv6_primary_cbhe.rep_serv_num, hdr.bpv6_primary_cbhe.cust_node_num, hdr.bpv6_primary_cbhe.cust_serv_num, hdr.bpv6_primary_cbhe.creation_ts, hdr.bpv6_primary_cbhe.creation_ts_seq_num, hdr.bpv6_primary_cbhe.lifetime, hdr.bpv6_primary_cbhe.dict_len, hdr.bpv6_payload.block_type_code, hdr.bpv6_payload.block_flags, hdr.bpv6_payload.payload_length, hdr.adu_1.adu, 8w0, hdr.adu_2.adu, hdr.adu_3.adu, 8w0, hdr.adu_4.adu, hdr.adu_5.adu, 8w0, hdr.adu_6.adu, hdr.adu_7.adu, 8w0, hdr.adu_8.adu, hdr.adu_9.adu, 8w0, hdr.adu_10.adu, hdr.adu_11.adu, 8w0, hdr.adu_12.adu, hdr.adu_13.adu }, zeros_as_ones = true); + } else if (meta.incomingV6) { + hdr.udp.checksum = udp_checksum.update(data = { hdr.ipv4.src_addr, hdr.ipv4.dst_addr, 8w0, hdr.ipv4.protocol, hdr.udp.hdr_length, hdr.udp.src_port, hdr.udp.dst_port, hdr.udp.hdr_length, hdr.bpv7_start_code.start_code, hdr.bpv7_primary_1.prim_initial_byte, hdr.bpv7_primary_1.version_num, hdr.bpv7_primary_1.bundle_flags, hdr.bpv7_primary_1.crc_type, hdr.bpv7_primary_1.dest_eid, hdr.bpv7_primary_1.src_eid, hdr.bpv7_primary_1.report_eid, hdr.bpv7_primary_1.creation_timestamp_time_initial_byte, hdr.bpv7_primary_1.creation_timestamp_time, hdr.bpv7_primary_1.creation_timestamp_seq_num_initial_byte, hdr.bpv7_primary_3.lifetime, hdr.bpv7_primary_3.crc_field_integer, hdr.bpv7_payload.initial_byte, hdr.bpv7_payload.block_type, hdr.bpv7_payload.block_num, hdr.bpv7_payload.block_flags, hdr.bpv7_payload.crc_type, hdr.bpv7_payload.adu_initial_byte, hdr.adu_1.adu, 8w0, hdr.adu_2.adu, hdr.adu_3.adu, 8w0, hdr.adu_4.adu, hdr.adu_5.adu, 8w0, hdr.adu_6.adu, hdr.adu_7.adu, 8w0, hdr.adu_8.adu, hdr.adu_9.adu, 8w0, hdr.adu_10.adu, hdr.adu_11.adu, 8w0, hdr.adu_12.adu, hdr.adu_13.adu }, zeros_as_ones = true); + } + } + pkt.emit(hdr); + } +} + +struct egress_headers_t { +} + +struct egress_metadata_t { +} + +parser EgressParser(packet_in pkt, out egress_headers_t hdr, out egress_metadata_t meta, out egress_intrinsic_metadata_t eg_intr_md) { + state start { + pkt.extract(eg_intr_md); + transition accept; + } +} + +control Egress(inout egress_headers_t hdr, inout egress_metadata_t meta, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_prsr_md, inout egress_intrinsic_metadata_for_deparser_t eg_dprsr_md, inout egress_intrinsic_metadata_for_output_port_t eg_oport_md) { + apply { + } +} + +control EgressDeparser(packet_out pkt, inout egress_headers_t hdr, in egress_metadata_t meta, in egress_intrinsic_metadata_for_deparser_t eg_dprsr_md) { + apply { + pkt.emit(hdr); + } +} + +Pipeline(IngressParser(), Ingress(), IngressDeparser(), EgressParser(), Egress(), EgressDeparser()) pipe; +Switch(pipe) main; diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switch_tofino_x1_mod.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switch_tofino_x1_mod.p4 new file mode 100644 index 00000000000..ac3b6063a83 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switch_tofino_x1_mod.p4 @@ -0,0 +1,5971 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include +#include +#include +#include + +const bit<32> PORT_TABLE_SIZE = 288 * 2; +const bit<32> VLAN_TABLE_SIZE = 4096; +const bit<32> BD_FLOOD_TABLE_SIZE = VLAN_TABLE_SIZE * 4; +const bit<32> PORT_VLAN_TABLE_SIZE = 1024; +const bit<32> BD_TABLE_SIZE = 5120; +const bit<32> MAC_TABLE_SIZE = 16384; +const bit<32> IPV4_HOST_TABLE_SIZE = 64 * 1024; +const bit<32> IPV4_LPM_TABLE_SIZE = 128 * 1024; +const bit<32> IPV4_LOCAL_HOST_TABLE_SIZE = 16 * 1024; +const bit<32> IPV6_HOST_TABLE_SIZE = 32 * 1024; +const bit<32> IPV6_LPM_TABLE_SIZE = 10 * 1024; +const bit<32> IPV6_LPM64_TABLE_SIZE = 32 * 1024; +const bit<32> ECMP_GROUP_TABLE_SIZE = 512; +const bit<32> ECMP_SELECT_TABLE_SIZE = 64 * ECMP_GROUP_TABLE_SIZE; +const bit<32> NEXTHOP_TABLE_SIZE = 65536; +const bit<32> PRE_INGRESS_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IPV4_ACL_TABLE_SIZE = 2048; +const bit<32> INGRESS_IPV6_ACL_TABLE_SIZE = 1024; +const bit<32> INGRESS_IP_MIRROR_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IP_DTEL_ACL_TABLE_SIZE = 512; +const bit<32> EGRESS_IPV4_ACL_TABLE_SIZE = 512; +const bit<32> EGRESS_IPV6_ACL_TABLE_SIZE = 512; +const bit<32> STORM_CONTROL_TABLE_SIZE = 256; +typedef bit<48> mac_addr_t; +typedef bit<32> ipv4_addr_t; +typedef bit<128> ipv6_addr_t; +typedef bit<12> vlan_id_t; +const int PAD_SIZE = 32; +const int MIN_SIZE = 64; +header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +header vlan_tag_h { + bit<3> pcp; + bit<1> dei; + vlan_id_t vid; + bit<16> ether_type; +} + +header mpls_h { + bit<20> label; + bit<3> exp; + bit<1> bos; + bit<8> ttl; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header router_alert_option_h { + bit<8> type; + bit<8> length; + bit<16> value; +} + +header ipv4_option_h { + bit<8> type; + bit<8> length; + bit<16> value; +} + +header ipv6_h { + bit<4> version; + bit<8> traffic_class; + bit<20> flow_label; + bit<16> payload_len; + bit<8> next_hdr; + bit<8> hop_limit; + ipv6_addr_t src_addr; + ipv6_addr_t dst_addr; +} + +header tcp_h { + bit<16> src_port; + bit<16> dst_port; + bit<32> seq_no; + bit<32> ack_no; + bit<4> data_offset; + bit<4> res; + bit<8> flags; + bit<16> window; + bit<16> checksum; + bit<16> urgent_ptr; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> length; + bit<16> checksum; +} + +header icmp_h { + bit<8> type; + bit<8> code; + bit<16> checksum; +} + +header igmp_h { + bit<8> type; + bit<8> code; + bit<16> checksum; +} + +header arp_h { + bit<16> hw_type; + bit<16> proto_type; + bit<8> hw_addr_len; + bit<8> proto_addr_len; + bit<16> opcode; +} + +header rocev2_bth_h { + bit<8> opcodee; + bit<1> se; + bit<1> migration_req; + bit<2> pad_count; + bit<4> transport_version; + bit<16> partition_key; + bit<1> f_res1; + bit<1> b_res1; + bit<6> reserved; + bit<24> dst_qp; + bit<1> ack_req; + bit<7> reserved2; +} + +header fcoe_fc_h { + bit<4> version; + bit<100> reserved; + bit<8> sof; + bit<8> r_ctl; + bit<24> d_id; + bit<8> cs_ctl; + bit<24> s_id; + bit<8> type; + bit<24> f_ctl; + bit<8> seq_id; + bit<8> df_ctl; + bit<16> seq_cnt; + bit<16> ox_id; + bit<16> rx_id; +} + +header ipv6_srh_h { + bit<8> next_hdr; + bit<8> hdr_ext_len; + bit<8> routing_type; + bit<8> seg_left; + bit<8> last_entry; + bit<8> flags; + bit<16> tag; +} + +header ipv6_segment_h { + bit<128> sid; +} + +header vxlan_h { + bit<8> flags; + bit<24> reserved; + bit<24> vni; + bit<8> reserved2; +} + +header vxlan_gpe_h { + bit<8> flags; + bit<16> reserved; + bit<24> vni; + bit<8> reserved2; +} + +header gre_h { + bit<16> flags_version; + bit<16> proto; +} + +header nvgre_h { + bit<32> vsid_flowid; +} + +header erspan_h { + bit<16> version_vlan; + bit<16> session_id; +} + +header erspan_type2_h { + bit<32> index; +} + +header erspan_type3_h { + bit<32> timestamp; + bit<32> ft_d_other; +} + +header erspan_platform_h { + bit<6> id; + bit<58> info; +} + +header gtpu_h { + bit<3> version; + bit<1> p; + bit<1> t; + bit<3> spare0; + bit<8> mesg_type; + bit<16> mesg_len; + bit<32> teid; + bit<24> seq_num; + bit<8> spare1; +} + +header geneve_h { + bit<2> version; + bit<6> opt_len; + bit<1> oam; + bit<1> critical; + bit<6> reserved; + bit<16> proto_type; + bit<24> vni; + bit<8> reserved2; +} + +header geneve_option_h { + bit<16> opt_class; + bit<8> opt_type; + bit<3> reserved; + bit<5> opt_len; +} + +header bfd_h { + bit<3> version; + bit<5> diag; + bit<8> flags; + bit<8> detect_multi; + bit<8> len; + bit<32> my_discriminator; + bit<32> your_discriminator; + bit<32> desired_min_tx_interval; + bit<32> req_min_rx_interval; + bit<32> req_min_echo_rx_interval; +} + +header dtel_report_v05_h { + bit<4> version; + bit<4> next_proto; + bit<3> d_q_f; + bit<15> reserved; + bit<6> hw_id; + bit<32> seq_number; + bit<32> timestamp; + bit<32> switch_id; +} + +header dtel_report_base_h { + bit<7> pad0; + PortId_t ingress_port; + bit<7> pad1; + PortId_t egress_port; + bit<3> pad2; + bit<5> queue_id; +} + +header dtel_drop_report_h { + bit<8> drop_reason; + bit<16> reserved; +} + +header dtel_switch_local_report_h { + bit<5> pad3; + bit<19> queue_occupancy; + bit<32> timestamp; +} + +header dtel_report_v10_h { + bit<4> version; + bit<4> length; + bit<3> next_proto; + bit<6> metadata_bits; + bit<6> reserved; + bit<3> d_q_f; + bit<6> hw_id; + bit<32> switch_id; + bit<32> seq_number; + bit<32> timestamp; +} + +header dtel_report_v20_h { + bit<4> version; + bit<4> hw_id; + bit<24> seq_number; + bit<32> switch_id; + bit<16> report_length; + bit<8> md_length; + bit<3> d_q_f; + bit<5> reserved; + bit<16> rep_md_bits; + bit<16> domain_specific_id; + bit<16> ds_md_bits; + bit<16> ds_md_status; +} + +header dtel_metadata_1_h { + bit<7> pad0; + PortId_t ingress_port; + bit<7> pad1; + PortId_t egress_port; +} + +header dtel_metadata_2_h { + bit<32> hop_latency; +} + +header dtel_metadata_3_h { + bit<3> pad2; + bit<5> queue_id; + bit<5> pad3; + bit<19> queue_occupancy; +} + +header dtel_metadata_4_h { + bit<16> pad; + bit<48> ingress_timestamp; +} + +header dtel_metadata_5_h { + bit<16> pad; + bit<48> egress_timestamp; +} + +header dtel_report_metadata_15_h { + bit<3> pad; + bit<5> queue_id; + bit<8> drop_reason; + bit<16> reserved; +} + +header fabric_h { + bit<8> reserved; + bit<3> color; + bit<5> qos; + bit<8> reserved2; +} + +header cpu_h { + bit<1> tx_bypass; + bit<1> capture_ts; + bit<1> reserved; + bit<5> egress_queue; + bit<16> ingress_port; + bit<16> port_lag_index; + bit<16> ingress_bd; + bit<16> reason_code; + bit<16> ether_type; +} + +header sfc_pause_h { + bit<8> version; + bit<8> dscp; + bit<16> duration_us; +} + +header padding_112b_h { + bit<112> pad_0; +} + +header padding_96b_h { + bit<96> pad; +} + +header pfc_h { + bit<16> opcode; + bit<8> reserved_zero; + bit<8> class_enable_vec; + bit<16> tstamp0; + bit<16> tstamp1; + bit<16> tstamp2; + bit<16> tstamp3; + bit<16> tstamp4; + bit<16> tstamp5; + bit<16> tstamp6; + bit<16> tstamp7; +} + +header timestamp_h { + bit<48> timestamp; +} + +header pad_h { + bit<(PAD_SIZE * 8)> data; +} + +const bit<32> MIN_TABLE_SIZE = 512; +const bit<32> LAG_TABLE_SIZE = 1024; +const bit<32> LAG_GROUP_TABLE_SIZE = 256; +const bit<32> LAG_MAX_MEMBERS_PER_GROUP = 64; +const bit<32> LAG_SELECTOR_TABLE_SIZE = 16384; +const bit<32> DTEL_GROUP_TABLE_SIZE = 4; +const bit<32> DTEL_MAX_MEMBERS_PER_GROUP = 64; +const bit<32> DTEL_SELECTOR_TABLE_SIZE = 256; +const bit<32> IPV4_DST_VTEP_TABLE_SIZE = 512; +const bit<32> IPV6_DST_VTEP_TABLE_SIZE = 512; +const bit<32> VNI_MAPPING_TABLE_SIZE = 1024; +const bit<32> BD_TO_VNI_MAPPING_SIZE = 4096; +typedef bit<32> switch_uint32_t; +typedef bit<16> switch_uint16_t; +typedef bit<8> switch_uint8_t; +typedef PortId_t switch_port_t; +const switch_port_t SWITCH_PORT_INVALID = 9w0x1ff; +typedef bit<7> switch_port_padding_t; +typedef QueueId_t switch_qid_t; +typedef ReplicationId_t switch_rid_t; +const switch_rid_t SWITCH_RID_DEFAULT = 0xffff; +typedef bit<3> switch_ingress_cos_t; +typedef bit<3> switch_digest_type_t; +const switch_digest_type_t SWITCH_DIGEST_TYPE_INVALID = 0; +const switch_digest_type_t SWITCH_DIGEST_TYPE_MAC_LEARNING = 1; +typedef bit<16> switch_ifindex_t; +typedef bit<10> switch_port_lag_index_t; +const switch_port_lag_index_t SWITCH_FLOOD = 0x3ff; +typedef bit<8> switch_isolation_group_t; +typedef bit<16> switch_bd_t; +const switch_bd_t SWITCH_DEFAULT_BD = 16w1; +const switch_bd_t SWITCH_BD_DEFAULT_VRF = 4097; +const bit<32> VRF_TABLE_SIZE = 1 << 8; +typedef bit<8> switch_vrf_t; +const switch_vrf_t SWITCH_DEFAULT_VRF = 1; +typedef bit<16> switch_nexthop_t; +typedef bit<10> switch_user_metadata_t; +typedef bit<32> switch_hash_t; +typedef bit<128> srv6_sid_t; +typedef bit<16> switch_xid_t; +typedef L2ExclusionId_t switch_yid_t; +typedef bit<32> switch_ig_port_lag_label_t; +typedef bit<16> switch_eg_port_lag_label_t; +typedef bit<16> switch_bd_label_t; +typedef bit<16> switch_mtu_t; +typedef bit<12> switch_stats_index_t; +typedef bit<16> switch_cpu_reason_t; +const switch_cpu_reason_t SWITCH_CPU_REASON_PTP = 0x8; +const switch_cpu_reason_t SWITCH_CPU_REASON_BFD = 0x9; +typedef bit<8> switch_fib_label_t; +struct switch_cpu_port_value_set_t { + bit<16> ether_type; + switch_port_t port; +} + +typedef bit<2> bfd_pkt_action_t; +const bfd_pkt_action_t BFD_PKT_ACTION_NORMAL = 0x0; +const bfd_pkt_action_t BFD_PKT_ACTION_TIMEOUT = 0x1; +const bfd_pkt_action_t BFD_PKT_ACTION_DROP = 0x2; +const bfd_pkt_action_t BFD_PKT_ACTION_INVALID = 0x3; +typedef bit<8> bfd_multiplier_t; +typedef bit<12> bfd_session_t; +typedef bit<4> bfd_pipe_t; +typedef bit<8> bfd_timer_t; +struct switch_bfd_metadata_t { + bfd_multiplier_t tx_mult; + bfd_multiplier_t rx_mult; + bfd_pkt_action_t pkt_action; + bfd_pipe_t pktgen_pipe; + bfd_session_t session_id; + bit<1> tx_timer_expired; + bit<1> session_offload; + bit<1> rx_recirc; + bit<1> pkt_tx; +} + +typedef bit<8> switch_drop_reason_t; +const switch_drop_reason_t SWITCH_DROP_REASON_UNKNOWN = 0; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_SRC_MAC_ZERO = 10; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_SRC_MAC_MULTICAST = 11; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_DST_MAC_ZERO = 12; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_ETHERNET_MISS = 13; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_SAME_MAC_CHECK = 17; +const switch_drop_reason_t SWITCH_DROP_REASON_SRC_MAC_ZERO = 14; +const switch_drop_reason_t SWITCH_DROP_REASON_SRC_MAC_MULTICAST = 15; +const switch_drop_reason_t SWITCH_DROP_REASON_DST_MAC_ZERO = 16; +const switch_drop_reason_t SWITCH_DROP_REASON_DST_MAC_MCAST_DST_IP_UCAST = 18; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_VERSION_INVALID = 25; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_TTL_ZERO = 26; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_MULTICAST = 27; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_LOOPBACK = 28; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_MISS = 29; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_IHL_INVALID = 30; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_INVALID_CHECKSUM = 31; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_DST_LOOPBACK = 32; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_UNSPECIFIED = 33; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_CLASS_E = 34; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_VERSION_INVALID = 40; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_TTL_ZERO = 41; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_MULTICAST = 42; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_LOOPBACK = 43; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_IHL_INVALID = 44; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_INVALID_CHECKSUM = 45; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_CLASS_E = 46; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_DST_LINK_LOCAL = 47; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_LINK_LOCAL = 48; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_DST_UNSPECIFIED = 49; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_UNSPECIFIED = 50; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_LPM4_MISS = 51; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_LPM6_MISS = 52; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_BLACKHOLE_ROUTE = 53; +const switch_drop_reason_t SWITCH_DROP_REASON_L3_PORT_RMAC_MISS = 54; +const switch_drop_reason_t SWITCH_DROP_REASON_PORT_VLAN_MAPPING_MISS = 55; +const switch_drop_reason_t SWITCH_DROP_REASON_STP_STATE_LEARNING = 56; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_STP_STATE_BLOCKING = 57; +const switch_drop_reason_t SWITCH_DROP_REASON_SAME_IFINDEX = 58; +const switch_drop_reason_t SWITCH_DROP_REASON_MULTICAST_SNOOPING_ENABLED = 59; +const switch_drop_reason_t SWITCH_DROP_REASON_IN_L3_EGRESS_LINK_DOWN = 60; +const switch_drop_reason_t SWITCH_DROP_REASON_MTU_CHECK_FAIL = 70; +const switch_drop_reason_t SWITCH_DROP_REASON_TRAFFIC_MANAGER = 71; +const switch_drop_reason_t SWITCH_DROP_REASON_STORM_CONTROL = 72; +const switch_drop_reason_t SWITCH_DROP_REASON_WRED = 73; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_PORT_METER = 75; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_ACL_METER = 76; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_PORT_METER = 77; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_ACL_METER = 78; +const switch_drop_reason_t SWITCH_DROP_REASON_ACL_DENY = 80; +const switch_drop_reason_t SWITCH_DROP_REASON_RACL_DENY = 81; +const switch_drop_reason_t SWITCH_DROP_REASON_URPF_CHECK_FAIL = 82; +const switch_drop_reason_t SWITCH_DROP_REASON_IPSG_MISS = 83; +const switch_drop_reason_t SWITCH_DROP_REASON_IFINDEX = 84; +const switch_drop_reason_t SWITCH_DROP_REASON_CPU_COLOR_YELLOW = 85; +const switch_drop_reason_t SWITCH_DROP_REASON_CPU_COLOR_RED = 86; +const switch_drop_reason_t SWITCH_DROP_REASON_STORM_CONTROL_COLOR_YELLOW = 87; +const switch_drop_reason_t SWITCH_DROP_REASON_STORM_CONTROL_COLOR_RED = 88; +const switch_drop_reason_t SWITCH_DROP_REASON_L2_MISS_UNICAST = 89; +const switch_drop_reason_t SWITCH_DROP_REASON_L2_MISS_MULTICAST = 90; +const switch_drop_reason_t SWITCH_DROP_REASON_L2_MISS_BROADCAST = 91; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_ACL_DENY = 92; +const switch_drop_reason_t SWITCH_DROP_REASON_NEXTHOP = 93; +const switch_drop_reason_t SWITCH_DROP_REASON_NON_IP_ROUTER_MAC = 94; +const switch_drop_reason_t SWITCH_DROP_REASON_MLAG_MEMBER = 95; +const switch_drop_reason_t SWITCH_DROP_REASON_L3_IPV4_DISABLE = 99; +const switch_drop_reason_t SWITCH_DROP_REASON_L3_IPV6_DISABLE = 100; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_PFC_WD_DROP = 101; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_PFC_WD_DROP = 102; +const switch_drop_reason_t SWITCH_DROP_REASON_MPLS_LABEL_DROP = 103; +const switch_drop_reason_t SWITCH_DROP_REASON_SRV6_MY_SID_DROP = 104; +const switch_drop_reason_t SWITCH_DROP_REASON_PORT_ISOLATION_DROP = 105; +const switch_drop_reason_t SWITCH_DROP_REASON_DMAC_RESERVED = 106; +const switch_drop_reason_t SWITCH_DROP_REASON_NON_ROUTABLE = 107; +const switch_drop_reason_t SWITCH_DROP_REASON_MPLS_DISABLE = 108; +const switch_drop_reason_t SWITCH_DROP_REASON_BFD = 109; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_STP_STATE_BLOCKING = 110; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_MULTICAST_DMAC_MISMATCH = 111; +const switch_drop_reason_t SWITCH_DROP_REASON_SIP_BC = 112; +const switch_drop_reason_t SWITCH_DROP_REASON_IPV6_MC_SCOPE0 = 113; +const switch_drop_reason_t SWITCH_DROP_REASON_IPV6_MC_SCOPE1 = 114; +typedef bit<1> switch_port_type_t; +const switch_port_type_t SWITCH_PORT_TYPE_NORMAL = 0; +const switch_port_type_t SWITCH_PORT_TYPE_CPU = 1; +typedef bit<2> switch_ip_type_t; +const switch_ip_type_t SWITCH_IP_TYPE_NONE = 0; +const switch_ip_type_t SWITCH_IP_TYPE_IPV4 = 1; +const switch_ip_type_t SWITCH_IP_TYPE_IPV6 = 2; +const switch_ip_type_t SWITCH_IP_TYPE_MPLS = 3; +typedef bit<2> switch_ip_frag_t; +const switch_ip_frag_t SWITCH_IP_FRAG_NON_FRAG = 0b0; +const switch_ip_frag_t SWITCH_IP_FRAG_HEAD = 0b10; +const switch_ip_frag_t SWITCH_IP_FRAG_NON_HEAD = 0b11; +typedef bit<2> switch_packet_action_t; +const switch_packet_action_t SWITCH_PACKET_ACTION_PERMIT = 0b0; +const switch_packet_action_t SWITCH_PACKET_ACTION_DROP = 0b1; +const switch_packet_action_t SWITCH_PACKET_ACTION_COPY = 0b10; +const switch_packet_action_t SWITCH_PACKET_ACTION_TRAP = 0b11; +typedef bit<16> switch_ingress_bypass_t; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_L2 = 16w0x1 << 0; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_L3 = 16w0x1 << 1; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_ACL = 16w0x1 << 2; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_SYSTEM_ACL = 16w0x1 << 3; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_QOS = 16w0x1 << 4; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_METER = 16w0x1 << 5; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_STORM_CONTROL = 16w0x1 << 6; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_STP = 16w0x1 << 7; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_SMAC = 16w0x1 << 8; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_NAT = 16w0x1 << 9; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_ROUTING_CHECK = 16w0x1 << 10; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_PV = 16w0x1 << 11; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_BFD_TX = 16w0x1 << 15; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_ALL = 16w0x7fff; +typedef bit<16> switch_pkt_length_t; +typedef bit<8> switch_pkt_src_t; +const switch_pkt_src_t SWITCH_PKT_SRC_BRIDGED = 0; +const switch_pkt_src_t SWITCH_PKT_SRC_CLONED_INGRESS = 1; +const switch_pkt_src_t SWITCH_PKT_SRC_CLONED_EGRESS = 2; +const switch_pkt_src_t SWITCH_PKT_SRC_DEFLECTED = 3; +const switch_pkt_src_t SWITCH_PKT_SRC_CLONED_EGRESS_IN_PKT = 4; +typedef bit<2> switch_pkt_color_t; +const switch_pkt_color_t SWITCH_METER_COLOR_GREEN = 0; +const switch_pkt_color_t SWITCH_METER_COLOR_YELLOW = 1; +const switch_pkt_color_t SWITCH_METER_COLOR_RED = 3; +typedef bit<2> switch_pkt_type_t; +const switch_pkt_type_t SWITCH_PKT_TYPE_UNICAST = 0; +const switch_pkt_type_t SWITCH_PKT_TYPE_MULTICAST = 1; +const switch_pkt_type_t SWITCH_PKT_TYPE_BROADCAST = 2; +typedef bit<8> switch_l4_port_label_t; +typedef bit<4> switch_etype_label_t; +typedef bit<8> switch_mac_addr_label_t; +typedef bit<2> switch_stp_state_t; +const switch_stp_state_t SWITCH_STP_STATE_FORWARDING = 0; +const switch_stp_state_t SWITCH_STP_STATE_BLOCKING = 1; +const switch_stp_state_t SWITCH_STP_STATE_LEARNING = 2; +typedef bit<10> switch_stp_group_t; +struct switch_stp_metadata_t { + switch_stp_group_t group; + switch_stp_state_t state_; +} + +typedef bit<2> switch_nexthop_type_t; +const switch_nexthop_type_t SWITCH_NEXTHOP_TYPE_IP = 0; +const switch_nexthop_type_t SWITCH_NEXTHOP_TYPE_MPLS = 1; +const switch_nexthop_type_t SWITCH_NEXTHOP_TYPE_TUNNEL_ENCAP = 2; +typedef bit<8> switch_sflow_id_t; +const switch_sflow_id_t SWITCH_SFLOW_INVALID_ID = 8w0xff; +struct switch_sflow_metadata_t { + switch_sflow_id_t session_id; + bit<1> sample_packet; +} + +typedef bit<8> switch_hostif_trap_t; +typedef bit<8> switch_copp_meter_id_t; +typedef bit<10> switch_meter_index_t; +typedef bit<8> switch_mirror_meter_id_t; +typedef bit<2> switch_qos_trust_mode_t; +const switch_qos_trust_mode_t SWITCH_QOS_TRUST_MODE_UNTRUSTED = 0; +const switch_qos_trust_mode_t SWITCH_QOS_TRUST_MODE_TRUST_DSCP = 1; +const switch_qos_trust_mode_t SWITCH_QOS_TRUST_MODE_TRUST_PCP = 2; +typedef bit<5> switch_qos_group_t; +typedef bit<8> switch_tc_t; +typedef bit<3> switch_cos_t; +typedef bit<11> switch_etrap_index_t; +typedef bit<2> switch_myip_type_t; +const switch_myip_type_t SWITCH_MYIP_NONE = 0; +const switch_myip_type_t SWITCH_MYIP = 1; +const switch_myip_type_t SWITCH_MYIP_SUBNET = 2; +struct switch_qos_metadata_t { + switch_qos_trust_mode_t trust_mode; + switch_qos_group_t group; + switch_tc_t tc; + switch_pkt_color_t color; + switch_pkt_color_t storm_control_color; + switch_meter_index_t port_meter_index; + switch_meter_index_t acl_meter_index; + switch_qid_t qid; + switch_ingress_cos_t icos; + bit<19> qdepth; + switch_etrap_index_t etrap_index; + switch_pkt_color_t etrap_color; + switch_tc_t etrap_tc; + bit<1> etrap_state; +} + +typedef bit<1> switch_learning_mode_t; +const switch_learning_mode_t SWITCH_LEARNING_MODE_DISABLED = 0; +const switch_learning_mode_t SWITCH_LEARNING_MODE_LEARN = 1; +struct switch_learning_digest_t { + switch_bd_t bd; + switch_port_lag_index_t port_lag_index; + mac_addr_t src_addr; +} + +struct switch_learning_metadata_t { + switch_learning_mode_t bd_mode; + switch_learning_mode_t port_mode; + switch_learning_digest_t digest; +} + +typedef bit<2> switch_multicast_mode_t; +const switch_multicast_mode_t SWITCH_MULTICAST_MODE_NONE = 0; +const switch_multicast_mode_t SWITCH_MULTICAST_MODE_PIM_SM = 1; +const switch_multicast_mode_t SWITCH_MULTICAST_MODE_PIM_BIDIR = 2; +typedef MulticastGroupId_t switch_mgid_t; +typedef bit<16> switch_multicast_rpf_group_t; +struct switch_multicast_metadata_t { + switch_mgid_t id; + bit<2> mode; + switch_multicast_rpf_group_t rpf_group; + bit<1> hit; +} + +typedef bit<2> switch_urpf_mode_t; +const switch_urpf_mode_t SWITCH_URPF_MODE_NONE = 0; +const switch_urpf_mode_t SWITCH_URPF_MODE_LOOSE = 1; +const switch_urpf_mode_t SWITCH_URPF_MODE_STRICT = 2; +typedef bit<10> switch_wred_index_t; +typedef bit<2> switch_ecn_codepoint_t; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_NON_ECT = 0b0; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_ECT0 = 0b10; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_ECT1 = 0b1; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_CE = 0b11; +const switch_ecn_codepoint_t NON_ECT = 0b0; +const switch_ecn_codepoint_t ECT0 = 0b10; +const switch_ecn_codepoint_t ECT1 = 0b1; +const switch_ecn_codepoint_t CE = 0b11; +typedef MirrorId_t switch_mirror_session_t; +const switch_mirror_session_t SWITCH_MIRROR_SESSION_CPU = 250; +typedef bit<8> switch_mirror_type_t; +struct switch_mirror_metadata_t { + switch_pkt_src_t src; + switch_mirror_type_t type; + switch_mirror_session_t session_id; + switch_mirror_meter_id_t meter_index; +} + +header switch_port_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<32> timestamp; + bit<6> _pad; + switch_mirror_session_t session_id; +} + +header switch_cpu_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + switch_port_padding_t _pad1; + switch_port_t port; + switch_bd_t bd; + bit<6> _pad2; + switch_port_lag_index_t port_lag_index; + switch_cpu_reason_t reason_code; +} + +typedef bit<1> switch_tunnel_mode_t; +const switch_tunnel_mode_t SWITCH_TUNNEL_MODE_PIPE = 0; +const switch_tunnel_mode_t SWITCH_TUNNEL_MODE_UNIFORM = 1; +const switch_tunnel_mode_t SWITCH_ECN_MODE_STANDARD = 0; +const switch_tunnel_mode_t SWITCH_ECN_MODE_COPY_FROM_OUTER = 1; +typedef bit<4> switch_tunnel_type_t; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_NONE = 0; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_VXLAN = 1; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_IPINIP = 2; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_GRE = 3; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_NVGRE = 4; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_MPLS = 5; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_SRV6 = 6; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_NVGRE_ST = 7; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_NONE = 0; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_VXLAN = 1; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_VXLAN = 2; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_IPINIP = 3; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_IPINIP = 4; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_NVGRE = 5; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_NVGRE = 6; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_MPLS = 7; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_SRV6_ENCAP = 8; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_SRV6_INSERT = 9; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_GRE = 10; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_GRE = 11; +enum switch_tunnel_term_mode_t { + P2P, + P2MP +} + +typedef bit<4> switch_tunnel_index_t; +typedef bit<4> switch_tunnel_mapper_index_t; +typedef bit<16> switch_tunnel_ip_index_t; +typedef bit<16> switch_tunnel_nexthop_t; +typedef bit<24> switch_tunnel_vni_t; +struct switch_tunnel_metadata_t { + switch_tunnel_type_t type; + switch_tunnel_mode_t ecn_mode; + switch_tunnel_index_t index; + switch_tunnel_mapper_index_t mapper_index; + switch_tunnel_ip_index_t dip_index; + switch_tunnel_vni_t vni; + switch_tunnel_mode_t qos_mode; + switch_tunnel_mode_t ttl_mode; + bit<8> decap_ttl; + bit<8> decap_tos; + bit<3> decap_exp; + bit<16> hash; + bool terminate; + bit<8> nvgre_flow_id; + bit<2> mpls_pop_count; + bit<3> mpls_push_count; + bit<8> mpls_encap_ttl; + bit<3> mpls_encap_exp; + bit<1> mpls_swap; + bit<128> srh_next_sid; + bit<8> srh_next_hdr; + bit<3> srv6_seg_len; + bit<6> srh_hdr_len; + bool remove_srh; + bool pop_active_segment; + bool srh_decap_forward; +} + +struct switch_nvgre_value_set_t { + bit<32> vsid_flowid; +} + +typedef bit<8> switch_dtel_report_type_t; +typedef bit<8> switch_ifa_sample_id_t; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_NONE = 0b0; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_DROP = 0b100; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_QUEUE = 0b10; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_FLOW = 0b1; +const switch_dtel_report_type_t SWITCH_DTEL_SUPPRESS_REPORT = 0b1000; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_IFA_CLONE = 0b10000; +const switch_dtel_report_type_t SWITCH_DTEL_IFA_EDGE = 0b100000; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_ETRAP_CHANGE = 0b1000000; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_ETRAP_HIT = 0b10000000; +typedef bit<6> switch_dtel_hw_id_t; +typedef bit<32> switch_dtel_switch_id_t; +const bit<16> DTEL_REPORT_V0_5_OUTER_HEADERS_LENGTH = 46; +const bit<16> DTEL_REPORT_V2_OUTER_HEADERS_LENGTH = 58; +struct switch_dtel_metadata_t { + switch_dtel_report_type_t report_type; + bit<1> ifa_gen_clone; + bit<1> ifa_cloned; + bit<32> latency; + switch_mirror_session_t session_id; + switch_mirror_session_t clone_session_id; + bit<32> hash; + bit<2> drop_report_flag; + bit<2> flow_report_flag; + bit<1> queue_report_flag; +} + +header switch_dtel_switch_local_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<32> timestamp; + bit<6> _pad; + switch_mirror_session_t session_id; + bit<32> hash; + switch_dtel_report_type_t report_type; + switch_port_padding_t _pad2; + switch_port_t ingress_port; + switch_port_padding_t _pad3; + switch_port_t egress_port; + bit<3> _pad4; + switch_qid_t qid; + bit<5> _pad5; + bit<19> qdepth; + bit<32> egress_timestamp; +} + +header switch_dtel_drop_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<32> timestamp; + bit<6> _pad; + switch_mirror_session_t session_id; + bit<32> hash; + switch_dtel_report_type_t report_type; + switch_port_padding_t _pad2; + switch_port_t ingress_port; + switch_port_padding_t _pad3; + switch_port_t egress_port; + bit<3> _pad4; + switch_qid_t qid; + switch_drop_reason_t drop_reason; +} + +header switch_simple_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<6> _pad; + switch_mirror_session_t session_id; +} + +@flexible struct switch_bridged_metadata_dtel_extension_t { + switch_dtel_report_type_t report_type; + switch_mirror_session_t session_id; + bit<32> hash; + switch_port_t egress_port; +} + +typedef bit<4> switch_ingress_nat_hit_type_t; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_NONE = 0; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_FLOW_NONE = 1; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_FLOW_NAPT = 2; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_FLOW_NAT = 3; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_DEST_NONE = 4; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_DEST_NAPT = 5; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_DEST_NAT = 6; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_SRC_NONE = 7; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_SRC_NAPT = 8; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_SRC_NAT = 9; +typedef bit<1> switch_nat_zone_t; +const switch_nat_zone_t SWITCH_NAT_INSIDE_ZONE_ID = 0; +const switch_nat_zone_t SWITCH_NAT_OUTSIDE_ZONE_ID = 1; +struct switch_nat_ingress_metadata_t { + switch_ingress_nat_hit_type_t hit; + switch_nat_zone_t ingress_zone; + bit<16> dnapt_index; + bit<16> snapt_index; + bool nat_disable; + bool dnat_pool_hit; +} + +enum bit<2> LinkToType { + Unknown = 0, + Switch = 1, + Server = 2 +} + +const bit<32> msb_set_32b = 32w0x80000000; +@pa_container_size("ingress" , "local_md.checks.same_if" , 16) @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_src_addr") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_dst_addr") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ipv6_flow_label") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_proto") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_ttl") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_tos") struct switch_flags_t { + bool ipv4_checksum_err; + bool inner_ipv4_checksum_err; + bool inner2_ipv4_checksum_err; + bool link_local; + bool routed; + bool l2_tunnel_encap; + bool acl_deny; + bool racl_deny; + bool port_vlan_miss; + bool rmac_hit; + bool dmac_miss; + switch_myip_type_t myip; + bool glean; + bool storm_control_drop; + bool port_meter_drop; + bool flood_to_multicast_routers; + bool peer_link; + bool capture_ts; + bool mac_pkt_class; + bool pfc_wd_drop; + bool bypass_egress; + bool mpls_trap; + bool srv6_trap; + bool mlag_member; + bool wred_drop; + bool port_isolation_packet_drop; + bool bport_isolation_packet_drop; + bool fib_lpm_miss; + bool fib_drop; + switch_packet_action_t meter_packet_action; + switch_packet_action_t vrf_ttl_violation; + bool vrf_ttl_violation_valid; + bool vlan_arp_suppress; + switch_packet_action_t vrf_ip_options_violation; + bool vrf_unknown_l3_multicast_trap; + bool bfd_to_cpu; + bool redirect_to_cpu; + bool copy_cancel; + bool to_cpu; +} + +struct switch_checks_t { + switch_port_lag_index_t same_if; + bool mrpf; + bool urpf; + switch_nat_zone_t same_zone_check; + switch_bd_t same_bd; + switch_mtu_t mtu; + bool stp; +} + +struct switch_ip_metadata_t { + bool unicast_enable; + bool multicast_enable; + bool multicast_snooping; +} + +struct switch_lookup_fields_t { + switch_pkt_type_t pkt_type; + mac_addr_t mac_src_addr; + mac_addr_t mac_dst_addr; + bit<16> mac_type; + bit<3> pcp; + bit<1> dei; + bit<16> arp_opcode; + switch_ip_type_t ip_type; + bit<8> ip_proto; + bit<8> ip_ttl; + bit<8> ip_tos; + switch_ip_frag_t ip_frag; + bit<128> ip_src_addr; + bit<128> ip_dst_addr; + bit<32> ip_src_addr_95_64; + bit<32> ip_dst_addr_95_64; + bit<20> ipv6_flow_label; + bit<8> tcp_flags; + bit<16> l4_src_port; + bit<16> l4_dst_port; + bit<8> inner_tcp_flags; + bit<16> inner_l4_src_port; + bit<16> inner_l4_dst_port; + bit<16> hash_l4_src_port; + bit<16> hash_l4_dst_port; + bool mpls_pkt; + bit<1> mpls_router_alert_label; + bit<20> mpls_lookup_label; +} + +struct switch_hash_fields_t { + mac_addr_t mac_src_addr; + mac_addr_t mac_dst_addr; + bit<16> mac_type; + switch_ip_type_t ip_type; + bit<8> ip_proto; + bit<128> ip_src_addr; + bit<128> ip_dst_addr; + bit<16> l4_src_port; + bit<16> l4_dst_port; + bit<20> ipv6_flow_label; + bit<32> outer_ip_src_addr; + bit<32> outer_ip_dst_addr; +} + +@flexible struct switch_bridged_metadata_base_t { + switch_port_t ingress_port; + switch_port_lag_index_t ingress_port_lag_index; + switch_bd_t ingress_bd; + switch_nexthop_t nexthop; + switch_pkt_type_t pkt_type; + bool routed; + bool bypass_egress; + switch_cpu_reason_t cpu_reason; + bit<32> timestamp; + switch_tc_t tc; + switch_qid_t qid; + switch_pkt_color_t color; + switch_vrf_t vrf; +} + +@flexible struct switch_bridged_metadata_acl_extension_t { + bit<16> l4_src_port; + bit<16> l4_dst_port; + bit<8> tcp_flags; +} + +@flexible struct switch_bridged_metadata_tunnel_extension_t { + switch_tunnel_nexthop_t tunnel_nexthop; + bool terminate; +} + +@pa_atomic("ingress" , "hdr.bridged_md.base_qid") @pa_container_size("ingress" , "hdr.bridged_md.base_qid" , 8) @pa_container_size("ingress" , "hdr.bridged_md.dtel_report_type" , 8) @pa_no_overlay("ingress" , "hdr.bridged_md.base_qid") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_0") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_1") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_2") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_3") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_4") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_5") @pa_no_overlay("egress" , "hdr.dtel_report.ingress_port") @pa_no_overlay("egress" , "hdr.dtel_report.egress_port") @pa_no_overlay("egress" , "hdr.dtel_report.queue_id") @pa_no_overlay("egress" , "hdr.dtel_drop_report.drop_reason") @pa_no_overlay("egress" , "hdr.dtel_drop_report.reserved") @pa_no_overlay("egress" , "hdr.dtel_switch_local_report.queue_occupancy") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.erspan_type2") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.udp") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel_switch_local_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel_switch_local_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel_switch_local_report") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel_report") @flexible header switch_fp_bridged_metadata_h { + bool ipv4_checksum_err; + bool link_local; + bool rmac_hit; + bool fib_drop; + bool fib_lpm_miss; + bit<1> multicast_hit; + bool acl_deny; + switch_myip_type_t myip; + switch_port_lag_index_t egress_port_lag_index; + switch_mgid_t multicast_id; + switch_ingress_bypass_t bypass; + switch_drop_reason_t drop_reason; + switch_hostif_trap_t hostif_trap_id; + switch_mirror_session_t mirror_session_id; + bool nat_disable; +} + +typedef bit<8> switch_bridge_type_t; +header switch_bridged_metadata_h { + switch_pkt_src_t src; + switch_bridge_type_t type; + switch_bridged_metadata_base_t base; + switch_bridged_metadata_acl_extension_t acl; + switch_bridged_metadata_dtel_extension_t dtel; +} + +struct switch_port_metadata_t { + switch_port_lag_index_t port_lag_index; + switch_ig_port_lag_label_t port_lag_label; +} + +struct switch_fp_port_metadata_t { + bit<1> unused; +} + +typedef bit<2> switch_cons_hash_ip_seq_t; +const switch_cons_hash_ip_seq_t SWITCH_CONS_HASH_IP_SEQ_NONE = 0; +const switch_cons_hash_ip_seq_t SWITCH_CONS_HASH_IP_SEQ_SIPDIP = 1; +const switch_cons_hash_ip_seq_t SWITCH_CONS_HASH_IP_SEQ_DIPSIP = 2; +@pa_auto_init_metadata @pa_container_size("ingress" , "local_md.mirror.src" , 8) @pa_container_size("ingress" , "local_md.mirror.type" , 8) @pa_container_size("ingress" , "smac_src_move" , 16) @pa_alias("ingress" , "local_md.egress_port" , "ig_intr_md_for_tm.ucast_egress_port") @pa_alias("ingress" , "local_md.qos.qid" , "ig_intr_md_for_tm.qid") @pa_alias("ingress" , "local_md.qos.icos" , "ig_intr_md_for_tm.ingress_cos") @pa_alias("ingress" , "ig_intr_md_for_dprsr.mirror_type" , "local_md.mirror.type") @pa_container_size("ingress" , "local_md.egress_port_lag_index" , 16) @pa_container_size("egress" , "local_md.mirror.src" , 8) @pa_container_size("egress" , "local_md.mirror.type" , 8) @pa_container_size("egress" , "hdr.dtel_drop_report.drop_reason" , 8) @pa_alias("ingress" , "local_md.ingress_outer_bd" , "local_md.bd") struct switch_local_metadata_t { + switch_port_t ingress_port; + switch_port_t egress_port; + switch_port_lag_index_t ingress_port_lag_index; + switch_port_lag_index_t egress_port_lag_index; + switch_bd_t bd; + switch_bd_t ingress_outer_bd; + switch_bd_t ingress_bd; + switch_vrf_t vrf; + switch_nexthop_t nexthop; + switch_tunnel_nexthop_t tunnel_nexthop; + switch_nexthop_t acl_nexthop; + bool acl_port_redirect; + switch_nexthop_t unused_nexthop; + bit<32> timestamp; + switch_hash_t hash; + switch_hash_t lag_hash; + switch_flags_t flags; + switch_checks_t checks; + switch_ingress_bypass_t bypass; + switch_ip_metadata_t ipv4; + switch_ip_metadata_t ipv6; + switch_ig_port_lag_label_t ingress_port_lag_label; + switch_bd_label_t bd_label; + switch_l4_port_label_t l4_src_port_label; + switch_l4_port_label_t l4_dst_port_label; + switch_etype_label_t etype_label; + switch_mac_addr_label_t qos_smac_label; + switch_mac_addr_label_t qos_dmac_label; + switch_mac_addr_label_t pbr_smac_label; + switch_mac_addr_label_t pbr_dmac_label; + switch_mac_addr_label_t mirror_smac_label; + switch_mac_addr_label_t mirror_dmac_label; + switch_drop_reason_t l2_drop_reason; + switch_drop_reason_t drop_reason; + switch_cpu_reason_t cpu_reason; + switch_lookup_fields_t lkp; + switch_hostif_trap_t hostif_trap_id; + switch_hash_fields_t hash_fields; + switch_multicast_metadata_t multicast; + switch_stp_metadata_t stp; + switch_qos_metadata_t qos; + switch_sflow_metadata_t sflow; + switch_tunnel_metadata_t tunnel; + switch_learning_metadata_t learning; + switch_mirror_metadata_t mirror; + switch_dtel_metadata_t dtel; + mac_addr_t same_mac; + switch_user_metadata_t user_metadata; + bit<10> partition_key; + bit<12> partition_index; + switch_fib_label_t fib_label; + switch_cons_hash_ip_seq_t cons_hash_v6_ip_seq; + switch_pkt_src_t pkt_src; + switch_pkt_length_t pkt_length; + bit<32> egress_timestamp; + bit<32> ingress_timestamp; + switch_eg_port_lag_label_t egress_port_lag_label; + switch_nexthop_type_t nexthop_type; + bool inner_ipv4_checksum_update_en; + switch_isolation_group_t port_isolation_group; + switch_isolation_group_t bport_isolation_group; +} + +struct switch_mirror_metadata_h { + switch_port_mirror_metadata_h port; + switch_cpu_mirror_metadata_h cpu; + switch_dtel_drop_mirror_metadata_h dtel_drop; + switch_dtel_switch_local_mirror_metadata_h dtel_switch_local; + switch_simple_mirror_metadata_h simple_mirror; +} + +struct switch_header_t { + switch_bridged_metadata_h bridged_md; + switch_fp_bridged_metadata_h fp_bridged_md; + ethernet_h ethernet; + fabric_h fabric; + cpu_h cpu; + timestamp_h timestamp; + vlan_tag_h[2] vlan_tag; + mpls_h[3] mpls; + ipv4_h ipv4; + ipv4_option_h ipv4_option; + ipv6_h ipv6; + arp_h arp; + ipv6_srh_h srh_base; + ipv6_segment_h[2] srh_seg_list; + udp_h udp; + icmp_h icmp; + igmp_h igmp; + tcp_h tcp; + dtel_report_v05_h dtel; + dtel_report_base_h dtel_report; + dtel_switch_local_report_h dtel_switch_local_report; + dtel_drop_report_h dtel_drop_report; + rocev2_bth_h rocev2_bth; + gtpu_h gtp; + vxlan_h vxlan; + gre_h gre; + nvgre_h nvgre; + geneve_h geneve; + erspan_h erspan; + erspan_type2_h erspan_type2; + erspan_type3_h erspan_type3; + erspan_platform_h erspan_platform; + ethernet_h inner_ethernet; + ipv4_h inner_ipv4; + ipv6_h inner_ipv6; + udp_h inner_udp; + tcp_h inner_tcp; + icmp_h inner_icmp; + igmp_h inner_igmp; + ipv4_h inner2_ipv4; + ipv6_h inner2_ipv6; + udp_h inner2_udp; + tcp_h inner2_tcp; + icmp_h inner2_icmp; + pad_h pad; +} + +action add_bridged_md(inout switch_bridged_metadata_h bridged_md, in switch_local_metadata_t local_md) { + bridged_md.setValid(); + bridged_md.src = SWITCH_PKT_SRC_BRIDGED; + // Why was the type missing here? + bridged_md.type = 0; + bridged_md.base.ingress_port = local_md.ingress_port; + bridged_md.base.ingress_port_lag_index = local_md.ingress_port_lag_index; + bridged_md.base.ingress_bd = local_md.bd; + bridged_md.base.nexthop = local_md.nexthop; + bridged_md.base.pkt_type = local_md.lkp.pkt_type; + bridged_md.base.routed = local_md.flags.routed; + bridged_md.base.bypass_egress = local_md.flags.bypass_egress; + bridged_md.base.cpu_reason = local_md.cpu_reason; + bridged_md.base.timestamp = local_md.timestamp; + bridged_md.base.tc = local_md.qos.tc; + bridged_md.base.qid = local_md.qos.qid; + bridged_md.base.color = local_md.qos.color; + bridged_md.base.vrf = local_md.vrf; + bridged_md.acl.l4_src_port = local_md.lkp.l4_src_port; + bridged_md.acl.l4_dst_port = local_md.lkp.l4_dst_port; + bridged_md.acl.tcp_flags = local_md.lkp.tcp_flags; + bridged_md.dtel.report_type = local_md.dtel.report_type; + bridged_md.dtel.session_id = local_md.dtel.session_id; + bridged_md.dtel.hash = local_md.lag_hash; + bridged_md.dtel.egress_port = local_md.egress_port; +} +action set_ig_intr_md(in switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm) { + ig_intr_md_for_tm.mcast_grp_b = local_md.multicast.id; + ig_intr_md_for_tm.level2_mcast_hash = local_md.lag_hash[28:16]; + ig_intr_md_for_tm.rid = local_md.bd; + ig_intr_md_for_tm.qid = local_md.qos.qid; + ig_intr_md_for_tm.ingress_cos = local_md.qos.icos; +} +control SetEgIntrMd(inout switch_header_t hdr, in switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, inout egress_intrinsic_metadata_for_output_port_t eg_intr_md_for_oport) { + apply { + if (local_md.mirror.type != 0) { + eg_intr_md_for_dprsr.mirror_type = (bit<3>)local_md.mirror.type; + } + } +} + +control EnableFragHash(inout switch_lookup_fields_t lkp) { + apply { + if (lkp.ip_frag != SWITCH_IP_FRAG_NON_FRAG) { + lkp.hash_l4_dst_port = 0; + lkp.hash_l4_src_port = 0; + } else { + lkp.hash_l4_dst_port = lkp.l4_dst_port; + lkp.hash_l4_src_port = lkp.l4_src_port; + } + } +} + +control Ipv4Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".ipv4_hash") Hash(HashAlgorithm_t.CRC32) ipv4_hash; + bit<32> ip_src_addr = lkp.ip_src_addr[95:64]; + bit<32> ip_dst_addr = lkp.ip_dst_addr[95:64]; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + apply { + hash = ipv4_hash.get({ ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control Ipv6Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".ipv6_hash") Hash(HashAlgorithm_t.CRC32) ipv6_hash; + bit<128> ip_src_addr = lkp.ip_src_addr; + bit<128> ip_dst_addr = lkp.ip_dst_addr; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + bit<20> ipv6_flow_label = lkp.ipv6_flow_label; + apply { + hash = ipv6_hash.get({ ipv6_flow_label, ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control NonIpHash(in switch_header_t hdr, in switch_local_metadata_t local_md, out switch_hash_t hash) { + @name(".non_ip_hash") Hash(HashAlgorithm_t.CRC32) non_ip_hash; + mac_addr_t mac_dst_addr = hdr.ethernet.dst_addr; + mac_addr_t mac_src_addr = hdr.ethernet.src_addr; + bit<16> mac_type = hdr.ethernet.ether_type; + switch_port_t port = local_md.ingress_port; + apply { + hash = non_ip_hash.get({ port, mac_type, mac_src_addr, mac_dst_addr }); + } +} + +control Lagv4Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".lag_v4_hash") Hash(HashAlgorithm_t.CRC32) lag_v4_hash; + bit<32> ip_src_addr = lkp.ip_src_addr[95:64]; + bit<32> ip_dst_addr = lkp.ip_dst_addr[95:64]; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + apply { + hash = lag_v4_hash.get({ ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control Lagv6Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".lag_v6_hash") Hash(HashAlgorithm_t.CRC32) lag_v6_hash; + bit<128> ip_src_addr = lkp.ip_src_addr; + bit<128> ip_dst_addr = lkp.ip_dst_addr; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + bit<20> ipv6_flow_label = lkp.ipv6_flow_label; + apply { + hash = lag_v6_hash.get({ ipv6_flow_label, ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control V4ConsistentHash(in bit<32> sip, in bit<32> dip, in bit<16> low_l4_port, in bit<16> high_l4_port, in bit<8> protocol, inout switch_hash_t hash) { + Hash(HashAlgorithm_t.CRC32) ipv4_inner_hash; + bit<32> high_ip; + bit<32> low_ip; + action step_v4() { + high_ip = max(sip, dip); + low_ip = min(sip, dip); + } + action v4_calc_hash() { + hash = ipv4_inner_hash.get({ low_ip, high_ip, protocol, low_l4_port, high_l4_port }); + } + apply { + step_v4(); + v4_calc_hash(); + } +} + +control V6ConsistentHash64bIpSeq(in bit<64> sip, in bit<64> dip, inout switch_cons_hash_ip_seq_t ip_seq) { + bit<32> high_63_32_ip; + bit<32> src_63_32_ip; + bit<32> high_31_0_ip; + bit<32> src_31_0_ip; + action step_63_32_v6() { + high_63_32_ip = max(sip[63:32], dip[63:32]); + src_63_32_ip = sip[63:32]; + } + action step_31_0_v6() { + high_31_0_ip = max(sip[31:0], dip[31:0]); + src_31_0_ip = sip[31:0]; + } + apply { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_SIPDIP; + step_63_32_v6(); + step_31_0_v6(); + if (sip[63:32] != dip[63:32]) { + if (high_63_32_ip == src_63_32_ip) { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_DIPSIP; + } + } else if (sip[31:0] != dip[31:0]) { + if (high_31_0_ip == src_31_0_ip) { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_DIPSIP; + } + } else { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_NONE; + } + } +} + +control V6ConsistentHash(in bit<128> sip, in bit<128> dip, in switch_cons_hash_ip_seq_t ip_seq, in bit<16> low_l4_port, in bit<16> high_l4_port, in bit<8> protocol, inout switch_hash_t hash) { + bit<32> high_31_0_ip; + bit<32> low_31_0_ip; + bit<32> src_31_0_ip; + Hash(HashAlgorithm_t.CRC32) ipv6_inner_hash_1; + Hash(HashAlgorithm_t.CRC32) ipv6_inner_hash_2; + bit<32> high_63_32_ip; + bit<32> src_63_32_ip; + action step_63_32_v6() { + high_63_32_ip = max(sip[63:32], dip[63:32]); + src_63_32_ip = sip[63:32]; + } + action step_31_0_v6() { + high_31_0_ip = max(sip[31:0], dip[31:0]); + src_31_0_ip = sip[31:0]; + } + action v6_calc_hash_sip_dip() { + hash = ipv6_inner_hash_1.get({ sip, dip, protocol, low_l4_port, high_l4_port }); + } + action v6_calc_hash_dip_sip() { + hash = ipv6_inner_hash_2.get({ dip, sip, protocol, low_l4_port, high_l4_port }); + } + apply { + if (ip_seq == SWITCH_CONS_HASH_IP_SEQ_SIPDIP) { + v6_calc_hash_sip_dip(); + } else if (ip_seq == SWITCH_CONS_HASH_IP_SEQ_DIPSIP) { + v6_calc_hash_dip_sip(); + } else { + step_63_32_v6(); + step_31_0_v6(); + if (sip[63:32] != dip[63:32]) { + if (high_63_32_ip == src_63_32_ip) { + v6_calc_hash_dip_sip(); + } else { + v6_calc_hash_sip_dip(); + } + } else if (high_31_0_ip == src_31_0_ip) { + v6_calc_hash_dip_sip(); + } else { + v6_calc_hash_sip_dip(); + } + } + } +} + +control StartConsistentInnerHash(in switch_header_t hd, inout switch_local_metadata_t ig_m) { + V6ConsistentHash64bIpSeq() compute_v6_cons_hash_64bit_ipseq; + apply { + if (hd.inner_ipv6.isValid() && ig_m.tunnel.type != SWITCH_INGRESS_TUNNEL_TYPE_NVGRE_ST) { + compute_v6_cons_hash_64bit_ipseq.apply(hd.inner_ipv6.src_addr[127:64], hd.inner_ipv6.dst_addr[127:64], ig_m.cons_hash_v6_ip_seq); + } + } +} + +control ConsistentInnerHash(in switch_header_t hd, inout switch_local_metadata_t ig_m) { + bit<32> high_31_0_ip; + bit<32> low_31_0_ip; + Hash(HashAlgorithm_t.CRC32) ipv6_inner_hash; + Hash>(HashAlgorithm_t.IDENTITY) l4_tcp_src_p_hash; + Hash>(HashAlgorithm_t.IDENTITY) l4_udp_src_p_hash; + bit<16> l4_src_port; + bit<16> low_l4_port = 0; + bit<16> high_l4_port = 0; + action step_tcp_src_port() { + l4_src_port = l4_tcp_src_p_hash.get({ 16w0 ++ +hd.inner_tcp.src_port }); + } + action step_udp_src_port() { + l4_src_port = l4_udp_src_p_hash.get({ 16w0 ++ +hd.inner_udp.src_port }); + } + action step_tcp_l4_port() { + high_l4_port = (bit<16>)max(l4_src_port, hd.inner_tcp.dst_port); + low_l4_port = (bit<16>)min(l4_src_port, hd.inner_tcp.dst_port); + } + action step_udp_l4_port() { + high_l4_port = (bit<16>)max(l4_src_port, hd.inner_udp.dst_port); + low_l4_port = (bit<16>)min(l4_src_port, hd.inner_udp.dst_port); + } + action step_31_0_v6() { + high_31_0_ip = max(hd.inner_ipv6.src_addr[31:0], hd.inner_ipv6.dst_addr[31:0]); + low_31_0_ip = min(hd.inner_ipv6.src_addr[31:0], hd.inner_ipv6.dst_addr[31:0]); + } + action v6_calc_31_0_hash() { + ig_m.hash = ipv6_inner_hash.get({ low_31_0_ip, high_31_0_ip, hd.inner_ipv6.next_hdr, low_l4_port, high_l4_port }); + } + V6ConsistentHash() compute_v6_cons_hash; + V4ConsistentHash() compute_v4_cons_hash; + apply { + if (hd.inner_udp.isValid()) { + step_udp_src_port(); + step_udp_l4_port(); + } else if (hd.inner_tcp.isValid()) { + step_tcp_src_port(); + step_tcp_l4_port(); + } + if (hd.inner_ipv6.isValid()) { + if (ig_m.tunnel.type != SWITCH_INGRESS_TUNNEL_TYPE_NVGRE_ST) { + compute_v6_cons_hash.apply(hd.inner_ipv6.src_addr, hd.inner_ipv6.dst_addr, ig_m.cons_hash_v6_ip_seq, low_l4_port, high_l4_port, hd.inner_ipv6.next_hdr, ig_m.hash); + } else { + step_31_0_v6(); + v6_calc_31_0_hash(); + } + } else if (hd.inner_ipv4.isValid()) { + compute_v4_cons_hash.apply(hd.inner_ipv4.src_addr, hd.inner_ipv4.dst_addr, low_l4_port, high_l4_port, hd.inner_ipv4.protocol, ig_m.hash); + } + } +} + +control InnerDtelv4Hash(in switch_header_t hdr, in switch_local_metadata_t local_md, out switch_hash_t hash) { + @name(".inner_dtelv4_hash") Hash(HashAlgorithm_t.CRC32) inner_dtelv4_hash; + bit<32> ip_src_addr = hdr.inner_ipv4.src_addr; + bit<32> ip_dst_addr = hdr.inner_ipv4.dst_addr; + bit<8> ip_proto = hdr.inner_ipv4.protocol; + bit<16> l4_src_port = hdr.udp.src_port; + apply { + hash = inner_dtelv4_hash.get({ ip_src_addr, ip_dst_addr, ip_proto, l4_src_port }); + } +} + +control InnerDtelv6Hash(in switch_header_t hdr, in switch_local_metadata_t local_md, out switch_hash_t hash) { + @name(".inner_dtelv6_hash") Hash(HashAlgorithm_t.CRC32) inner_dtelv6_hash; + bit<128> ip_src_addr = hdr.ipv6.src_addr; + bit<128> ip_dst_addr = hdr.ipv6.dst_addr; + bit<8> ip_proto = hdr.ipv6.next_hdr; + bit<16> l4_src_port = hdr.udp.src_port; + bit<20> ipv6_flow_label = hdr.inner_ipv6.flow_label; + apply { + hash = inner_dtelv6_hash.get({ ipv6_flow_label, ip_src_addr, ip_dst_addr, ip_proto, l4_src_port }); + } +} + +control RotateHash(inout switch_local_metadata_t local_md) { + @name(".rotate_by_0") action rotate_by_0() { + } + @name(".rotate_by_1") action rotate_by_1() { + local_md.hash[15:0] = local_md.hash[1 - 1:0] ++ local_md.hash[15:1]; + } + @name(".rotate_by_2") action rotate_by_2() { + local_md.hash[15:0] = local_md.hash[2 - 1:0] ++ local_md.hash[15:2]; + } + @name(".rotate_by_3") action rotate_by_3() { + local_md.hash[15:0] = local_md.hash[3 - 1:0] ++ local_md.hash[15:3]; + } + @name(".rotate_by_4") action rotate_by_4() { + local_md.hash[15:0] = local_md.hash[4 - 1:0] ++ local_md.hash[15:4]; + } + @name(".rotate_by_5") action rotate_by_5() { + local_md.hash[15:0] = local_md.hash[5 - 1:0] ++ local_md.hash[15:5]; + } + @name(".rotate_by_6") action rotate_by_6() { + local_md.hash[15:0] = local_md.hash[6 - 1:0] ++ local_md.hash[15:6]; + } + @name(".rotate_by_7") action rotate_by_7() { + local_md.hash[15:0] = local_md.hash[7 - 1:0] ++ local_md.hash[15:7]; + } + @name(".rotate_by_8") action rotate_by_8() { + local_md.hash[15:0] = local_md.hash[8 - 1:0] ++ local_md.hash[15:8]; + } + @name(".rotate_by_9") action rotate_by_9() { + local_md.hash[15:0] = local_md.hash[9 - 1:0] ++ local_md.hash[15:9]; + } + @name(".rotate_by_10") action rotate_by_10() { + local_md.hash[15:0] = local_md.hash[10 - 1:0] ++ local_md.hash[15:10]; + } + @name(".rotate_by_11") action rotate_by_11() { + local_md.hash[15:0] = local_md.hash[11 - 1:0] ++ local_md.hash[15:11]; + } + @name(".rotate_by_12") action rotate_by_12() { + local_md.hash[15:0] = local_md.hash[12 - 1:0] ++ local_md.hash[15:12]; + } + @name(".rotate_by_13") action rotate_by_13() { + local_md.hash[15:0] = local_md.hash[13 - 1:0] ++ local_md.hash[15:13]; + } + @name(".rotate_by_14") action rotate_by_14() { + local_md.hash[15:0] = local_md.hash[14 - 1:0] ++ local_md.hash[15:14]; + } + @name(".rotate_by_15") action rotate_by_15() { + local_md.hash[15:0] = local_md.hash[15 - 1:0] ++ local_md.hash[15:15]; + } + @name(".rotate_hash") table rotate_hash { + actions = { + rotate_by_0; + rotate_by_1; + rotate_by_2; + rotate_by_3; + rotate_by_4; + rotate_by_5; + rotate_by_6; + rotate_by_7; + rotate_by_8; + rotate_by_9; + rotate_by_10; + rotate_by_11; + rotate_by_12; + rotate_by_13; + rotate_by_14; + rotate_by_15; + } + size = 16; + default_action = rotate_by_0; + } + apply { + rotate_hash.apply(); + } +} + +control PreIngressAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + bool is_acl_enabled; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".set_acl_status") action set_acl_status(bool enabled) { + is_acl_enabled = enabled; + } + @name(".device_to_acl") table device_to_acl { + actions = { + set_acl_status; + } + default_action = set_acl_status(false); + size = 1; + } + @name(".pre_ingress_acl_no_action") action no_action() { + stats.count(); + } + @name(".pre_ingress_acl_deny") action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + @name(".pre_ingress_acl_set_vrf") action set_vrf(switch_vrf_t vrf) { + local_md.vrf = vrf; + stats.count(); + } + @name(".pre_ingress_acl") table acl { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + local_md.lkp.mac_type : ternary; + local_md.lkp.ip_src_addr: ternary; + local_md.lkp.ip_dst_addr: ternary; + local_md.lkp.ip_tos : ternary; + local_md.ingress_port : ternary; + } + actions = { + set_vrf; + deny; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + device_to_acl.apply(); + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0) && is_acl_enabled == true) { + acl.apply(); + } + } +} + +control IngressIpAcl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.flags.fib_lpm_miss = false; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + trap; + copy_to_cpu; + permit; + redirect_nexthop; + redirect_port; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressInnerIpv4Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + table acl { + key = { + hdr.inner_ipv4.src_addr : ternary; + hdr.inner_ipv4.dst_addr : ternary; + hdr.inner_ipv4.protocol : ternary; + hdr.inner_ipv4.diffserv : ternary; + hdr.inner_tcp.src_port : ternary; + hdr.inner_tcp.dst_port : ternary; + hdr.inner_udp.src_port : ternary; + hdr.inner_udp.dst_port : ternary; + hdr.inner_ipv4.ttl : ternary; + hdr.inner_tcp.flags : ternary; + local_md.tunnel.vni : ternary; + local_md.ingress_port_lag_index: ternary; + } + actions = { + set_dtel_report_type; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressInnerIpv6Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + table acl { + key = { + hdr.inner_ipv6.src_addr : ternary; + hdr.inner_ipv6.dst_addr : ternary; + hdr.inner_ipv6.next_hdr : ternary; + hdr.inner_ipv6.traffic_class : ternary; + hdr.inner_tcp.src_port : ternary; + hdr.inner_tcp.dst_port : ternary; + hdr.inner_udp.src_port : ternary; + hdr.inner_udp.dst_port : ternary; + hdr.inner_ipv6.hop_limit : ternary; + hdr.inner_tcp.flags : ternary; + local_md.tunnel.vni : ternary; + local_md.ingress_port_lag_index: ternary; + } + actions = { + set_dtel_report_type; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressIpv4Acl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.flags.fib_lpm_miss = false; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr[95:64]: ternary; + local_md.lkp.ip_dst_addr[95:64]: ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + trap; + copy_to_cpu; + permit; + redirect_nexthop; + redirect_port; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressIpv6Acl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.flags.fib_lpm_miss = false; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + trap; + copy_to_cpu; + permit; + redirect_nexthop; + redirect_port; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressTosMirrorAcl(inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_tos : ternary; + local_md.ingress_port_lag_label: ternary; + } + actions = { + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressMacAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.flags.fib_lpm_miss = false; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + permit; + redirect_nexthop; + redirect_port; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +struct switch_acl_sample_info_t { + bit<32> current; + bit<32> rate; +} + +control IngressIpDtelSampleAcl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + const bit<32> acl_sample_session_size = 256; + Register>(acl_sample_session_size) samplers; + RegisterAction, bit<1>>(samplers) sample_packet = { + void apply(inout switch_acl_sample_info_t reg, out bit<1> flag) { + if (reg.current > 0) { + reg.current = reg.current - 1; + } else { + reg.current = reg.rate; + flag = 1; + } + } + }; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action ifa_clone_sample(switch_ifa_sample_id_t ifa_sample_session) { + local_md.dtel.ifa_gen_clone = sample_packet.execute(ifa_sample_session); + stats.count(); + } + action ifa_clone_sample_and_set_dtel_report_type(switch_ifa_sample_id_t ifa_sample_session, switch_dtel_report_type_t type) { + local_md.dtel.report_type = type; + local_md.dtel.ifa_gen_clone = sample_packet.execute(ifa_sample_session); + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + set_dtel_report_type; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control LOU(inout switch_local_metadata_t local_md) { + const switch_uint32_t table_size = 64 * 1024; + @name(".set_ingress_src_port_label") action set_src_port_label(bit<8> label) { + local_md.l4_src_port_label = label; + } + @name(".set_ingress_dst_port_label") action set_dst_port_label(bit<8> label) { + local_md.l4_dst_port_label = label; + } + @name(".ingress_l4_dst_port") @use_hash_action(1) table l4_dst_port { + key = { + local_md.lkp.l4_dst_port: exact; + } + actions = { + set_dst_port_label; + } + const default_action = set_dst_port_label(0); + size = table_size; + } + @name(".ingress_l4_src_port") @use_hash_action(1) table l4_src_port { + key = { + local_md.lkp.l4_src_port: exact; + } + actions = { + set_src_port_label; + } + const default_action = set_src_port_label(0); + size = table_size; + } + @name(".set_tcp_flags") action set_tcp_flags(bit<8> flags) { + local_md.lkp.tcp_flags = flags; + } + @name(".ingress_lou_tcp") table tcp { + key = { + local_md.lkp.tcp_flags: exact; + } + actions = { + NoAction; + set_tcp_flags; + } + size = 256; + } + apply { + l4_src_port.apply(); + l4_dst_port.apply(); + } +} + +control IngressSystemAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr)(switch_uint32_t table_size=512) { + const switch_uint32_t drop_stats_table_size = 8192; + DirectCounter>(CounterType_t.PACKETS) stats; + @name(".ingress_copp_meter") Meter>(1 << 8, MeterType_t.PACKETS) copp_meter; + DirectCounter>(CounterType_t.PACKETS) copp_stats; + switch_copp_meter_id_t copp_meter_id; + @name(".ingress_system_acl_permit") action permit() { + local_md.drop_reason = SWITCH_DROP_REASON_UNKNOWN; + } + @name(".ingress_system_acl_drop") action drop(switch_drop_reason_t drop_reason, bool disable_learning) { + ig_intr_md_for_dprsr.drop_ctl = 0x1; + ig_intr_md_for_dprsr.digest_type = (disable_learning ? SWITCH_DIGEST_TYPE_INVALID : ig_intr_md_for_dprsr.digest_type); + local_md.drop_reason = drop_reason; + } + @name(".ingress_system_acl_copy_to_cpu") action copy_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + local_md.qos.qid = (overwrite_qid ? qid : local_md.qos.qid); + ig_intr_md_for_tm.copy_to_cpu = 1w1; + ig_intr_md_for_dprsr.digest_type = (disable_learning ? SWITCH_DIGEST_TYPE_INVALID : ig_intr_md_for_dprsr.digest_type); + ig_intr_md_for_tm.packet_color = (bit<2>)copp_meter.execute(meter_id); + copp_meter_id = meter_id; + local_md.cpu_reason = reason_code; + local_md.drop_reason = SWITCH_DROP_REASON_UNKNOWN; + } + @name(".ingress_system_acl_copy_sflow_to_cpu") action copy_sflow_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + copy_to_cpu(reason_code + (bit<16>)local_md.sflow.session_id, qid, meter_id, disable_learning, overwrite_qid); + } + @name(".ingress_system_acl_redirect_to_cpu") action redirect_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + ig_intr_md_for_dprsr.drop_ctl = 0b1; + local_md.flags.redirect_to_cpu = true; + copy_to_cpu(reason_code, qid, meter_id, disable_learning, overwrite_qid); + } + @name(".ingress_system_acl_redirect_sflow_to_cpu") action redirect_sflow_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + ig_intr_md_for_dprsr.drop_ctl = 0b1; + local_md.flags.redirect_to_cpu = true; + copy_sflow_to_cpu(reason_code, qid, meter_id, disable_learning, overwrite_qid); + } + @name(".ingress_system_acl") table system_acl { + key = { + local_md.ingress_port_lag_label : ternary; + local_md.bd : ternary; + local_md.ingress_port_lag_index : ternary; + local_md.lkp.pkt_type : ternary; + local_md.lkp.mac_type : ternary; + local_md.lkp.mac_dst_addr : ternary; + local_md.lkp.ip_type : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.arp_opcode : ternary; + local_md.flags.vlan_arp_suppress : ternary; + local_md.flags.vrf_ttl_violation : ternary; + local_md.flags.vrf_ttl_violation_valid : ternary; + local_md.flags.vrf_ip_options_violation: ternary; + local_md.flags.port_vlan_miss : ternary; + local_md.flags.acl_deny : ternary; + local_md.flags.racl_deny : ternary; + local_md.flags.rmac_hit : ternary; + local_md.flags.dmac_miss : ternary; + local_md.flags.myip : ternary; + local_md.flags.glean : ternary; + local_md.flags.routed : ternary; + local_md.flags.fib_lpm_miss : ternary; + local_md.flags.fib_drop : ternary; + local_md.qos.storm_control_color : ternary; + local_md.flags.link_local : ternary; + local_md.checks.same_if : ternary; + local_md.flags.pfc_wd_drop : ternary; + local_md.ipv4.unicast_enable : ternary; + local_md.ipv6.unicast_enable : ternary; + local_md.sflow.sample_packet : ternary; + local_md.l2_drop_reason : ternary; + local_md.drop_reason : ternary; + local_md.hostif_trap_id : ternary; + hdr.ipv4_option.isValid() : ternary; + } + actions = { + permit; + drop; + copy_to_cpu; + redirect_to_cpu; + copy_sflow_to_cpu; + redirect_sflow_to_cpu; + } + const default_action = permit; + size = table_size; + } + @name(".ingress_copp_drop") action copp_drop() { + ig_intr_md_for_tm.copy_to_cpu = 1w0; + copp_stats.count(); + } + @name(".ingress_copp_permit") action copp_permit() { + copp_stats.count(); + } + @name(".ingress_copp") table copp { + key = { + ig_intr_md_for_tm.packet_color: ternary; + copp_meter_id : ternary; + } + actions = { + copp_permit; + copp_drop; + } + const default_action = copp_permit; + size = 1 << 8 + 1; + counters = copp_stats; + } + @name(".ingress_drop_stats_count") action count() { + stats.count(); + } + @name(".ingress_drop_stats") table drop_stats { + key = { + local_md.drop_reason : exact @name("drop_reason") ; + local_md.ingress_port: exact @name("port") ; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + counters = stats; + size = drop_stats_table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_SYSTEM_ACL != 0)) { + switch (system_acl.apply().action_run) { + copy_to_cpu: { + copp.apply(); + } + redirect_to_cpu: { + copp.apply(); + } + default: { + } + } + } + drop_stats.apply(); + } +} + +control EgressLOU(inout switch_local_metadata_t local_md) { + const switch_uint32_t table_size = 64 * 1024; + @name(".set_egress_src_port_label") action set_src_port_label(bit<8> label) { + local_md.l4_src_port_label = label; + } + @name(".set_egress_dst_port_label") action set_dst_port_label(bit<8> label) { + local_md.l4_dst_port_label = label; + } + @name(".egress_l4_dst_port") @use_hash_action(1) table l4_dst_port { + key = { + local_md.lkp.l4_dst_port: exact; + } + actions = { + set_dst_port_label; + } + const default_action = set_dst_port_label(0); + size = table_size; + } + @name(".egress_l4_src_port") @use_hash_action(1) table l4_src_port { + key = { + local_md.lkp.l4_src_port: exact; + } + actions = { + set_src_port_label; + } + const default_action = set_src_port_label(0); + size = table_size; + } + apply { + l4_src_port.apply(); + l4_dst_port.apply(); + } +} + +control EgressMacAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action permit(switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + hdr.ethernet.ether_type : ternary; + local_md.egress_port_lag_label: ternary; + } + actions = { + deny(); + permit(); + mirror_out(); + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressIpv4Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action permit(switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ipv4.src_addr : ternary; + hdr.ipv4.dst_addr : ternary; + hdr.ipv4.protocol : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.egress_port_lag_label: ternary; + hdr.ethernet.ether_type : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + } + actions = { + deny(); + permit(); + mirror_out(); + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressIpv6Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action permit(switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ipv6.src_addr : ternary; + hdr.ipv6.dst_addr : ternary; + hdr.ipv6.next_hdr : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.egress_port_lag_label: ternary; + hdr.ethernet.ether_type : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + } + actions = { + deny(); + permit(); + mirror_out(); + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressTosMirrorAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ipv4.diffserv : ternary; + hdr.ipv6.traffic_class : ternary; + hdr.ipv4.isValid() : ternary; + hdr.ipv6.isValid() : ternary; + local_md.egress_port_lag_label: ternary; + } + actions = { + mirror_out; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressSystemAcl(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, out egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr)(switch_uint32_t table_size=512) { + const switch_uint32_t drop_stats_table_size = 8192; + DirectCounter>(CounterType_t.PACKETS) stats; + @name(".egress_copp_meter") Meter>(1 << 8, MeterType_t.PACKETS) copp_meter; + DirectCounter>(CounterType_t.PACKETS) copp_stats; + switch_copp_meter_id_t copp_meter_id; + switch_pkt_color_t copp_color; + @name(".egress_system_acl_drop") action drop(switch_drop_reason_t reason_code) { + local_md.drop_reason = reason_code; + eg_intr_md_for_dprsr.drop_ctl = 0x1; + local_md.mirror.type = 0; + } + @name(".egress_system_acl_ingress_timestamp") action insert_timestamp() { + } + @name(".egress_system_acl") table system_acl { + key = { + eg_intr_md.egress_port : ternary; + local_md.flags.acl_deny : ternary; + local_md.checks.mtu : ternary; + local_md.flags.wred_drop : ternary; + local_md.flags.pfc_wd_drop : ternary; + local_md.flags.port_isolation_packet_drop : ternary; + local_md.flags.bport_isolation_packet_drop: ternary; + } + actions = { + NoAction; + drop; + insert_timestamp; + } + const default_action = NoAction; + size = table_size; + } + @name(".egress_drop_stats_count") action count() { + stats.count(); + } + @name(".egress_drop_stats") table drop_stats { + key = { + local_md.drop_reason : exact @name("drop_reason") ; + eg_intr_md.egress_port: exact @name("port") ; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + counters = stats; + size = drop_stats_table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + switch (system_acl.apply().action_run) { + default: { + } + } + } + drop_stats.apply(); + } +} + +control IngressSTP(in switch_local_metadata_t local_md, inout switch_stp_metadata_t stp_md)(bool multiple_stp_enable=false, switch_uint32_t table_size=4096) { + const bit<32> stp_state_size = 1 << 19; + Hash>(HashAlgorithm_t.IDENTITY) hash; + @name(".ingress_stp.stp0") Register, bit<32>>(stp_state_size, 0) stp0; + RegisterAction, bit<32>, bit<1>>(stp0) stp_check0 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + } + }; + @name(".ingress_stp.stp1") Register, bit<32>>(stp_state_size, 0) stp1; + RegisterAction, bit<32>, bit<1>>(stp1) stp_check1 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + } + }; + @name(".ingress_stp.set_stp_state") action set_stp_state(switch_stp_state_t stp_state) { + stp_md.state_ = stp_state; + } + @name(".ingress_stp.mstp") table mstp { + key = { + local_md.ingress_port: exact; + stp_md.group : exact; + } + actions = { + NoAction; + set_stp_state; + } + size = table_size; + const default_action = NoAction; + } + apply { + } +} + +control EgressSTP(in switch_local_metadata_t local_md, in switch_port_t port, out bool stp_state) { + @name(".egress_stp.stp") Register, bit<32>>(1 << 19, 0) stp; + Hash>(HashAlgorithm_t.IDENTITY) hash; + RegisterAction, bit<32>, bit<1>>(stp) stp_check = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + } + }; + apply { + } +} + +control SMAC(in mac_addr_t src_addr, in mac_addr_t dst_addr, inout switch_local_metadata_t local_md, inout switch_digest_type_t digest_type)(switch_uint32_t table_size) { + bool src_miss; + switch_port_lag_index_t src_move; + @name(".smac_miss") action smac_miss() { + src_miss = true; + } + @name(".smac_hit") action smac_hit(switch_port_lag_index_t port_lag_index) { + src_move = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".smac") table smac { + key = { + local_md.bd: exact; + src_addr : exact; + } + actions = { + @defaultonly smac_miss; + smac_hit; + } + const default_action = smac_miss; + size = table_size; + idle_timeout = true; + } + action notify() { + digest_type = SWITCH_DIGEST_TYPE_MAC_LEARNING; + } + table learning { + key = { + src_miss: exact; + src_move: ternary; + } + actions = { + NoAction; + notify; + } + const default_action = NoAction; + const entries = { + (true, default) : notify(); + (false, 0 &&& 0x3ff) : NoAction(); + (false, default) : notify(); + } + size = MIN_TABLE_SIZE; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_SMAC != 0)) { + smac.apply(); + } + if (local_md.learning.bd_mode == SWITCH_LEARNING_MODE_LEARN && local_md.learning.port_mode == SWITCH_LEARNING_MODE_LEARN) { + learning.apply(); + } + } +} + +control DMAC_t(in mac_addr_t dst_addr, inout switch_local_metadata_t local_md); +control DMAC(in mac_addr_t dst_addr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size) { + @name(".dmac_miss") action dmac_miss() { + local_md.egress_port_lag_index = SWITCH_FLOOD; + local_md.flags.dmac_miss = true; + } + @name(".dmac_hit") action dmac_hit(switch_port_lag_index_t port_lag_index) { + local_md.egress_port_lag_index = port_lag_index; + local_md.checks.same_if = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".dmac_redirect") action dmac_redirect(switch_nexthop_t nexthop_index) { + local_md.nexthop = nexthop_index; + } + @pack(2) @name(".dmac") table dmac { + key = { + local_md.bd: exact; + dst_addr : exact; + } + actions = { + dmac_miss; + dmac_hit; + dmac_redirect; + } + const default_action = dmac_miss; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_L2 != 0) && local_md.acl_port_redirect == false) { + dmac.apply(); + } + } +} + +control IngressBd(in switch_bd_t bd, in switch_pkt_type_t pkt_type)(switch_uint32_t table_size) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_bd_stats_count") action count() { + stats.count(); + } + @name(".ingress_bd_stats") table bd_stats { + key = { + bd : exact; + pkt_type: exact; + } + actions = { + count; + @defaultonly NoAction; + } + const default_action = NoAction; + size = 3 * table_size; + counters = stats; + } + apply { + bd_stats.apply(); + } +} + +control EgressBDStats(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_bd_stats_count") action count() { + stats.count(sizeInBytes(hdr.bridged_md)); + } + @name(".egress_bd_stats") table bd_stats { + key = { + local_md.bd[12:0] : exact; + local_md.lkp.pkt_type: exact; + } + actions = { + count; + @defaultonly NoAction; + } + size = 3 * BD_TABLE_SIZE; + counters = stats; + } + apply { + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + bd_stats.apply(); + } + } +} + +control EgressBD(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".set_egress_bd_mapping") action set_bd_properties(mac_addr_t smac, switch_mtu_t mtu) { + hdr.ethernet.src_addr = smac; + local_md.checks.mtu = mtu; + } + @name(".egress_bd_mapping") table bd_mapping { + key = { + local_md.bd[12:0]: exact; + } + actions = { + set_bd_properties; + } + const default_action = set_bd_properties(0, 0x3fff); + size = 5120; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + bd_mapping.apply(); + } + } +} + +control VlanDecap(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".remove_vlan_tag") action remove_vlan_tag() { + hdr.ethernet.ether_type = hdr.vlan_tag[0].ether_type; + hdr.vlan_tag.pop_front(1); + } + @name(".vlan_decap") table vlan_decap { + key = { + hdr.vlan_tag[0].isValid(): ternary; + } + actions = { + NoAction; + remove_vlan_tag; + } + const default_action = NoAction; + } + apply { + if (!local_md.flags.bypass_egress) { + if (hdr.vlan_tag[0].isValid()) { + hdr.ethernet.ether_type = hdr.vlan_tag[0].ether_type; + hdr.vlan_tag[0].setInvalid(); + local_md.pkt_length = local_md.pkt_length - 4; + } + } + } +} + +control VlanXlate(inout switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t bd_table_size, switch_uint32_t port_bd_table_size) { + @name(".set_vlan_untagged") action set_vlan_untagged() { + local_md.lkp.mac_type = hdr.ethernet.ether_type; + } + @name(".set_vlan_tagged") action set_vlan_tagged(vlan_id_t vid) { + hdr.vlan_tag[0].setValid(); + hdr.vlan_tag[0].ether_type = hdr.ethernet.ether_type; + hdr.vlan_tag[0].vid = vid; + hdr.ethernet.ether_type = 0x8100; + local_md.lkp.mac_type = hdr.ethernet.ether_type; + } + @name(".port_bd_to_vlan_mapping") table port_bd_to_vlan_mapping { + key = { + local_md.egress_port_lag_index: exact @name("port_lag_index") ; + local_md.bd : exact @name("bd") ; + } + actions = { + set_vlan_untagged; + set_vlan_tagged; + } + const default_action = set_vlan_untagged; + size = port_bd_table_size; + } + @name(".bd_to_vlan_mapping") table bd_to_vlan_mapping { + key = { + local_md.bd: exact @name("bd") ; + } + actions = { + set_vlan_untagged; + set_vlan_tagged; + } + const default_action = set_vlan_untagged; + size = bd_table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + if (!port_bd_to_vlan_mapping.apply().hit) { + bd_to_vlan_mapping.apply(); + } + } + } +} + +action fib_hit(inout switch_local_metadata_t local_md, switch_nexthop_t nexthop_index, switch_fib_label_t fib_label) { + local_md.nexthop = nexthop_index; + local_md.flags.routed = true; +} +action fib_miss(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; +} +action fib_miss_lpm4(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; + local_md.flags.fib_lpm_miss = true; +} +action fib_miss_lpm6(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; + local_md.flags.fib_lpm_miss = true; +} +action fib_drop(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; + local_md.flags.fib_drop = true; +} +action fib_myip(inout switch_local_metadata_t local_md, switch_myip_type_t myip) { + local_md.flags.myip = myip; +} +control Fibv4(in bit<32> dst_addr, inout switch_local_metadata_t local_md)(switch_uint32_t host_table_size, switch_uint32_t lpm_table_size, bool local_host_enable=false, switch_uint32_t local_host_table_size=1024) { + @pack(2) @name(".ipv4_host") table host { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : exact @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = host_table_size; + } + @name(".ipv4_local_host") table local_host { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : exact @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = local_host_table_size; + } + Alpm(number_partitions = 2048, subtrees_per_partition = 2) algo_lpm; + @name(".ipv4_lpm") table lpm32 { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : lpm @name("ip_dst_addr") ; + } + actions = { + fib_miss_lpm4(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss_lpm4(local_md); + size = lpm_table_size; + implementation = algo_lpm; + requires_versioning = false; + } + apply { + if (local_host_enable) { + if (!local_host.apply().hit) { + if (!host.apply().hit) { + lpm32.apply(); + } + } + } else { + if (!host.apply().hit) { + lpm32.apply(); + } + } + } +} + +control Fibv6(in bit<128> dst_addr, inout switch_local_metadata_t local_md)(switch_uint32_t host_table_size, switch_uint32_t host64_table_size, switch_uint32_t lpm_table_size, switch_uint32_t lpm64_table_size=1024) { + @name(".ipv6_host") table host { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : exact @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = host_table_size; + } + Alpm(number_partitions = 1024, subtrees_per_partition = 1) algo_lpm128; + @name(".ipv6_lpm128") table lpm128 { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : lpm @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = lpm_table_size; + implementation = algo_lpm128; + requires_versioning = false; + } + Alpm(number_partitions = 1024, subtrees_per_partition = 2) algo_lpm64; + @name(".ipv6_lpm64") table lpm64 { + key = { + local_md.vrf : exact @name("vrf") ; + dst_addr[127:64]: lpm @name("ip_dst_addr") ; + } + actions = { + fib_miss_lpm6(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss_lpm6(local_md); + size = lpm64_table_size; + implementation = algo_lpm64; + requires_versioning = false; + } + apply { + if (!host.apply().hit) { + if (!lpm128.apply().hit) { + lpm64.apply(); + } + } + } +} + +control EgressVRF(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".set_vrf_properties") action set_vrf_properties(mac_addr_t smac) { + hdr.ethernet.src_addr = smac; + } + @use_hash_action(1) @name(".vrf_mapping") table vrf_mapping { + key = { + local_md.vrf: exact @name("vrf") ; + } + actions = { + set_vrf_properties; + } + const default_action = set_vrf_properties(0); + size = VRF_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + vrf_mapping.apply(); + if (hdr.ipv4.isValid()) { + hdr.ipv4.ttl = hdr.ipv4.ttl - 1; + } else if (hdr.ipv6.isValid()) { + hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1; + } + } + } +} + +control MTU(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=16) { + action ipv4_mtu_check() { + local_md.checks.mtu = local_md.checks.mtu |-| hdr.ipv4.total_len; + } + action ipv6_mtu_check() { + local_md.checks.mtu = local_md.checks.mtu |-| hdr.ipv6.payload_len; + } + action mtu_miss() { + local_md.checks.mtu = 16w0xffff; + } + table mtu { + key = { + local_md.flags.bypass_egress: exact; + local_md.flags.routed : exact; + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + } + actions = { + ipv4_mtu_check; + ipv6_mtu_check; + mtu_miss; + } + const default_action = mtu_miss; + const entries = { + (false, true, true, false) : ipv4_mtu_check(); + (false, true, false, true) : ipv6_mtu_check(); + } + size = table_size; + } + apply { + mtu.apply(); + } +} + +control Nexthop(inout switch_local_metadata_t local_md)(switch_uint32_t nexthop_table_size, switch_uint32_t ecmp_group_table_size, switch_uint32_t ecmp_selection_table_size, switch_uint32_t ecmp_max_members_per_group=64) { + Hash(HashAlgorithm_t.IDENTITY) selector_hash; + @name(".nexthop_ecmp_action_profile") ActionProfile(ecmp_selection_table_size) ecmp_action_profile; + @name(".nexthop_ecmp_selector") ActionSelector(ecmp_action_profile, selector_hash, SelectorMode_t.FAIR, ecmp_max_members_per_group, ecmp_group_table_size) ecmp_selector; + @name(".nexthop_set_nexthop_properties") action set_nexthop_properties(switch_port_lag_index_t port_lag_index, switch_bd_t bd, switch_nat_zone_t zone) { + local_md.egress_port_lag_index = port_lag_index; + local_md.checks.same_if = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".set_ecmp_properties") action set_ecmp_properties(switch_port_lag_index_t port_lag_index, switch_bd_t bd, switch_nexthop_t nexthop_index, switch_nat_zone_t zone) { + local_md.nexthop = nexthop_index; + set_nexthop_properties(port_lag_index, bd, zone); + } + @name(".set_nexthop_properties_post_routed_flood") action set_nexthop_properties_post_routed_flood(switch_bd_t bd, switch_mgid_t mgid, switch_nat_zone_t zone) { + local_md.egress_port_lag_index = 0; + local_md.multicast.id = mgid; + } + @name(".set_nexthop_properties_glean") action set_nexthop_properties_glean(switch_hostif_trap_t trap_id) { + local_md.flags.glean = true; + local_md.hostif_trap_id = trap_id; + } + @name(".set_ecmp_properties_glean") action set_ecmp_properties_glean(switch_hostif_trap_t trap_id) { + set_nexthop_properties_glean(trap_id); + } + @name(".set_nexthop_properties_drop") action set_nexthop_properties_drop(switch_drop_reason_t drop_reason) { + local_md.drop_reason = drop_reason; + } + @name(".set_ecmp_properties_drop") action set_ecmp_properties_drop() { + set_nexthop_properties_drop(SWITCH_DROP_REASON_NEXTHOP); + } + @ways(2) @name(".ecmp_table") table ecmp { + key = { + local_md.nexthop : exact; + local_md.hash[15:0]: selector; + } + actions = { + @defaultonly NoAction; + set_ecmp_properties; + set_ecmp_properties_drop; + set_ecmp_properties_glean; + } + const default_action = NoAction; + size = ecmp_group_table_size; + implementation = ecmp_selector; + } + @name(".nexthop") table nexthop { + key = { + local_md.nexthop: exact; + } + actions = { + @defaultonly NoAction; + set_nexthop_properties; + set_nexthop_properties_drop; + set_nexthop_properties_glean; + set_nexthop_properties_post_routed_flood; + } + const default_action = NoAction; + size = nexthop_table_size; + } + apply { + if (local_md.acl_port_redirect == true) { + local_md.flags.routed = false; + local_md.nexthop = 0; + } else { + switch (nexthop.apply().action_run) { + NoAction: { + ecmp.apply(); + } + default: { + } + } + } + } +} + +control Neighbor(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".neighbor_rewrite_l2") action rewrite_l2(switch_bd_t bd, mac_addr_t dmac) { + hdr.ethernet.dst_addr = dmac; + } + @use_hash_action(1) @name(".neighbor") table neighbor { + key = { + local_md.nexthop: exact; + } + actions = { + rewrite_l2; + } + const default_action = rewrite_l2(0, 0); + size = NEXTHOP_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + neighbor.apply(); + } + } +} + +control OuterNexthop(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".outer_nexthop_rewrite_l2") action rewrite_l2(switch_bd_t bd) { + local_md.bd = bd; + } + @use_hash_action(1) @name(".outer_nexthop") table outer_nexthop { + key = { + local_md.nexthop: exact; + } + actions = { + rewrite_l2; + } + const default_action = rewrite_l2(0); + size = NEXTHOP_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + outer_nexthop.apply(); + } + } +} + +parser SwitchIngressParser(packet_in pkt, out switch_header_t hdr, out switch_local_metadata_t local_md, out ingress_intrinsic_metadata_t ig_intr_md) { + Checksum() ipv4_checksum; + Checksum() inner_ipv4_checksum; + Checksum() inner2_ipv4_checksum; + @name(".ingress_udp_port_vxlan") value_set>(1) udp_port_vxlan; + @name(".ingress_cpu_port") value_set(1) cpu_port; + @name(".ingress_nvgre_st_key") value_set(1) nvgre_st_key; + state start { + pkt.extract(ig_intr_md); + local_md.ingress_port = ig_intr_md.ingress_port; + local_md.timestamp = ig_intr_md.ingress_mac_tstamp[31:0]; + transition parse_port_metadata; + } + state parse_resubmit { + transition accept; + } + state parse_port_metadata { + switch_port_metadata_t port_md = port_metadata_unpack(pkt); + local_md.ingress_port_lag_index = port_md.port_lag_index; + local_md.ingress_port_lag_label = port_md.port_lag_label; + transition parse_packet; + } + state parse_packet { + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type, local_md.ingress_port) { + cpu_port: parse_cpu; + (0x800, default): parse_ipv4; + (0x806, default): parse_arp; + (0x86dd, default): parse_ipv6; + (0x8100, default): parse_vlan; + (0x88a8, default): parse_vlan; + default: accept; + } + } + state parse_cpu { + pkt.extract(hdr.fabric); + pkt.extract(hdr.cpu); + local_md.bypass = hdr.cpu.reason_code; + transition select(hdr.cpu.ether_type) { + 0x800: parse_ipv4; + 0x806: parse_arp; + 0x86dd: parse_ipv6; + 0x8100: parse_vlan; + 0x88a8: parse_vlan; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + ipv4_checksum.add(hdr.ipv4); + transition select(hdr.ipv4.ihl) { + 5: parse_ipv4_no_options; + 6: parse_ipv4_options; + default: accept; + } + } + state parse_ipv4_options { + pkt.extract(hdr.ipv4_option); + ipv4_checksum.add(hdr.ipv4_option); + transition parse_ipv4_no_options; + } + state parse_ipv4_no_options { + local_md.flags.ipv4_checksum_err = ipv4_checksum.verify(); + transition select(hdr.ipv4.protocol, hdr.ipv4.frag_offset) { + (1, 0): parse_icmp; + (2, 0): parse_igmp; + (6, 0): parse_tcp; + (17, 0): parse_udp; + default: accept; + } + } + state parse_arp { + pkt.extract(hdr.arp); + transition accept; + } + state parse_vlan { + pkt.extract(hdr.vlan_tag.next); + transition select(hdr.vlan_tag.last.ether_type) { + 0x806: parse_arp; + 0x800: parse_ipv4; + 0x8100: parse_vlan; + 0x86dd: parse_ipv6; + default: accept; + } + } + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition select(hdr.ipv6.next_hdr) { + 58: parse_icmp; + 6: parse_tcp; + 17: parse_udp; + default: accept; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + 2123: parse_gtp_u; + 4791: parse_rocev2; + default: accept; + } + } + state parse_tcp { + pkt.extract(hdr.tcp); + transition accept; + } + state parse_icmp { + pkt.extract(hdr.icmp); + transition accept; + } + state parse_igmp { + pkt.extract(hdr.igmp); + transition accept; + } + state parse_gtp_u { + pkt.extract(hdr.gtp); + transition accept; + } + state parse_rocev2 { + transition accept; + } + state parse_ipinip { + local_md.tunnel.type = SWITCH_INGRESS_TUNNEL_TYPE_IPINIP; + transition parse_inner_ipv4; + } + state parse_ipv6inip { + local_md.tunnel.type = SWITCH_INGRESS_TUNNEL_TYPE_IPINIP; + transition parse_inner_ipv6; + } + state parse_inner_ipv4 { + pkt.extract(hdr.inner_ipv4); + inner_ipv4_checksum.add(hdr.inner_ipv4); + local_md.flags.inner_ipv4_checksum_err = inner_ipv4_checksum.verify(); + transition select(hdr.inner_ipv4.protocol) { + 1: parse_inner_icmp; + 2: parse_inner_igmp; + 6: parse_inner_tcp; + 17: parse_inner_udp; + default: accept; + } + } + state parse_inner_ipv6 { + pkt.extract(hdr.inner_ipv6); + transition select(hdr.inner_ipv6.next_hdr) { + 58: parse_inner_icmp; + 6: parse_inner_tcp; + 17: parse_inner_udp; + default: accept; + } + } + state parse_inner_udp { + pkt.extract(hdr.inner_udp); + transition accept; + } + state parse_inner_tcp { + pkt.extract(hdr.inner_tcp); + transition accept; + } + state parse_inner_icmp { + pkt.extract(hdr.inner_icmp); + transition accept; + } + state parse_inner_igmp { + pkt.extract(hdr.inner_igmp); + transition accept; + } +} + +parser SwitchEgressParser(packet_in pkt, out switch_header_t hdr, out switch_local_metadata_t local_md, out egress_intrinsic_metadata_t eg_intr_md) { + @name(".egress_udp_port_vxlan") value_set>(1) udp_port_vxlan; + @name(".egress_cpu_port") value_set(1) cpu_port; + @name(".egress_nvgre_st_key") value_set(1) nvgre_st_key; + @critical state start { + pkt.extract(eg_intr_md); + local_md.pkt_length = eg_intr_md.pkt_length; + local_md.egress_port = eg_intr_md.egress_port; + local_md.qos.qdepth = eg_intr_md.deq_qdepth; + switch_port_mirror_metadata_h mirror_md = pkt.lookahead(); + transition select(eg_intr_md.deflection_flag, mirror_md.src, mirror_md.type) { + (1, default, default): parse_deflected_pkt; + (default, SWITCH_PKT_SRC_BRIDGED, default): parse_bridged_pkt; + (default, default, 1): parse_port_mirrored_metadata; + (default, SWITCH_PKT_SRC_CLONED_EGRESS, 2): parse_cpu_mirrored_metadata; + (default, SWITCH_PKT_SRC_CLONED_INGRESS, 3): parse_dtel_drop_metadata_from_ingress; + (default, default, 3): parse_dtel_drop_metadata_from_egress; + (default, default, 4): parse_dtel_switch_local_metadata; + (default, default, 5): parse_simple_mirrored_metadata; + } + } + state parse_bridged_pkt { + pkt.extract(hdr.bridged_md); + local_md.pkt_src = SWITCH_PKT_SRC_BRIDGED; + local_md.ingress_port = hdr.bridged_md.base.ingress_port; + local_md.egress_port_lag_index = hdr.bridged_md.base.ingress_port_lag_index; + local_md.bd = hdr.bridged_md.base.ingress_bd; + local_md.nexthop = hdr.bridged_md.base.nexthop; + local_md.cpu_reason = hdr.bridged_md.base.cpu_reason; + local_md.flags.routed = hdr.bridged_md.base.routed; + local_md.flags.bypass_egress = hdr.bridged_md.base.bypass_egress; + local_md.lkp.pkt_type = hdr.bridged_md.base.pkt_type; + local_md.ingress_timestamp = hdr.bridged_md.base.timestamp; + local_md.qos.tc = hdr.bridged_md.base.tc; + local_md.qos.qid = hdr.bridged_md.base.qid; + local_md.qos.color = hdr.bridged_md.base.color; + local_md.vrf = hdr.bridged_md.base.vrf; + local_md.lkp.l4_src_port = hdr.bridged_md.acl.l4_src_port; + local_md.lkp.l4_dst_port = hdr.bridged_md.acl.l4_dst_port; + local_md.lkp.tcp_flags = hdr.bridged_md.acl.tcp_flags; + local_md.dtel.report_type = hdr.bridged_md.dtel.report_type; + local_md.dtel.hash = hdr.bridged_md.dtel.hash; + local_md.dtel.session_id = hdr.bridged_md.dtel.session_id; + transition parse_ethernet; + } + state parse_deflected_pkt { + pkt.extract(hdr.bridged_md); + local_md.pkt_src = SWITCH_PKT_SRC_DEFLECTED; + local_md.mirror.type = 255; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = hdr.bridged_md.dtel.report_type; + local_md.dtel.hash = hdr.bridged_md.dtel.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = hdr.bridged_md.dtel.session_id; + local_md.qos.qid = hdr.bridged_md.base.qid; + local_md.ingress_timestamp = hdr.bridged_md.base.timestamp; + hdr.dtel_report = { 0, hdr.bridged_md.base.ingress_port, 0, hdr.bridged_md.dtel.egress_port, 0, hdr.bridged_md.base.qid }; + hdr.dtel_drop_report = { SWITCH_DROP_REASON_TRAFFIC_MANAGER, 0 }; + transition accept; + } + state parse_port_mirrored_with_bridged_metadata { + switch_port_mirror_metadata_h port_md; + switch_bridged_metadata_h b_md; + pkt.extract(port_md); + pkt.extract(b_md); + pkt.extract(hdr.ethernet); + local_md.pkt_src = port_md.src; + local_md.mirror.session_id = port_md.session_id; + local_md.ingress_timestamp = port_md.timestamp; + local_md.flags.bypass_egress = true; + local_md.mirror.type = port_md.type; + local_md.dtel.session_id = 0; + transition accept; + } + state parse_port_mirrored_metadata { + switch_port_mirror_metadata_h port_md; + pkt.extract(port_md); + pkt.extract(hdr.ethernet); + local_md.pkt_src = port_md.src; + local_md.mirror.session_id = port_md.session_id; + local_md.ingress_timestamp = port_md.timestamp; + local_md.flags.bypass_egress = true; + local_md.mirror.type = port_md.type; + local_md.dtel.session_id = 0; + transition accept; + } + state parse_cpu_mirrored_metadata { + switch_cpu_mirror_metadata_h cpu_md; + pkt.extract(cpu_md); + pkt.extract(hdr.ethernet); + local_md.pkt_src = cpu_md.src; + local_md.flags.bypass_egress = true; + local_md.bd = cpu_md.bd; + local_md.cpu_reason = cpu_md.reason_code; + local_md.mirror.type = cpu_md.type; + local_md.dtel.session_id = 0; + transition accept; + } + state parse_dtel_drop_metadata_from_egress { + switch_dtel_drop_mirror_metadata_h dtel_md; + pkt.extract(dtel_md); + local_md.pkt_src = dtel_md.src; + local_md.mirror.type = dtel_md.type; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = dtel_md.report_type; + local_md.dtel.hash = dtel_md.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = dtel_md.session_id; + local_md.ingress_timestamp = dtel_md.timestamp; + hdr.dtel_report = { 0, dtel_md.ingress_port, 0, dtel_md.egress_port, 0, dtel_md.qid }; + hdr.dtel_drop_report = { dtel_md.drop_reason, 0 }; + transition accept; + } + state parse_dtel_drop_metadata_from_ingress { + switch_dtel_drop_mirror_metadata_h dtel_md; + pkt.extract(dtel_md); + local_md.pkt_src = dtel_md.src; + local_md.mirror.type = dtel_md.type; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = dtel_md.report_type; + local_md.dtel.hash = dtel_md.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = dtel_md.session_id; + local_md.ingress_timestamp = dtel_md.timestamp; + hdr.dtel_report = { 0, dtel_md.ingress_port, 0, SWITCH_PORT_INVALID, 0, dtel_md.qid }; + hdr.dtel_drop_report = { dtel_md.drop_reason, 0 }; + transition accept; + } + state parse_dtel_switch_local_metadata { + switch_dtel_switch_local_mirror_metadata_h dtel_md; + pkt.extract(dtel_md); + local_md.pkt_src = dtel_md.src; + local_md.mirror.type = dtel_md.type; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = dtel_md.report_type; + local_md.dtel.hash = dtel_md.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = dtel_md.session_id; + local_md.ingress_timestamp = dtel_md.timestamp; + hdr.dtel_report = { 0, dtel_md.ingress_port, 0, dtel_md.egress_port, 0, dtel_md.qid }; + hdr.dtel_switch_local_report = { 0, dtel_md.qdepth, dtel_md.egress_timestamp }; + transition accept; + } + state parse_simple_mirrored_metadata { + switch_simple_mirror_metadata_h simple_mirror_md; + pkt.extract(simple_mirror_md); + local_md.pkt_src = simple_mirror_md.src; + local_md.mirror.type = simple_mirror_md.type; + local_md.mirror.session_id = simple_mirror_md.session_id; + local_md.flags.bypass_egress = true; + transition parse_ethernet; + } + state parse_packet { + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type, eg_intr_md.egress_port) { + cpu_port: parse_cpu; + (0x800, default): parse_ipv4; + (0x86dd, default): parse_ipv6; + (0x8100, default): parse_vlan; + (0x88a8, default): parse_vlan; + default: parse_pad; + } + } + state parse_cpu { + local_md.flags.bypass_egress = true; + transition parse_pad; + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition select(hdr.ipv4.protocol, hdr.ipv4.ihl, hdr.ipv4.frag_offset) { + (17, 5, 0): parse_udp; + (default, 6, default): parse_ipv4_options; + default: parse_pad; + } + } + state parse_ipv4_options { + pkt.extract(hdr.ipv4_option); + transition select(hdr.ipv4.protocol, hdr.ipv4.frag_offset) { + (17, 0): parse_udp; + default: parse_pad; + } + } + state parse_vlan { + pkt.extract(hdr.vlan_tag.next); + transition select(hdr.vlan_tag.last.ether_type) { + 0x800: parse_ipv4; + 0x8100: parse_vlan; + 0x86dd: parse_ipv6; + default: parse_pad; + } + } + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition select(hdr.ipv6.next_hdr) { + default: parse_pad; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + default: parse_pad; + } + } + state egress_parse_sfc_pause { + transition parse_pad; + } + state parse_tcp { + pkt.extract(hdr.tcp); + transition parse_pad; + } + state parse_inner_ipv4 { + pkt.extract(hdr.inner_ipv4); + transition parse_pad; + } + state parse_inner_ipv6 { + pkt.extract(hdr.inner_ipv6); + transition parse_pad; + } + state parse_pad { + transition accept; + } +} + +control IngressMirror(inout switch_header_t hdr, in switch_local_metadata_t local_md, in ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr) { + Mirror() mirror; + apply { + if (ig_intr_md_for_dprsr.mirror_type == 1) { + mirror.emit(local_md.mirror.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.timestamp, 0, local_md.mirror.session_id }); + } else if (ig_intr_md_for_dprsr.mirror_type == 3) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.timestamp, 0, local_md.dtel.session_id, local_md.lag_hash, local_md.dtel.report_type, 0, local_md.ingress_port, 0, local_md.egress_port, 0, local_md.qos.qid, local_md.drop_reason }); + } else if (ig_intr_md_for_dprsr.mirror_type == 6) { + } + } +} + +control EgressMirror(inout switch_header_t hdr, in switch_local_metadata_t local_md, in egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr) { + Mirror() mirror; + apply { + if (eg_intr_md_for_dprsr.mirror_type == 1) { + mirror.emit(local_md.mirror.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.ingress_timestamp, 0, local_md.mirror.session_id }); + } else if (eg_intr_md_for_dprsr.mirror_type == 2) { + mirror.emit(local_md.mirror.session_id, { local_md.mirror.src, local_md.mirror.type, 0, local_md.ingress_port, local_md.bd, 0, local_md.egress_port_lag_index, local_md.cpu_reason }); + } else if (eg_intr_md_for_dprsr.mirror_type == 4) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.ingress_timestamp, 0, local_md.dtel.session_id, local_md.dtel.hash, local_md.dtel.report_type, 0, local_md.ingress_port, 0, local_md.egress_port, 0, local_md.qos.qid, 0, local_md.qos.qdepth, local_md.egress_timestamp }); + } else if (eg_intr_md_for_dprsr.mirror_type == 3) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.ingress_timestamp, 0, local_md.dtel.session_id, local_md.dtel.hash, local_md.dtel.report_type, 0, local_md.ingress_port, 0, local_md.egress_port, 0, local_md.qos.qid, local_md.drop_reason }); + } else if (eg_intr_md_for_dprsr.mirror_type == 5) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, 0, local_md.dtel.session_id }); + } + } +} + +control IngressNatChecksum(inout switch_header_t hdr, in switch_local_metadata_t local_md) { + Checksum() tcp_checksum; + Checksum() udp_checksum; + apply { + } +} + +control SwitchIngressDeparser(packet_out pkt, inout switch_header_t hdr, in switch_local_metadata_t local_md, in ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr) { + IngressMirror() mirror; + @name(".learning_digest") Digest() digest; + apply { + mirror.apply(hdr, local_md, ig_intr_md_for_dprsr); + if (ig_intr_md_for_dprsr.digest_type == SWITCH_DIGEST_TYPE_MAC_LEARNING) { + digest.pack({ local_md.ingress_outer_bd, local_md.ingress_port_lag_index, hdr.ethernet.src_addr }); + } + pkt.emit(hdr.bridged_md); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.vlan_tag); + pkt.emit(hdr.arp); + pkt.emit(hdr.ipv4); + pkt.emit(hdr.ipv4_option); + pkt.emit(hdr.ipv6); + pkt.emit(hdr.udp); + pkt.emit(hdr.tcp); + pkt.emit(hdr.icmp); + pkt.emit(hdr.igmp); + pkt.emit(hdr.rocev2_bth); + pkt.emit(hdr.gre); + pkt.emit(hdr.inner_ipv4); + pkt.emit(hdr.inner_ipv6); + pkt.emit(hdr.inner_udp); + pkt.emit(hdr.inner_tcp); + pkt.emit(hdr.inner_icmp); + } +} + +control SwitchEgressDeparser(packet_out pkt, inout switch_header_t hdr, in switch_local_metadata_t local_md, in egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr) { + EgressMirror() mirror; + Checksum() ipv4_checksum; + Checksum() inner_ipv4_checksum; + apply { + mirror.apply(hdr, local_md, eg_intr_md_for_dprsr); + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.total_len, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.src_addr, hdr.ipv4.dst_addr, hdr.ipv4_option.type, hdr.ipv4_option.length, hdr.ipv4_option.value }); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.fabric); + pkt.emit(hdr.cpu); + pkt.emit(hdr.timestamp); + pkt.emit(hdr.vlan_tag); + pkt.emit(hdr.ipv4); + pkt.emit(hdr.ipv4_option); + pkt.emit(hdr.ipv6); + pkt.emit(hdr.udp); + pkt.emit(hdr.dtel); + pkt.emit(hdr.dtel_report); + pkt.emit(hdr.dtel_switch_local_report); + pkt.emit(hdr.dtel_drop_report); + pkt.emit(hdr.gre); + pkt.emit(hdr.erspan); + pkt.emit(hdr.erspan_type2); + pkt.emit(hdr.erspan_type3); + pkt.emit(hdr.erspan_platform); + pkt.emit(hdr.inner_ethernet); + pkt.emit(hdr.inner_ipv4); + pkt.emit(hdr.inner_ipv6); + pkt.emit(hdr.inner_udp); + } +} + +control MirrorRewrite(inout switch_header_t hdr, inout switch_local_metadata_t local_md, out egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr)(switch_uint32_t table_size=1024) { + bit<16> length; + action add_ethernet_header(in mac_addr_t src_addr, in mac_addr_t dst_addr, in bit<16> ether_type) { + hdr.ethernet.setValid(); + hdr.ethernet.ether_type = ether_type; + hdr.ethernet.src_addr = src_addr; + hdr.ethernet.dst_addr = dst_addr; + } + action add_vlan_tag(vlan_id_t vid, bit<3> pcp, bit<16> ether_type) { + hdr.vlan_tag[0].setValid(); + hdr.vlan_tag[0].pcp = pcp; + hdr.vlan_tag[0].vid = vid; + hdr.vlan_tag[0].ether_type = ether_type; + } + action add_ipv4_header(in bit<8> diffserv, in bit<8> ttl, in bit<8> protocol, in ipv4_addr_t src_addr, in ipv4_addr_t dst_addr) { + hdr.ipv4.setValid(); + hdr.ipv4.version = 4w4; + hdr.ipv4.ihl = 4w5; + hdr.ipv4.diffserv = diffserv; + hdr.ipv4.identification = 0; + hdr.ipv4.flags = 0; + hdr.ipv4.frag_offset = 0; + hdr.ipv4.ttl = ttl; + hdr.ipv4.protocol = protocol; + hdr.ipv4.src_addr = src_addr; + hdr.ipv4.dst_addr = dst_addr; + hdr.ipv6.setInvalid(); + } + action add_gre_header(in bit<16> proto) { + hdr.gre.setValid(); + hdr.gre.proto = proto; + hdr.gre.flags_version = 0; + } + action add_erspan_common(bit<16> version_vlan, bit<10> session_id) { + hdr.erspan.setValid(); + hdr.erspan.version_vlan = version_vlan; + hdr.erspan.session_id = (bit<16>)session_id; + } + action add_erspan_type2(bit<10> session_id) { + add_erspan_common(0x1000, session_id); + hdr.erspan_type2.setValid(); + hdr.erspan_type2.index = 0; + } + action add_erspan_type3(bit<10> session_id, bit<32> timestamp, bool opt_sub_header) { + add_erspan_common(0x2000, session_id); + hdr.erspan_type3.setValid(); + hdr.erspan_type3.timestamp = timestamp; + hdr.erspan_type3.ft_d_other = 0x4; + if (opt_sub_header) { + } + } + @name(".rewrite_") action rewrite_(switch_qid_t qid) { + local_md.qos.qid = qid; + } + @name(".rewrite_erspan_type2") action rewrite_erspan_type2(switch_qid_t qid, mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type2((bit<10>)local_md.mirror.session_id); + add_gre_header(0x88be); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w32; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, 0x800); + } + @name(".rewrite_erspan_type2_with_vlan") action rewrite_erspan_type2_with_vlan(switch_qid_t qid, bit<16> ether_type, mac_addr_t smac, mac_addr_t dmac, bit<3> pcp, vlan_id_t vid, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type2((bit<10>)local_md.mirror.session_id); + add_gre_header(0x88be); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w32; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, ether_type); + add_vlan_tag(vid, pcp, 0x800); + } + @name(".rewrite_erspan_type3") action rewrite_erspan_type3(switch_qid_t qid, mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type3((bit<10>)local_md.mirror.session_id, (bit<32>)local_md.ingress_timestamp, false); + add_gre_header(0x22eb); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w36; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, 0x800); + } + @name(".rewrite_erspan_type3_with_vlan") action rewrite_erspan_type3_with_vlan(switch_qid_t qid, bit<16> ether_type, mac_addr_t smac, mac_addr_t dmac, bit<3> pcp, vlan_id_t vid, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type3((bit<10>)local_md.mirror.session_id, (bit<32>)local_md.ingress_timestamp, false); + add_gre_header(0x22eb); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w36; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, ether_type); + add_vlan_tag(vid, pcp, 0x800); + } + action rewrite_dtel_report(mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl, bit<16> udp_dst_port, switch_mirror_session_t session_id, bit<16> max_pkt_len) { + hdr.udp.setValid(); + hdr.udp.dst_port = udp_dst_port; + hdr.udp.checksum = 0; + add_ipv4_header(tos, ttl, 17, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w28; + hdr.udp.length = local_md.pkt_length + 16w8; + hdr.ipv4.flags = 2; + add_ethernet_header(smac, dmac, 0x800); + local_md.dtel.session_id = session_id; + } + @name(".rewrite_dtel_report_with_entropy") action rewrite_dtel_report_with_entropy(mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl, bit<16> udp_dst_port, switch_mirror_session_t session_id, bit<16> max_pkt_len) { + rewrite_dtel_report(smac, dmac, sip, dip, tos, ttl, udp_dst_port, session_id, max_pkt_len); + hdr.udp.src_port = local_md.dtel.hash[15:0]; + } + @name(".rewrite_dtel_report_without_entropy") action rewrite_dtel_report_without_entropy(mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl, bit<16> udp_dst_port, bit<16> udp_src_port, switch_mirror_session_t session_id, bit<16> max_pkt_len) { + rewrite_dtel_report(smac, dmac, sip, dip, tos, ttl, udp_dst_port, session_id, max_pkt_len); + hdr.udp.src_port = udp_src_port; + } + @name(".rewrite_ip_udp_lengths") action rewrite_ip_udp_lengths() { + hdr.ipv4.total_len = local_md.pkt_length - 16w14; + hdr.udp.length = local_md.pkt_length - 16w34; + } + @name(".rewrite_dtel_ifa_clone") action rewrite_dtel_ifa_clone() { + local_md.dtel.ifa_cloned = 1; + } + @ways(2) @name(".mirror_rewrite") table rewrite { + key = { + local_md.mirror.session_id: exact; + } + actions = { + NoAction; + rewrite_; + rewrite_erspan_type2; + rewrite_erspan_type2_with_vlan; + rewrite_erspan_type3; + rewrite_erspan_type3_with_vlan; + rewrite_dtel_report_with_entropy; + rewrite_dtel_report_without_entropy; + rewrite_ip_udp_lengths; + } + const default_action = NoAction; + size = table_size; + } + action adjust_length(bit<16> length_offset) { + local_md.pkt_length = local_md.pkt_length + length_offset; + local_md.mirror.type = 0; + } + table pkt_length { + key = { + local_md.mirror.type: exact; + } + actions = { + adjust_length; + } + const entries = { + 2 : adjust_length(0xfff2); + 1 : adjust_length(0xfff4); + 3 : adjust_length(0x1); + 4 : adjust_length(0xffff); + 5 : adjust_length(0xfff8); + 255 : adjust_length(20); + } + } + action rewrite_ipv4_udp_len_truncate() { + } + table pkt_len_trunc_adjustment { + key = { + hdr.udp.isValid() : exact; + hdr.ipv4.isValid(): exact; + } + actions = { + NoAction; + rewrite_ipv4_udp_len_truncate; + } + const default_action = NoAction; + const entries = { + (true, true) : rewrite_ipv4_udp_len_truncate(); + } + } + apply { + if (local_md.pkt_src != SWITCH_PKT_SRC_BRIDGED) { + pkt_length.apply(); + rewrite.apply(); + } + } +} + +control IngressRmac(inout switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t port_vlan_table_size, switch_uint32_t vlan_table_size=4096) { + @name(".rmac_miss") action rmac_miss() { + } + @name(".rmac_hit") action rmac_hit() { + local_md.flags.rmac_hit = true; + } + @name(".pv_rmac") table pv_rmac { + key = { + local_md.ingress_port_lag_index: ternary; + hdr.vlan_tag[0].isValid() : ternary; + hdr.vlan_tag[0].vid : ternary; + hdr.ethernet.dst_addr : ternary; + } + actions = { + rmac_miss; + rmac_hit; + } + const default_action = rmac_miss; + size = port_vlan_table_size; + } + @name(".vlan_rmac") table vlan_rmac { + key = { + hdr.vlan_tag[0].vid : exact; + hdr.ethernet.dst_addr: exact; + } + actions = { + @defaultonly rmac_miss; + rmac_hit; + } + const default_action = rmac_miss; + size = vlan_table_size; + } + apply { + switch (pv_rmac.apply().action_run) { + rmac_miss: { + if (hdr.vlan_tag[0].isValid()) { + vlan_rmac.apply(); + } + } + } + } +} + +control IngressPortMirror(in switch_port_t port, inout switch_mirror_metadata_t mirror_md)(switch_uint32_t table_size=288) { + @name(".set_ingress_mirror_id") action set_ingress_mirror_id(switch_mirror_session_t session_id, switch_mirror_meter_id_t meter_index) { + mirror_md.type = 1; + mirror_md.src = SWITCH_PKT_SRC_CLONED_INGRESS; + mirror_md.session_id = session_id; + } + @name(".ingress_port_mirror") table ingress_port_mirror { + key = { + port: exact; + } + actions = { + NoAction; + set_ingress_mirror_id; + } + const default_action = NoAction; + size = table_size; + } + apply { + ingress_port_mirror.apply(); + } +} + +control EgressPortMirror(in switch_port_t port, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=288) { + @name(".set_egress_mirror_id") action set_egress_mirror_id(switch_mirror_session_t session_id, switch_mirror_meter_id_t meter_index) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + } + @name(".egress_port_mirror") table egress_port_mirror { + key = { + port: exact; + } + actions = { + NoAction; + set_egress_mirror_id; + } + const default_action = NoAction; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + egress_port_mirror.apply(); + } + } +} + +control IngressPortMapping(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr)(switch_uint32_t port_vlan_table_size, switch_uint32_t bd_table_size, switch_uint32_t port_table_size=288, switch_uint32_t vlan_table_size=4096, switch_uint32_t double_tag_table_size=1024) { + IngressPortMirror(port_table_size) port_mirror; + IngressRmac(port_vlan_table_size, vlan_table_size) rmac; + @name(".bd_action_profile") ActionProfile(bd_table_size) bd_action_profile; + Hash>(HashAlgorithm_t.IDENTITY) hash; + const bit<32> vlan_membership_size = 1 << 19; + @name(".vlan_membership") Register, bit<32>>(vlan_membership_size, 0) vlan_membership; + RegisterAction, bit<32>, bit<1>>(vlan_membership) check_vlan_membership = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = ~val; + } + }; + action terminate_cpu_packet() { + local_md.ingress_port = (switch_port_t)hdr.cpu.ingress_port; + local_md.egress_port_lag_index = (switch_port_lag_index_t)hdr.cpu.port_lag_index; + ig_intr_md_for_tm.qid = (switch_qid_t)hdr.cpu.egress_queue; + local_md.flags.bypass_egress = (bool)hdr.cpu.tx_bypass; + hdr.ethernet.ether_type = hdr.cpu.ether_type; + } + @name(".set_cpu_port_properties") action set_cpu_port_properties(switch_port_lag_index_t port_lag_index, switch_ig_port_lag_label_t port_lag_label, switch_yid_t exclusion_id, switch_pkt_color_t color, switch_tc_t tc) { + local_md.ingress_port_lag_index = port_lag_index; + local_md.ingress_port_lag_label = port_lag_label; + local_md.qos.color = color; + local_md.qos.tc = tc; + ig_intr_md_for_tm.level2_exclusion_id = exclusion_id; + // terminate_cpu_packet(); + } + @name(".set_port_properties") action set_port_properties(switch_yid_t exclusion_id, switch_learning_mode_t learning_mode, switch_pkt_color_t color, switch_tc_t tc, switch_meter_index_t meter_index, switch_sflow_id_t sflow_session_id, bool mac_pkt_class, switch_ig_port_lag_label_t in_ports_group_label) { + local_md.qos.color = color; + local_md.qos.tc = tc; + ig_intr_md_for_tm.level2_exclusion_id = exclusion_id; + local_md.learning.port_mode = learning_mode; + local_md.checks.same_if = SWITCH_FLOOD; + local_md.flags.mac_pkt_class = mac_pkt_class; + local_md.sflow.session_id = sflow_session_id; + } + @placement_priority(2) @name(".ingress_port_mapping") table port_mapping { + key = { + local_md.ingress_port: exact; + } + actions = { + set_port_properties; + set_cpu_port_properties; + } + size = port_table_size; + } + @name(".port_vlan_miss") action port_vlan_miss() { + } + @name(".set_bd_properties") action set_bd_properties(switch_bd_t bd, switch_vrf_t vrf, bool vlan_arp_suppress, switch_packet_action_t vrf_ttl_violation, bool vrf_ttl_violation_valid, switch_packet_action_t vrf_ip_options_violation, bool vrf_unknown_l3_multicast_trap, switch_bd_label_t bd_label, switch_stp_group_t stp_group, switch_learning_mode_t learning_mode, bool ipv4_unicast_enable, bool ipv4_multicast_enable, bool igmp_snooping_enable, bool ipv6_unicast_enable, bool ipv6_multicast_enable, bool mld_snooping_enable, bool mpls_enable, switch_multicast_rpf_group_t mrpf_group, switch_nat_zone_t zone) { + local_md.bd = bd; + local_md.flags.vlan_arp_suppress = vlan_arp_suppress; + local_md.ingress_outer_bd = bd; + local_md.bd_label = bd_label; + local_md.vrf = vrf; + local_md.flags.vrf_ttl_violation = vrf_ttl_violation; + local_md.flags.vrf_ttl_violation_valid = vrf_ttl_violation_valid; + local_md.flags.vrf_ip_options_violation = vrf_ip_options_violation; + local_md.flags.vrf_unknown_l3_multicast_trap = vrf_unknown_l3_multicast_trap; + local_md.stp.group = stp_group; + local_md.multicast.rpf_group = mrpf_group; + local_md.learning.bd_mode = learning_mode; + local_md.ipv4.unicast_enable = ipv4_unicast_enable; + local_md.ipv4.multicast_enable = ipv4_multicast_enable; + local_md.ipv4.multicast_snooping = igmp_snooping_enable; + local_md.ipv6.unicast_enable = ipv6_unicast_enable; + local_md.ipv6.multicast_enable = ipv6_multicast_enable; + local_md.ipv6.multicast_snooping = mld_snooping_enable; + } + @placement_priority(2) @name(".port_vlan_to_bd_mapping") table port_vlan_to_bd_mapping { + key = { + local_md.ingress_port_lag_index: ternary; + hdr.vlan_tag[0].isValid() : ternary; + hdr.vlan_tag[0].vid : ternary; + } + actions = { + NoAction; + port_vlan_miss; + set_bd_properties; + } + const default_action = NoAction; + implementation = bd_action_profile; + size = port_vlan_table_size; + } + @placement_priority(2) @name(".vlan_to_bd_mapping") table vlan_to_bd_mapping { + key = { + hdr.vlan_tag[0].vid: exact; + } + actions = { + NoAction; + port_vlan_miss; + set_bd_properties; + } + const default_action = port_vlan_miss; + implementation = bd_action_profile; + size = vlan_table_size; + } + @name(".cpu_to_bd_mapping") table cpu_to_bd_mapping { + key = { + hdr.cpu.ingress_bd: exact; + } + actions = { + NoAction; + port_vlan_miss; + set_bd_properties; + } + const default_action = port_vlan_miss; + implementation = bd_action_profile; + size = bd_table_size; + } + @name(".set_peer_link_properties") action set_peer_link_properties() { + local_md.flags.peer_link = true; + } + @name(".peer_link") table peer_link { + key = { + local_md.ingress_port_lag_index: exact; + } + actions = { + NoAction; + set_peer_link_properties; + } + const default_action = NoAction; + size = port_table_size; + } + apply { + port_mirror.apply(local_md.ingress_port, local_md.mirror); + switch (port_mapping.apply().action_run) { + set_port_properties: { + if (!port_vlan_to_bd_mapping.apply().hit) { + if (hdr.vlan_tag[0].isValid()) { + vlan_to_bd_mapping.apply(); + } + } + rmac.apply(hdr, local_md); + } + set_cpu_port_properties: { + // Need to check whether the cpu header is valid. + // Otherwise you are setting garbage. + if (hdr.cpu.isValid()) { + terminate_cpu_packet(); + } + } + } + if (hdr.vlan_tag[0].isValid() && !hdr.vlan_tag[1].isValid() && (bit<1>)local_md.flags.port_vlan_miss == 0) { + bit<32> pv_hash_ = hash.get({ local_md.ingress_port[6:0], hdr.vlan_tag[0].vid }); + local_md.flags.port_vlan_miss = (bool)check_vlan_membership.execute(pv_hash_); + } + } +} + +control IngressPortStats(in switch_header_t hdr, in switch_port_t port, in bit<1> drop, in bit<1> copy_to_cpu) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_ip_stats_count") action no_action() { + stats.count(); + } + @name(".ingress_ip_port_stats") table ingress_ip_port_stats { + key = { + port : exact; + hdr.ipv4.isValid() : ternary; + hdr.ipv6.isValid() : ternary; + drop : ternary; + copy_to_cpu : ternary; + hdr.ethernet.dst_addr: ternary; + } + actions = { + no_action; + } + const default_action = no_action; + size = 512; + counters = stats; + } + apply { + ingress_ip_port_stats.apply(); + } +} + +control EgressPortStats(in switch_header_t hdr, in switch_port_t port, in bit<1> drop, in bit<1> copy_to_cpu) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_ip_stats_count") action no_action() { + stats.count(); + } + @name(".egress_ip_port_stats") table egress_ip_port_stats { + key = { + port : exact; + hdr.ipv4.isValid() : ternary; + hdr.ipv6.isValid() : ternary; + drop : ternary; + copy_to_cpu : ternary; + hdr.ethernet.dst_addr: ternary; + } + actions = { + no_action; + } + const default_action = no_action; + size = 512; + counters = stats; + } + apply { + egress_ip_port_stats.apply(); + } +} + +control LAG(inout switch_local_metadata_t local_md, in switch_hash_t hash, out switch_port_t egress_port) { + Hash(HashAlgorithm_t.CRC16) selector_hash; + @name(".lag_action_profile") ActionProfile(LAG_SELECTOR_TABLE_SIZE) lag_action_profile; + @name(".lag_selector") ActionSelector(lag_action_profile, selector_hash, SelectorMode_t.FAIR, LAG_MAX_MEMBERS_PER_GROUP, LAG_GROUP_TABLE_SIZE) lag_selector; + @name(".set_lag_port") action set_lag_port(switch_port_t port) { + egress_port = port; + } + @name(".set_peer_link_port") action set_peer_link_port(switch_port_t port, switch_port_lag_index_t port_lag_index) { + egress_port = port; + local_md.egress_port_lag_index = port_lag_index; + local_md.checks.same_if = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".lag_miss") action lag_miss() { + } + @name(".lag_table") table lag { + key = { + local_md.egress_port_lag_index: exact @name("port_lag_index") ; + hash : selector; + } + actions = { + lag_miss; + set_lag_port; + } + const default_action = lag_miss; + size = LAG_TABLE_SIZE; + implementation = lag_selector; + } + apply { + egress_port = 8; + // lag.apply(); + } +} + +control EgressPortMapping(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, in switch_port_t port)(switch_uint32_t table_size=288) { + @name(".set_port_normal") action set_port_normal(switch_port_lag_index_t port_lag_index, switch_eg_port_lag_label_t port_lag_label, switch_qos_group_t qos_group, switch_meter_index_t meter_index, switch_sflow_id_t sflow_session_id, bool mlag_member, switch_eg_port_lag_label_t acl_out_ports) { + local_md.egress_port_lag_index = port_lag_index; + local_md.egress_port_lag_label = port_lag_label; + local_md.qos.group = qos_group; + } + @name(".set_port_cpu") action set_port_cpu() { + local_md.flags.to_cpu = true; + } + @name(".egress_port_mapping") table port_mapping { + key = { + port: exact; + } + actions = { + set_port_normal; + set_port_cpu; + } + size = table_size; + } + @name(".set_egress_ingress_port_properties") action set_egress_ingress_port_properties(switch_isolation_group_t port_isolation_group, switch_isolation_group_t bport_isolation_group) { + local_md.port_isolation_group = port_isolation_group; + local_md.bport_isolation_group = bport_isolation_group; + } + @placement_priority(1) @name(".egress_ingress_port_mapping") table egress_ingress_port_mapping { + key = { + local_md.ingress_port: exact; + } + actions = { + NoAction; + set_egress_ingress_port_properties; + } + const default_action = NoAction; + size = table_size; + } + apply { + port_mapping.apply(); + egress_ingress_port_mapping.apply(); + } +} + +control EgressPortIsolation(inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md)(switch_uint32_t table_size=288) { + @name(".isolate_packet_port") action isolate_packet_port() { + local_md.flags.port_isolation_packet_drop = true; + } + @name(".egress_port_isolation") table egress_port_isolation { + key = { + eg_intr_md.egress_port : exact; + local_md.port_isolation_group: exact; + } + actions = { + NoAction; + isolate_packet_port; + } + const default_action = NoAction; + size = table_size; + } + @name(".isolate_packet_bport") action isolate_packet_bport() { + local_md.flags.bport_isolation_packet_drop = true; + } + @name(".egress_bport_isolation") table egress_bport_isolation { + key = { + eg_intr_md.egress_port : exact; + local_md.flags.routed : exact; + local_md.bport_isolation_group: exact; + } + actions = { + NoAction; + isolate_packet_bport; + } + const default_action = NoAction; + size = table_size; + } + apply { + egress_port_isolation.apply(); + egress_bport_isolation.apply(); + } +} + +control EgressCpuRewrite(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, in switch_port_t port)(switch_uint32_t table_size=288) { + @name(".cpu_rewrite") action cpu_rewrite() { + hdr.fabric.setValid(); + hdr.fabric.reserved = 0; + hdr.fabric.color = 0; + hdr.fabric.qos = 0; + hdr.fabric.reserved2 = 0; + hdr.cpu.setValid(); + hdr.cpu.egress_queue = 0; + hdr.cpu.tx_bypass = 0; + hdr.cpu.capture_ts = 0; + hdr.cpu.reserved = 0; + hdr.cpu.ingress_port = (bit<16>)local_md.ingress_port; + hdr.cpu.port_lag_index = (bit<16>)local_md.egress_port_lag_index; + hdr.cpu.ingress_bd = (bit<16>)local_md.bd; + hdr.cpu.reason_code = local_md.cpu_reason; + hdr.cpu.ether_type = hdr.ethernet.ether_type; + hdr.ethernet.ether_type = 0x9000; + } + @name(".cpu_port_rewrite") table cpu_port_rewrite { + key = { + port: exact; + } + actions = { + cpu_rewrite; + } + size = table_size; + } + apply { + cpu_port_rewrite.apply(); + } +} + +action set_port_state_properties(inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout switch_local_metadata_t local_md, switch_port_lag_index_t port_lag_index, switch_ig_port_lag_label_t port_lag_label, switch_yid_t exclusion_id, switch_meter_index_t meter_index, switch_sflow_id_t sflow_session_id) { + local_md.ingress_port_lag_index = port_lag_index; + local_md.ingress_port_lag_label = port_lag_label; + ig_intr_md_for_tm.level2_exclusion_id = exclusion_id; + local_md.checks.same_if = SWITCH_FLOOD; + local_md.sflow.session_id = sflow_session_id; +} +action set_bd_state_properties(inout switch_local_metadata_t local_md, switch_bd_label_t bd_label, bool ipv4_unicast_enable, bool ipv4_multicast_enable, bool igmp_snooping_enable, bool ipv6_unicast_enable, bool ipv6_multicast_enable, bool mld_snooping_enable, bool mpls_enable, bool vlan_arp_suppress, switch_packet_action_t vrf_ttl_violation, bool vrf_ttl_violation_valid, switch_packet_action_t vrf_ip_options_violation, switch_nat_zone_t zone) { + local_md.bd_label = bd_label; + local_md.flags.vlan_arp_suppress = vlan_arp_suppress; + local_md.ipv4.unicast_enable = ipv4_unicast_enable; + local_md.ipv4.multicast_enable = ipv4_multicast_enable; + local_md.ipv4.multicast_snooping = igmp_snooping_enable; + local_md.ipv6.unicast_enable = ipv6_unicast_enable; + local_md.ipv6.multicast_enable = ipv6_multicast_enable; + local_md.ipv6.multicast_snooping = mld_snooping_enable; + local_md.flags.vrf_ttl_violation = vrf_ttl_violation; + local_md.flags.vrf_ttl_violation_valid = vrf_ttl_violation_valid; + local_md.flags.vrf_ip_options_violation = vrf_ip_options_violation; +} +control IngressPortBDState(inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout switch_local_metadata_t local_md) { + @use_hash_action(0) table port_state { + key = { + local_md.ingress_port: exact; + } + actions = { + set_port_state_properties(ig_intr_md_for_tm, local_md); + } + const default_action = set_port_state_properties(ig_intr_md_for_tm, local_md, 0, 0, 0, 0, 0); + size = 512; + } + @use_hash_action(0) table bd_state { + key = { + local_md.bd[12:0]: exact; + } + actions = { + set_bd_state_properties(local_md); + } + const default_action = set_bd_state_properties(local_md, 0, false, false, false, false, false, false, false, false, 0, false, 0, 0); + size = 8192; + } + apply { + port_state.apply(); + bd_state.apply(); + } +} + +control PktValidation(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + const switch_uint32_t table_size = MIN_TABLE_SIZE; + action valid_ethernet_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.pkt_type = pkt_type; + local_md.lkp.mac_dst_addr = hdr.ethernet.dst_addr; + } + @name(".malformed_eth_pkt") action malformed_eth_pkt(bit<8> reason) { + local_md.lkp.mac_dst_addr = hdr.ethernet.dst_addr; + local_md.lkp.mac_type = hdr.ethernet.ether_type; + local_md.l2_drop_reason = reason; + } + @name(".valid_pkt_untagged") action valid_pkt_untagged(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_type = hdr.ethernet.ether_type; + valid_ethernet_pkt(pkt_type); + } + @name(".valid_pkt_tagged") action valid_pkt_tagged(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_type = hdr.vlan_tag[0].ether_type; + local_md.lkp.pcp = hdr.vlan_tag[0].pcp; + valid_ethernet_pkt(pkt_type); + } + @name(".validate_ethernet") table validate_ethernet { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + hdr.vlan_tag[0].isValid(): ternary; + } + actions = { + malformed_eth_pkt; + valid_pkt_untagged; + valid_pkt_tagged; + } + size = table_size; + } + @name(".valid_arp_pkt") action valid_arp_pkt() { + local_md.lkp.arp_opcode = hdr.arp.opcode; + } + @name(".valid_ipv6_pkt") action valid_ipv6_pkt(bool is_link_local) { + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV6; + local_md.lkp.ip_tos = hdr.ipv6.traffic_class; + local_md.lkp.ip_proto = hdr.ipv6.next_hdr; + local_md.lkp.ip_ttl = hdr.ipv6.hop_limit; + local_md.lkp.ip_src_addr = hdr.ipv6.src_addr; + local_md.lkp.ip_dst_addr = hdr.ipv6.dst_addr; + local_md.lkp.ipv6_flow_label = hdr.ipv6.flow_label; + local_md.flags.link_local = is_link_local; + } + @name(".valid_ipv4_pkt") action valid_ipv4_pkt(switch_ip_frag_t ip_frag, bool is_link_local) { + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV4; + local_md.lkp.ip_tos = hdr.ipv4.diffserv; + local_md.lkp.ip_proto = hdr.ipv4.protocol; + local_md.lkp.ip_ttl = hdr.ipv4.ttl; + local_md.lkp.ip_src_addr[63:0] = 64w0; + local_md.lkp.ip_src_addr[95:64] = hdr.ipv4.src_addr; + local_md.lkp.ip_src_addr[127:96] = 32w0; + local_md.lkp.ip_dst_addr[63:0] = 64w0; + local_md.lkp.ip_dst_addr[95:64] = hdr.ipv4.dst_addr; + local_md.lkp.ip_dst_addr[127:96] = 32w0; + local_md.lkp.ip_frag = ip_frag; + local_md.flags.link_local = is_link_local; + } + @name(".malformed_ipv4_pkt") action malformed_ipv4_pkt(bit<8> reason, switch_ip_frag_t ip_frag) { + valid_ipv4_pkt(ip_frag, false); + local_md.drop_reason = reason; + } + @name(".malformed_ipv6_pkt") action malformed_ipv6_pkt(bit<8> reason) { + valid_ipv6_pkt(false); + local_md.drop_reason = reason; + } + @name(".validate_ip") table validate_ip { + key = { + hdr.arp.isValid() : ternary; + hdr.ipv4.isValid() : ternary; + local_md.flags.ipv4_checksum_err: ternary; + hdr.ipv4.version : ternary; + hdr.ipv4.ihl : ternary; + hdr.ipv4.flags : ternary; + hdr.ipv4.frag_offset : ternary; + hdr.ipv4.ttl : ternary; + hdr.ipv4.src_addr[31:0] : ternary; + hdr.ipv6.isValid() : ternary; + hdr.ipv6.version : ternary; + hdr.ipv6.hop_limit : ternary; + hdr.ipv6.src_addr[127:0] : ternary; + } + actions = { + malformed_ipv4_pkt; + malformed_ipv6_pkt; + valid_arp_pkt; + valid_ipv4_pkt; + valid_ipv6_pkt; + } + size = table_size; + } + action set_tcp_ports() { + local_md.lkp.l4_src_port = hdr.tcp.src_port; + local_md.lkp.l4_dst_port = hdr.tcp.dst_port; + local_md.lkp.tcp_flags = hdr.tcp.flags; + } + action set_udp_ports() { + local_md.lkp.l4_src_port = hdr.udp.src_port; + local_md.lkp.l4_dst_port = hdr.udp.dst_port; + local_md.lkp.tcp_flags = 0; + } + action set_icmp_type() { + local_md.lkp.l4_src_port[7:0] = hdr.icmp.type; + local_md.lkp.l4_src_port[15:8] = hdr.icmp.code; + local_md.lkp.l4_dst_port = 0; + local_md.lkp.tcp_flags = 0; + } + action set_igmp_type() { + local_md.lkp.l4_src_port[7:0] = hdr.igmp.type; + local_md.lkp.l4_src_port[15:8] = 0; + local_md.lkp.l4_dst_port = 0; + local_md.lkp.tcp_flags = 0; + } + table validate_other { + key = { + hdr.tcp.isValid() : exact; + hdr.udp.isValid() : exact; + hdr.icmp.isValid(): exact; + hdr.igmp.isValid(): exact; + } + actions = { + NoAction; + set_tcp_ports; + set_udp_ports; + set_icmp_type; + } + const default_action = NoAction; + const entries = { + (true, false, false, false) : set_tcp_ports(); + (false, true, false, false) : set_udp_ports(); + (false, false, true, false) : set_icmp_type(); + } + size = 16; + } + apply { + validate_ethernet.apply(); + validate_ip.apply(); + validate_other.apply(); + } +} + +control SameMacCheck(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".compute_same_mac_check") action compute_same_mac_check() { + local_md.drop_reason = SWITCH_DROP_REASON_OUTER_SAME_MAC_CHECK; + } + @ways(1) @name(".same_mac_check") table same_mac_check { + key = { + local_md.same_mac: exact; + } + actions = { + NoAction; + compute_same_mac_check; + } + const default_action = NoAction; + const entries = { + 48w0x0 : compute_same_mac_check(); + } + } + apply { + local_md.same_mac = hdr.ethernet.src_addr ^ hdr.ethernet.dst_addr; + same_mac_check.apply(); + } +} + +control InnerPktValidation(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".valid_inner_ethernet_pkt") action valid_ethernet_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.pkt_type = pkt_type; + } + @name(".valid_inner_ipv4_pkt") action valid_ipv4_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_type = 0x800; + local_md.lkp.pkt_type = pkt_type; + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV4; + local_md.lkp.ip_ttl = hdr.inner_ipv4.ttl; + local_md.lkp.ip_proto = hdr.inner_ipv4.protocol; + local_md.lkp.ip_src_addr[63:0] = 64w0; + local_md.lkp.ip_src_addr[95:64] = hdr.inner_ipv4.src_addr; + local_md.lkp.ip_src_addr[127:96] = 32w0; + local_md.lkp.ip_dst_addr[63:0] = 64w0; + local_md.lkp.ip_dst_addr[95:64] = hdr.inner_ipv4.dst_addr; + local_md.lkp.ip_dst_addr[127:96] = 32w0; + } + @name(".valid_inner_ipv6_pkt") action valid_ipv6_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_type = 0x86dd; + local_md.lkp.pkt_type = pkt_type; + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV6; + local_md.lkp.ip_ttl = hdr.inner_ipv6.hop_limit; + local_md.lkp.ip_proto = hdr.inner_ipv6.next_hdr; + local_md.lkp.ip_src_addr = hdr.inner_ipv6.src_addr; + local_md.lkp.ip_dst_addr = hdr.inner_ipv6.dst_addr; + local_md.lkp.ipv6_flow_label = hdr.inner_ipv6.flow_label; + local_md.flags.link_local = false; + } + action set_tcp_ports() { + local_md.lkp.l4_src_port = hdr.inner_tcp.src_port; + local_md.lkp.l4_dst_port = hdr.inner_tcp.dst_port; + local_md.lkp.tcp_flags = hdr.inner_tcp.flags; + } + action set_udp_ports() { + local_md.lkp.l4_src_port = hdr.inner_udp.src_port; + local_md.lkp.l4_dst_port = hdr.inner_udp.dst_port; + local_md.lkp.tcp_flags = 0; + } + action set_icmp_type() { + local_md.lkp.l4_src_port[7:0] = hdr.inner_icmp.type; + local_md.lkp.l4_src_port[15:8] = hdr.inner_icmp.code; + local_md.lkp.l4_dst_port = 0; + local_md.lkp.tcp_flags = 0; + } + action set_igmp_type() { + local_md.lkp.l4_src_port[7:0] = hdr.inner_igmp.type; + local_md.lkp.l4_src_port[15:8] = 0; + local_md.lkp.l4_dst_port = 0; + local_md.lkp.tcp_flags = 0; + } + @name(".valid_inner_ipv4_tcp_pkt") action valid_ipv4_tcp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv4_pkt(pkt_type); + set_tcp_ports(); + } + @name(".valid_inner_ipv4_udp_pkt") action valid_ipv4_udp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv4_pkt(pkt_type); + set_udp_ports(); + } + @name(".valid_inner_ipv4_icmp_pkt") action valid_ipv4_icmp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv4_pkt(pkt_type); + set_icmp_type(); + } + @name(".valid_inner_ipv4_igmp_pkt") action valid_ipv4_igmp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv4_pkt(pkt_type); + set_igmp_type(); + } + @name(".valid_inner_ipv6_tcp_pkt") action valid_ipv6_tcp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv6_pkt(pkt_type); + set_tcp_ports(); + } + @name(".valid_inner_ipv6_udp_pkt") action valid_ipv6_udp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv6_pkt(pkt_type); + set_udp_ports(); + } + @name(".valid_inner_ipv6_icmp_pkt") action valid_ipv6_icmp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv6_pkt(pkt_type); + set_icmp_type(); + } + @name(".malformed_l2_inner_pkt") action malformed_l2_pkt(bit<8> reason) { + local_md.l2_drop_reason = reason; + } + @name(".malformed_l3_inner_pkt") action malformed_l3_pkt(bit<8> reason) { + local_md.drop_reason = reason; + } + @name(".validate_inner_ethernet") table validate_ethernet { + key = { + hdr.inner_ethernet.isValid() : ternary; + hdr.inner_ipv6.isValid() : ternary; + hdr.inner_ipv6.version : ternary; + hdr.inner_ipv6.hop_limit : ternary; + hdr.inner_ipv4.isValid() : ternary; + local_md.flags.inner_ipv4_checksum_err: ternary; + hdr.inner_ipv4.version : ternary; + hdr.inner_ipv4.ihl : ternary; + hdr.inner_ipv4.ttl : ternary; + hdr.inner_tcp.isValid() : ternary; + hdr.inner_udp.isValid() : ternary; + hdr.inner_icmp.isValid() : ternary; + hdr.inner_igmp.isValid() : ternary; + } + actions = { + NoAction; + valid_ipv4_tcp_pkt; + valid_ipv4_udp_pkt; + valid_ipv4_icmp_pkt; + valid_ipv4_igmp_pkt; + valid_ipv4_pkt; + valid_ipv6_tcp_pkt; + valid_ipv6_udp_pkt; + valid_ipv6_icmp_pkt; + valid_ipv6_pkt; + malformed_l2_pkt; + malformed_l3_pkt; + } + size = MIN_TABLE_SIZE; + } + apply { + validate_ethernet.apply(); + } +} + +control EgressPacketValidation(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + action valid_ipv4_pkt() { + local_md.lkp.ip_src_addr[63:0] = 64w0; + local_md.lkp.ip_src_addr[95:64] = hdr.ipv4.src_addr; + local_md.lkp.ip_src_addr[127:96] = 32w0; + local_md.lkp.ip_dst_addr[63:0] = 64w0; + local_md.lkp.ip_dst_addr[95:64] = hdr.ipv4.dst_addr; + local_md.lkp.ip_dst_addr[127:96] = 32w0; + local_md.lkp.ip_tos = hdr.ipv4.diffserv; + local_md.lkp.ip_proto = hdr.ipv4.protocol; + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV4; + } + action valid_ipv6_pkt() { + local_md.lkp.ip_src_addr = hdr.ipv6.src_addr; + local_md.lkp.ip_dst_addr = hdr.ipv6.dst_addr; + local_md.lkp.ip_tos = hdr.ipv6.traffic_class; + local_md.lkp.ip_proto = hdr.ipv6.next_hdr; + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV6; + } + @name(".egress_pkt_validation") table validate_ip { + key = { + hdr.ipv4.isValid(): ternary; + hdr.ipv6.isValid(): ternary; + } + actions = { + valid_ipv4_pkt; + valid_ipv6_pkt; + } + const entries = { + (true, false) : valid_ipv4_pkt; + (false, true) : valid_ipv6_pkt; + } + size = MIN_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress) { + validate_ip.apply(); + } + } +} + +control MulticastFlooding(inout switch_local_metadata_t local_md)(switch_uint32_t table_size) { + @name(".mcast_flood") action flood(switch_mgid_t mgid) { + local_md.multicast.id = mgid; + } + @name(".bd_flood") table bd_flood { + key = { + local_md.bd : exact @name("bd") ; + local_md.lkp.pkt_type: exact @name("pkt_type") ; + } + actions = { + flood; + } + size = table_size; + } + apply { + bd_flood.apply(); + } +} + +control Replication(in egress_intrinsic_metadata_t eg_intr_md, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=4096) { + @name(".mc_rid_hit") action rid_hit_mc(switch_bd_t bd) { + local_md.checks.same_bd = bd ^ local_md.bd; + local_md.bd = bd; + } + action rid_miss() { + local_md.flags.routed = false; + } + @name(".rid") table rid { + key = { + eg_intr_md.egress_rid: exact; + } + actions = { + rid_miss; + rid_hit_mc; + } + size = table_size; + const default_action = rid_miss; + } + apply { + if (eg_intr_md.egress_rid != 0) { + rid.apply(); + } + } +} + +control ECNAcl(in switch_local_metadata_t local_md, in switch_lookup_fields_t lkp, inout switch_pkt_color_t pkt_color)(switch_uint32_t table_size=512) { + @name(".ecn_acl.set_ingress_color") action set_ingress_color(switch_pkt_color_t color) { + pkt_color = color; + } + @name(".ecn_acl.acl") table acl { + key = { + local_md.ingress_port_lag_label: ternary; + lkp.ip_tos : ternary; + lkp.tcp_flags : ternary; + } + actions = { + NoAction; + set_ingress_color; + } + const default_action = NoAction; + size = table_size; + } + apply { + acl.apply(); + } +} + +control IngressPFCWd(in switch_port_t port, in switch_qid_t qid, out bool flag)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_pfcwd.acl_deny") action acl_deny() { + flag = true; + stats.count(); + } + @ways(2) @name(".ingress_pfcwd.acl") table acl { + key = { + qid : exact; + port: exact; + } + actions = { + @defaultonly NoAction; + acl_deny; + } + const default_action = NoAction; + counters = stats; + size = table_size; + } + apply { + acl.apply(); + } +} + +control EgressPFCWd(in switch_port_t port, in switch_qid_t qid, out bool flag)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_pfcwd.acl_deny") action acl_deny() { + flag = true; + stats.count(); + } + @ways(2) @name(".egress_pfcwd.acl") table acl { + key = { + qid : exact; + port: exact; + } + actions = { + @defaultonly NoAction; + acl_deny; + } + const default_action = NoAction; + counters = stats; + size = table_size; + } + apply { + acl.apply(); + } +} + +control IngressQoSMap(inout switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t dscp_map_size=4096, switch_uint32_t pcp_map_size=512) { + @name(".ingress_qos_map.set_ingress_tc") action set_ingress_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + } + @name(".ingress_qos_map.set_ingress_color") action set_ingress_color(switch_pkt_color_t color) { + local_md.qos.color = color; + } + @name(".ingress_qos_map.set_ingress_tc_and_color") action set_ingress_tc_and_color(switch_tc_t tc, switch_pkt_color_t color) { + set_ingress_tc(tc); + set_ingress_color(color); + } + @name(".ingress_qos_map.dscp_tc_map") table dscp_tc_map { + key = { + local_md.ingress_port : exact; + local_md.lkp.ip_tos[7:2]: exact; + } + actions = { + NoAction; + set_ingress_tc; + set_ingress_color; + set_ingress_tc_and_color; + } + const default_action = NoAction; + size = dscp_map_size; + } + @name(".ingress_qos_map.pcp_tc_map") table pcp_tc_map { + key = { + local_md.ingress_port: exact; + local_md.lkp.pcp : exact; + } + actions = { + NoAction; + set_ingress_tc; + set_ingress_color; + set_ingress_tc_and_color; + } + const default_action = NoAction; + size = pcp_map_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_QOS != 0)) { + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_NONE) { + switch (dscp_tc_map.apply().action_run) { + NoAction: { + pcp_tc_map.apply(); + } + } + } else { + pcp_tc_map.apply(); + } + } + } +} + +control IngressTC(inout switch_local_metadata_t local_md) { + const bit<32> tc_table_size = 1024; + @name(".ingress_tc.set_icos") action set_icos(switch_cos_t icos) { + local_md.qos.icos = icos; + } + @name(".ingress_tc.set_queue") action set_queue(switch_qid_t qid) { + local_md.qos.qid = qid; + } + @name(".ingress_tc.set_icos_and_queue") action set_icos_and_queue(switch_cos_t icos, switch_qid_t qid) { + set_icos(icos); + set_queue(qid); + } + @name(".ingress_tc.traffic_class") table traffic_class { + key = { + local_md.ingress_port: ternary @name("port") ; + local_md.qos.color : ternary @name("color") ; + local_md.qos.tc : exact @name("tc") ; + } + actions = { + set_icos; + set_queue; + set_icos_and_queue; + } + size = tc_table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_QOS != 0)) { + traffic_class.apply(); + } + } +} + +control PPGStats(inout switch_local_metadata_t local_md) { + const bit<32> ppg_table_size = 1024; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) ppg_stats; + @name(".ppg_stats.count") action count() { + ppg_stats.count(); + } + @ways(2) @name(".ppg_stats.ppg") table ppg { + key = { + local_md.ingress_port: exact @name("port") ; + local_md.qos.icos : exact @name("icos") ; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + size = ppg_table_size; + counters = ppg_stats; + } + apply { + ppg.apply(); + } +} + +control EgressQoS(inout switch_header_t hdr, in switch_port_t port, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=1024) { + @name(".egress_qos.set_ipv4_dscp") action set_ipv4_dscp(bit<6> dscp, bit<3> exp) { + hdr.ipv4.diffserv[7:2] = dscp; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_ipv4_tos") action set_ipv4_tos(switch_uint8_t tos, bit<3> exp) { + hdr.ipv4.diffserv = tos; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_ipv6_dscp") action set_ipv6_dscp(bit<6> dscp, bit<3> exp) { + hdr.ipv6.traffic_class[7:2] = dscp; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_ipv6_tos") action set_ipv6_tos(switch_uint8_t tos, bit<3> exp) { + hdr.ipv6.traffic_class = tos; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_vlan_pcp") action set_vlan_pcp(bit<3> pcp, bit<3> exp) { + hdr.vlan_tag[0].pcp = pcp; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.qos_map") table qos_map { + key = { + local_md.qos.group: ternary @name("group") ; + local_md.qos.tc : ternary @name("tc") ; + local_md.qos.color: ternary @name("color") ; + hdr.ipv4.isValid(): ternary; + hdr.ipv6.isValid(): ternary; + } + actions = { + NoAction; + set_ipv4_dscp; + set_ipv4_tos; + set_ipv6_dscp; + set_ipv6_tos; + set_vlan_pcp; + } + const default_action = NoAction; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + qos_map.apply(); + } + } +} + +control EgressQueue(in switch_port_t port, inout switch_local_metadata_t local_md)(switch_uint32_t queue_table_size=1024) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) queue_stats; + @name(".egress_queue.count") action count() { + queue_stats.count(); + } + @ways(2) @pack(2) @name(".egress_queue.queue") table queue { + key = { + port : exact; + local_md.qos.qid: exact @name("qid") ; + } + actions = { + @defaultonly NoAction; + count; + } + size = queue_table_size; + const default_action = NoAction; + counters = queue_stats; + } + apply { + queue.apply(); + } +} + +control StormControl(inout switch_local_metadata_t local_md, in switch_pkt_type_t pkt_type, out bool flag)(switch_uint32_t table_size=256, switch_uint32_t meter_size=1024) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) storm_control_stats; + @name(".storm_control.meter") Meter>(meter_size, MeterType_t.BYTES) meter; + @name(".storm_control.count") action count() { + storm_control_stats.count(); + flag = false; + } + @name(".storm_control.drop_and_count") action drop_and_count() { + storm_control_stats.count(); + flag = true; + } + @name(".storm_control.stats") table stats { + key = { + local_md.qos.storm_control_color: exact; + pkt_type : ternary; + local_md.ingress_port : exact; + local_md.flags.dmac_miss : ternary; + } + actions = { + @defaultonly NoAction; + count; + drop_and_count; + } + const default_action = NoAction; + size = table_size * 2; + counters = storm_control_stats; + } + @name(".storm_control.set_meter") action set_meter(bit<16> index) { + local_md.qos.storm_control_color = (bit<2>)meter.execute(index); + } + @name(".storm_control.storm_control") table storm_control { + key = { + local_md.ingress_port : exact; + pkt_type : ternary; + local_md.flags.dmac_miss: ternary; + } + actions = { + @defaultonly NoAction; + set_meter; + } + const default_action = NoAction; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_STORM_CONTROL != 0)) { + storm_control.apply(); + } + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_STORM_CONTROL != 0)) { + stats.apply(); + } + } +} + +control IngressMirrorMeter(inout switch_local_metadata_t local_md)(switch_uint32_t table_size=256) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_mirror_meter.meter") Meter>(table_size, MeterType_t.PACKETS) meter; + switch_pkt_color_t color; + @name(".ingress_mirror_meter.mirror_and_count") action mirror_and_count() { + stats.count(); + } + @name(".ingress_mirror_meter.no_mirror_and_count") action no_mirror_and_count() { + stats.count(); + local_md.mirror.type = 0; + } + @ways(2) @name(".ingress_mirror_meter.meter_action") table meter_action { + key = { + color : exact; + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + mirror_and_count; + no_mirror_and_count; + } + const default_action = NoAction; + size = table_size * 2; + counters = stats; + } + @name(".ingress_mirror_meter.set_meter") action set_meter(bit<9> index) { + color = (bit<2>)meter.execute(index); + } + @name(".ingress_mirror_meter.meter_index") table meter_index { + key = { + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + set_meter; + } + const default_action = NoAction; + size = table_size; + } + apply { + } +} + +control EgressMirrorMeter(inout switch_local_metadata_t local_md)(switch_uint32_t table_size=256) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_mirror_meter.meter") Meter>(table_size, MeterType_t.PACKETS) meter; + switch_pkt_color_t color; + @name(".egress_mirror_meter.mirror_and_count") action mirror_and_count() { + stats.count(); + } + @name(".egress_mirror_meter.no_mirror_and_count") action no_mirror_and_count() { + stats.count(); + local_md.mirror.type = 0; + } + @ways(2) @name(".egress_mirror_meter.meter_action") table meter_action { + key = { + color : exact; + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + mirror_and_count; + no_mirror_and_count; + } + const default_action = NoAction; + size = table_size * 2; + counters = stats; + } + @name(".egress_mirror_meter.set_meter") action set_meter(bit<9> index) { + color = (bit<2>)meter.execute(index); + } + @name(".egress_mirror_meter.meter_index") table meter_index { + key = { + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + set_meter; + } + const default_action = NoAction; + size = table_size; + } + apply { + } +} + +control WRED(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, out bool wred_drop) { + switch_wred_index_t index; + bit<1> wred_flag; + const switch_uint32_t wred_size = 1 << 10; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_wred.wred") Wred, switch_wred_index_t>(wred_size, 1, 0) wred; + @name(".egress_wred.set_wred_index") action set_wred_index(switch_wred_index_t wred_index) { + index = wred_index; + wred_flag = (bit<1>)wred.execute(local_md.qos.qdepth, wred_index); + } + @name(".egress_wred.wred_index") table wred_index { + key = { + eg_intr_md.egress_port: exact @name("port") ; + local_md.qos.qid : exact @name("qid") ; + local_md.qos.color : exact @name("color") ; + } + actions = { + NoAction; + set_wred_index; + } + const default_action = NoAction; + size = wred_size; + } + @name(".egress_wred.set_ipv4_ecn") action set_ipv4_ecn() { + hdr.ipv4.diffserv[1:0] = SWITCH_ECN_CODEPOINT_CE; + wred_drop = false; + } + @name(".egress_wred.set_ipv6_ecn") action set_ipv6_ecn() { + hdr.ipv6.traffic_class[1:0] = SWITCH_ECN_CODEPOINT_CE; + wred_drop = false; + } + @name(".egress_wred.drop") action drop() { + wred_drop = true; + } + @name(".egress_wred.v4_wred_action") table v4_wred_action { + key = { + index : exact; + hdr.ipv4.diffserv[1:0]: exact; + } + actions = { + NoAction; + drop; + set_ipv4_ecn; + } + size = 3 * wred_size; + } + @name(".egress_wred.v6_wred_action") table v6_wred_action { + key = { + index : exact; + hdr.ipv6.traffic_class[1:0]: exact; + } + actions = { + NoAction; + drop; + set_ipv6_ecn; + } + size = 3 * wred_size; + } + @name(".egress_wred.count") action count() { + stats.count(); + } + @ways(2) @name(".egress_wred.wred_stats") table wred_stats { + key = { + eg_intr_md.egress_port: exact @name("port") ; + local_md.qos.qid : exact @name("qid") ; + local_md.qos.color : exact @name("color") ; + wred_drop : exact; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + size = 2 * wred_size; + counters = stats; + } + apply { + if (!local_md.flags.bypass_egress) { + wred_index.apply(); + } + if (!local_md.flags.bypass_egress && wred_flag == 1) { + if (hdr.ipv4.isValid()) { + switch (v4_wred_action.apply().action_run) { + NoAction: { + } + default: { + wred_stats.apply(); + } + } + } else if (hdr.ipv6.isValid()) { + switch (v6_wred_action.apply().action_run) { + NoAction: { + } + default: { + wred_stats.apply(); + } + } + } + } + } +} + +control DeflectOnDrop(in switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm)(switch_uint32_t table_size=1024) { + @name(".dtel.dod.enable_dod") action enable_dod() { + ig_intr_md_for_tm.deflect_on_drop = 1w1; + } + @name(".dtel.dod.disable_dod") action disable_dod() { + ig_intr_md_for_tm.deflect_on_drop = 1w0; + } + @name(".deflect_on_drop.config") table config { + key = { + local_md.dtel.report_type : ternary; + ig_intr_md_for_tm.ucast_egress_port: ternary @name("egress_port") ; + local_md.qos.qid : ternary @name("qid") ; + local_md.multicast.id : ternary; + local_md.cpu_reason : ternary; + } + actions = { + enable_dod; + disable_dod; + } + size = table_size; + const default_action = disable_dod; + } + apply { + config.apply(); + } +} + +control MirrorOnDrop(in switch_drop_reason_t drop_reason, inout switch_dtel_metadata_t dtel_md, inout switch_mirror_metadata_t mirror_md) { + @name(".dtel.mod.mirror") action mirror() { + mirror_md.type = 3; + mirror_md.src = SWITCH_PKT_SRC_CLONED_INGRESS; + } + @name(".dtel.mod.mirror_and_set_d_bit") action mirror_and_set_d_bit() { + dtel_md.report_type = dtel_md.report_type | SWITCH_DTEL_REPORT_TYPE_DROP; + mirror_md.type = 3; + mirror_md.src = SWITCH_PKT_SRC_CLONED_INGRESS; + } + @name(".mirror_on_drop.config") table config { + key = { + drop_reason : ternary; + dtel_md.report_type: ternary; + } + actions = { + NoAction; + mirror; + mirror_and_set_d_bit; + } + const default_action = NoAction; + } + apply { + config.apply(); + } +} + +control DropReport(in switch_header_t hdr, in switch_local_metadata_t local_md, in bit<32> hash, inout bit<2> flag) { + @name(".drop_report.array1") Register, bit<17>>(1 << 17, 0) array1; + @name(".drop_report.array2") Register, bit<17>>(1 << 17, 0) array2; + RegisterAction, bit<17>, bit<1>>(array1) filter1 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + val = 0b1; + } + }; + RegisterAction, bit<17>, bit<1>>(array2) filter2 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + val = 0b1; + } + }; + apply { + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_DROP | SWITCH_DTEL_SUPPRESS_REPORT | SWITCH_DTEL_REPORT_TYPE_ETRAP_CHANGE) == SWITCH_DTEL_REPORT_TYPE_DROP && hdr.dtel_drop_report.isValid()) { + flag[0:0] = filter1.execute(hash[17 - 1:0]); + } + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_DROP | SWITCH_DTEL_SUPPRESS_REPORT | SWITCH_DTEL_REPORT_TYPE_ETRAP_CHANGE) == SWITCH_DTEL_REPORT_TYPE_DROP && hdr.dtel_drop_report.isValid()) { + flag[1:1] = filter2.execute(hash[31:32 - 17]); + } + } +} + +struct switch_queue_alert_threshold_t { + bit<32> qdepth; + bit<32> latency; +} + +struct switch_queue_report_quota_t { + bit<32> counter; + bit<32> latency; +} + +control QueueReport(inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, out bit<1> qalert) { + bit<16> quota_; + const bit<32> queue_table_size = 1024; + const bit<32> queue_register_size = 2048; + @name(".queue_report.thresholds") Register>(queue_register_size) thresholds; + RegisterAction, bit<1>>(thresholds) check_thresholds = { + void apply(inout switch_queue_alert_threshold_t reg, out bit<1> flag) { + if (reg.latency <= local_md.dtel.latency || reg.qdepth <= (bit<32>)local_md.qos.qdepth) { + flag = 1; + } + } + }; + @name(".dtel.queue_report.set_qmask") action set_qmask(bit<32> quantization_mask) { + local_md.dtel.latency = local_md.dtel.latency & quantization_mask; + } + @name(".dtel.queue_report.set_qalert") action set_qalert(bit<16> index, bit<16> quota, bit<32> quantization_mask) { + qalert = check_thresholds.execute(index); + quota_ = quota; + set_qmask(quantization_mask); + } + @name(".queue_report.queue_alert") @ways(2) table queue_alert { + key = { + local_md.qos.qid : exact @name("qid") ; + local_md.egress_port: exact @name("port") ; + } + actions = { + set_qalert; + set_qmask; + } + size = queue_table_size; + } + @name(".queue_report.quotas") Register>(queue_register_size) quotas; + RegisterAction, bit<1>>(quotas) reset_quota = { + void apply(inout switch_queue_report_quota_t reg, out bit<1> flag) { + flag = 0; + reg.counter = (bit<32>)quota_[15:0]; + } + }; + RegisterAction, bit<1>>(quotas) check_latency_and_update_quota = { + void apply(inout switch_queue_report_quota_t reg, out bit<1> flag) { + if (reg.counter > 0) { + reg.counter = reg.counter - 1; + flag = 1; + } + if (reg.latency != local_md.dtel.latency) { + reg.latency = local_md.dtel.latency; + flag = 1; + } + } + }; + RegisterAction, bit<1>>(quotas) update_quota = { + void apply(inout switch_queue_report_quota_t reg, out bit<1> flag) { + if (reg.counter > 0) { + reg.counter = reg.counter - 1; + flag = 1; + } + } + }; + @name(".dtel.queue_report.reset_quota_") action reset_quota_(bit<16> index) { + qalert = reset_quota.execute(index); + } + @name(".dtel.queue_report.update_quota_") action update_quota_(bit<16> index) { + qalert = update_quota.execute(index); + } + @name(".dtel.queue_report.check_latency_and_update_quota_") action check_latency_and_update_quota_(bit<16> index) { + qalert = check_latency_and_update_quota.execute(index); + } + @name(".queue_report.check_quota") table check_quota { + key = { + local_md.pkt_src : exact; + qalert : exact; + local_md.qos.qid : exact @name("qid") ; + local_md.egress_port: exact @name("port") ; + } + actions = { + NoAction; + reset_quota_; + update_quota_; + check_latency_and_update_quota_; + } + const default_action = NoAction; + size = 3 * queue_table_size; + } + apply { + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + queue_alert.apply(); + } + check_quota.apply(); + } +} + +control FlowReport(in switch_local_metadata_t local_md, out bit<2> flag) { + bit<16> digest; + Hash>(HashAlgorithm_t.CRC16) hash; + @name(".flow_report.array1") Register, bit<16>>(1 << 16, 0) array1; + @name(".flow_report.array2") Register, bit<16>>(1 << 16, 0) array2; + @reduction_or_group("filter") RegisterAction, bit<16>, bit<2>>(array1) filter1 = { + void apply(inout bit<16> reg, out bit<2> rv) { + if (reg == 16w0) { + rv = 0b10; + } else if (reg == digest) { + rv = 0b1; + } + reg = digest; + } + }; + @reduction_or_group("filter") RegisterAction, bit<16>, bit<2>>(array2) filter2 = { + void apply(inout bit<16> reg, out bit<2> rv) { + if (reg == 16w0) { + rv = 0b10; + } else if (reg == digest) { + rv = 0b1; + } + reg = digest; + } + }; + apply { + digest = hash.get({ local_md.dtel.latency, local_md.ingress_port, local_md.egress_port, local_md.dtel.hash }); + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_FLOW | SWITCH_DTEL_SUPPRESS_REPORT) == SWITCH_DTEL_REPORT_TYPE_FLOW && local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + flag = filter1.execute(local_md.dtel.hash[15:0]); + } + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_FLOW | SWITCH_DTEL_SUPPRESS_REPORT) == SWITCH_DTEL_REPORT_TYPE_FLOW && local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + flag = flag | filter2.execute(local_md.dtel.hash[31:16]); + } + } +} + +control IngressDtel(in switch_header_t hdr, in switch_lookup_fields_t lkp, inout switch_local_metadata_t local_md, in bit<16> hash, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, inout ingress_intrinsic_metadata_for_tm_t ig_intr_for_tm) { + DeflectOnDrop() dod; + MirrorOnDrop() mod; + Hash(HashAlgorithm_t.IDENTITY) selector_hash; + @name(".dtel.dtel_action_profile") ActionProfile(DTEL_SELECTOR_TABLE_SIZE) dtel_action_profile; + @name(".dtel.session_selector") ActionSelector(dtel_action_profile, selector_hash, SelectorMode_t.FAIR, DTEL_MAX_MEMBERS_PER_GROUP, DTEL_GROUP_TABLE_SIZE) session_selector; + @name(".dtel.set_mirror_session") action set_mirror_session(switch_mirror_session_t session_id) { + local_md.dtel.session_id = session_id; + } + @name(".dtel.mirror_session") table mirror_session { + key = { + hdr.ethernet.isValid(): ternary; + hash : selector; + } + actions = { + NoAction; + set_mirror_session; + } + implementation = session_selector; + } + apply { + dod.apply(local_md, ig_intr_for_tm); + if (local_md.mirror.type == 0) { + mod.apply(local_md.drop_reason, local_md.dtel, local_md.mirror); + } + mirror_session.apply(); + } +} + +control DtelConfig(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr) { + @name(".dtel_config.seq_number") Register, switch_mirror_session_t>(1024) seq_number; + RegisterAction, switch_mirror_session_t, bit<32>>(seq_number) get_seq_number = { + void apply(inout bit<32> reg, out bit<32> rv) { + reg = reg + 1; + rv = reg; + } + }; + @name(".dtel_config.mirror_switch_local") action mirror_switch_local() { + local_md.mirror.type = 4; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + } + @name(".dtel_config.mirror_switch_local_and_set_q_bit") action mirror_switch_local_and_set_q_bit() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_REPORT_TYPE_QUEUE; + mirror_switch_local(); + } + action mirror_switch_local_and_drop() { + mirror_switch_local(); + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.mirror_switch_local_and_set_f_bit_and_drop") action mirror_switch_local_and_set_f_bit_and_drop() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_REPORT_TYPE_FLOW; + mirror_switch_local(); + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.mirror_switch_local_and_set_q_f_bits_and_drop") action mirror_switch_local_and_set_q_f_bits_and_drop() { + local_md.dtel.report_type = local_md.dtel.report_type | (SWITCH_DTEL_REPORT_TYPE_QUEUE | SWITCH_DTEL_REPORT_TYPE_FLOW); + mirror_switch_local(); + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.mirror_drop") action mirror_drop() { + local_md.mirror.type = 3; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + } + @name(".dtel_config.mirror_drop_and_set_q_bit") action mirror_drop_and_set_q_bit() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_REPORT_TYPE_QUEUE; + mirror_drop(); + } + @name(".dtel_config.mirror_clone") action mirror_clone() { + local_md.mirror.type = 5; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.dtel.session_id = local_md.dtel.clone_session_id; + } + @name(".dtel_config.drop") action drop() { + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.update") action update(switch_dtel_switch_id_t switch_id, switch_dtel_hw_id_t hw_id, bit<4> next_proto, switch_dtel_report_type_t report_type) { + hdr.dtel.setValid(); + hdr.dtel.hw_id = hw_id; + hdr.dtel.switch_id = switch_id; + hdr.dtel.d_q_f = (bit<3>)report_type; + hdr.dtel.version = 0; + hdr.dtel.next_proto = next_proto; + hdr.dtel.reserved = 0; + hdr.dtel.seq_number = get_seq_number.execute(local_md.mirror.session_id); + hdr.dtel.timestamp = (bit<32>)local_md.ingress_timestamp; + } + @name(".dtel_config.update_and_mirror_truncate") action update_and_mirror_truncate(switch_dtel_switch_id_t switch_id, switch_dtel_hw_id_t hw_id, bit<4> next_proto, bit<8> md_length, bit<16> rep_md_bits, switch_dtel_report_type_t report_type) { + update(switch_id, hw_id, next_proto, report_type); + local_md.mirror.type = 5; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.update_and_set_etrap") action update_and_set_etrap(switch_dtel_switch_id_t switch_id, switch_dtel_hw_id_t hw_id, bit<4> next_proto, bit<8> md_length, bit<16> rep_md_bits, switch_dtel_report_type_t report_type, bit<2> etrap_status) { + hdr.dtel.setValid(); + hdr.dtel.hw_id = hw_id; + hdr.dtel.switch_id = switch_id; + hdr.dtel.d_q_f = (bit<3>)report_type; + hdr.dtel.version = 0; + hdr.dtel.next_proto = next_proto; + hdr.dtel.reserved[14:13] = etrap_status; + hdr.dtel.seq_number = get_seq_number.execute(local_md.mirror.session_id); + hdr.dtel.timestamp = (bit<32>)local_md.ingress_timestamp; + } + @name(".dtel_config.set_ipv4_dscp_all") action set_ipv4_dscp_all(bit<6> dscp) { + hdr.ipv4.diffserv[7:2] = dscp; + } + @name(".dtel_config.set_ipv6_dscp_all") action set_ipv6_dscp_all(bit<6> dscp) { + hdr.ipv6.traffic_class[7:2] = dscp; + } + @name(".dtel_config.set_ipv4_dscp_2") action set_ipv4_dscp_2(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[2:2] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_2") action set_ipv6_dscp_2(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[2:2] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_3") action set_ipv4_dscp_3(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[3:3] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_3") action set_ipv6_dscp_3(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[3:3] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_4") action set_ipv4_dscp_4(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[4:4] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_4") action set_ipv6_dscp_4(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[4:4] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_5") action set_ipv4_dscp_5(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[5:5] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_5") action set_ipv6_dscp_5(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[5:5] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_6") action set_ipv4_dscp_6(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[6:6] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_6") action set_ipv6_dscp_6(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[6:6] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_7") action set_ipv4_dscp_7(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[7:7] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_7") action set_ipv6_dscp_7(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[7:7] = dscp_bit_value; + } + @name(".dtel_config.config") @ignore_table_dependency("SwitchEgress.system_acl.copp") table config { + key = { + local_md.pkt_src : ternary; + local_md.dtel.report_type : ternary; + local_md.dtel.drop_report_flag : ternary; + local_md.dtel.flow_report_flag : ternary; + local_md.dtel.queue_report_flag: ternary; + local_md.drop_reason : ternary; + local_md.mirror.type : ternary; + hdr.dtel_drop_report.isValid() : ternary; + local_md.lkp.tcp_flags[2:0] : ternary; + } + actions = { + NoAction; + drop; + mirror_switch_local; + mirror_switch_local_and_set_q_bit; + mirror_drop; + mirror_drop_and_set_q_bit; + update; + update_and_mirror_truncate; + } + const default_action = NoAction; + } + apply { + config.apply(); + } +} + +control IntEdge(inout switch_local_metadata_t local_md)(switch_uint32_t port_table_size=288) { + @name(".dtel.int_edge.set_clone_mirror_session_id") action set_clone_mirror_session_id(switch_mirror_session_t session_id) { + local_md.dtel.clone_session_id = session_id; + } + @name(".dtel.int_edge.set_ifa_edge") action set_ifa_edge() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_IFA_EDGE; + } + @name(".dtel.int_edge.port_lookup") table port_lookup { + key = { + local_md.egress_port: exact; + } + actions = { + NoAction; + set_clone_mirror_session_id; + set_ifa_edge; + } + const default_action = NoAction; + size = port_table_size; + } + apply { + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + port_lookup.apply(); + } + } +} + +control EgressDtel(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, in bit<32> hash) { + DropReport() drop_report; + QueueReport() queue_report; + FlowReport() flow_report; + IntEdge() int_edge; + @name(".dtel.convert_ingress_port") action convert_ingress_port(switch_port_t port) { + hdr.dtel_report.ingress_port = port; + } + @name(".dtel.ingress_port_conversion") table ingress_port_conversion { + key = { + hdr.dtel_report.ingress_port: exact @name("port") ; + hdr.dtel_report.isValid() : exact @name("dtel_report_valid") ; + } + actions = { + NoAction; + convert_ingress_port; + } + const default_action = NoAction; + } + @name(".dtel.convert_egress_port") action convert_egress_port(switch_port_t port) { + hdr.dtel_report.egress_port = port; + } + @name(".dtel.egress_port_conversion") table egress_port_conversion { + key = { + hdr.dtel_report.egress_port: exact @name("port") ; + hdr.dtel_report.isValid() : exact @name("dtel_report_valid") ; + } + actions = { + NoAction; + convert_egress_port; + } + const default_action = NoAction; + } + action update_dtel_timestamps() { + local_md.dtel.latency = eg_intr_md_from_prsr.global_tstamp[31:0] - local_md.ingress_timestamp[31:0]; + local_md.egress_timestamp[31:0] = eg_intr_md_from_prsr.global_tstamp[31:0]; + } + apply { + update_dtel_timestamps(); + if (local_md.pkt_src == SWITCH_PKT_SRC_DEFLECTED && hdr.dtel_drop_report.isValid()) { + local_md.egress_port = hdr.dtel_report.egress_port; + } + ingress_port_conversion.apply(); + egress_port_conversion.apply(); + queue_report.apply(local_md, eg_intr_md, local_md.dtel.queue_report_flag); + flow_report.apply(local_md, local_md.dtel.flow_report_flag); + drop_report.apply(hdr, local_md, hash, local_md.dtel.drop_report_flag); + } +} + +struct switch_sflow_info_t { + bit<32> current; + bit<32> rate; +} + +control IngressSflow(inout switch_local_metadata_t local_md) { + const bit<32> sflow_session_size = 256; + @name(".ingress_sflow_samplers") Register>(sflow_session_size) samplers; + RegisterAction, bit<1>>(samplers) sample_packet = { + void apply(inout switch_sflow_info_t reg, out bit<1> flag) { + if (reg.current > 0) { + reg.current = reg.current - 1; + } else { + reg.current = reg.rate; + flag = 1; + } + } + }; + apply { + if (local_md.sflow.session_id != SWITCH_SFLOW_INVALID_ID) { + local_md.sflow.sample_packet = sample_packet.execute(local_md.sflow.session_id); + } + } +} + +control EgressSflow(inout switch_local_metadata_t local_md) { + const bit<32> sflow_session_size = 256; + Register>(sflow_session_size) samplers; + RegisterAction, bit<1>>(samplers) sample_packet = { + void apply(inout switch_sflow_info_t reg, out bit<1> flag) { + if (reg.current > 0) { + reg.current = reg.current - 1; + } else { + reg.current = reg.rate; + flag = 1; + } + } + }; + apply { + } +} + +control SwitchIngress(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_intr_from_prsr, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm) { + IngressPortMapping(PORT_VLAN_TABLE_SIZE, BD_TABLE_SIZE) ingress_port_mapping; + PktValidation() pkt_validation; + SMAC(MAC_TABLE_SIZE) smac; + DMAC(MAC_TABLE_SIZE) dmac; + IngressSflow() sflow; + IngressBd(BD_TABLE_SIZE) bd_stats; + EnableFragHash() enable_frag_hash; + Ipv4Hash() ipv4_hash; + Ipv6Hash() ipv6_hash; + NonIpHash() non_ip_hash; + Lagv4Hash() lagv4_hash; + Lagv6Hash() lagv6_hash; + LOU() lou; + Fibv4(IPV4_HOST_TABLE_SIZE, IPV4_LPM_TABLE_SIZE, true, IPV4_LOCAL_HOST_TABLE_SIZE) ipv4_fib; + Fibv6(IPV6_HOST_TABLE_SIZE, 64, IPV6_LPM_TABLE_SIZE, IPV6_LPM64_TABLE_SIZE) ipv6_fib; + PreIngressAcl(PRE_INGRESS_ACL_TABLE_SIZE) pre_ingress_acl; + IngressIpv4Acl(INGRESS_IPV4_ACL_TABLE_SIZE) ingress_ipv4_acl; + IngressIpv6Acl(INGRESS_IPV6_ACL_TABLE_SIZE) ingress_ipv6_acl; + IngressIpAcl(INGRESS_IP_MIRROR_ACL_TABLE_SIZE) ingress_ip_mirror_acl; + IngressIpDtelSampleAcl(INGRESS_IP_DTEL_ACL_TABLE_SIZE) ingress_ip_dtel_acl; + ECNAcl() ecn_acl; + IngressPFCWd(512) pfc_wd; + IngressQoSMap() qos_map; + IngressTC() traffic_class; + PPGStats() ppg_stats; + StormControl() storm_control; + Nexthop(NEXTHOP_TABLE_SIZE, ECMP_GROUP_TABLE_SIZE, ECMP_SELECT_TABLE_SIZE) nexthop; + LAG() lag; + MulticastFlooding(BD_FLOOD_TABLE_SIZE) flood; + IngressSystemAcl() system_acl; + IngressDtel() dtel; + SameMacCheck() same_mac_check; + apply { + pkt_validation.apply(hdr, local_md); + ingress_port_mapping.apply(hdr, local_md, ig_intr_md_for_tm, ig_intr_md_for_dprsr); + pre_ingress_acl.apply(hdr, local_md); + lou.apply(local_md); + enable_frag_hash.apply(local_md.lkp); + smac.apply(hdr.ethernet.src_addr, hdr.ethernet.dst_addr, local_md, ig_intr_md_for_dprsr.digest_type); + bd_stats.apply(local_md.bd, local_md.lkp.pkt_type); + same_mac_check.apply(hdr, local_md); + if (local_md.flags.rmac_hit) { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_L3 != 0) && local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV4 && local_md.ipv4.unicast_enable) { + ipv4_fib.apply(local_md.lkp.ip_dst_addr[95:64], local_md); + ingress_ipv4_acl.apply(local_md, local_md.nexthop); + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV4) { + ingress_ipv6_acl.apply(local_md, local_md.nexthop); + } + } else if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_L3 != 0) && local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV6 && local_md.ipv6.unicast_enable) { + ipv6_fib.apply(local_md.lkp.ip_dst_addr, local_md); + ingress_ipv6_acl.apply(local_md, local_md.nexthop); + } else { + dmac.apply(local_md.lkp.mac_dst_addr, local_md); + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV6) { + ingress_ipv4_acl.apply(local_md, local_md.nexthop); + } + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV4) { + ingress_ipv6_acl.apply(local_md, local_md.nexthop); + } + } + } else { + dmac.apply(local_md.lkp.mac_dst_addr, local_md); + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV6) { + ingress_ipv4_acl.apply(local_md, local_md.nexthop); + } + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV4) { + ingress_ipv6_acl.apply(local_md, local_md.nexthop); + } + } + ingress_ip_mirror_acl.apply(local_md, local_md.unused_nexthop); + sflow.apply(local_md); + if (local_md.lkp.ip_type == SWITCH_IP_TYPE_NONE) { + non_ip_hash.apply(hdr, local_md, local_md.lag_hash); + } else if (local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV4) { + lagv4_hash.apply(local_md.lkp, local_md.lag_hash); + } else { + lagv6_hash.apply(local_md.lkp, local_md.lag_hash); + } + if (local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV4) { + ipv4_hash.apply(local_md.lkp, local_md.hash); + } else { + ipv6_hash.apply(local_md.lkp, local_md.hash); + } + nexthop.apply(local_md); + qos_map.apply(hdr, local_md); + traffic_class.apply(local_md); + storm_control.apply(local_md, local_md.lkp.pkt_type, local_md.flags.storm_control_drop); + ppg_stats.apply(local_md); + if (local_md.egress_port_lag_index == SWITCH_FLOOD) { + flood.apply(local_md); + } else { + lag.apply(local_md, local_md.lag_hash, ig_intr_md_for_tm.ucast_egress_port); + } + ecn_acl.apply(local_md, local_md.lkp, ig_intr_md_for_tm.packet_color); + pfc_wd.apply(local_md.ingress_port, local_md.qos.qid, local_md.flags.pfc_wd_drop); + system_acl.apply(hdr, local_md, ig_intr_md_for_tm, ig_intr_md_for_dprsr); + ingress_ip_dtel_acl.apply(local_md, local_md.unused_nexthop); + dtel.apply(hdr, local_md.lkp, local_md, local_md.lag_hash[15:0], ig_intr_md_for_dprsr, ig_intr_md_for_tm); + add_bridged_md(hdr.bridged_md, local_md); + set_ig_intr_md(local_md, ig_intr_md_for_dprsr, ig_intr_md_for_tm); + } +} + +control SwitchEgress(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, inout egress_intrinsic_metadata_for_output_port_t eg_intr_md_for_oport) { + EgressPortMapping() egress_port_mapping; + EgressPortMirror(288) port_mirror; + EgressLOU() lou; + EgressIpv4Acl(EGRESS_IPV4_ACL_TABLE_SIZE) egress_ipv4_acl; + EgressIpv6Acl(EGRESS_IPV6_ACL_TABLE_SIZE) egress_ipv6_acl; + EgressQoS() qos; + EgressQueue() queue; + EgressSystemAcl() system_acl; + EgressPFCWd(512) pfc_wd; + EgressVRF() egress_vrf; + EgressBD() egress_bd; + OuterNexthop() outer_nexthop; + EgressBDStats() egress_bd_stats; + MirrorRewrite() mirror_rewrite; + VlanXlate(VLAN_TABLE_SIZE, PORT_VLAN_TABLE_SIZE) vlan_xlate; + VlanDecap() vlan_decap; + MTU() mtu; + WRED() wred; + EgressDtel() dtel; + DtelConfig() dtel_config; + EgressCpuRewrite() cpu_rewrite; + EgressPortIsolation() port_isolation; + Neighbor() neighbor; + SetEgIntrMd() set_eg_intr_md; + apply { + egress_port_mapping.apply(hdr, local_md, eg_intr_md_for_dprsr, eg_intr_md.egress_port); + if (local_md.pkt_src != SWITCH_PKT_SRC_BRIDGED) { + mirror_rewrite.apply(hdr, local_md, eg_intr_md_for_dprsr); + } else { + port_mirror.apply(eg_intr_md.egress_port, local_md); + } + vlan_decap.apply(hdr, local_md); + qos.apply(hdr, eg_intr_md.egress_port, local_md); + wred.apply(hdr, local_md, eg_intr_md, local_md.flags.wred_drop); + egress_vrf.apply(hdr, local_md); + outer_nexthop.apply(hdr, local_md); + egress_bd.apply(hdr, local_md); + lou.apply(local_md); + if (hdr.ipv4.isValid()) { + egress_ipv4_acl.apply(hdr, local_md); + } else if (hdr.ipv6.isValid()) { + egress_ipv6_acl.apply(hdr, local_md); + } + neighbor.apply(hdr, local_md); + egress_bd_stats.apply(hdr, local_md); + mtu.apply(hdr, local_md); + vlan_xlate.apply(hdr, local_md); + pfc_wd.apply(eg_intr_md.egress_port, local_md.qos.qid, local_md.flags.pfc_wd_drop); + dtel.apply(hdr, local_md, eg_intr_md, eg_intr_md_from_prsr, local_md.dtel.hash); + port_isolation.apply(local_md, eg_intr_md); + system_acl.apply(hdr, local_md, eg_intr_md, eg_intr_md_for_dprsr); + dtel_config.apply(hdr, local_md, eg_intr_md_for_dprsr); + cpu_rewrite.apply(hdr, local_md, eg_intr_md_for_dprsr, eg_intr_md.egress_port); + set_eg_intr_md.apply(hdr, local_md, eg_intr_md_for_dprsr, eg_intr_md_for_oport); + queue.apply(eg_intr_md.egress_port, local_md); + } +} + +Pipeline(SwitchIngressParser(), SwitchIngress(), SwitchIngressDeparser(), SwitchEgressParser(), SwitchEgress(), SwitchEgressDeparser()) pipe; + +Switch(pipe) main; + diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switchml.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switchml.p4 new file mode 100644 index 00000000000..9e16410be22 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino1_only/switchml.p4 @@ -0,0 +1,1683 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include +#include +#include +#include + +const int register_size = 16384; +const int num_slots = register_size / 2; +const int max_num_workers = 32; +const int max_num_workers_log2 = 5; +const int forwarding_table_size = 1024; +const int max_num_queue_pairs_per_worker = 512; +const int max_num_queue_pairs_per_worker_log2 = 9; +const int max_num_queue_pairs = max_num_queue_pairs_per_worker * max_num_workers; +const int max_num_queue_pairs_log2 = max_num_queue_pairs_per_worker_log2 + max_num_workers_log2; +const bit<16> null_level1_exclusion_id = 0xffff; +typedef bit<3> mirror_type_t; +const mirror_type_t MIRROR_TYPE_I2E = 1; +const mirror_type_t MIRROR_TYPE_E2E = 2; +typedef bit<48> mac_addr_t; +typedef bit<16> ether_type_t; +const ether_type_t ETHERTYPE_IPV4 = 16w0x800; +const ether_type_t ETHERTYPE_ARP = 16w0x806; +const ether_type_t ETHERTYPE_ROCEv1 = 16w0x8915; +typedef bit<32> ipv4_addr_t; +enum bit<8> ip_protocol_t { + ICMP = 1, + UDP = 17 +} + +enum bit<16> arp_opcode_t { + REQUEST = 1, + REPLY = 2 +} + +enum bit<8> icmp_type_t { + ECHO_REPLY = 0, + ECHO_REQUEST = 8 +} + +typedef bit<16> udp_port_t; +const udp_port_t UDP_PORT_ROCEV2 = 4791; +const udp_port_t UDP_PORT_SWITCHML_BASE = 0xbee0; +const udp_port_t UDP_PORT_SWITCHML_MASK = 0xfff0; +typedef bit<128> ib_gid_t; +typedef bit<24> sequence_number_t; +typedef bit<24> queue_pair_t; +typedef bit<32> rkey_t; +typedef bit<64> addr_t; +enum bit<8> ib_opcode_t { + UC_SEND_FIRST = 8w0b100000, + UC_SEND_MIDDLE = 8w0b100001, + UC_SEND_LAST = 8w0b100010, + UC_SEND_LAST_IMMEDIATE = 8w0b100011, + UC_SEND_ONLY = 8w0b100100, + UC_SEND_ONLY_IMMEDIATE = 8w0b100101, + UC_RDMA_WRITE_FIRST = 8w0b100110, + UC_RDMA_WRITE_MIDDLE = 8w0b100111, + UC_RDMA_WRITE_LAST = 8w0b101000, + UC_RDMA_WRITE_LAST_IMMEDIATE = 8w0b101001, + UC_RDMA_WRITE_ONLY = 8w0b101010, + UC_RDMA_WRITE_ONLY_IMMEDIATE = 8w0b101011 +} + +typedef bit<(max_num_queue_pairs_log2)> queue_pair_index_t; +enum bit<2> worker_type_t { + FORWARD_ONLY = 0, + SWITCHML_UDP = 1, + ROCEv2 = 2 +} + +typedef bit<16> worker_id_t; +typedef bit<32> worker_bitmap_t; +struct worker_bitmap_pair_t { + worker_bitmap_t first; + worker_bitmap_t second; +} + +typedef bit<8> num_workers_t; +struct num_workers_pair_t { + num_workers_t first; + num_workers_t second; +} + +typedef bit<15> pool_index_t; +typedef bit<14> pool_index_by2_t; +typedef bit<16> worker_pool_index_t; +typedef bit<32> value_t; +struct value_pair_t { + value_t first; + value_t second; +} + +typedef int<8> exponent_t; +struct exponent_pair_t { + exponent_t first; + exponent_t second; +} + +typedef bit<16> msg_id_t; +enum bit<3> packet_size_t { + IBV_MTU_128 = 0, + IBV_MTU_256 = 1, + IBV_MTU_512 = 2, + IBV_MTU_1024 = 3 +} + +typedef bit<16> drop_probability_t; +typedef bit<32> counter_t; +typedef bit<4> packet_type_underlying_t; +enum bit<4> packet_type_t { + MIRROR = 0x0, + BROADCAST = 0x1, + RETRANSMIT = 0x2, + IGNORE = 0x3, + CONSUME0 = 0x4, + CONSUME1 = 0x5, + CONSUME2 = 0x6, + CONSUME3 = 0x7, + HARVEST0 = 0x8, + HARVEST1 = 0x9, + HARVEST2 = 0xa, + HARVEST3 = 0xb, + HARVEST4 = 0xc, + HARVEST5 = 0xd, + HARVEST6 = 0xe, + HARVEST7 = 0xf +} + +struct port_metadata_t { + drop_probability_t ingress_drop_probability; + drop_probability_t egress_drop_probability; +} + +@pa_no_overlay("ingress" , "ig_md.switchml_md.simulate_egress_drop") @flexible header switchml_md_h { + MulticastGroupId_t mgid; + queue_pair_index_t recirc_port_selector; + packet_size_t packet_size; + worker_type_t worker_type; + worker_id_t worker_id; + bit<16> src_port; + bit<16> dst_port; + packet_type_t packet_type; + bit<16> ether_type_msb; + pool_index_t pool_index; + num_workers_t first_last_flag; + worker_bitmap_t map_result; + worker_bitmap_t worker_bitmap_before; + bit<32> tsi; + bit<8> job_number; + PortId_t ingress_port; + bool simulate_egress_drop; + num_workers_t num_workers; + exponent_t e0; + exponent_t e1; + msg_id_t msg_id; + bool first_packet; + bool last_packet; +} + +@flexible header switchml_rdma_md_h { + bit<64> rdma_addr; +} + +@flexible struct ingress_metadata_t { + switchml_md_h switchml_md; + switchml_rdma_md_h switchml_rdma_md; + worker_bitmap_t worker_bitmap; + bool checksum_err_ipv4; + bool update_ipv4_checksum; + mac_addr_t switch_mac; + ipv4_addr_t switch_ip; + port_metadata_t port_metadata; +} + +struct egress_metadata_t { + switchml_md_h switchml_md; + switchml_rdma_md_h switchml_rdma_md; + bool checksum_err_ipv4; + bool update_ipv4_checksum; +} + +header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + ip_protocol_t protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header icmp_h { + icmp_type_t msg_type; + bit<8> msg_code; + bit<16> checksum; +} + +header arp_h { + bit<16> hw_type; + ether_type_t proto_type; + bit<8> hw_addr_len; + bit<8> proto_addr_len; + arp_opcode_t opcode; +} + +header arp_ipv4_h { + mac_addr_t src_hw_addr; + ipv4_addr_t src_proto_addr; + mac_addr_t dst_hw_addr; + ipv4_addr_t dst_proto_addr; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> length; + bit<16> checksum; +} + +header switchml_h { + bit<4> msg_type; + bit<1> unused; + packet_size_t size; + bit<8> job_number; + bit<32> tsi; + bit<16> pool_index; +} + +header ib_bth_h { + ib_opcode_t opcode; + bit<1> se; + bit<1> migration_req; + bit<2> pad_count; + bit<4> transport_version; + bit<16> partition_key; + bit<1> f_res1; + bit<1> b_res1; + bit<6> reserved; + queue_pair_t dst_qp; + bit<1> ack_req; + bit<7> reserved2; + sequence_number_t psn; +} + +@pa_container_size("ingress" , "hdr.ib_bth.psn" , 32) header ib_reth_h { + bit<64> addr; + bit<32> r_key; + bit<32> len; +} + +header ib_immediate_h { + bit<32> immediate; +} + +header ib_icrc_h { + bit<32> icrc; +} + +header exponents_h { + exponent_t e0; + exponent_t e1; +} + +header data_h { + value_t d00; + value_t d01; + value_t d02; + value_t d03; + value_t d04; + value_t d05; + value_t d06; + value_t d07; + value_t d08; + value_t d09; + value_t d10; + value_t d11; + value_t d12; + value_t d13; + value_t d14; + value_t d15; + value_t d16; + value_t d17; + value_t d18; + value_t d19; + value_t d20; + value_t d21; + value_t d22; + value_t d23; + value_t d24; + value_t d25; + value_t d26; + value_t d27; + value_t d28; + value_t d29; + value_t d30; + value_t d31; +} + +struct header_t { + ethernet_h ethernet; + arp_h arp; + arp_ipv4_h arp_ipv4; + ipv4_h ipv4; + icmp_h icmp; + udp_h udp; + switchml_h switchml; + exponents_h exponents; + ib_bth_h ib_bth; + ib_reth_h ib_reth; + ib_immediate_h ib_immediate; + data_h d0; + data_h d1; + ib_icrc_h ib_icrc; +} + +parser IngressParser(packet_in pkt, out header_t hdr, out ingress_metadata_t ig_md, out ingress_intrinsic_metadata_t ig_intr_md) { + Checksum() ipv4_checksum; + state start { + pkt.extract(ig_intr_md); + transition select(ig_intr_md.resubmit_flag) { + 1: parse_resubmit; + default: parse_port_metadata; + } + } + state parse_resubmit { + pkt.advance(64); + transition parse_ethernet; + } + state parse_port_metadata { + ig_md.port_metadata = port_metadata_unpack(pkt); + transition select(ig_intr_md.ingress_port) { + 64: parse_recirculate; + 68: parse_recirculate; + 320: parse_ethernet; + 0x80 &&& 0x180: parse_recirculate; + 0x100 &&& 0x180: parse_recirculate; + 0x180 &&& 0x180: parse_recirculate; + default: parse_ethernet; + } + } + state parse_recirculate { + pkt.extract(ig_md.switchml_md); + pkt.extract(ig_md.switchml_rdma_md); + transition select(ig_md.switchml_md.packet_type) { + packet_type_t.CONSUME0: parse_consume; + packet_type_t.CONSUME1: parse_consume; + packet_type_t.CONSUME2: parse_consume; + packet_type_t.CONSUME3: parse_consume; + default: parse_harvest; + } + } + state parse_consume { + pkt.extract(hdr.d0); + pkt.extract(hdr.d1); + transition accept; + } + state parse_harvest { + hdr.d0.setValid(); + hdr.d1.setValid(); + transition accept; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type) { + ETHERTYPE_ARP: parse_arp; + ETHERTYPE_IPV4: parse_ipv4; + default: accept_regular; + } + } + state parse_arp { + pkt.extract(hdr.arp); + transition select(hdr.arp.hw_type, hdr.arp.proto_type) { + (0x1, ETHERTYPE_IPV4): parse_arp_ipv4; + default: accept_regular; + } + } + state parse_arp_ipv4 { + pkt.extract(hdr.arp_ipv4); + transition accept_regular; + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + ipv4_checksum.add(hdr.ipv4); + ig_md.checksum_err_ipv4 = ipv4_checksum.verify(); + ig_md.update_ipv4_checksum = false; + transition select(hdr.ipv4.ihl, hdr.ipv4.frag_offset, hdr.ipv4.protocol) { + (5, 0, ip_protocol_t.ICMP): parse_icmp; + (5, 0, ip_protocol_t.UDP): parse_udp; + default: accept_regular; + } + } + state parse_icmp { + pkt.extract(hdr.icmp); + transition accept_regular; + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + UDP_PORT_ROCEV2: parse_ib_bth; + UDP_PORT_SWITCHML_BASE &&& UDP_PORT_SWITCHML_MASK: parse_switchml; + default: accept_regular; + } + } + state parse_ib_bth { + pkt.extract(hdr.ib_bth); + transition select(hdr.ib_bth.opcode) { + ib_opcode_t.UC_SEND_FIRST: parse_ib_payload; + ib_opcode_t.UC_SEND_MIDDLE: parse_ib_payload; + ib_opcode_t.UC_SEND_LAST: parse_ib_payload; + ib_opcode_t.UC_SEND_LAST_IMMEDIATE: parse_ib_immediate; + ib_opcode_t.UC_SEND_ONLY: parse_ib_payload; + ib_opcode_t.UC_SEND_ONLY_IMMEDIATE: parse_ib_immediate; + ib_opcode_t.UC_RDMA_WRITE_FIRST: parse_ib_reth; + ib_opcode_t.UC_RDMA_WRITE_MIDDLE: parse_ib_payload; + ib_opcode_t.UC_RDMA_WRITE_LAST: parse_ib_payload; + ib_opcode_t.UC_RDMA_WRITE_LAST_IMMEDIATE: parse_ib_immediate; + ib_opcode_t.UC_RDMA_WRITE_ONLY: parse_ib_reth; + ib_opcode_t.UC_RDMA_WRITE_ONLY_IMMEDIATE: parse_ib_reth_immediate; + default: accept_regular; + } + } + state parse_ib_immediate { + pkt.extract(hdr.ib_immediate); + transition parse_ib_payload; + } + state parse_ib_reth { + pkt.extract(hdr.ib_reth); + transition parse_ib_payload; + } + state parse_ib_reth_immediate { + pkt.extract(hdr.ib_reth); + pkt.extract(hdr.ib_immediate); + transition parse_ib_payload; + } + state parse_ib_payload { + pkt.extract(hdr.d0); + pkt.extract(hdr.d1); + ig_md.switchml_md.setValid(); + ig_md.switchml_md.ether_type_msb = 16w0xffff; + ig_md.switchml_md.packet_type = packet_type_t.CONSUME0; + ig_md.switchml_rdma_md.setValid(); + transition accept; + } + state parse_switchml { + pkt.extract(hdr.switchml); + pkt.extract(hdr.exponents); + transition parse_values; + } + state parse_values { + pkt.extract(hdr.d0); + pkt.extract(hdr.d1); + ig_md.switchml_md.setValid(); + ig_md.switchml_md.packet_type = packet_type_t.CONSUME0; + ig_md.switchml_rdma_md.setValid(); + transition accept; + } + state accept_regular { + ig_md.switchml_md.setValid(); + ig_md.switchml_md.packet_type = packet_type_t.IGNORE; + ig_md.switchml_rdma_md.setValid(); + transition accept; + } +} + +control IngressDeparser(packet_out pkt, inout header_t hdr, in ingress_metadata_t ig_md, in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + Checksum() ipv4_checksum; + apply { + if (ig_md.update_ipv4_checksum) { + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.total_len, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.src_addr, hdr.ipv4.dst_addr }); + } + pkt.emit(ig_md.switchml_md); + pkt.emit(ig_md.switchml_rdma_md); + pkt.emit(hdr); + } +} + +parser EgressParser(packet_in pkt, out header_t hdr, out egress_metadata_t eg_md, out egress_intrinsic_metadata_t eg_intr_md) { + state start { + pkt.extract(eg_intr_md); + transition select(eg_intr_md.pkt_length) { + 0: parse_switchml_md; + default: parse_switchml_md; + } + } + state parse_switchml_md { + pkt.extract(eg_md.switchml_md); + transition parse_switchml_rdma_md; + } + state parse_switchml_rdma_md { + pkt.extract(eg_md.switchml_rdma_md); + transition accept; + } +} + +control EgressDeparser(packet_out pkt, inout header_t hdr, in egress_metadata_t eg_md, in egress_intrinsic_metadata_for_deparser_t eg_intr_dprs_md) { + Checksum() ipv4_checksum; + apply { + if (eg_md.update_ipv4_checksum) { + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.total_len, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.src_addr, hdr.ipv4.dst_addr }); + } + pkt.emit(hdr); + } +} + +control ARPandICMPResponder(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + action send_back() { + ig_tm_md.ucast_egress_port = ig_intr_md.ingress_port; + } + action send_arp_reply(mac_addr_t switch_mac, ipv4_addr_t switch_ip) { + hdr.ethernet.dst_addr = hdr.arp_ipv4.src_hw_addr; + hdr.ethernet.src_addr = switch_mac; + hdr.arp.opcode = arp_opcode_t.REPLY; + hdr.arp_ipv4.dst_hw_addr = hdr.arp_ipv4.src_hw_addr; + hdr.arp_ipv4.dst_proto_addr = hdr.arp_ipv4.src_proto_addr; + hdr.arp_ipv4.src_hw_addr = switch_mac; + hdr.arp_ipv4.src_proto_addr = switch_ip; + send_back(); + } + action send_icmp_echo_reply(mac_addr_t switch_mac, ipv4_addr_t switch_ip) { + hdr.ethernet.dst_addr = hdr.ethernet.src_addr; + hdr.ethernet.src_addr = switch_mac; + hdr.ipv4.dst_addr = hdr.ipv4.src_addr; + hdr.ipv4.src_addr = switch_ip; + hdr.icmp.msg_type = icmp_type_t.ECHO_REPLY; + hdr.icmp.checksum = 0; + send_back(); + } + table arp_icmp { + key = { + hdr.arp_ipv4.isValid() : exact; + hdr.icmp.isValid() : exact; + hdr.arp.opcode : ternary; + hdr.arp_ipv4.dst_proto_addr: ternary; + hdr.icmp.msg_type : ternary; + hdr.ipv4.dst_addr : ternary; + } + actions = { + send_arp_reply; + send_icmp_echo_reply; + } + size = 2; + } + apply { + arp_icmp.apply(); + } +} + +control Forwarder(in header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + action set_egress_port(bit<9> egress_port) { + ig_tm_md.ucast_egress_port = egress_port; + ig_tm_md.bypass_egress = 1w1; + ig_dprsr_md.drop_ctl[0:0] = 0; + ig_md.switchml_md.setInvalid(); + ig_md.switchml_rdma_md.setInvalid(); + } + action flood(MulticastGroupId_t flood_mgid) { + ig_tm_md.mcast_grp_a = flood_mgid; + ig_tm_md.level1_exclusion_id = 7w0b1000000 ++ ig_intr_md.ingress_port; + ig_tm_md.bypass_egress = 1w1; + ig_dprsr_md.drop_ctl[0:0] = 0; + ig_md.switchml_md.setInvalid(); + ig_md.switchml_rdma_md.setInvalid(); + } + table forward { + key = { + hdr.ethernet.dst_addr: exact; + } + actions = { + set_egress_port; + flood; + } + size = forwarding_table_size; + } + apply { + forward.apply(); + } +} + +control IngressDropSimulator(inout port_metadata_t port_metadata) { + Random() rng; + apply { + if (port_metadata.ingress_drop_probability != 0) { + port_metadata.ingress_drop_probability = rng.get() |+| port_metadata.ingress_drop_probability; + } + } +} + +control EgressDropSimulator(inout port_metadata_t port_metadata, in queue_pair_index_t qp_index, out bool simulate_egress_drop) { + Random() rng; + Counter(max_num_queue_pairs, CounterType_t.PACKETS) simulated_drop_packet_counter; + apply { + if (port_metadata.egress_drop_probability != 0) { + port_metadata.egress_drop_probability = rng.get() |+| port_metadata.egress_drop_probability; + } + simulate_egress_drop = false; + if (port_metadata.egress_drop_probability == 0xffff) { + simulate_egress_drop = true; + } + if (port_metadata.ingress_drop_probability == 0xffff || port_metadata.egress_drop_probability == 0xffff) { + simulated_drop_packet_counter.count(qp_index); + } + } +} + +control UDPReceiver(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + DirectCounter(CounterType_t.PACKETS_AND_BYTES) receive_counter; + action drop() { + ig_dprsr_md.drop_ctl[0:0] = 1; + ig_md.switchml_md.packet_type = packet_type_t.IGNORE; + receive_counter.count(); + } + action forward() { + ig_md.switchml_md.packet_type = packet_type_t.IGNORE; + receive_counter.count(); + } + action set_bitmap(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap) { + receive_counter.count(); + ig_md.worker_bitmap = worker_bitmap; + ig_md.switchml_md.num_workers = num_workers; + ig_md.switchml_md.mgid = mgid; + ig_md.switchml_md.packet_size = hdr.switchml.size; + ig_md.switchml_md.worker_type = worker_type; + ig_md.switchml_md.worker_id = worker_id; + ig_md.switchml_md.dst_port = hdr.udp.src_port; + ig_md.switchml_md.src_port = hdr.udp.dst_port; + ig_md.switchml_md.tsi = hdr.switchml.tsi; + ig_md.switchml_md.job_number = hdr.switchml.job_number; + ig_md.switchml_md.pool_index = hdr.switchml.pool_index[13:0] ++ hdr.switchml.pool_index[15:15]; + ig_md.switchml_md.first_packet = true; + ig_md.switchml_md.last_packet = true; + ig_md.switchml_md.e0 = hdr.exponents.e0; + ig_md.switchml_md.e1 = hdr.exponents.e1; + hdr.ethernet.setInvalid(); + hdr.ipv4.setInvalid(); + hdr.udp.setInvalid(); + hdr.switchml.setInvalid(); + hdr.exponents.setInvalid(); + } + table receive_udp { + key = { + ig_intr_md.ingress_port: ternary; + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + hdr.ipv4.src_addr : ternary; + hdr.ipv4.dst_addr : ternary; + hdr.udp.dst_port : ternary; + ig_prsr_md.parser_err : ternary; + } + actions = { + drop; + set_bitmap; + @defaultonly forward; + } + const default_action = forward; + size = max_num_workers + 16; + counters = receive_counter; + } + apply { + receive_udp.apply(); + } +} + +control UDPSender(inout egress_metadata_t eg_md, in egress_intrinsic_metadata_t eg_intr_md, inout header_t hdr) { + DirectCounter(CounterType_t.PACKETS_AND_BYTES) send_counter; + action set_switch_mac_and_ip(mac_addr_t switch_mac, ipv4_addr_t switch_ip) { + hdr.ethernet.src_addr = switch_mac; + hdr.ipv4.src_addr = switch_ip; + hdr.udp.src_port = eg_md.switchml_md.src_port; + hdr.ethernet.ether_type = ETHERTYPE_IPV4; + hdr.ipv4.version = 4; + hdr.ipv4.ihl = 5; + hdr.ipv4.diffserv = 0x0; + hdr.ipv4.total_len = hdr.ipv4.minSizeInBytes() + (hdr.udp.minSizeInBytes() + hdr.switchml.minSizeInBytes() + hdr.exponents.minSizeInBytes()); + ; + hdr.ipv4.identification = 0x0; + hdr.ipv4.flags = 0b0; + hdr.ipv4.frag_offset = 0; + hdr.ipv4.ttl = 64; + hdr.ipv4.protocol = ip_protocol_t.UDP; + hdr.ipv4.hdr_checksum = 0; + hdr.ipv4.src_addr = switch_ip; + eg_md.update_ipv4_checksum = true; + hdr.udp.length = hdr.udp.minSizeInBytes() + hdr.switchml.minSizeInBytes() + hdr.exponents.minSizeInBytes(); + hdr.switchml.setValid(); + hdr.switchml.msg_type = 1; + hdr.switchml.unused = 0; + hdr.switchml.size = eg_md.switchml_md.packet_size; + hdr.switchml.job_number = eg_md.switchml_md.job_number; + hdr.switchml.tsi = eg_md.switchml_md.tsi; + hdr.switchml.pool_index[13:0] = eg_md.switchml_md.pool_index[14:1]; + } + table switch_mac_and_ip { + actions = { + @defaultonly set_switch_mac_and_ip; + } + size = 1; + } + action set_dst_addr(mac_addr_t eth_dst_addr, ipv4_addr_t ip_dst_addr) { + hdr.ethernet.dst_addr = eth_dst_addr; + hdr.ipv4.dst_addr = ip_dst_addr; + hdr.udp.dst_port = eg_md.switchml_md.dst_port; + hdr.udp.checksum = 0; + eg_md.update_ipv4_checksum = true; + hdr.switchml.pool_index[15:15] = eg_md.switchml_md.pool_index[0:0]; + hdr.exponents.setValid(); + hdr.exponents.e0 = eg_md.switchml_md.e0; + hdr.exponents.e1 = eg_md.switchml_md.e1; + send_counter.count(); + } + table dst_addr { + key = { + eg_md.switchml_md.worker_id: exact; + } + actions = { + set_dst_addr; + } + size = max_num_workers; + counters = send_counter; + } + apply { + hdr.ethernet.setValid(); + hdr.ipv4.setValid(); + hdr.udp.setValid(); + hdr.switchml.setValid(); + hdr.switchml.pool_index = 16w0; + switch_mac_and_ip.apply(); + dst_addr.apply(); + if (eg_md.switchml_md.packet_size == packet_size_t.IBV_MTU_256) { + hdr.ipv4.total_len = hdr.ipv4.total_len + 256; + hdr.udp.length = hdr.udp.length + 256; + } else if (eg_md.switchml_md.packet_size == packet_size_t.IBV_MTU_1024) { + hdr.ipv4.total_len = hdr.ipv4.total_len + 1024; + hdr.udp.length = hdr.udp.length + 1024; + } + } +} + +typedef bit<32> return_t; +struct receiver_data_t { + bit<32> next_sequence_number; + bit<32> pool_index; +} + +control RDMAReceiver(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + DirectCounter(CounterType_t.PACKETS_AND_BYTES) rdma_receive_counter; + Register(max_num_queue_pairs) receiver_data_register; + Counter(max_num_queue_pairs, CounterType_t.PACKETS) rdma_packet_counter; + Counter(max_num_queue_pairs, CounterType_t.PACKETS) rdma_message_counter; + Counter(max_num_queue_pairs, CounterType_t.PACKETS) rdma_sequence_violation_counter; + bool message_possibly_received; + bool sequence_violation; + RegisterAction(receiver_data_register) receiver_reset_action = { + void apply(inout receiver_data_t value, out return_t read_value) { + if ((bit<32>)hdr.ib_bth.psn == 0xffffff) { + value.next_sequence_number = 0; + } else { + value.next_sequence_number = (bit<32>)hdr.ib_bth.psn + 1; + } + value.pool_index = (bit<32>)hdr.ib_reth.r_key; + read_value = value.pool_index; + } + }; + RegisterAction(receiver_data_register) receiver_increment_action = { + void apply(inout receiver_data_t value, out return_t read_value) { + if ((bit<32>)hdr.ib_bth.psn == value.next_sequence_number) { + value.pool_index = value.pool_index |+| 2; + if (value.next_sequence_number == 0xffffff) { + value.next_sequence_number = 0; + } else { + value.next_sequence_number = value.next_sequence_number + 1; + } + } else { + value.pool_index = 0x80000000; + value.next_sequence_number = value.next_sequence_number; + } + read_value = value.pool_index; + } + }; + action set_bitmap(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + ig_md.worker_bitmap = worker_bitmap; + ig_md.switchml_md.num_workers = num_workers; + ig_md.switchml_md.mgid = mgid; + ig_md.switchml_md.worker_type = worker_type; + ig_md.switchml_md.worker_id = worker_id; + ig_md.switchml_md.dst_port = hdr.udp.src_port; + ig_md.switchml_rdma_md.rdma_addr = hdr.ib_reth.addr; + ig_md.switchml_md.tsi = hdr.ib_reth.len; + ig_md.switchml_md.packet_size = packet_size; + ig_md.switchml_md.recirc_port_selector = (queue_pair_index_t)(hdr.ib_bth.dst_qp[16 + max_num_workers_log2 - 1:16] ++ hdr.ib_bth.dst_qp[max_num_queue_pairs_per_worker_log2 - 1:0]); + hdr.ethernet.setInvalid(); + hdr.ipv4.setInvalid(); + hdr.udp.setInvalid(); + hdr.ib_bth.setInvalid(); + hdr.ib_reth.setInvalid(); + rdma_receive_counter.count(); + } + action assign_pool_index(bit<32> result) { + ig_md.switchml_md.pool_index = result[14:0]; + } + action process_immediate() { + ig_md.switchml_md.msg_id = hdr.ib_immediate.immediate[31:16]; + ig_md.switchml_md.e0 = (exponent_t)hdr.ib_immediate.immediate[15:8]; + ig_md.switchml_md.e1 = (exponent_t)hdr.ib_immediate.immediate[7:0]; + hdr.ib_immediate.setInvalid(); + } + action only_packet(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + set_bitmap(mgid, worker_type, worker_id, num_workers, worker_bitmap, packet_size); + ig_md.switchml_md.first_packet = true; + ig_md.switchml_md.last_packet = true; + return_t result = receiver_reset_action.execute(hdr.ib_bth.dst_qp[16 + max_num_workers_log2 - 1:16] ++ hdr.ib_bth.dst_qp[max_num_queue_pairs_per_worker_log2 - 1:0]); + assign_pool_index(result); + message_possibly_received = true; + sequence_violation = (bool)result[31:31]; + } + action only_packet_with_immediate(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + process_immediate(); + only_packet(mgid, worker_type, worker_id, num_workers, worker_bitmap, packet_size); + } + action first_packet(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + set_bitmap(mgid, worker_type, worker_id, num_workers, worker_bitmap, packet_size); + ig_md.switchml_md.first_packet = true; + ig_md.switchml_md.last_packet = false; + return_t result = receiver_reset_action.execute(hdr.ib_bth.dst_qp[16 + max_num_workers_log2 - 1:16] ++ hdr.ib_bth.dst_qp[max_num_queue_pairs_per_worker_log2 - 1:0]); + assign_pool_index(result); + message_possibly_received = false; + sequence_violation = (bool)result[31:31]; + } + action middle_packet(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + set_bitmap(mgid, worker_type, worker_id, num_workers, worker_bitmap, packet_size); + ig_md.switchml_md.first_packet = false; + ig_md.switchml_md.last_packet = false; + return_t result = receiver_increment_action.execute(hdr.ib_bth.dst_qp[16 + max_num_workers_log2 - 1:16] ++ hdr.ib_bth.dst_qp[max_num_queue_pairs_per_worker_log2 - 1:0]); + ig_dprsr_md.drop_ctl[0:0] = result[31:31]; + assign_pool_index(result); + message_possibly_received = false; + sequence_violation = (bool)result[31:31]; + } + action last_packet(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + set_bitmap(mgid, worker_type, worker_id, num_workers, worker_bitmap, packet_size); + ig_md.switchml_md.first_packet = false; + ig_md.switchml_md.last_packet = true; + return_t result = receiver_increment_action.execute(hdr.ib_bth.dst_qp[16 + max_num_workers_log2 - 1:16] ++ hdr.ib_bth.dst_qp[max_num_queue_pairs_per_worker_log2 - 1:0]); + ig_dprsr_md.drop_ctl[0:0] = result[31:31]; + assign_pool_index(result); + message_possibly_received = true; + sequence_violation = (bool)result[31:31]; + } + action last_packet_with_immediate(MulticastGroupId_t mgid, worker_type_t worker_type, worker_id_t worker_id, num_workers_t num_workers, worker_bitmap_t worker_bitmap, packet_size_t packet_size) { + process_immediate(); + last_packet(mgid, worker_type, worker_id, num_workers, worker_bitmap, packet_size); + } + action forward() { + ig_md.switchml_md.packet_type = packet_type_t.IGNORE; + rdma_receive_counter.count(); + } + table receive_roce { + key = { + hdr.ipv4.src_addr : exact; + hdr.ipv4.dst_addr : exact; + hdr.ib_bth.partition_key: exact; + hdr.ib_bth.opcode : exact; + hdr.ib_bth.dst_qp : ternary; + } + actions = { + only_packet; + only_packet_with_immediate; + first_packet; + middle_packet; + last_packet; + last_packet_with_immediate; + @defaultonly forward; + } + const default_action = forward; + size = max_num_workers * 6; + counters = rdma_receive_counter; + } + apply { + if (receive_roce.apply().hit) { + rdma_packet_counter.count(ig_md.switchml_md.recirc_port_selector); + if (sequence_violation) { + rdma_sequence_violation_counter.count(ig_md.switchml_md.recirc_port_selector); + } else if (message_possibly_received) { + rdma_message_counter.count(ig_md.switchml_md.recirc_port_selector); + } + } + } +} + +control RDMASender(inout header_t hdr, inout egress_metadata_t eg_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, inout egress_intrinsic_metadata_for_deparser_t eg_intr_dprs_md) { + rkey_t rdma_rkey; + mac_addr_t rdma_switch_mac; + ipv4_addr_t rdma_switch_ip; + DirectCounter(CounterType_t.PACKETS_AND_BYTES) rdma_send_counter; + action set_switch_mac_and_ip(mac_addr_t switch_mac, ipv4_addr_t switch_ip, bit<31> message_length, pool_index_t first_last_mask) { + rdma_switch_mac = switch_mac; + rdma_switch_ip = switch_ip; + } + table switch_mac_and_ip { + actions = { + @defaultonly set_switch_mac_and_ip; + } + size = 1; + } + action fill_in_roce_fields(mac_addr_t dest_mac, ipv4_addr_t dest_ip) { + hdr.switchml.setInvalid(); + hdr.exponents.setInvalid(); + hdr.ethernet.setValid(); + hdr.ethernet.dst_addr = dest_mac; + hdr.ethernet.src_addr = rdma_switch_mac; + hdr.ethernet.ether_type = ETHERTYPE_IPV4; + hdr.ipv4.setValid(); + hdr.ipv4.version = 4; + hdr.ipv4.ihl = 5; + hdr.ipv4.diffserv = 0x2; + hdr.ipv4.identification = 0x1; + hdr.ipv4.flags = 0b10; + hdr.ipv4.frag_offset = 0; + hdr.ipv4.ttl = 64; + hdr.ipv4.protocol = ip_protocol_t.UDP; + hdr.ipv4.hdr_checksum = 0; + hdr.ipv4.src_addr = rdma_switch_ip; + hdr.ipv4.dst_addr = dest_ip; + hdr.ipv4.total_len = hdr.ib_icrc.minSizeInBytes() + hdr.ib_bth.minSizeInBytes() + hdr.udp.minSizeInBytes() + hdr.ipv4.minSizeInBytes(); + eg_md.update_ipv4_checksum = true; + hdr.udp.setValid(); + hdr.udp.src_port = 1w1 ++ eg_md.switchml_md.worker_id[14:0]; + hdr.udp.dst_port = UDP_PORT_ROCEV2; + hdr.udp.checksum = 0; + hdr.udp.length = hdr.ib_icrc.minSizeInBytes() + hdr.ib_bth.minSizeInBytes() + hdr.udp.minSizeInBytes(); + hdr.ib_bth.setValid(); + hdr.ib_bth.opcode = ib_opcode_t.UC_RDMA_WRITE_ONLY; + hdr.ib_bth.se = 0; + hdr.ib_bth.migration_req = 1; + hdr.ib_bth.pad_count = 0; + hdr.ib_bth.transport_version = 0; + hdr.ib_bth.partition_key = 0xffff; + hdr.ib_bth.f_res1 = 0; + hdr.ib_bth.b_res1 = 0; + hdr.ib_bth.reserved = 0; + hdr.ib_bth.dst_qp = 0; + hdr.ib_bth.ack_req = 0; + hdr.ib_bth.reserved2 = 0; + rdma_send_counter.count(); + } + action fill_in_roce_write_fields(mac_addr_t dest_mac, ipv4_addr_t dest_ip, rkey_t rkey) { + fill_in_roce_fields(dest_mac, dest_ip); + rdma_rkey = rkey; + } + table create_roce_packet { + key = { + eg_md.switchml_md.worker_id: exact; + } + actions = { + fill_in_roce_fields; + fill_in_roce_write_fields; + } + size = max_num_workers; + counters = rdma_send_counter; + } + DirectRegister>() psn_register; + DirectRegisterAction, bit<32>>(psn_register) psn_action = { + void apply(inout bit<32> value, out bit<32> read_value) { + bit<32> masked_sequence_number = value & 0xffffff; + read_value = masked_sequence_number; + bit<32> incremented_value = value + 1; + value = incremented_value; + } + }; + action add_qpn_and_psn(queue_pair_t qpn) { + hdr.ib_bth.dst_qp = qpn; + hdr.ib_bth.psn = psn_action.execute()[23:0]; + } + table fill_in_qpn_and_psn { + key = { + eg_md.switchml_md.worker_id : exact; + eg_md.switchml_md.pool_index: ternary; + } + actions = { + add_qpn_and_psn; + } + size = max_num_queue_pairs; + registers = psn_register; + } + action set_opcode_common(ib_opcode_t opcode) { + hdr.ib_bth.opcode = opcode; + } + action set_immediate() { + hdr.ib_immediate.setValid(); + hdr.ib_immediate.immediate = eg_md.switchml_md.msg_id ++ +(bit<8>)eg_md.switchml_md.e0 ++ +(bit<8>)eg_md.switchml_md.e1; + } + action set_rdma() { + hdr.ib_reth.setValid(); + hdr.ib_reth.r_key = rdma_rkey; + hdr.ib_reth.len = eg_md.switchml_md.tsi; + hdr.ib_reth.addr = eg_md.switchml_rdma_md.rdma_addr; + } + action set_first() { + set_opcode_common(ib_opcode_t.UC_RDMA_WRITE_FIRST); + set_rdma(); + hdr.udp.length = hdr.udp.length + (bit<16>)hdr.ib_reth.minSizeInBytes(); + hdr.ipv4.total_len = hdr.ipv4.total_len + (bit<16>)hdr.ib_reth.minSizeInBytes(); + } + action set_middle() { + set_opcode_common(ib_opcode_t.UC_RDMA_WRITE_MIDDLE); + } + action set_last_immediate() { + set_opcode_common(ib_opcode_t.UC_RDMA_WRITE_LAST_IMMEDIATE); + set_immediate(); + hdr.udp.length = hdr.udp.length + (bit<16>)hdr.ib_immediate.minSizeInBytes(); + hdr.ipv4.total_len = hdr.ipv4.total_len + (bit<16>)hdr.ib_immediate.minSizeInBytes(); + } + action set_only_immediate() { + set_opcode_common(ib_opcode_t.UC_RDMA_WRITE_ONLY_IMMEDIATE); + set_rdma(); + set_immediate(); + hdr.udp.length = hdr.udp.length + (bit<16>)(hdr.ib_immediate.minSizeInBytes() + hdr.ib_reth.minSizeInBytes()); + hdr.ipv4.total_len = hdr.ipv4.total_len + (bit<16>)(hdr.ib_immediate.minSizeInBytes() + hdr.ib_reth.minSizeInBytes()); + } + table set_opcodes { + key = { + eg_md.switchml_md.first_packet: exact; + eg_md.switchml_md.last_packet : exact; + } + actions = { + set_first; + set_middle; + set_last_immediate; + set_only_immediate; + } + size = 4; + const entries = { + (true, false) : set_first(); + (false, false) : set_middle(); + (false, true) : set_last_immediate(); + (true, true) : set_only_immediate(); + } + } + apply { + switch_mac_and_ip.apply(); + create_roce_packet.apply(); + if (eg_md.switchml_md.packet_size == packet_size_t.IBV_MTU_256) { + hdr.ipv4.total_len = hdr.ipv4.total_len + 256; + hdr.udp.length = hdr.udp.length + 256; + } else if (eg_md.switchml_md.packet_size == packet_size_t.IBV_MTU_1024) { + hdr.ipv4.total_len = hdr.ipv4.total_len + 1024; + hdr.udp.length = hdr.udp.length + 1024; + } + fill_in_qpn_and_psn.apply(); + set_opcodes.apply(); + } +} + +control ReconstructWorkerBitmap(inout ingress_metadata_t ig_md) { + action reconstruct_worker_bitmap_from_worker_id(worker_bitmap_t bitmap) { + ig_md.worker_bitmap = bitmap; + } + table reconstruct_worker_bitmap { + key = { + ig_md.switchml_md.worker_id: ternary; + } + actions = { + reconstruct_worker_bitmap_from_worker_id; + } + const entries = { + 0 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 0); + 1 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 1); + 2 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 2); + 3 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 3); + 4 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 4); + 5 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 5); + 6 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 6); + 7 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 7); + 8 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 8); + 9 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 9); + 10 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 10); + 11 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 11); + 12 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 12); + 13 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 13); + 14 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 14); + 15 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 15); + 16 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 16); + 17 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 17); + 18 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 18); + 19 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 19); + 20 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 20); + 21 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 21); + 22 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 22); + 23 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 23); + 24 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 24); + 25 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 25); + 26 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 26); + 27 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 27); + 28 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 28); + 29 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 29); + 30 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 30); + 31 &&& 0x1f : reconstruct_worker_bitmap_from_worker_id(1 << 31); + } + } + apply { + reconstruct_worker_bitmap.apply(); + } +} + +control UpdateAndCheckWorkerBitmap(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + Register(num_slots) worker_bitmap; + RegisterAction(worker_bitmap) worker_bitmap_update_set0 = { + void apply(inout worker_bitmap_pair_t value, out worker_bitmap_t return_value) { + return_value = value.first; + value.first = value.first | ig_md.worker_bitmap; + value.second = value.second & ~ig_md.worker_bitmap; + } + }; + RegisterAction(worker_bitmap) worker_bitmap_update_set1 = { + void apply(inout worker_bitmap_pair_t value, out worker_bitmap_t return_value) { + return_value = value.second; + value.first = value.first & ~ig_md.worker_bitmap; + value.second = value.second | ig_md.worker_bitmap; + } + }; + action drop() { + ig_dprsr_md.drop_ctl[0:0] = 1; + ig_md.switchml_md.packet_type = packet_type_t.IGNORE; + } + action simulate_drop() { + drop(); + } + action check_worker_bitmap_action() { + ig_md.switchml_md.map_result = ig_md.switchml_md.worker_bitmap_before & ig_md.worker_bitmap; + } + action update_worker_bitmap_set0_action() { + ig_md.switchml_md.worker_bitmap_before = worker_bitmap_update_set0.execute(ig_md.switchml_md.pool_index[14:1]); + check_worker_bitmap_action(); + } + action update_worker_bitmap_set1_action() { + ig_md.switchml_md.worker_bitmap_before = worker_bitmap_update_set1.execute(ig_md.switchml_md.pool_index[14:1]); + check_worker_bitmap_action(); + } + table update_and_check_worker_bitmap { + key = { + ig_md.switchml_md.pool_index : ternary; + ig_md.switchml_md.packet_type : ternary; + ig_md.port_metadata.ingress_drop_probability: ternary; + } + actions = { + update_worker_bitmap_set0_action; + update_worker_bitmap_set1_action; + drop; + simulate_drop; + NoAction; + } + const entries = { + (default, packet_type_t.CONSUME0, 0xffff) : simulate_drop(); + (15w0 &&& 15w1, packet_type_t.CONSUME0, default) : update_worker_bitmap_set0_action(); + (15w1 &&& 15w1, packet_type_t.CONSUME0, default) : update_worker_bitmap_set1_action(); + (15w0 &&& 15w1, packet_type_t.CONSUME1, default) : update_worker_bitmap_set0_action(); + (15w1 &&& 15w1, packet_type_t.CONSUME1, default) : update_worker_bitmap_set1_action(); + (15w0 &&& 15w1, packet_type_t.CONSUME2, default) : update_worker_bitmap_set0_action(); + (15w1 &&& 15w1, packet_type_t.CONSUME2, default) : update_worker_bitmap_set1_action(); + (15w0 &&& 15w1, packet_type_t.CONSUME3, default) : update_worker_bitmap_set0_action(); + (15w1 &&& 15w1, packet_type_t.CONSUME3, default) : update_worker_bitmap_set1_action(); + } + const default_action = NoAction; + } + apply { + update_and_check_worker_bitmap.apply(); + } +} + +control WorkersCounter(in header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) { + Register(register_size) workers_count; + RegisterAction(workers_count) workers_count_action = { + void apply(inout num_workers_pair_t value, out num_workers_t read_value) { + read_value = value.first; + if (value.first == 0) { + value.first = ig_md.switchml_md.num_workers - 1; + } else { + value.first = value.first - 1; + } + } + }; + RegisterAction(workers_count) read_workers_count_action = { + void apply(inout num_workers_pair_t value, out num_workers_t read_value) { + read_value = value.first; + } + }; + action count_workers_action() { + ig_md.switchml_md.first_last_flag = workers_count_action.execute(ig_md.switchml_md.pool_index); + } + action single_worker_count_action() { + workers_count_action.execute(ig_md.switchml_md.pool_index); + ig_md.switchml_md.first_last_flag = 1; + } + action single_worker_read_action() { + ig_md.switchml_md.first_last_flag = 0; + } + action read_count_workers_action() { + ig_md.switchml_md.first_last_flag = read_workers_count_action.execute(ig_md.switchml_md.pool_index); + } + table count_workers { + key = { + ig_md.switchml_md.num_workers: ternary; + ig_md.switchml_md.map_result : ternary; + ig_md.switchml_md.packet_type: ternary; + } + actions = { + single_worker_count_action; + single_worker_read_action; + count_workers_action; + read_count_workers_action; + @defaultonly NoAction; + } + const entries = { + (1, 0, packet_type_t.CONSUME0) : single_worker_count_action(); + (1, 0, packet_type_t.CONSUME1) : single_worker_count_action(); + (1, 0, packet_type_t.CONSUME2) : single_worker_count_action(); + (1, 0, packet_type_t.CONSUME3) : single_worker_count_action(); + (1, default, packet_type_t.CONSUME0) : single_worker_read_action(); + (1, default, packet_type_t.CONSUME1) : single_worker_read_action(); + (1, default, packet_type_t.CONSUME2) : single_worker_read_action(); + (1, default, packet_type_t.CONSUME3) : single_worker_read_action(); + (default, 0, packet_type_t.CONSUME0) : count_workers_action(); + (default, 0, packet_type_t.CONSUME1) : count_workers_action(); + (default, 0, packet_type_t.CONSUME2) : count_workers_action(); + (default, 0, packet_type_t.CONSUME3) : count_workers_action(); + (default, default, packet_type_t.CONSUME0) : read_count_workers_action(); + (default, default, packet_type_t.CONSUME1) : read_count_workers_action(); + (default, default, packet_type_t.CONSUME2) : read_count_workers_action(); + (default, default, packet_type_t.CONSUME3) : read_count_workers_action(); + } + const default_action = NoAction; + } + apply { + count_workers.apply(); + } +} + +control Exponents(in exponent_t exponent0, in exponent_t exponent1, out exponent_t max_exponent0, out exponent_t max_exponent1, inout ingress_metadata_t ig_md) { + Register(register_size) exponents; + RegisterAction(exponents) write_read0_register_action = { + void apply(inout exponent_pair_t value, out exponent_t read_value) { + value.first = exponent0; + value.second = exponent1; + read_value = value.first; + } + }; + action write_read0_action() { + max_exponent0 = write_read0_register_action.execute(ig_md.switchml_md.pool_index); + } + RegisterAction(exponents) max_read0_register_action = { + void apply(inout exponent_pair_t value, out exponent_t read_value) { + value.first = max(value.first, exponent0); + value.second = max(value.second, exponent1); + read_value = value.first; + } + }; + action max_read0_action() { + max_exponent0 = max_read0_register_action.execute(ig_md.switchml_md.pool_index); + } + RegisterAction(exponents) read0_register_action = { + void apply(inout exponent_pair_t value, out exponent_t read_value) { + read_value = value.first; + } + }; + action read0_action() { + max_exponent0 = read0_register_action.execute(ig_md.switchml_md.pool_index); + } + RegisterAction(exponents) read1_register_action = { + void apply(inout exponent_pair_t value, out exponent_t read_value) { + read_value = value.second; + } + }; + action read1_action() { + max_exponent1 = read1_register_action.execute(ig_md.switchml_md.pool_index); + } + table exponent_max { + key = { + ig_md.switchml_md.worker_bitmap_before: ternary; + ig_md.switchml_md.map_result : ternary; + ig_md.switchml_md.packet_type : ternary; + } + actions = { + write_read0_action; + max_read0_action; + read0_action; + read1_action; + @defaultonly NoAction; + } + size = 4; + const entries = { + (32w0, default, packet_type_t.CONSUME0) : write_read0_action(); + (default, 32w0, packet_type_t.CONSUME0) : max_read0_action(); + (default, default, packet_type_t.CONSUME0) : read0_action(); + (default, default, packet_type_t.HARVEST7) : read1_action(); + } + const default_action = NoAction; + } + apply { + exponent_max.apply(); + } +} + +control Processor(in value_t value0, in value_t value1, out value_t value0_out, out value_t value1_out, in switchml_md_h switchml_md) { + Register(register_size) values; + RegisterAction(values) write_read1_register_action = { + void apply(inout value_pair_t value, out value_t read_value) { + value.first = value0; + value.second = value1; + read_value = value.second; + } + }; + action write_read1_action() { + value1_out = write_read1_register_action.execute(switchml_md.pool_index); + } + RegisterAction(values) sum_read1_register_action = { + void apply(inout value_pair_t value, out value_t read_value) { + value.first = value.first + value0; + value.second = value.second + value1; + read_value = value.second; + } + }; + action sum_read1_action() { + value1_out = sum_read1_register_action.execute(switchml_md.pool_index); + } + RegisterAction(values) read0_register_action = { + void apply(inout value_pair_t value, out value_t read_value) { + read_value = value.first; + } + }; + action read0_action() { + value0_out = read0_register_action.execute(switchml_md.pool_index); + } + RegisterAction(values) read1_register_action = { + void apply(inout value_pair_t value, out value_t read_value) { + read_value = value.second; + } + }; + action read1_action() { + value1_out = read1_register_action.execute(switchml_md.pool_index); + } + table sum { + key = { + switchml_md.worker_bitmap_before: ternary; + switchml_md.map_result : ternary; + switchml_md.packet_type : ternary; + } + actions = { + write_read1_action; + sum_read1_action; + read0_action; + read1_action; + NoAction; + } + size = 20; + const entries = { + (32w0, default, packet_type_t.CONSUME0) : write_read1_action(); + (32w0, default, packet_type_t.CONSUME1) : write_read1_action(); + (32w0, default, packet_type_t.CONSUME2) : write_read1_action(); + (32w0, default, packet_type_t.CONSUME3) : write_read1_action(); + (default, 32w0, packet_type_t.CONSUME0) : sum_read1_action(); + (default, 32w0, packet_type_t.CONSUME1) : sum_read1_action(); + (default, 32w0, packet_type_t.CONSUME2) : sum_read1_action(); + (default, 32w0, packet_type_t.CONSUME3) : sum_read1_action(); + (default, default, packet_type_t.CONSUME0) : read1_action(); + (default, default, packet_type_t.CONSUME1) : read1_action(); + (default, default, packet_type_t.CONSUME2) : read1_action(); + (default, default, packet_type_t.CONSUME3) : read1_action(); + (default, default, packet_type_t.HARVEST0) : read1_action(); + (default, default, packet_type_t.HARVEST1) : read0_action(); + (default, default, packet_type_t.HARVEST2) : read1_action(); + (default, default, packet_type_t.HARVEST3) : read0_action(); + (default, default, packet_type_t.HARVEST4) : read1_action(); + (default, default, packet_type_t.HARVEST5) : read0_action(); + (default, default, packet_type_t.HARVEST6) : read1_action(); + (default, default, packet_type_t.HARVEST7) : read0_action(); + } + const default_action = NoAction; + } + apply { + sum.apply(); + } +} + +control NextStepSelector(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + bool count_consume; + bool count_broadcast; + bool count_retransmit; + bool count_recirculate; + bool count_drop; + Counter(register_size, CounterType_t.PACKETS) broadcast_counter; + Counter(register_size, CounterType_t.PACKETS) retransmit_counter; + Counter(register_size, CounterType_t.PACKETS) recirculate_counter; + Counter(register_size, CounterType_t.PACKETS) drop_counter; + action recirculate_for_consume(packet_type_t packet_type, PortId_t recirc_port) { + hdr.d0.setInvalid(); + hdr.d1.setInvalid(); + ig_tm_md.ucast_egress_port = recirc_port; + ig_tm_md.bypass_egress = 1w1; + ig_dprsr_md.drop_ctl[0:0] = 0; + ig_md.switchml_md.packet_type = packet_type; + count_consume = true; + count_recirculate = true; + } + action recirculate_for_harvest(packet_type_t packet_type, PortId_t recirc_port) { + ig_tm_md.ucast_egress_port = recirc_port; + ig_tm_md.bypass_egress = 1w1; + ig_dprsr_md.drop_ctl[0:0] = 0; + ig_md.switchml_md.packet_type = packet_type; + } + action recirculate_for_CONSUME1(PortId_t recirc_port) { + recirculate_for_consume(packet_type_t.CONSUME1, recirc_port); + } + action recirculate_for_CONSUME2_same_port_next_pipe() { + recirculate_for_consume(packet_type_t.CONSUME2, 2w2 ++ ig_intr_md.ingress_port[6:0]); + } + action recirculate_for_CONSUME3_same_port_next_pipe() { + recirculate_for_consume(packet_type_t.CONSUME3, 2w3 ++ ig_intr_md.ingress_port[6:0]); + } + action recirculate_for_HARVEST1(PortId_t recirc_port) { + hdr.d0.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST1, recirc_port); + } + action recirculate_for_HARVEST2(PortId_t recirc_port) { + hdr.d1.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST2, recirc_port); + } + action recirculate_for_HARVEST3(PortId_t recirc_port) { + hdr.d0.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST3, recirc_port); + } + action recirculate_for_HARVEST4(PortId_t recirc_port) { + hdr.d1.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST4, recirc_port); + } + action recirculate_for_HARVEST5(PortId_t recirc_port) { + hdr.d0.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST5, recirc_port); + } + action recirculate_for_HARVEST6(PortId_t recirc_port) { + hdr.d1.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST6, recirc_port); + } + action recirculate_for_HARVEST7(PortId_t recirc_port) { + hdr.d0.setInvalid(); + recirculate_for_harvest(packet_type_t.HARVEST7, recirc_port); + } + action finish_consume() { + ig_dprsr_md.drop_ctl[0:0] = 1; + count_consume = true; + count_drop = true; + } + action broadcast() { + hdr.d1.setInvalid(); + ig_tm_md.mcast_grp_a = ig_md.switchml_md.mgid; + ig_tm_md.level1_exclusion_id = null_level1_exclusion_id; + ig_md.switchml_md.packet_type = packet_type_t.BROADCAST; + ig_tm_md.bypass_egress = 1w0; + ig_dprsr_md.drop_ctl[0:0] = 0; + count_broadcast = true; + } + action retransmit() { + hdr.d1.setInvalid(); + ig_tm_md.ucast_egress_port = ig_md.switchml_md.ingress_port; + ig_md.switchml_md.packet_type = packet_type_t.RETRANSMIT; + ig_tm_md.bypass_egress = 1w0; + ig_dprsr_md.drop_ctl[0:0] = 0; + count_retransmit = true; + } + action drop() { + ig_dprsr_md.drop_ctl[0:0] = 1; + ig_md.switchml_md.packet_type = packet_type_t.IGNORE; + count_drop = true; + } + table next_step { + key = { + ig_md.switchml_md.packet_size : ternary; + ig_md.switchml_md.worker_id : ternary; + ig_md.switchml_md.packet_type : ternary; + ig_md.switchml_md.first_last_flag: ternary; + ig_md.switchml_md.map_result : ternary; + } + actions = { + recirculate_for_CONSUME1; + recirculate_for_CONSUME2_same_port_next_pipe; + recirculate_for_CONSUME3_same_port_next_pipe; + recirculate_for_HARVEST1; + recirculate_for_HARVEST2; + recirculate_for_HARVEST3; + recirculate_for_HARVEST4; + recirculate_for_HARVEST5; + recirculate_for_HARVEST6; + recirculate_for_HARVEST7; + finish_consume; + broadcast; + retransmit; + drop; + } + const default_action = drop(); + size = 128; + } + apply { + count_consume = false; + count_broadcast = false; + count_retransmit = false; + count_recirculate = false; + count_drop = false; + next_step.apply(); + if (count_consume || count_drop) { + drop_counter.count(ig_md.switchml_md.pool_index); + } + if (count_recirculate) { + recirculate_counter.count(ig_md.switchml_md.pool_index); + } + if (count_broadcast) { + broadcast_counter.count(ig_md.switchml_md.pool_index); + } + if (count_retransmit) { + retransmit_counter.count(ig_md.switchml_md.pool_index); + } + } +} + +control Ingress(inout header_t hdr, inout ingress_metadata_t ig_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_prsr_md, inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md, inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) { + ARPandICMPResponder() arp_icmp_responder; + Forwarder() forwarder; + IngressDropSimulator() ingress_drop_sim; + EgressDropSimulator() egress_drop_sim; + RDMAReceiver() rdma_receiver; + UDPReceiver() udp_receiver; + WorkersCounter() workers_counter; + ReconstructWorkerBitmap() reconstruct_worker_bitmap; + UpdateAndCheckWorkerBitmap() update_and_check_worker_bitmap; + NextStepSelector() next_step_selector; + Exponents() exponents; + Processor() value00; + Processor() value01; + Processor() value02; + Processor() value03; + Processor() value04; + Processor() value05; + Processor() value06; + Processor() value07; + Processor() value08; + Processor() value09; + Processor() value10; + Processor() value11; + Processor() value12; + Processor() value13; + Processor() value14; + Processor() value15; + Processor() value16; + Processor() value17; + Processor() value18; + Processor() value19; + Processor() value20; + Processor() value21; + Processor() value22; + Processor() value23; + Processor() value24; + Processor() value25; + Processor() value26; + Processor() value27; + Processor() value28; + Processor() value29; + Processor() value30; + Processor() value31; + apply { + if (ig_md.switchml_md.packet_type == packet_type_t.CONSUME0) { + if (hdr.ib_bth.isValid()) { + rdma_receiver.apply(hdr, ig_md, ig_intr_md, ig_prsr_md, ig_dprsr_md, ig_tm_md); + } else { + udp_receiver.apply(hdr, ig_md, ig_intr_md, ig_prsr_md, ig_dprsr_md, ig_tm_md); + } + ingress_drop_sim.apply(ig_md.port_metadata); + egress_drop_sim.apply(ig_md.port_metadata, hdr.ib_bth.dst_qp[16 + max_num_workers_log2 - 1:16] ++ hdr.ib_bth.dst_qp[max_num_queue_pairs_per_worker_log2 - 1:0], ig_md.switchml_md.simulate_egress_drop); + ig_md.switchml_md.ingress_port = ig_intr_md.ingress_port; + } else if (ig_md.switchml_md.packet_type == packet_type_t.CONSUME1 || ig_md.switchml_md.packet_type == packet_type_t.CONSUME2 || ig_md.switchml_md.packet_type == packet_type_t.CONSUME3) { + reconstruct_worker_bitmap.apply(ig_md); + } + if (ig_dprsr_md.drop_ctl[0:0] == 1w0) { + if (ig_md.switchml_md.packet_type == packet_type_t.CONSUME0 || ig_md.switchml_md.packet_type == packet_type_t.CONSUME1 || ig_md.switchml_md.packet_type == packet_type_t.CONSUME2 || ig_md.switchml_md.packet_type == packet_type_t.CONSUME3) { + update_and_check_worker_bitmap.apply(hdr, ig_md, ig_intr_md, ig_dprsr_md, ig_tm_md); + workers_counter.apply(hdr, ig_md, ig_dprsr_md); + } + if ((packet_type_underlying_t)ig_md.switchml_md.packet_type >= (packet_type_underlying_t)packet_type_t.CONSUME0) { + if (ig_md.switchml_md.last_packet) { + exponents.apply(ig_md.switchml_md.e0, ig_md.switchml_md.e1, ig_md.switchml_md.e0, ig_md.switchml_md.e1, ig_md); + } + value00.apply(hdr.d0.d00, hdr.d1.d00, hdr.d0.d00, hdr.d1.d00, ig_md.switchml_md); + value01.apply(hdr.d0.d01, hdr.d1.d01, hdr.d0.d01, hdr.d1.d01, ig_md.switchml_md); + value02.apply(hdr.d0.d02, hdr.d1.d02, hdr.d0.d02, hdr.d1.d02, ig_md.switchml_md); + value03.apply(hdr.d0.d03, hdr.d1.d03, hdr.d0.d03, hdr.d1.d03, ig_md.switchml_md); + value04.apply(hdr.d0.d04, hdr.d1.d04, hdr.d0.d04, hdr.d1.d04, ig_md.switchml_md); + value05.apply(hdr.d0.d05, hdr.d1.d05, hdr.d0.d05, hdr.d1.d05, ig_md.switchml_md); + value06.apply(hdr.d0.d06, hdr.d1.d06, hdr.d0.d06, hdr.d1.d06, ig_md.switchml_md); + value07.apply(hdr.d0.d07, hdr.d1.d07, hdr.d0.d07, hdr.d1.d07, ig_md.switchml_md); + value08.apply(hdr.d0.d08, hdr.d1.d08, hdr.d0.d08, hdr.d1.d08, ig_md.switchml_md); + value09.apply(hdr.d0.d09, hdr.d1.d09, hdr.d0.d09, hdr.d1.d09, ig_md.switchml_md); + value10.apply(hdr.d0.d10, hdr.d1.d10, hdr.d0.d10, hdr.d1.d10, ig_md.switchml_md); + value11.apply(hdr.d0.d11, hdr.d1.d11, hdr.d0.d11, hdr.d1.d11, ig_md.switchml_md); + value12.apply(hdr.d0.d12, hdr.d1.d12, hdr.d0.d12, hdr.d1.d12, ig_md.switchml_md); + value13.apply(hdr.d0.d13, hdr.d1.d13, hdr.d0.d13, hdr.d1.d13, ig_md.switchml_md); + value14.apply(hdr.d0.d14, hdr.d1.d14, hdr.d0.d14, hdr.d1.d14, ig_md.switchml_md); + value15.apply(hdr.d0.d15, hdr.d1.d15, hdr.d0.d15, hdr.d1.d15, ig_md.switchml_md); + value16.apply(hdr.d0.d16, hdr.d1.d16, hdr.d0.d16, hdr.d1.d16, ig_md.switchml_md); + value17.apply(hdr.d0.d17, hdr.d1.d17, hdr.d0.d17, hdr.d1.d17, ig_md.switchml_md); + value18.apply(hdr.d0.d18, hdr.d1.d18, hdr.d0.d18, hdr.d1.d18, ig_md.switchml_md); + value19.apply(hdr.d0.d19, hdr.d1.d19, hdr.d0.d19, hdr.d1.d19, ig_md.switchml_md); + value20.apply(hdr.d0.d20, hdr.d1.d20, hdr.d0.d20, hdr.d1.d20, ig_md.switchml_md); + value21.apply(hdr.d0.d21, hdr.d1.d21, hdr.d0.d21, hdr.d1.d21, ig_md.switchml_md); + value22.apply(hdr.d0.d22, hdr.d1.d22, hdr.d0.d22, hdr.d1.d22, ig_md.switchml_md); + value23.apply(hdr.d0.d23, hdr.d1.d23, hdr.d0.d23, hdr.d1.d23, ig_md.switchml_md); + value24.apply(hdr.d0.d24, hdr.d1.d24, hdr.d0.d24, hdr.d1.d24, ig_md.switchml_md); + value25.apply(hdr.d0.d25, hdr.d1.d25, hdr.d0.d25, hdr.d1.d25, ig_md.switchml_md); + value26.apply(hdr.d0.d26, hdr.d1.d26, hdr.d0.d26, hdr.d1.d26, ig_md.switchml_md); + value27.apply(hdr.d0.d27, hdr.d1.d27, hdr.d0.d27, hdr.d1.d27, ig_md.switchml_md); + value28.apply(hdr.d0.d28, hdr.d1.d28, hdr.d0.d28, hdr.d1.d28, ig_md.switchml_md); + value29.apply(hdr.d0.d29, hdr.d1.d29, hdr.d0.d29, hdr.d1.d29, ig_md.switchml_md); + value30.apply(hdr.d0.d30, hdr.d1.d30, hdr.d0.d30, hdr.d1.d30, ig_md.switchml_md); + value31.apply(hdr.d0.d31, hdr.d1.d31, hdr.d0.d31, hdr.d1.d31, ig_md.switchml_md); + next_step_selector.apply(hdr, ig_md, ig_intr_md, ig_dprsr_md, ig_tm_md); + } else { + arp_icmp_responder.apply(hdr, ig_md, ig_intr_md, ig_prsr_md, ig_dprsr_md, ig_tm_md); + forwarder.apply(hdr, ig_md, ig_intr_md, ig_dprsr_md, ig_tm_md); + } + } + } +} + +control Egress(inout header_t hdr, inout egress_metadata_t eg_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, inout egress_intrinsic_metadata_for_deparser_t eg_intr_dprs_md, inout egress_intrinsic_metadata_for_output_port_t eg_intr_oport_md) { + RDMASender() rdma_sender; + UDPSender() udp_sender; + apply { + if (eg_md.switchml_md.packet_type == packet_type_t.BROADCAST || eg_md.switchml_md.packet_type == packet_type_t.RETRANSMIT) { + if (eg_md.switchml_md.simulate_egress_drop) { + eg_md.switchml_md.packet_type = packet_type_t.IGNORE; + eg_intr_dprs_md.drop_ctl[0:0] = 1; + } + if (eg_md.switchml_md.packet_type == packet_type_t.BROADCAST) { + eg_md.switchml_md.worker_id = eg_intr_md.egress_rid; + } + if (eg_md.switchml_md.worker_type == worker_type_t.ROCEv2) { + rdma_sender.apply(hdr, eg_md, eg_intr_md, eg_intr_md_from_prsr, eg_intr_dprs_md); + } else { + udp_sender.apply(eg_md, eg_intr_md, hdr); + } + } + } +} + +Pipeline(IngressParser(), Ingress(), IngressDeparser(), EgressParser(), Egress(), EgressDeparser()) pipe; + +Switch(pipe) main; + diff --git a/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino2_only/switch_tofino2_y1_mod.p4 b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino2_only/switch_tofino2_y1_mod.p4 new file mode 100644 index 00000000000..eb21c1de408 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test/p4-programs/tofino2_only/switch_tofino2_y1_mod.p4 @@ -0,0 +1,6869 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include +#include +#include +#include + +const bit<32> PORT_TABLE_SIZE = 288 * 2; +const bit<32> VLAN_TABLE_SIZE = 4096; +const bit<32> BD_FLOOD_TABLE_SIZE = VLAN_TABLE_SIZE * 4; +const bit<32> PORT_VLAN_TABLE_SIZE = 1024; +const bit<32> BD_TABLE_SIZE = 5120; +const bit<32> MAC_TABLE_SIZE = 64 * 1024; +const bit<32> IPV4_HOST_TABLE_SIZE = 64 * 1024; +const bit<32> IPV4_LPM_TABLE_SIZE = 512 * 1024; +const bit<32> IPV6_HOST_TABLE_SIZE = 64 * 1024; +const bit<32> IPV6_LPM_TABLE_SIZE = 512; +const bit<32> IPV6_LPM64_TABLE_SIZE = 128 * 1024; +const bit<32> ECMP_GROUP_TABLE_SIZE = 1024; +const bit<32> ECMP_SELECT_TABLE_SIZE = 65536; +const bit<32> NEXTHOP_TABLE_SIZE = 1 << 16; +const bit<32> TUNNEL_NEXTHOP_TABLE_SIZE = 32768; +const bit<32> TUNNEL_OBJECT_SIZE = 1 << 8; +const bit<32> TUNNEL_ENCAP_IPV4_SIZE = 4096; +const bit<32> TUNNEL_ENCAP_IPV6_SIZE = 0; +const bit<32> TUNNEL_ENCAP_IP_SIZE = TUNNEL_ENCAP_IPV4_SIZE + TUNNEL_ENCAP_IPV6_SIZE; +const bit<32> RID_TABLE_SIZE = 16384; +const bit<32> INGRESS_MAC_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IPV4_ACL_TABLE_SIZE = 2048; +const bit<32> INGRESS_IPV6_ACL_TABLE_SIZE = 1024; +const bit<32> INGRESS_IP_MIRROR_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IP_QOS_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IP_DTEL_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IPV4_DTEL_ACL_TABLE_SIZE = 512; +const bit<32> INGRESS_IPV6_DTEL_ACL_TABLE_SIZE = 512; +const bit<32> EGRESS_IPV4_ACL_TABLE_SIZE = 512; +const bit<32> EGRESS_IPV6_ACL_TABLE_SIZE = 512; +const bit<32> STORM_CONTROL_TABLE_SIZE = 256; +typedef bit<48> mac_addr_t; +typedef bit<32> ipv4_addr_t; +typedef bit<128> ipv6_addr_t; +typedef bit<12> vlan_id_t; +const int PAD_SIZE = 32; +const int MIN_SIZE = 64; +@pa_container_size("ingress" , "hdr.ethernet.src_addr" , 16 , 32) @pa_container_size("ingress" , "hdr.ethernet.dst_addr" , 16 , 32) @pa_container_size("ingress" , "hdr.ethernet.$valid" , 16) header ethernet_h { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +header vlan_tag_h { + bit<3> pcp; + bit<1> dei; + vlan_id_t vid; + bit<16> ether_type; +} + +header mpls_h { + bit<20> label; + bit<3> exp; + bit<1> bos; + bit<8> ttl; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdr_checksum; + ipv4_addr_t src_addr; + ipv4_addr_t dst_addr; +} + +header router_alert_option_h { + bit<8> type; + bit<8> length; + bit<16> value; +} + +header ipv4_option_h { + bit<8> type; + bit<8> length; + bit<16> value; +} + +header ipv6_h { + bit<4> version; + bit<8> traffic_class; + bit<20> flow_label; + bit<16> payload_len; + bit<8> next_hdr; + bit<8> hop_limit; + ipv6_addr_t src_addr; + ipv6_addr_t dst_addr; +} + +header tcp_h { + bit<16> src_port; + bit<16> dst_port; + bit<32> seq_no; + bit<32> ack_no; + bit<4> data_offset; + bit<4> res; + bit<8> flags; + bit<16> window; + bit<16> checksum; + bit<16> urgent_ptr; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> length; + bit<16> checksum; +} + +header icmp_h { + bit<8> type; + bit<8> code; + bit<16> checksum; +} + +header igmp_h { + bit<8> type; + bit<8> code; + bit<16> checksum; +} + +header arp_h { + bit<16> hw_type; + bit<16> proto_type; + bit<8> hw_addr_len; + bit<8> proto_addr_len; + bit<16> opcode; +} + +header rocev2_bth_h { + bit<8> opcodee; + bit<1> se; + bit<1> migration_req; + bit<2> pad_count; + bit<4> transport_version; + bit<16> partition_key; + bit<1> f_res1; + bit<1> b_res1; + bit<6> reserved; + bit<24> dst_qp; + bit<1> ack_req; + bit<7> reserved2; +} + +header fcoe_fc_h { + bit<4> version; + bit<100> reserved; + bit<8> sof; + bit<8> r_ctl; + bit<24> d_id; + bit<8> cs_ctl; + bit<24> s_id; + bit<8> type; + bit<24> f_ctl; + bit<8> seq_id; + bit<8> df_ctl; + bit<16> seq_cnt; + bit<16> ox_id; + bit<16> rx_id; +} + +header ipv6_srh_h { + bit<8> next_hdr; + bit<8> hdr_ext_len; + bit<8> routing_type; + bit<8> seg_left; + bit<8> last_entry; + bit<8> flags; + bit<16> tag; +} + +header ipv6_segment_h { + bit<128> sid; +} + +header vxlan_h { + bit<8> flags; + bit<24> reserved; + bit<24> vni; + bit<8> reserved2; +} + +header vxlan_gpe_h { + bit<8> flags; + bit<16> reserved; + bit<24> vni; + bit<8> reserved2; +} + +header gre_h { + bit<16> flags_version; + bit<16> proto; +} + +header nvgre_h { + bit<32> vsid_flowid; +} + +header erspan_h { + bit<16> version_vlan; + bit<16> session_id; +} + +header erspan_type2_h { + bit<32> index; +} + +header erspan_type3_h { + bit<32> timestamp; + bit<32> ft_d_other; +} + +header erspan_platform_h { + bit<6> id; + bit<58> info; +} + +header gtpu_h { + bit<3> version; + bit<1> p; + bit<1> t; + bit<3> spare0; + bit<8> mesg_type; + bit<16> mesg_len; + bit<32> teid; + bit<24> seq_num; + bit<8> spare1; +} + +header geneve_h { + bit<2> version; + bit<6> opt_len; + bit<1> oam; + bit<1> critical; + bit<6> reserved; + bit<16> proto_type; + bit<24> vni; + bit<8> reserved2; +} + +header geneve_option_h { + bit<16> opt_class; + bit<8> opt_type; + bit<3> reserved; + bit<5> opt_len; +} + +header bfd_h { + bit<3> version; + bit<5> diag; + bit<8> flags; + bit<8> detect_multi; + bit<8> len; + bit<32> my_discriminator; + bit<32> your_discriminator; + bit<32> desired_min_tx_interval; + bit<32> req_min_rx_interval; + bit<32> req_min_echo_rx_interval; +} + +header dtel_report_v05_h { + bit<4> version; + bit<4> next_proto; + bit<3> d_q_f; + bit<15> reserved; + bit<6> hw_id; + bit<32> seq_number; + bit<32> timestamp; + bit<32> switch_id; +} + +header dtel_report_base_h { + bit<7> pad0; + PortId_t ingress_port; + bit<7> pad1; + PortId_t egress_port; + bit<1> pad2; + bit<7> queue_id; +} + +header dtel_drop_report_h { + bit<8> drop_reason; + bit<16> reserved; +} + +header dtel_switch_local_report_h { + bit<5> pad3; + bit<19> queue_occupancy; + bit<32> timestamp; +} + +header dtel_report_v10_h { + bit<4> version; + bit<4> length; + bit<3> next_proto; + bit<6> metadata_bits; + bit<6> reserved; + bit<3> d_q_f; + bit<6> hw_id; + bit<32> switch_id; + bit<32> seq_number; + bit<32> timestamp; +} + +header dtel_report_v20_h { + bit<4> version; + bit<4> hw_id; + bit<24> seq_number; + bit<32> switch_id; + bit<16> report_length; + bit<8> md_length; + bit<3> d_q_f; + bit<5> reserved; + bit<16> rep_md_bits; + bit<16> domain_specific_id; + bit<16> ds_md_bits; + bit<16> ds_md_status; +} + +header dtel_metadata_1_h { + bit<7> pad0; + PortId_t ingress_port; + bit<7> pad1; + PortId_t egress_port; +} + +header dtel_metadata_2_h { + bit<32> hop_latency; +} + +header dtel_metadata_3_h { + bit<1> pad2; + bit<7> queue_id; + bit<5> pad3; + bit<19> queue_occupancy; +} + +header dtel_metadata_4_h { + bit<16> pad; + bit<48> ingress_timestamp; +} + +header dtel_metadata_5_h { + bit<16> pad; + bit<48> egress_timestamp; +} + +header dtel_report_metadata_15_h { + bit<1> pad; + bit<7> queue_id; + bit<8> drop_reason; + bit<16> reserved; +} + +header fabric_h { + bit<8> reserved; + bit<3> color; + bit<5> qos; + bit<8> reserved2; +} + +header cpu_h { + bit<1> tx_bypass; + bit<1> capture_ts; + bit<1> reserved; + bit<5> egress_queue; + bit<16> ingress_port; + bit<16> port_lag_index; + bit<16> ingress_bd; + bit<16> reason_code; + bit<16> ether_type; +} + +header sfc_pause_h { + bit<8> version; + bit<8> dscp; + bit<16> duration_us; +} + +header padding_112b_h { + bit<112> pad_0; +} + +header padding_96b_h { + bit<96> pad; +} + +header pfc_h { + bit<16> opcode; + bit<8> reserved_zero; + bit<8> class_enable_vec; + bit<16> tstamp0; + bit<16> tstamp1; + bit<16> tstamp2; + bit<16> tstamp3; + bit<16> tstamp4; + bit<16> tstamp5; + bit<16> tstamp6; + bit<16> tstamp7; +} + +header timestamp_h { + bit<48> timestamp; +} + +header pad_h { + bit<(PAD_SIZE * 8)> data; +} + +const bit<32> MIN_TABLE_SIZE = 512; +const bit<32> LAG_TABLE_SIZE = 1024; +const bit<32> LAG_GROUP_TABLE_SIZE = 256; +const bit<32> LAG_MAX_MEMBERS_PER_GROUP = 64; +const bit<32> LAG_SELECTOR_TABLE_SIZE = 16384; +const bit<32> DTEL_GROUP_TABLE_SIZE = 4; +const bit<32> DTEL_MAX_MEMBERS_PER_GROUP = 64; +const bit<32> DTEL_SELECTOR_TABLE_SIZE = 256; +const bit<32> IPV4_DST_VTEP_TABLE_SIZE = 512; +const bit<32> IPV6_DST_VTEP_TABLE_SIZE = 512; +const bit<32> VNI_MAPPING_TABLE_SIZE = 5 * 1024; +@pa_container_size("ingress" , "local_md.lkp.ip_dst_addr" , 32) @pa_container_size("ingress" , "_ipv4_lpm_partition_key" , 32) @pa_container_size("ingress" , "hdr.ipv4.dst_addr" , 32) @pa_container_size("ingress" , "hdr.ipv6.dst_addr" , 32) @pa_container_size("ingress" , "hdr.inner_ipv4.dst_addr" , 32) @pa_container_size("ingress" , "hdr.inner_ipv6.dst_addr" , 32) typedef bit<32> switch_uint32_t; +typedef bit<16> switch_uint16_t; +typedef bit<8> switch_uint8_t; +typedef PortId_t switch_port_t; +const switch_port_t SWITCH_PORT_INVALID = 9w0x1ff; +typedef bit<7> switch_port_padding_t; +typedef QueueId_t switch_qid_t; +typedef ReplicationId_t switch_rid_t; +const switch_rid_t SWITCH_RID_DEFAULT = 0xffff; +typedef bit<3> switch_ingress_cos_t; +typedef bit<3> switch_digest_type_t; +const switch_digest_type_t SWITCH_DIGEST_TYPE_INVALID = 0; +const switch_digest_type_t SWITCH_DIGEST_TYPE_MAC_LEARNING = 1; +typedef bit<16> switch_ifindex_t; +typedef bit<10> switch_port_lag_index_t; +const switch_port_lag_index_t SWITCH_FLOOD = 0x3ff; +typedef bit<8> switch_isolation_group_t; +typedef bit<16> switch_bd_t; +const switch_bd_t SWITCH_DEFAULT_BD = 16w1; +const switch_bd_t SWITCH_BD_DEFAULT_VRF = 4097; +const bit<32> VRF_TABLE_SIZE = 1 << 8; +typedef bit<8> switch_vrf_t; +const switch_vrf_t SWITCH_DEFAULT_VRF = 1; +typedef bit<16> switch_nexthop_t; +typedef bit<10> switch_user_metadata_t; +typedef bit<32> switch_hash_t; +typedef bit<128> srv6_sid_t; +typedef bit<16> switch_xid_t; +typedef L2ExclusionId_t switch_yid_t; +typedef bit<32> switch_ig_port_lag_label_t; +typedef bit<16> switch_eg_port_lag_label_t; +typedef bit<16> switch_bd_label_t; +typedef bit<16> switch_mtu_t; +typedef bit<12> switch_stats_index_t; +typedef bit<16> switch_cpu_reason_t; +const switch_cpu_reason_t SWITCH_CPU_REASON_PTP = 0x8; +const switch_cpu_reason_t SWITCH_CPU_REASON_BFD = 0x9; +typedef bit<8> switch_fib_label_t; +struct switch_cpu_port_value_set_t { + bit<16> ether_type; + switch_port_t port; +} + +typedef bit<2> bfd_pkt_action_t; +const bfd_pkt_action_t BFD_PKT_ACTION_NORMAL = 0x0; +const bfd_pkt_action_t BFD_PKT_ACTION_TIMEOUT = 0x1; +const bfd_pkt_action_t BFD_PKT_ACTION_DROP = 0x2; +const bfd_pkt_action_t BFD_PKT_ACTION_INVALID = 0x3; +typedef bit<8> bfd_multiplier_t; +typedef bit<12> bfd_session_t; +typedef bit<4> bfd_pipe_t; +typedef bit<8> bfd_timer_t; +struct switch_bfd_metadata_t { + bfd_multiplier_t tx_mult; + bfd_multiplier_t rx_mult; + bfd_pkt_action_t pkt_action; + bfd_pipe_t pktgen_pipe; + bfd_session_t session_id; + bit<1> tx_timer_expired; + bit<1> session_offload; + bit<1> rx_recirc; + bit<1> pkt_tx; +} + +typedef bit<8> switch_drop_reason_t; +const switch_drop_reason_t SWITCH_DROP_REASON_UNKNOWN = 0; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_SRC_MAC_ZERO = 10; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_SRC_MAC_MULTICAST = 11; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_DST_MAC_ZERO = 12; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_ETHERNET_MISS = 13; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_SAME_MAC_CHECK = 17; +const switch_drop_reason_t SWITCH_DROP_REASON_SRC_MAC_ZERO = 14; +const switch_drop_reason_t SWITCH_DROP_REASON_SRC_MAC_MULTICAST = 15; +const switch_drop_reason_t SWITCH_DROP_REASON_DST_MAC_ZERO = 16; +const switch_drop_reason_t SWITCH_DROP_REASON_DST_MAC_MCAST_DST_IP_UCAST = 18; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_VERSION_INVALID = 25; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_TTL_ZERO = 26; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_MULTICAST = 27; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_LOOPBACK = 28; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_MISS = 29; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_IHL_INVALID = 30; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_INVALID_CHECKSUM = 31; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_DST_LOOPBACK = 32; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_UNSPECIFIED = 33; +const switch_drop_reason_t SWITCH_DROP_REASON_OUTER_IP_SRC_CLASS_E = 34; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_VERSION_INVALID = 40; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_TTL_ZERO = 41; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_MULTICAST = 42; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_LOOPBACK = 43; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_IHL_INVALID = 44; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_INVALID_CHECKSUM = 45; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_CLASS_E = 46; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_DST_LINK_LOCAL = 47; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_LINK_LOCAL = 48; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_DST_UNSPECIFIED = 49; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_SRC_UNSPECIFIED = 50; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_LPM4_MISS = 51; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_LPM6_MISS = 52; +const switch_drop_reason_t SWITCH_DROP_REASON_IP_BLACKHOLE_ROUTE = 53; +const switch_drop_reason_t SWITCH_DROP_REASON_L3_PORT_RMAC_MISS = 54; +const switch_drop_reason_t SWITCH_DROP_REASON_PORT_VLAN_MAPPING_MISS = 55; +const switch_drop_reason_t SWITCH_DROP_REASON_STP_STATE_LEARNING = 56; +const switch_drop_reason_t SWITCH_DROP_REASON_STP_STATE_BLOCKING = 57; +const switch_drop_reason_t SWITCH_DROP_REASON_SAME_IFINDEX = 58; +const switch_drop_reason_t SWITCH_DROP_REASON_MULTICAST_SNOOPING_ENABLED = 59; +const switch_drop_reason_t SWITCH_DROP_REASON_MTU_CHECK_FAIL = 70; +const switch_drop_reason_t SWITCH_DROP_REASON_TRAFFIC_MANAGER = 71; +const switch_drop_reason_t SWITCH_DROP_REASON_STORM_CONTROL = 72; +const switch_drop_reason_t SWITCH_DROP_REASON_WRED = 73; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_PORT_METER = 75; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_ACL_METER = 76; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_PORT_METER = 77; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_ACL_METER = 78; +const switch_drop_reason_t SWITCH_DROP_REASON_ACL_DENY = 80; +const switch_drop_reason_t SWITCH_DROP_REASON_RACL_DENY = 81; +const switch_drop_reason_t SWITCH_DROP_REASON_URPF_CHECK_FAIL = 82; +const switch_drop_reason_t SWITCH_DROP_REASON_IPSG_MISS = 83; +const switch_drop_reason_t SWITCH_DROP_REASON_IFINDEX = 84; +const switch_drop_reason_t SWITCH_DROP_REASON_CPU_COLOR_YELLOW = 85; +const switch_drop_reason_t SWITCH_DROP_REASON_CPU_COLOR_RED = 86; +const switch_drop_reason_t SWITCH_DROP_REASON_STORM_CONTROL_COLOR_YELLOW = 87; +const switch_drop_reason_t SWITCH_DROP_REASON_STORM_CONTROL_COLOR_RED = 88; +const switch_drop_reason_t SWITCH_DROP_REASON_L2_MISS_UNICAST = 89; +const switch_drop_reason_t SWITCH_DROP_REASON_L2_MISS_MULTICAST = 90; +const switch_drop_reason_t SWITCH_DROP_REASON_L2_MISS_BROADCAST = 91; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_ACL_DENY = 92; +const switch_drop_reason_t SWITCH_DROP_REASON_NEXTHOP = 93; +const switch_drop_reason_t SWITCH_DROP_REASON_NON_IP_ROUTER_MAC = 94; +const switch_drop_reason_t SWITCH_DROP_REASON_MLAG_MEMBER = 95; +const switch_drop_reason_t SWITCH_DROP_REASON_L3_IPV4_DISABLE = 99; +const switch_drop_reason_t SWITCH_DROP_REASON_L3_IPV6_DISABLE = 100; +const switch_drop_reason_t SWITCH_DROP_REASON_INGRESS_PFC_WD_DROP = 101; +const switch_drop_reason_t SWITCH_DROP_REASON_EGRESS_PFC_WD_DROP = 102; +const switch_drop_reason_t SWITCH_DROP_REASON_MPLS_LABEL_DROP = 103; +const switch_drop_reason_t SWITCH_DROP_REASON_SRV6_MY_SID_DROP = 104; +const switch_drop_reason_t SWITCH_DROP_REASON_PORT_ISOLATION_DROP = 105; +const switch_drop_reason_t SWITCH_DROP_REASON_DMAC_RESERVED = 106; +const switch_drop_reason_t SWITCH_DROP_REASON_NON_ROUTABLE = 107; +const switch_drop_reason_t SWITCH_DROP_REASON_MPLS_DISABLE = 108; +const switch_drop_reason_t SWITCH_DROP_REASON_BFD = 109; +typedef bit<1> switch_port_type_t; +const switch_port_type_t SWITCH_PORT_TYPE_NORMAL = 0; +const switch_port_type_t SWITCH_PORT_TYPE_CPU = 1; +typedef bit<2> switch_ip_type_t; +const switch_ip_type_t SWITCH_IP_TYPE_NONE = 0; +const switch_ip_type_t SWITCH_IP_TYPE_IPV4 = 1; +const switch_ip_type_t SWITCH_IP_TYPE_IPV6 = 2; +const switch_ip_type_t SWITCH_IP_TYPE_MPLS = 3; +typedef bit<2> switch_ip_frag_t; +const switch_ip_frag_t SWITCH_IP_FRAG_NON_FRAG = 0b0; +const switch_ip_frag_t SWITCH_IP_FRAG_HEAD = 0b10; +const switch_ip_frag_t SWITCH_IP_FRAG_NON_HEAD = 0b11; +typedef bit<2> switch_packet_action_t; +const switch_packet_action_t SWITCH_PACKET_ACTION_PERMIT = 0b0; +const switch_packet_action_t SWITCH_PACKET_ACTION_DROP = 0b1; +const switch_packet_action_t SWITCH_PACKET_ACTION_COPY = 0b10; +const switch_packet_action_t SWITCH_PACKET_ACTION_TRAP = 0b11; +typedef bit<16> switch_ingress_bypass_t; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_L2 = 16w0x1 << 0; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_L3 = 16w0x1 << 1; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_ACL = 16w0x1 << 2; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_SYSTEM_ACL = 16w0x1 << 3; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_QOS = 16w0x1 << 4; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_METER = 16w0x1 << 5; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_STORM_CONTROL = 16w0x1 << 6; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_STP = 16w0x1 << 7; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_SMAC = 16w0x1 << 8; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_NAT = 16w0x1 << 9; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_ROUTING_CHECK = 16w0x1 << 10; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_PV = 16w0x1 << 11; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_BFD_TX = 16w0x1 << 15; +const switch_ingress_bypass_t SWITCH_INGRESS_BYPASS_ALL = 16w0x7fff; +typedef bit<16> switch_pkt_length_t; +typedef bit<8> switch_pkt_src_t; +const switch_pkt_src_t SWITCH_PKT_SRC_BRIDGED = 0; +const switch_pkt_src_t SWITCH_PKT_SRC_CLONED_INGRESS = 1; +const switch_pkt_src_t SWITCH_PKT_SRC_CLONED_EGRESS = 2; +const switch_pkt_src_t SWITCH_PKT_SRC_DEFLECTED = 3; +const switch_pkt_src_t SWITCH_PKT_SRC_CLONED_EGRESS_IN_PKT = 4; +typedef bit<2> switch_pkt_color_t; +const switch_pkt_color_t SWITCH_METER_COLOR_GREEN = 0; +const switch_pkt_color_t SWITCH_METER_COLOR_YELLOW = 1; +const switch_pkt_color_t SWITCH_METER_COLOR_RED = 3; +typedef bit<2> switch_pkt_type_t; +const switch_pkt_type_t SWITCH_PKT_TYPE_UNICAST = 0; +const switch_pkt_type_t SWITCH_PKT_TYPE_MULTICAST = 1; +const switch_pkt_type_t SWITCH_PKT_TYPE_BROADCAST = 2; +typedef bit<8> switch_l4_port_label_t; +typedef bit<2> switch_stp_state_t; +const switch_stp_state_t SWITCH_STP_STATE_FORWARDING = 0; +const switch_stp_state_t SWITCH_STP_STATE_BLOCKING = 1; +const switch_stp_state_t SWITCH_STP_STATE_LEARNING = 2; +typedef bit<10> switch_stp_group_t; +struct switch_stp_metadata_t { + switch_stp_group_t group; + switch_stp_state_t state_; +} + +typedef bit<2> switch_nexthop_type_t; +const switch_nexthop_type_t SWITCH_NEXTHOP_TYPE_IP = 0; +const switch_nexthop_type_t SWITCH_NEXTHOP_TYPE_MPLS = 1; +const switch_nexthop_type_t SWITCH_NEXTHOP_TYPE_TUNNEL_ENCAP = 2; +typedef bit<8> switch_sflow_id_t; +const switch_sflow_id_t SWITCH_SFLOW_INVALID_ID = 8w0xff; +struct switch_sflow_metadata_t { + switch_sflow_id_t session_id; + bit<1> sample_packet; +} + +typedef bit<8> switch_hostif_trap_t; +typedef bit<8> switch_copp_meter_id_t; +typedef bit<10> switch_meter_index_t; +typedef bit<8> switch_mirror_meter_id_t; +typedef bit<2> switch_qos_trust_mode_t; +const switch_qos_trust_mode_t SWITCH_QOS_TRUST_MODE_UNTRUSTED = 0; +const switch_qos_trust_mode_t SWITCH_QOS_TRUST_MODE_TRUST_DSCP = 1; +const switch_qos_trust_mode_t SWITCH_QOS_TRUST_MODE_TRUST_PCP = 2; +typedef bit<5> switch_qos_group_t; +typedef bit<8> switch_tc_t; +typedef bit<3> switch_cos_t; +typedef bit<11> switch_etrap_index_t; +typedef bit<2> switch_myip_type_t; +const switch_myip_type_t SWITCH_MYIP_NONE = 0; +const switch_myip_type_t SWITCH_MYIP = 1; +const switch_myip_type_t SWITCH_MYIP_SUBNET = 2; +struct switch_qos_metadata_t { + switch_qos_trust_mode_t trust_mode; + switch_qos_group_t group; + switch_tc_t tc; + switch_pkt_color_t color; + switch_pkt_color_t storm_control_color; + switch_meter_index_t port_meter_index; + switch_meter_index_t acl_meter_index; + switch_qid_t qid; + switch_ingress_cos_t icos; + bit<19> qdepth; + switch_etrap_index_t etrap_index; + switch_pkt_color_t etrap_color; + switch_tc_t etrap_tc; + bit<1> etrap_state; + bit<2> outer_ecn; +} + +typedef bit<1> switch_learning_mode_t; +const switch_learning_mode_t SWITCH_LEARNING_MODE_DISABLED = 0; +const switch_learning_mode_t SWITCH_LEARNING_MODE_LEARN = 1; +struct switch_learning_digest_t { + switch_bd_t bd; + switch_port_lag_index_t port_lag_index; + mac_addr_t src_addr; +} + +struct switch_learning_metadata_t { + switch_learning_mode_t bd_mode; + switch_learning_mode_t port_mode; + switch_learning_digest_t digest; +} + +typedef bit<2> switch_multicast_mode_t; +const switch_multicast_mode_t SWITCH_MULTICAST_MODE_NONE = 0; +const switch_multicast_mode_t SWITCH_MULTICAST_MODE_PIM_SM = 1; +const switch_multicast_mode_t SWITCH_MULTICAST_MODE_PIM_BIDIR = 2; +typedef MulticastGroupId_t switch_mgid_t; +typedef bit<16> switch_multicast_rpf_group_t; +struct switch_multicast_metadata_t { + switch_mgid_t id; + bit<2> mode; + switch_multicast_rpf_group_t rpf_group; + bit<1> hit; +} + +typedef bit<2> switch_urpf_mode_t; +const switch_urpf_mode_t SWITCH_URPF_MODE_NONE = 0; +const switch_urpf_mode_t SWITCH_URPF_MODE_LOOSE = 1; +const switch_urpf_mode_t SWITCH_URPF_MODE_STRICT = 2; +typedef bit<10> switch_wred_index_t; +typedef bit<2> switch_ecn_codepoint_t; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_NON_ECT = 0b0; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_ECT0 = 0b10; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_ECT1 = 0b1; +const switch_ecn_codepoint_t SWITCH_ECN_CODEPOINT_CE = 0b11; +const switch_ecn_codepoint_t NON_ECT = 0b0; +const switch_ecn_codepoint_t ECT0 = 0b10; +const switch_ecn_codepoint_t ECT1 = 0b1; +const switch_ecn_codepoint_t CE = 0b11; +typedef MirrorId_t switch_mirror_session_t; +const switch_mirror_session_t SWITCH_MIRROR_SESSION_CPU = 250; +typedef bit<8> switch_mirror_type_t; +struct switch_mirror_metadata_t { + switch_pkt_src_t src; + switch_mirror_type_t type; + switch_mirror_session_t session_id; + switch_mirror_meter_id_t meter_index; +} + +header switch_port_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<32> timestamp; + switch_mirror_session_t session_id; +} + +header switch_cpu_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + switch_port_padding_t _pad1; + switch_port_t port; + switch_bd_t bd; + bit<6> _pad2; + switch_port_lag_index_t port_lag_index; + switch_cpu_reason_t reason_code; +} + +typedef bit<1> switch_tunnel_mode_t; +const switch_tunnel_mode_t SWITCH_TUNNEL_MODE_PIPE = 0; +const switch_tunnel_mode_t SWITCH_TUNNEL_MODE_UNIFORM = 1; +const switch_tunnel_mode_t SWITCH_ECN_MODE_STANDARD = 0; +const switch_tunnel_mode_t SWITCH_ECN_MODE_COPY_FROM_OUTER = 1; +typedef bit<4> switch_tunnel_type_t; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_NONE = 0; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_VXLAN = 1; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_IPINIP = 2; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_GRE = 3; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_NVGRE = 4; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_MPLS = 5; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_SRV6 = 6; +const switch_tunnel_type_t SWITCH_INGRESS_TUNNEL_TYPE_NVGRE_ST = 7; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_NONE = 0; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_VXLAN = 1; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_VXLAN = 2; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_IPINIP = 3; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_IPINIP = 4; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_NVGRE = 5; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_NVGRE = 6; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_MPLS = 7; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_SRV6_ENCAP = 8; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_SRV6_INSERT = 9; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV4_GRE = 10; +const switch_tunnel_type_t SWITCH_EGRESS_TUNNEL_TYPE_IPV6_GRE = 11; +enum switch_tunnel_term_mode_t { + P2P, + P2MP +} + +typedef bit<8> switch_tunnel_index_t; +typedef bit<13> switch_tunnel_ip_index_t; +typedef bit<16> switch_tunnel_nexthop_t; +typedef bit<24> switch_tunnel_vni_t; +struct switch_tunnel_metadata_t { + switch_tunnel_type_t type; + switch_tunnel_mode_t ecn_mode; + switch_tunnel_index_t index; + switch_tunnel_ip_index_t dip_index; + switch_tunnel_vni_t vni; + switch_tunnel_mode_t qos_mode; + switch_tunnel_mode_t ttl_mode; + bit<8> decap_ttl; + bit<8> decap_tos; + bit<3> decap_exp; + bit<16> hash; + bool terminate; + bit<8> nvgre_flow_id; + bit<2> mpls_pop_count; + bit<3> mpls_push_count; + bit<8> mpls_encap_ttl; + bit<3> mpls_encap_exp; + bit<1> mpls_swap; + bit<128> srh_next_sid; + bit<8> srh_next_hdr; + bit<3> srv6_seg_len; + bit<6> srh_hdr_len; + bool remove_srh; + bool pop_active_segment; + bool srh_decap_forward; +} + +struct switch_nvgre_value_set_t { + bit<32> vsid_flowid; +} + +typedef bit<8> switch_dtel_report_type_t; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_NONE = 0b0; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_DROP = 0b100; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_QUEUE = 0b10; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_FLOW = 0b1; +const switch_dtel_report_type_t SWITCH_DTEL_SUPPRESS_REPORT = 0b1000; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_IFA_CLONE = 0b10000; +const switch_dtel_report_type_t SWITCH_DTEL_IFA_EDGE = 0b100000; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_ETRAP_CHANGE = 0b1000000; +const switch_dtel_report_type_t SWITCH_DTEL_REPORT_TYPE_ETRAP_HIT = 0b10000000; +typedef bit<8> switch_ifa_sample_id_t; +typedef bit<6> switch_dtel_hw_id_t; +typedef bit<32> switch_dtel_switch_id_t; +const bit<16> DTEL_REPORT_V0_5_OUTER_HEADERS_LENGTH = 46; +const bit<16> DTEL_REPORT_V2_OUTER_HEADERS_LENGTH = 58; +struct switch_dtel_metadata_t { + switch_dtel_report_type_t report_type; + bit<1> ifa_gen_clone; + bit<1> ifa_cloned; + bit<32> latency; + switch_mirror_session_t session_id; + switch_mirror_session_t clone_session_id; + bit<32> hash; + bit<2> drop_report_flag; + bit<2> flow_report_flag; + bit<1> queue_report_flag; +} + +typedef bit<4> switch_ingress_nat_hit_type_t; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_NONE = 0; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_FLOW_NONE = 1; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_FLOW_NAPT = 2; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_FLOW_NAT = 3; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_DEST_NONE = 4; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_DEST_NAPT = 5; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_DEST_NAT = 6; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_SRC_NONE = 7; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_SRC_NAPT = 8; +const switch_ingress_nat_hit_type_t SWITCH_NAT_HIT_TYPE_SRC_NAT = 9; +typedef bit<1> switch_nat_zone_t; +const switch_nat_zone_t SWITCH_NAT_INSIDE_ZONE_ID = 0; +const switch_nat_zone_t SWITCH_NAT_OUTSIDE_ZONE_ID = 1; +struct switch_nat_ingress_metadata_t { + switch_ingress_nat_hit_type_t hit; + switch_nat_zone_t ingress_zone; + bit<16> dnapt_index; + bit<16> snapt_index; + bool nat_disable; + bool dnat_pool_hit; +} + +header switch_dtel_switch_local_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<32> timestamp; + switch_mirror_session_t session_id; + bit<32> hash; + switch_dtel_report_type_t report_type; + switch_port_padding_t _pad2; + switch_port_t ingress_port; + switch_port_padding_t _pad3; + switch_port_t egress_port; + bit<1> _pad4; + switch_qid_t qid; + bit<5> _pad5; + bit<19> qdepth; + bit<32> egress_timestamp; +} + +header switch_dtel_drop_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + bit<32> timestamp; + switch_mirror_session_t session_id; + bit<32> hash; + switch_dtel_report_type_t report_type; + switch_port_padding_t _pad2; + switch_port_t ingress_port; + switch_port_padding_t _pad3; + switch_port_t egress_port; + bit<1> _pad4; + switch_qid_t qid; + switch_drop_reason_t drop_reason; +} + +header switch_simple_mirror_metadata_h { + switch_pkt_src_t src; + switch_mirror_type_t type; + switch_mirror_session_t session_id; +} + +@flexible struct switch_bridged_metadata_dtel_extension_t { + switch_dtel_report_type_t report_type; + switch_mirror_session_t session_id; + bit<32> hash; + switch_port_t egress_port; +} + +enum bit<2> LinkToType { + Unknown = 0, + Switch = 1, + Server = 2 +} + +const bit<32> msb_set_32b = 32w0x80000000; +@pa_container_size("ingress" , "local_md.checks.same_if" , 16) @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_src_addr") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_dst_addr") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ipv6_flow_label") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_proto") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_ttl") @pa_mutually_exclusive("ingress" , "lkp.arp_opcode" , "lkp.ip_tos") struct switch_flags_t { + bool ipv4_checksum_err; + bool inner_ipv4_checksum_err; + bool inner2_ipv4_checksum_err; + bool link_local; + bool routed; + bool acl_deny; + bool racl_deny; + bool port_vlan_miss; + bool rmac_hit; + bool dmac_miss; + switch_myip_type_t myip; + bool glean; + bool storm_control_drop; + bool port_meter_drop; + bool flood_to_multicast_routers; + bool peer_link; + bool capture_ts; + bool mac_pkt_class; + bool pfc_wd_drop; + bool bypass_egress; + bool mpls_trap; + bool srv6_trap; + bool mlag_member; + bool wred_drop; + bool port_isolation_packet_drop; + bool bport_isolation_packet_drop; + bool fib_lpm_miss; + bool fib_drop; + switch_packet_action_t meter_packet_action; + switch_packet_action_t vrf_ttl_violation; + bool vrf_ttl_violation_valid; + bool vlan_arp_suppress; + switch_packet_action_t vrf_ip_options_violation; + bool vrf_unknown_l3_multicast_trap; + bool bfd_to_cpu; + bool redirect_to_cpu; + bool copy_cancel; +} + +struct switch_checks_t { + switch_port_lag_index_t same_if; + bool mrpf; + bool urpf; + switch_nat_zone_t same_zone_check; + switch_bd_t same_bd; + switch_mtu_t mtu; + bool stp; +} + +struct switch_ip_metadata_t { + bool unicast_enable; + bool multicast_enable; + bool multicast_snooping; +} + +struct switch_lookup_fields_t { + switch_pkt_type_t pkt_type; + mac_addr_t mac_dst_addr; + bit<16> mac_type; + bit<3> pcp; + bit<1> dei; + bit<16> arp_opcode; + switch_ip_type_t ip_type; + bit<8> ip_proto; + bit<8> ip_ttl; + bit<8> ip_tos; + switch_ip_frag_t ip_frag; + bit<128> ip_src_addr; + bit<128> ip_dst_addr; + bit<20> ipv6_flow_label; + bit<8> tcp_flags; + bit<16> l4_src_port; + bit<16> l4_dst_port; + bit<16> hash_l4_src_port; + bit<16> hash_l4_dst_port; + bool mpls_pkt; + bit<1> mpls_router_alert_label; + bit<20> mpls_lookup_label; +} + +struct switch_hash_fields_t { + mac_addr_t mac_src_addr; + mac_addr_t mac_dst_addr; + bit<16> mac_type; + switch_ip_type_t ip_type; + bit<8> ip_proto; + bit<128> ip_src_addr; + bit<128> ip_dst_addr; + bit<16> l4_src_port; + bit<16> l4_dst_port; + bit<20> ipv6_flow_label; + bit<32> outer_ip_src_addr; + bit<32> outer_ip_dst_addr; +} + +@flexible struct switch_bridged_metadata_t { + switch_port_t ingress_port; + switch_port_lag_index_t ingress_port_lag_index; + switch_bd_t ingress_bd; + switch_nexthop_t nexthop; + switch_pkt_type_t pkt_type; + bool routed; + bool bypass_egress; + switch_cpu_reason_t cpu_reason; + bit<32> timestamp; + switch_tc_t tc; + switch_qid_t qid; + switch_pkt_color_t color; + switch_vrf_t vrf; +} + +@flexible struct switch_bridged_metadata_acl_extension_t { + bit<16> l4_src_port; + bit<16> l4_dst_port; + bit<8> tcp_flags; +} + +@flexible struct switch_bridged_metadata_tunnel_extension_t { + switch_tunnel_nexthop_t tunnel_nexthop; + switch_tunnel_mode_t ttl_mode; + switch_tunnel_mode_t qos_mode; + bool terminate; +} + +@pa_atomic("ingress" , "hdr.bridged_md.base_qid") @pa_container_size("ingress" , "hdr.bridged_md.base_qid" , 8) @pa_container_size("ingress" , "hdr.bridged_md.dtel_report_type" , 8) @pa_no_overlay("ingress" , "hdr.bridged_md.base_qid") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_0") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_1") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_2") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_3") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_4") @pa_no_overlay("ingress" , "hdr.bridged_md.__pad_5") @pa_no_overlay("egress" , "hdr.dtel_report.ingress_port") @pa_no_overlay("egress" , "hdr.dtel_report.egress_port") @pa_no_overlay("egress" , "hdr.dtel_report.queue_id") @pa_no_overlay("egress" , "hdr.dtel_drop_report.drop_reason") @pa_no_overlay("egress" , "hdr.dtel_drop_report.reserved") @pa_no_overlay("egress" , "hdr.dtel_switch_local_report.queue_occupancy") @pa_no_overlay("egress" , "hdr.ipv4.ttl" , "hdr.ipv6.traffic_class") @pa_no_overlay("egress" , "hdr.ipv6.hop_limit" , "hdr.ipv4.diffserv") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.vxlan") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.vxlan") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.vxlan") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.vxlan") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.erspan_type2") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.udp") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel_switch_local_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel_switch_local_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel_switch_local_report") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.erspan" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type2" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.erspan_type3" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.gre" , "hdr.dtel_report") @pa_mutually_exclusive("egress" , "hdr.vxlan" , "hdr.dtel") @pa_mutually_exclusive("egress" , "hdr.vxlan" , "hdr.dtel_drop_report") @pa_mutually_exclusive("egress" , "hdr.vxlan" , "hdr.dtel_switch_locael_report") @pa_mutually_exclusive("egress" , "hdr.vxlan" , "hdr.dtel_report") @flexible header switch_fp_bridged_metadata_h { + bool ipv4_checksum_err; + bool link_local; + bool port_vlan_miss; + bool rmac_hit; + bool dmac_miss; + bool fib_drop; + bool fib_lpm_miss; + bit<1> multicast_hit; + bool acl_deny; + bool nat_disable; + switch_myip_type_t myip; + switch_port_lag_index_t egress_port_lag_index; + switch_fib_label_t fib_label; + switch_mgid_t multicast_id; + switch_ingress_bypass_t bypass; + switch_drop_reason_t l2_drop_reason; + switch_drop_reason_t drop_reason; + switch_stp_state_t stp_state; + bool vlan_arp_suppress; +} + +@flexible header switch_fp2_bridged_metadata_h { + switch_hostif_trap_t hostif_trap_id; + switch_pkt_src_t mirror_src; + switch_mirror_session_t mirror_session_id; + switch_mirror_meter_id_t mirror_meter_index; + switch_dtel_report_type_t dtel_report_type; + switch_hash_t hash; + switch_hash_t lag_hash; + switch_mirror_type_t mirror_type; + bit<6> mirror_type_pad; + switch_packet_action_t meter_packet_action; + bool nat_disable; +} + +typedef bit<8> switch_bridge_type_t; +header switch_bridged_metadata_h { + switch_pkt_src_t src; + switch_bridge_type_t type; + switch_bridged_metadata_t base; + switch_bridged_metadata_acl_extension_t acl; + switch_bridged_metadata_tunnel_extension_t tunnel; + switch_bridged_metadata_dtel_extension_t dtel; +} + +struct switch_port_metadata_t { + switch_port_lag_index_t port_lag_index; + switch_ig_port_lag_label_t port_lag_label; +} + +struct switch_fp_port_metadata_t { + bit<1> unused; +} + +typedef bit<2> switch_cons_hash_ip_seq_t; +const switch_cons_hash_ip_seq_t SWITCH_CONS_HASH_IP_SEQ_NONE = 0; +const switch_cons_hash_ip_seq_t SWITCH_CONS_HASH_IP_SEQ_SIPDIP = 1; +const switch_cons_hash_ip_seq_t SWITCH_CONS_HASH_IP_SEQ_DIPSIP = 2; +@pa_auto_init_metadata @pa_container_size("ingress" , "local_md.mirror.src" , 8) @pa_container_size("ingress" , "local_md.mirror.type" , 8) @pa_container_size("ingress" , "smac_src_move" , 16) @pa_alias("ingress" , "local_md.egress_port" , "ig_intr_md_for_tm.ucast_egress_port") @pa_alias("ingress" , "local_md.multicast.id" , "ig_intr_md_for_tm.mcast_grp_b") @pa_alias("ingress" , "local_md.qos.qid" , "ig_intr_md_for_tm.qid") @pa_alias("ingress" , "local_md.qos.icos" , "ig_intr_md_for_tm.ingress_cos") @pa_alias("ingress" , "ig_intr_md_for_dprsr.mirror_type" , "local_md.mirror.type") @pa_container_size("ingress" , "local_md.egress_port_lag_index" , 16) @pa_container_size("egress" , "local_md.mirror.src" , 8) @pa_container_size("egress" , "local_md.mirror.type" , 8) @pa_container_size("egress" , "hdr.dtel_drop_report.drop_reason" , 8) struct switch_local_metadata_t { + switch_port_t ingress_port; + switch_port_t egress_port; + switch_port_lag_index_t ingress_port_lag_index; + switch_port_lag_index_t egress_port_lag_index; + switch_bd_t bd; + switch_bd_t ingress_outer_bd; + switch_vrf_t vrf; + switch_nexthop_t nexthop; + switch_tunnel_nexthop_t tunnel_nexthop; + switch_nexthop_t acl_nexthop; + bool acl_port_redirect; + switch_nexthop_t unused_nexthop; + bit<32> timestamp; + switch_hash_t hash; + switch_hash_t lag_hash; + switch_flags_t flags; + switch_checks_t checks; + switch_ingress_bypass_t bypass; + switch_ip_metadata_t ipv4; + switch_ip_metadata_t ipv6; + switch_ig_port_lag_label_t ingress_port_lag_label; + switch_bd_label_t bd_label; + switch_l4_port_label_t l4_src_port_label; + switch_l4_port_label_t l4_dst_port_label; + switch_drop_reason_t l2_drop_reason; + switch_drop_reason_t drop_reason; + switch_cpu_reason_t cpu_reason; + switch_lookup_fields_t lkp; + switch_hostif_trap_t hostif_trap_id; + switch_hash_fields_t hash_fields; + switch_multicast_metadata_t multicast; + switch_stp_metadata_t stp; + switch_qos_metadata_t qos; + switch_sflow_metadata_t sflow; + switch_tunnel_metadata_t tunnel; + switch_learning_metadata_t learning; + switch_mirror_metadata_t mirror; + switch_dtel_metadata_t dtel; + mac_addr_t same_mac; + switch_user_metadata_t user_metadata; + bit<10> partition_key; + bit<12> partition_index; + switch_fib_label_t fib_label; + switch_cons_hash_ip_seq_t cons_hash_v6_ip_seq; + switch_pkt_src_t pkt_src; + switch_pkt_length_t pkt_length; + bit<32> egress_timestamp; + bit<32> ingress_timestamp; + switch_eg_port_lag_label_t egress_port_lag_label; + switch_nexthop_type_t nexthop_type; + bool inner_ipv4_checksum_update_en; +} + +struct switch_mirror_metadata_h { + switch_port_mirror_metadata_h port; + switch_cpu_mirror_metadata_h cpu; + switch_dtel_drop_mirror_metadata_h dtel_drop; + switch_dtel_switch_local_mirror_metadata_h dtel_switch_local; + switch_simple_mirror_metadata_h simple_mirror; +} + +struct switch_header_t { + switch_bridged_metadata_h bridged_md; + switch_fp_bridged_metadata_h fp_bridged_md; + switch_fp2_bridged_metadata_h fp2_bridged_md; + ethernet_h ethernet; + fabric_h fabric; + cpu_h cpu; + timestamp_h timestamp; + vlan_tag_h[2] vlan_tag; + mpls_h[3] mpls; + ipv4_h ipv4; + ipv4_option_h ipv4_option; + ipv6_h ipv6; + arp_h arp; + ipv6_srh_h srh_base; + ipv6_segment_h[2] srh_seg_list; + udp_h udp; + icmp_h icmp; + igmp_h igmp; + tcp_h tcp; + dtel_report_v05_h dtel; + dtel_report_base_h dtel_report; + dtel_switch_local_report_h dtel_switch_local_report; + dtel_drop_report_h dtel_drop_report; + rocev2_bth_h rocev2_bth; + gtpu_h gtp; + vxlan_h vxlan; + gre_h gre; + nvgre_h nvgre; + geneve_h geneve; + erspan_h erspan; + erspan_type2_h erspan_type2; + erspan_type3_h erspan_type3; + erspan_platform_h erspan_platform; + ethernet_h inner_ethernet; + ipv4_h inner_ipv4; + ipv6_h inner_ipv6; + udp_h inner_udp; + tcp_h inner_tcp; + icmp_h inner_icmp; + igmp_h inner_igmp; + ipv4_h inner2_ipv4; + ipv6_h inner2_ipv6; + udp_h inner2_udp; + tcp_h inner2_tcp; + icmp_h inner2_icmp; + pad_h pad; +} + +action add_bridged_md(inout switch_bridged_metadata_h bridged_md, in switch_local_metadata_t local_md) { + bridged_md.setValid(); + bridged_md.src = SWITCH_PKT_SRC_BRIDGED; + // Why was the type missing here? + bridged_md.type = 0; + bridged_md.base.ingress_port = local_md.ingress_port; + bridged_md.base.ingress_port_lag_index = local_md.ingress_port_lag_index; + bridged_md.base.ingress_bd = local_md.bd; + bridged_md.base.nexthop = local_md.nexthop; + bridged_md.base.pkt_type = local_md.lkp.pkt_type; + bridged_md.base.routed = local_md.flags.routed; + bridged_md.base.bypass_egress = local_md.flags.bypass_egress; + bridged_md.base.cpu_reason = local_md.cpu_reason; + bridged_md.base.timestamp = local_md.timestamp; + bridged_md.base.tc = local_md.qos.tc; + bridged_md.base.qid = local_md.qos.qid; + bridged_md.base.color = local_md.qos.color; + bridged_md.base.vrf = local_md.vrf; + bridged_md.acl.l4_src_port = local_md.lkp.l4_src_port; + bridged_md.acl.l4_dst_port = local_md.lkp.l4_dst_port; + bridged_md.acl.tcp_flags = local_md.lkp.tcp_flags; + bridged_md.tunnel.tunnel_nexthop = local_md.tunnel_nexthop; + bridged_md.tunnel.ttl_mode = local_md.tunnel.ttl_mode; + bridged_md.tunnel.qos_mode = local_md.tunnel.qos_mode; + bridged_md.tunnel.terminate = local_md.tunnel.terminate; + bridged_md.dtel.report_type = local_md.dtel.report_type; + bridged_md.dtel.session_id = local_md.dtel.session_id; + bridged_md.dtel.hash = local_md.lag_hash; + bridged_md.dtel.egress_port = local_md.egress_port; +} +action set_ig_intr_md(in switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm) { + ig_intr_md_for_tm.mcast_grp_b = local_md.multicast.id; + ig_intr_md_for_tm.level2_mcast_hash = local_md.lag_hash[28:16]; + ig_intr_md_for_tm.rid = local_md.bd; + ig_intr_md_for_tm.qid = local_md.qos.qid; + ig_intr_md_for_tm.ingress_cos = local_md.qos.icos; +} +control SetEgIntrMd(inout switch_header_t hdr, in switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, inout egress_intrinsic_metadata_for_output_port_t eg_intr_md_for_oport) { + apply { + if (local_md.mirror.type != 0) { + eg_intr_md_for_dprsr.mirror_type = (bit<4>)local_md.mirror.type; + if (local_md.mirror.src == SWITCH_PKT_SRC_CLONED_EGRESS_IN_PKT) { + eg_intr_md_for_dprsr.mirror_io_select = 0; + } else { + eg_intr_md_for_dprsr.mirror_io_select = 1; + } + } + } +} + +control EnableFragHash(inout switch_lookup_fields_t lkp) { + apply { + if (lkp.ip_frag != SWITCH_IP_FRAG_NON_FRAG) { + lkp.hash_l4_dst_port = 0; + lkp.hash_l4_src_port = 0; + } else { + lkp.hash_l4_dst_port = lkp.l4_dst_port; + lkp.hash_l4_src_port = lkp.l4_src_port; + } + } +} + +control Ipv4Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".ipv4_hash") Hash>(HashAlgorithm_t.CRC32) ipv4_hash; + bit<32> ip_src_addr = lkp.ip_src_addr[95:64]; + bit<32> ip_dst_addr = lkp.ip_dst_addr[95:64]; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + apply { + hash[31:0] = ipv4_hash.get({ ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control Ipv6Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".ipv6_hash") Hash>(HashAlgorithm_t.CRC32) ipv6_hash; + bit<128> ip_src_addr = lkp.ip_src_addr; + bit<128> ip_dst_addr = lkp.ip_dst_addr; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + bit<20> ipv6_flow_label = lkp.ipv6_flow_label; + apply { + hash[31:0] = ipv6_hash.get({ ipv6_flow_label, ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control NonIpHash(in switch_header_t hdr, in switch_local_metadata_t local_md, out switch_hash_t hash) { + @name(".non_ip_hash") Hash>(HashAlgorithm_t.CRC32) non_ip_hash; + mac_addr_t mac_dst_addr = hdr.ethernet.dst_addr; + mac_addr_t mac_src_addr = hdr.ethernet.src_addr; + bit<16> mac_type = hdr.ethernet.ether_type; + switch_port_t port = local_md.ingress_port; + apply { + hash[31:0] = non_ip_hash.get({ port, mac_type, mac_src_addr, mac_dst_addr }); + } +} + +control Lagv4Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".lag_v4_hash") Hash>(HashAlgorithm_t.CRC32) lag_v4_hash; + bit<32> ip_src_addr = lkp.ip_src_addr[95:64]; + bit<32> ip_dst_addr = lkp.ip_dst_addr[95:64]; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + apply { + hash[31:0] = lag_v4_hash.get({ ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control Lagv6Hash(in switch_lookup_fields_t lkp, out switch_hash_t hash) { + @name(".lag_v6_hash") Hash>(HashAlgorithm_t.CRC32) lag_v6_hash; + bit<128> ip_src_addr = lkp.ip_src_addr; + bit<128> ip_dst_addr = lkp.ip_dst_addr; + bit<8> ip_proto = lkp.ip_proto; + bit<16> l4_dst_port = lkp.hash_l4_dst_port; + bit<16> l4_src_port = lkp.hash_l4_src_port; + bit<20> ipv6_flow_label = lkp.ipv6_flow_label; + apply { + hash[31:0] = lag_v6_hash.get({ ipv6_flow_label, ip_src_addr, ip_dst_addr, ip_proto, l4_dst_port, l4_src_port }); + } +} + +control V4ConsistentHash(in bit<32> sip, in bit<32> dip, in bit<16> low_l4_port, in bit<16> high_l4_port, in bit<8> protocol, inout switch_hash_t hash) { + Hash>(HashAlgorithm_t.CRC32) ipv4_inner_hash; + bit<32> high_ip; + bit<32> low_ip; + action step_v4() { + high_ip = max(sip, dip); + low_ip = min(sip, dip); + } + action v4_calc_hash() { + hash[31:0] = ipv4_inner_hash.get({ low_ip, high_ip, protocol, low_l4_port, high_l4_port }); + } + apply { + step_v4(); + v4_calc_hash(); + } +} + +control V6ConsistentHash64bIpSeq(in bit<64> sip, in bit<64> dip, inout switch_cons_hash_ip_seq_t ip_seq) { + bit<32> high_63_32_ip; + bit<32> src_63_32_ip; + bit<32> high_31_0_ip; + bit<32> src_31_0_ip; + action step_63_32_v6() { + high_63_32_ip = max(sip[63:32], dip[63:32]); + src_63_32_ip = sip[63:32]; + } + action step_31_0_v6() { + high_31_0_ip = max(sip[31:0], dip[31:0]); + src_31_0_ip = sip[31:0]; + } + apply { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_SIPDIP; + step_63_32_v6(); + step_31_0_v6(); + if (sip[63:32] != dip[63:32]) { + if (high_63_32_ip == src_63_32_ip) { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_DIPSIP; + } + } else if (sip[31:0] != dip[31:0]) { + if (high_31_0_ip == src_31_0_ip) { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_DIPSIP; + } + } else { + ip_seq = SWITCH_CONS_HASH_IP_SEQ_NONE; + } + } +} + +control V6ConsistentHash(in bit<128> sip, in bit<128> dip, in switch_cons_hash_ip_seq_t ip_seq, in bit<16> low_l4_port, in bit<16> high_l4_port, in bit<8> protocol, inout switch_hash_t hash) { + bit<32> high_31_0_ip; + bit<32> low_31_0_ip; + bit<32> src_31_0_ip; + Hash>(HashAlgorithm_t.CRC32) ipv6_inner_hash_1; + Hash>(HashAlgorithm_t.CRC32) ipv6_inner_hash_2; + bit<32> high_63_32_ip; + bit<32> src_63_32_ip; + action step_63_32_v6() { + high_63_32_ip = max(sip[63:32], dip[63:32]); + src_63_32_ip = sip[63:32]; + } + action step_31_0_v6() { + high_31_0_ip = max(sip[31:0], dip[31:0]); + src_31_0_ip = sip[31:0]; + } + action v6_calc_hash_sip_dip() { + hash[31:0] = ipv6_inner_hash_1.get({ sip, dip, protocol, low_l4_port, high_l4_port }); + } + action v6_calc_hash_dip_sip() { + hash[31:0] = ipv6_inner_hash_2.get({ dip, sip, protocol, low_l4_port, high_l4_port }); + } + apply { + if (ip_seq == SWITCH_CONS_HASH_IP_SEQ_SIPDIP) { + v6_calc_hash_sip_dip(); + } else if (ip_seq == SWITCH_CONS_HASH_IP_SEQ_DIPSIP) { + v6_calc_hash_dip_sip(); + } else { + step_63_32_v6(); + step_31_0_v6(); + if (sip[63:32] != dip[63:32]) { + if (high_63_32_ip == src_63_32_ip) { + v6_calc_hash_dip_sip(); + } else { + v6_calc_hash_sip_dip(); + } + } else if (high_31_0_ip == src_31_0_ip) { + v6_calc_hash_dip_sip(); + } else { + v6_calc_hash_sip_dip(); + } + } + } +} + +control StartConsistentInnerHash(in switch_header_t hd, inout switch_local_metadata_t ig_m) { + V6ConsistentHash64bIpSeq() compute_v6_cons_hash_64bit_ipseq; + apply { + if (hd.inner_ipv6.isValid() && ig_m.tunnel.type != SWITCH_INGRESS_TUNNEL_TYPE_NVGRE_ST) { + compute_v6_cons_hash_64bit_ipseq.apply(hd.inner_ipv6.src_addr[127:64], hd.inner_ipv6.dst_addr[127:64], ig_m.cons_hash_v6_ip_seq); + } + } +} + +control ConsistentInnerHash(in switch_header_t hd, inout switch_local_metadata_t ig_m) { + bit<32> high_31_0_ip; + bit<32> low_31_0_ip; + Hash>(HashAlgorithm_t.CRC32) ipv6_inner_hash; + Hash>(HashAlgorithm_t.IDENTITY) l4_tcp_src_p_hash; + Hash>(HashAlgorithm_t.IDENTITY) l4_udp_src_p_hash; + bit<16> l4_src_port; + bit<16> low_l4_port = 0; + bit<16> high_l4_port = 0; + action step_tcp_src_port() { + l4_src_port = l4_tcp_src_p_hash.get({ 16w0 ++ +hd.inner_tcp.src_port }); + } + action step_udp_src_port() { + l4_src_port = l4_udp_src_p_hash.get({ 16w0 ++ +hd.inner_udp.src_port }); + } + action step_tcp_l4_port() { + high_l4_port = (bit<16>)max(l4_src_port, hd.inner_tcp.dst_port); + low_l4_port = (bit<16>)min(l4_src_port, hd.inner_tcp.dst_port); + } + action step_udp_l4_port() { + high_l4_port = (bit<16>)max(l4_src_port, hd.inner_udp.dst_port); + low_l4_port = (bit<16>)min(l4_src_port, hd.inner_udp.dst_port); + } + action step_31_0_v6() { + high_31_0_ip = max(hd.inner_ipv6.src_addr[31:0], hd.inner_ipv6.dst_addr[31:0]); + low_31_0_ip = min(hd.inner_ipv6.src_addr[31:0], hd.inner_ipv6.dst_addr[31:0]); + } + action v6_calc_31_0_hash() { + ig_m.hash[31:0] = ipv6_inner_hash.get({ low_31_0_ip, high_31_0_ip, hd.inner_ipv6.next_hdr, low_l4_port, high_l4_port }); + } + V6ConsistentHash() compute_v6_cons_hash; + V4ConsistentHash() compute_v4_cons_hash; + apply { + if (hd.inner_udp.isValid()) { + step_udp_src_port(); + step_udp_l4_port(); + } else if (hd.inner_tcp.isValid()) { + step_tcp_src_port(); + step_tcp_l4_port(); + } + if (hd.inner_ipv6.isValid()) { + if (ig_m.tunnel.type != SWITCH_INGRESS_TUNNEL_TYPE_NVGRE_ST) { + compute_v6_cons_hash.apply(hd.inner_ipv6.src_addr, hd.inner_ipv6.dst_addr, ig_m.cons_hash_v6_ip_seq, low_l4_port, high_l4_port, hd.inner_ipv6.next_hdr, ig_m.hash); + } else { + step_31_0_v6(); + v6_calc_31_0_hash(); + } + } else if (hd.inner_ipv4.isValid()) { + compute_v4_cons_hash.apply(hd.inner_ipv4.src_addr, hd.inner_ipv4.dst_addr, low_l4_port, high_l4_port, hd.inner_ipv4.protocol, ig_m.hash); + } + } +} + +control InnerDtelv4Hash(in switch_header_t hdr, in switch_local_metadata_t local_md, out switch_hash_t hash) { + @name(".inner_dtelv4_hash") Hash>(HashAlgorithm_t.CRC32) inner_dtelv4_hash; + bit<32> ip_src_addr = hdr.inner_ipv4.src_addr; + bit<32> ip_dst_addr = hdr.inner_ipv4.dst_addr; + bit<8> ip_proto = hdr.inner_ipv4.protocol; + bit<16> l4_src_port = hdr.udp.src_port; + apply { + hash[31:0] = inner_dtelv4_hash.get({ ip_src_addr, ip_dst_addr, ip_proto, l4_src_port }); + } +} + +control InnerDtelv6Hash(in switch_header_t hdr, in switch_local_metadata_t local_md, out switch_hash_t hash) { + @name(".inner_dtelv6_hash") Hash>(HashAlgorithm_t.CRC32) inner_dtelv6_hash; + bit<128> ip_src_addr = hdr.ipv6.src_addr; + bit<128> ip_dst_addr = hdr.ipv6.dst_addr; + bit<8> ip_proto = hdr.ipv6.next_hdr; + bit<16> l4_src_port = hdr.udp.src_port; + bit<20> ipv6_flow_label = hdr.inner_ipv6.flow_label; + apply { + hash[31:0] = inner_dtelv6_hash.get({ ipv6_flow_label, ip_src_addr, ip_dst_addr, ip_proto, l4_src_port }); + } +} + +control RotateHash(inout switch_local_metadata_t local_md) { + @name(".rotate_by_0") action rotate_by_0() { + } + @name(".rotate_by_1") action rotate_by_1() { + local_md.hash[15:0] = local_md.hash[1 - 1:0] ++ local_md.hash[15:1]; + } + @name(".rotate_by_2") action rotate_by_2() { + local_md.hash[15:0] = local_md.hash[2 - 1:0] ++ local_md.hash[15:2]; + } + @name(".rotate_by_3") action rotate_by_3() { + local_md.hash[15:0] = local_md.hash[3 - 1:0] ++ local_md.hash[15:3]; + } + @name(".rotate_by_4") action rotate_by_4() { + local_md.hash[15:0] = local_md.hash[4 - 1:0] ++ local_md.hash[15:4]; + } + @name(".rotate_by_5") action rotate_by_5() { + local_md.hash[15:0] = local_md.hash[5 - 1:0] ++ local_md.hash[15:5]; + } + @name(".rotate_by_6") action rotate_by_6() { + local_md.hash[15:0] = local_md.hash[6 - 1:0] ++ local_md.hash[15:6]; + } + @name(".rotate_by_7") action rotate_by_7() { + local_md.hash[15:0] = local_md.hash[7 - 1:0] ++ local_md.hash[15:7]; + } + @name(".rotate_by_8") action rotate_by_8() { + local_md.hash[15:0] = local_md.hash[8 - 1:0] ++ local_md.hash[15:8]; + } + @name(".rotate_by_9") action rotate_by_9() { + local_md.hash[15:0] = local_md.hash[9 - 1:0] ++ local_md.hash[15:9]; + } + @name(".rotate_by_10") action rotate_by_10() { + local_md.hash[15:0] = local_md.hash[10 - 1:0] ++ local_md.hash[15:10]; + } + @name(".rotate_by_11") action rotate_by_11() { + local_md.hash[15:0] = local_md.hash[11 - 1:0] ++ local_md.hash[15:11]; + } + @name(".rotate_by_12") action rotate_by_12() { + local_md.hash[15:0] = local_md.hash[12 - 1:0] ++ local_md.hash[15:12]; + } + @name(".rotate_by_13") action rotate_by_13() { + local_md.hash[15:0] = local_md.hash[13 - 1:0] ++ local_md.hash[15:13]; + } + @name(".rotate_by_14") action rotate_by_14() { + local_md.hash[15:0] = local_md.hash[14 - 1:0] ++ local_md.hash[15:14]; + } + @name(".rotate_by_15") action rotate_by_15() { + local_md.hash[15:0] = local_md.hash[15 - 1:0] ++ local_md.hash[15:15]; + } + @name(".rotate_hash") table rotate_hash { + actions = { + rotate_by_0; + rotate_by_1; + rotate_by_2; + rotate_by_3; + rotate_by_4; + rotate_by_5; + rotate_by_6; + rotate_by_7; + rotate_by_8; + rotate_by_9; + rotate_by_10; + rotate_by_11; + rotate_by_12; + rotate_by_13; + rotate_by_14; + rotate_by_15; + } + size = 16; + default_action = rotate_by_0; + } + apply { + rotate_hash.apply(); + } +} + +control PreIngressAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + bool is_acl_enabled; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".set_acl_status") action set_acl_status(bool enabled) { + is_acl_enabled = enabled; + } + @name(".device_to_acl") table device_to_acl { + actions = { + set_acl_status; + } + default_action = set_acl_status(false); + size = 1; + } + @name(".pre_ingress_acl_no_action") action no_action() { + stats.count(); + } + @name(".pre_ingress_acl_deny") action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + @name(".pre_ingress_acl_set_vrf") action set_vrf(switch_vrf_t vrf) { + local_md.vrf = vrf; + stats.count(); + } + @name(".pre_ingress_acl") table acl { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + local_md.lkp.mac_type : ternary; + local_md.lkp.ip_src_addr: ternary; + local_md.lkp.ip_dst_addr: ternary; + local_md.lkp.ip_tos : ternary; + local_md.ingress_port : ternary; + } + actions = { + set_vrf; + deny; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + device_to_acl.apply(); + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0) && is_acl_enabled == true) { + acl.apply(); + } + } +} + +control IngressIpAcl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + trap; + copy_to_cpu; + permit; + redirect_nexthop; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressInnerIpv4Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + table acl { + key = { + hdr.inner_ipv4.src_addr : ternary; + hdr.inner_ipv4.dst_addr : ternary; + hdr.inner_ipv4.protocol : ternary; + hdr.inner_ipv4.diffserv : ternary; + hdr.inner_tcp.src_port : ternary; + hdr.inner_tcp.dst_port : ternary; + hdr.inner_udp.src_port : ternary; + hdr.inner_udp.dst_port : ternary; + hdr.inner_ipv4.ttl : ternary; + hdr.inner_tcp.flags : ternary; + local_md.tunnel.vni : ternary; + local_md.ingress_port_lag_index: ternary; + } + actions = { + set_dtel_report_type; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressInnerIpv6Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + table acl { + key = { + hdr.inner_ipv6.src_addr : ternary; + hdr.inner_ipv6.dst_addr : ternary; + hdr.inner_ipv6.next_hdr : ternary; + hdr.inner_ipv6.traffic_class : ternary; + hdr.inner_tcp.src_port : ternary; + hdr.inner_tcp.dst_port : ternary; + hdr.inner_udp.src_port : ternary; + hdr.inner_udp.dst_port : ternary; + hdr.inner_ipv6.hop_limit : ternary; + hdr.inner_tcp.flags : ternary; + local_md.tunnel.vni : ternary; + local_md.ingress_port_lag_index: ternary; + } + actions = { + set_dtel_report_type; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressIpv4Acl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr[95:64]: ternary; + local_md.lkp.ip_dst_addr[95:64]: ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + trap; + copy_to_cpu; + permit; + redirect_nexthop; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressIpv6Acl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + trap; + copy_to_cpu; + permit; + redirect_nexthop; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressTosMirrorAcl(inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_tos : ternary; + local_md.ingress_port_lag_label: ternary; + } + actions = { + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control IngressMacAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action redirect_nexthop(switch_user_metadata_t user_metadata, switch_nexthop_t nexthop_index) { + acl_nexthop = nexthop_index; + local_md.user_metadata = user_metadata; + stats.count(); + } + table acl { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + deny; + permit; + redirect_nexthop; + mirror_in; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +struct switch_acl_sample_info_t { + bit<32> current; + bit<32> rate; +} + +control IngressIpDtelSampleAcl(inout switch_local_metadata_t local_md, out switch_nexthop_t acl_nexthop)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + const bit<32> acl_sample_session_size = 256; + Register>(acl_sample_session_size) samplers; + RegisterAction, bit<1>>(samplers) sample_packet = { + void apply(inout switch_acl_sample_info_t reg, out bit<1> flag) { + if (reg.current > 0) { + reg.current = reg.current - 1; + } else { + reg.current = reg.rate; + flag = 1; + } + } + }; + action set_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + stats.count(); + } + action set_color(switch_pkt_color_t color) { + local_md.qos.color = color; + stats.count(); + } + action redirect_port(switch_user_metadata_t user_metadata, switch_port_lag_index_t egress_port_lag_index) { + local_md.flags.acl_deny = false; + local_md.egress_port_lag_index = egress_port_lag_index; + local_md.acl_port_redirect = true; + local_md.user_metadata = user_metadata; + stats.count(); + } + action mirror_in(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_INGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + action set_dtel_report_type(switch_dtel_report_type_t type) { + local_md.dtel.report_type = local_md.dtel.report_type | type; + stats.count(); + } + action no_action() { + stats.count(); + } + action permit(switch_user_metadata_t user_metadata, switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.user_metadata = user_metadata; + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action copy_to_cpu(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action trap(switch_hostif_trap_t trap_id, switch_meter_index_t meter_index) { + local_md.hostif_trap_id = trap_id; + local_md.qos.acl_meter_index = meter_index; + deny(); + } + action ifa_clone_sample(switch_ifa_sample_id_t ifa_sample_session) { + local_md.dtel.ifa_gen_clone = sample_packet.execute(ifa_sample_session); + stats.count(); + } + action ifa_clone_sample_and_set_dtel_report_type(switch_ifa_sample_id_t ifa_sample_session, switch_dtel_report_type_t type) { + local_md.dtel.report_type = type; + local_md.dtel.ifa_gen_clone = sample_packet.execute(ifa_sample_session); + stats.count(); + } + table acl { + key = { + local_md.lkp.ip_src_addr : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_tos : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + local_md.lkp.mac_type : ternary; + local_md.ingress_port_lag_label: ternary; + local_md.bd_label : ternary; + } + actions = { + set_dtel_report_type; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_ACL != 0)) { + acl.apply(); + } + } +} + +control LOU(inout switch_local_metadata_t local_md) { + const switch_uint32_t table_size = 64 * 1024; + @name(".set_ingress_src_port_label") action set_src_port_label(bit<8> label) { + local_md.l4_src_port_label = label; + } + @name(".set_ingress_dst_port_label") action set_dst_port_label(bit<8> label) { + local_md.l4_dst_port_label = label; + } + @name(".ingress_l4_dst_port") @use_hash_action(1) table l4_dst_port { + key = { + local_md.lkp.l4_dst_port: exact; + } + actions = { + set_dst_port_label; + } + const default_action = set_dst_port_label(0); + size = table_size; + } + @name(".ingress_l4_src_port") @use_hash_action(1) table l4_src_port { + key = { + local_md.lkp.l4_src_port: exact; + } + actions = { + set_src_port_label; + } + const default_action = set_src_port_label(0); + size = table_size; + } + @name(".set_tcp_flags") action set_tcp_flags(bit<8> flags) { + local_md.lkp.tcp_flags = flags; + } + @name(".ingress_lou_tcp") table tcp { + key = { + local_md.lkp.tcp_flags: exact; + } + actions = { + NoAction; + set_tcp_flags; + } + size = 256; + } + apply { + l4_src_port.apply(); + l4_dst_port.apply(); + } +} + +control IngressSystemAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr)(switch_uint32_t table_size=512) { + const switch_uint32_t drop_stats_table_size = 8192; + DirectCounter>(CounterType_t.PACKETS) stats; + @name(".ingress_copp_meter") Meter>(1 << 8, MeterType_t.PACKETS) copp_meter; + DirectCounter>(CounterType_t.PACKETS) copp_stats; + switch_copp_meter_id_t copp_meter_id; + @name(".ingress_system_acl_permit") action permit() { + local_md.drop_reason = SWITCH_DROP_REASON_UNKNOWN; + } + @name(".ingress_system_acl_drop") action drop(switch_drop_reason_t drop_reason, bool disable_learning) { + ig_intr_md_for_dprsr.drop_ctl = 0x1; + ig_intr_md_for_dprsr.digest_type = (disable_learning ? SWITCH_DIGEST_TYPE_INVALID : ig_intr_md_for_dprsr.digest_type); + local_md.drop_reason = drop_reason; + } + @name(".ingress_system_acl_copy_to_cpu") action copy_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + local_md.qos.qid = (overwrite_qid ? qid : local_md.qos.qid); + ig_intr_md_for_tm.copy_to_cpu = 1w1; + ig_intr_md_for_dprsr.digest_type = (disable_learning ? SWITCH_DIGEST_TYPE_INVALID : ig_intr_md_for_dprsr.digest_type); + ig_intr_md_for_tm.packet_color = (bit<2>)copp_meter.execute(meter_id); + copp_meter_id = meter_id; + local_md.cpu_reason = reason_code; + local_md.drop_reason = SWITCH_DROP_REASON_UNKNOWN; + } + @name(".ingress_system_acl_copy_sflow_to_cpu") action copy_sflow_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + copy_to_cpu(reason_code + (bit<16>)local_md.sflow.session_id, qid, meter_id, disable_learning, overwrite_qid); + } + @name(".ingress_system_acl_redirect_to_cpu") action redirect_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + ig_intr_md_for_dprsr.drop_ctl = 0b1; + local_md.flags.redirect_to_cpu = true; + copy_to_cpu(reason_code, qid, meter_id, disable_learning, overwrite_qid); + } + @name(".ingress_system_acl_redirect_sflow_to_cpu") action redirect_sflow_to_cpu(switch_cpu_reason_t reason_code, switch_qid_t qid, switch_copp_meter_id_t meter_id, bool disable_learning, bool overwrite_qid) { + ig_intr_md_for_dprsr.drop_ctl = 0b1; + local_md.flags.redirect_to_cpu = true; + copy_sflow_to_cpu(reason_code, qid, meter_id, disable_learning, overwrite_qid); + } + @name(".ingress_system_acl") table system_acl { + key = { + local_md.ingress_port_lag_label : ternary; + local_md.bd : ternary; + local_md.ingress_port_lag_index : ternary; + local_md.lkp.pkt_type : ternary; + local_md.lkp.mac_type : ternary; + local_md.lkp.mac_dst_addr : ternary; + local_md.lkp.ip_type : ternary; + local_md.lkp.ip_ttl : ternary; + local_md.lkp.ip_proto : ternary; + local_md.lkp.ip_frag : ternary; + local_md.lkp.ip_dst_addr : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.lkp.arp_opcode : ternary; + local_md.flags.vlan_arp_suppress : ternary; + local_md.flags.vrf_ttl_violation : ternary; + local_md.flags.vrf_ttl_violation_valid : ternary; + local_md.flags.vrf_ip_options_violation: ternary; + local_md.flags.port_vlan_miss : ternary; + local_md.flags.acl_deny : ternary; + local_md.flags.racl_deny : ternary; + local_md.flags.rmac_hit : ternary; + local_md.flags.dmac_miss : ternary; + local_md.flags.myip : ternary; + local_md.flags.glean : ternary; + local_md.flags.routed : ternary; + local_md.flags.fib_lpm_miss : ternary; + local_md.flags.fib_drop : ternary; + local_md.qos.storm_control_color : ternary; + local_md.flags.link_local : ternary; + local_md.checks.same_if : ternary; + local_md.stp.state_ : ternary; + local_md.flags.pfc_wd_drop : ternary; + local_md.ipv4.unicast_enable : ternary; + local_md.ipv6.unicast_enable : ternary; + local_md.sflow.sample_packet : ternary; + local_md.l2_drop_reason : ternary; + local_md.drop_reason : ternary; + local_md.tunnel.terminate : ternary; + local_md.hostif_trap_id : ternary; + hdr.ipv4_option.isValid() : ternary; + } + actions = { + permit; + drop; + copy_to_cpu; + redirect_to_cpu; + copy_sflow_to_cpu; + redirect_sflow_to_cpu; + } + const default_action = permit; + size = table_size; + } + @name(".ingress_copp_drop") action copp_drop() { + ig_intr_md_for_tm.copy_to_cpu = 1w0; + copp_stats.count(); + } + @name(".ingress_copp_permit") action copp_permit() { + copp_stats.count(); + } + @name(".ingress_copp") table copp { + key = { + ig_intr_md_for_tm.packet_color: ternary; + copp_meter_id : ternary; + } + actions = { + copp_permit; + copp_drop; + } + const default_action = copp_permit; + size = 1 << 8 + 1; + counters = copp_stats; + } + @name(".ingress_drop_stats_count") action count() { + stats.count(); + } + @name(".ingress_drop_stats") table drop_stats { + key = { + local_md.drop_reason : exact @name("drop_reason") ; + local_md.ingress_port: exact @name("port") ; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + counters = stats; + size = drop_stats_table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_SYSTEM_ACL != 0)) { + switch (system_acl.apply().action_run) { + copy_to_cpu: { + copp.apply(); + } + redirect_to_cpu: { + copp.apply(); + } + default: { + } + } + } + drop_stats.apply(); + } +} + +control EgressLOU(inout switch_local_metadata_t local_md) { + const switch_uint32_t table_size = 64 * 1024; + @name(".set_egress_src_port_label") action set_src_port_label(bit<8> label) { + local_md.l4_src_port_label = label; + } + @name(".set_egress_dst_port_label") action set_dst_port_label(bit<8> label) { + local_md.l4_dst_port_label = label; + } + @name(".egress_l4_dst_port") @use_hash_action(1) table l4_dst_port { + key = { + local_md.lkp.l4_dst_port: exact; + } + actions = { + set_dst_port_label; + } + const default_action = set_dst_port_label(0); + size = table_size; + } + @name(".egress_l4_src_port") @use_hash_action(1) table l4_src_port { + key = { + local_md.lkp.l4_src_port: exact; + } + actions = { + set_src_port_label; + } + const default_action = set_src_port_label(0); + size = table_size; + } + apply { + l4_src_port.apply(); + l4_dst_port.apply(); + } +} + +control EgressMacAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action permit(switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + hdr.ethernet.ether_type : ternary; + local_md.egress_port_lag_label: ternary; + } + actions = { + deny(); + permit(); + mirror_out(); + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressIpv4Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action permit(switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ipv4.src_addr : ternary; + hdr.ipv4.dst_addr : ternary; + hdr.ipv4.protocol : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.egress_port_lag_label: ternary; + hdr.ethernet.ether_type : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + } + actions = { + deny(); + permit(); + mirror_out(); + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressIpv6Acl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action deny() { + local_md.flags.acl_deny = true; + stats.count(); + } + action permit(switch_meter_index_t meter_index) { + local_md.flags.acl_deny = false; + local_md.qos.acl_meter_index = meter_index; + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ipv6.src_addr : ternary; + hdr.ipv6.dst_addr : ternary; + hdr.ipv6.next_hdr : ternary; + local_md.lkp.tcp_flags : ternary; + local_md.lkp.l4_src_port : ternary; + local_md.lkp.l4_dst_port : ternary; + local_md.egress_port_lag_label: ternary; + hdr.ethernet.ether_type : ternary; + local_md.l4_src_port_label : ternary; + local_md.l4_dst_port_label : ternary; + } + actions = { + deny(); + permit(); + mirror_out(); + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressTosMirrorAcl(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + action no_action() { + stats.count(); + } + action mirror_out(switch_mirror_meter_id_t meter_index, switch_mirror_session_t session_id) { + local_md.mirror.type = 1; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.mirror.session_id = session_id; + local_md.mirror.meter_index = meter_index; + stats.count(); + } + table acl { + key = { + hdr.ipv4.diffserv : ternary; + hdr.ipv6.traffic_class : ternary; + hdr.ipv4.isValid() : ternary; + hdr.ipv6.isValid() : ternary; + local_md.egress_port_lag_label: ternary; + } + actions = { + mirror_out; + no_action; + } + const default_action = no_action; + counters = stats; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + acl.apply(); + } + } +} + +control EgressSystemAcl(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, out egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr)(switch_uint32_t table_size=512) { + const switch_uint32_t drop_stats_table_size = 8192; + DirectCounter>(CounterType_t.PACKETS) stats; + @name(".egress_copp_meter") Meter>(1 << 8, MeterType_t.PACKETS) copp_meter; + DirectCounter>(CounterType_t.PACKETS) copp_stats; + switch_copp_meter_id_t copp_meter_id; + switch_pkt_color_t copp_color; + @name(".egress_system_acl_drop") action drop(switch_drop_reason_t reason_code) { + local_md.drop_reason = reason_code; + eg_intr_md_for_dprsr.drop_ctl = 0x1; + local_md.mirror.type = 0; + } + @name(".egress_system_acl_ingress_timestamp") action insert_timestamp() { + } + @name(".egress_system_acl") table system_acl { + key = { + eg_intr_md.egress_port : ternary; + local_md.flags.acl_deny : ternary; + local_md.checks.mtu : ternary; + local_md.checks.stp : ternary; + local_md.flags.wred_drop : ternary; + local_md.flags.pfc_wd_drop: ternary; + } + actions = { + NoAction; + drop; + insert_timestamp; + } + const default_action = NoAction; + size = table_size; + } + @name(".egress_drop_stats_count") action count() { + stats.count(); + } + @name(".egress_drop_stats") table drop_stats { + key = { + local_md.drop_reason : exact @name("drop_reason") ; + eg_intr_md.egress_port: exact @name("port") ; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + counters = stats; + size = drop_stats_table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + switch (system_acl.apply().action_run) { + default: { + } + } + } + drop_stats.apply(); + } +} + +control IngressSTP(in switch_local_metadata_t local_md, inout switch_stp_metadata_t stp_md)(bool multiple_stp_enable=false, switch_uint32_t table_size=4096) { + const bit<32> stp_state_size = 1 << 19; + Hash>(HashAlgorithm_t.IDENTITY) hash; + @name(".ingress_stp.stp0") Register, bit<32>>(stp_state_size, 0) stp0; + RegisterAction, bit<32>, bit<1>>(stp0) stp_check0 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + } + }; + @name(".ingress_stp.stp1") Register, bit<32>>(stp_state_size, 0) stp1; + RegisterAction, bit<32>, bit<1>>(stp1) stp_check1 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + } + }; + @name(".ingress_stp.set_stp_state") action set_stp_state(switch_stp_state_t stp_state) { + stp_md.state_ = stp_state; + } + @name(".ingress_stp.mstp") table mstp { + key = { + local_md.ingress_port: exact; + stp_md.group : exact; + } + actions = { + NoAction; + set_stp_state; + } + size = table_size; + const default_action = NoAction; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_STP != 0)) { + if (multiple_stp_enable) { + mstp.apply(); + } else { + if (local_md.bd[15:12] == 0) { + stp_md.state_[0:0] = stp_check0.execute(hash.get({ local_md.bd[11:0], local_md.ingress_port[6:0] })); + stp_md.state_[1:1] = stp_check1.execute(hash.get({ local_md.bd[11:0], local_md.ingress_port[6:0] })); + } + } + } + } +} + +control EgressSTP(in switch_local_metadata_t local_md, in switch_port_t port, out bool stp_state) { + @name(".egress_stp.stp") Register, bit<32>>(1 << 19, 0) stp; + Hash>(HashAlgorithm_t.IDENTITY) hash; + RegisterAction, bit<32>, bit<1>>(stp) stp_check = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + } + }; + apply { + if (!local_md.flags.bypass_egress) { + if (local_md.bd[15:12] == 0) { + bit<32> stp_hash_ = hash.get({ local_md.bd[11:0], port[6:0] }); + stp_state = (bool)stp_check.execute(stp_hash_); + } + } + } +} + +control SMAC(in mac_addr_t src_addr, in mac_addr_t dst_addr, inout switch_local_metadata_t local_md, inout switch_digest_type_t digest_type)(switch_uint32_t table_size) { + bool src_miss; + switch_port_lag_index_t src_move; + @name(".smac_miss") action smac_miss() { + src_miss = true; + } + @name(".smac_hit") action smac_hit(switch_port_lag_index_t port_lag_index) { + src_move = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".smac") table smac { + key = { + local_md.bd: exact; + src_addr : exact; + } + actions = { + @defaultonly smac_miss; + smac_hit; + } + const default_action = smac_miss; + size = table_size; + idle_timeout = true; + } + action notify() { + digest_type = SWITCH_DIGEST_TYPE_MAC_LEARNING; + } + table learning { + key = { + src_miss: exact; + src_move: ternary; + } + actions = { + NoAction; + notify; + } + const default_action = NoAction; + const entries = { + (true, default) : notify(); + (false, 0 &&& 0x3ff) : NoAction(); + (false, default) : notify(); + } + size = MIN_TABLE_SIZE; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_SMAC != 0)) { + smac.apply(); + } + if (local_md.learning.bd_mode == SWITCH_LEARNING_MODE_LEARN && local_md.learning.port_mode == SWITCH_LEARNING_MODE_LEARN) { + learning.apply(); + } + } +} + +control DMAC_t(in mac_addr_t dst_addr, inout switch_local_metadata_t local_md); +control DMAC(in mac_addr_t dst_addr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size) { + @name(".dmac_miss") action dmac_miss() { + local_md.egress_port_lag_index = SWITCH_FLOOD; + local_md.flags.dmac_miss = true; + } + @name(".dmac_hit") action dmac_hit(switch_port_lag_index_t port_lag_index) { + local_md.egress_port_lag_index = port_lag_index; + local_md.checks.same_if = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".dmac_redirect") action dmac_redirect(switch_nexthop_t nexthop_index) { + local_md.nexthop = nexthop_index; + } + @pack(2) @name(".dmac") table dmac { + key = { + local_md.bd: exact; + dst_addr : exact; + } + actions = { + dmac_miss; + dmac_hit; + dmac_redirect; + } + const default_action = dmac_miss; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_L2 != 0)) { + dmac.apply(); + } + } +} + +control IngressBd(in switch_bd_t bd, in switch_pkt_type_t pkt_type)(switch_uint32_t table_size) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_bd_stats_count") action count() { + stats.count(); + } + @name(".ingress_bd_stats") table bd_stats { + key = { + bd : exact; + pkt_type: exact; + } + actions = { + count; + @defaultonly NoAction; + } + const default_action = NoAction; + size = 3 * table_size; + counters = stats; + } + apply { + bd_stats.apply(); + } +} + +control EgressBDStats(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_bd_stats_count") action count() { + stats.count(sizeInBytes(hdr.bridged_md)); + } + @name(".egress_bd_stats") table bd_stats { + key = { + local_md.bd[12:0] : exact; + local_md.lkp.pkt_type: exact; + } + actions = { + count; + @defaultonly NoAction; + } + size = 3 * BD_TABLE_SIZE; + counters = stats; + } + apply { + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + bd_stats.apply(); + } + } +} + +control EgressBD(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".set_egress_bd_mapping") action set_bd_properties(mac_addr_t smac, switch_mtu_t mtu) { + hdr.ethernet.src_addr = smac; + local_md.checks.mtu = mtu; + } + @name(".egress_bd_mapping") table bd_mapping { + key = { + local_md.bd[12:0]: exact; + } + actions = { + set_bd_properties; + } + const default_action = set_bd_properties(0, 0x3fff); + size = 5120; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + bd_mapping.apply(); + } + } +} + +control VlanDecap(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".remove_vlan_tag") action remove_vlan_tag() { + hdr.ethernet.ether_type = hdr.vlan_tag[0].ether_type; + hdr.vlan_tag.pop_front(1); + } + @name(".vlan_decap") table vlan_decap { + key = { + hdr.vlan_tag[0].isValid(): ternary; + } + actions = { + NoAction; + remove_vlan_tag; + } + const default_action = NoAction; + } + apply { + if (!local_md.flags.bypass_egress) { + if (hdr.vlan_tag[0].isValid()) { + hdr.ethernet.ether_type = hdr.vlan_tag[0].ether_type; + hdr.vlan_tag[0].setInvalid(); + local_md.pkt_length = local_md.pkt_length - 4; + } + } + } +} + +control VlanXlate(inout switch_header_t hdr, in switch_local_metadata_t local_md)(switch_uint32_t bd_table_size, switch_uint32_t port_bd_table_size) { + @name(".set_vlan_untagged") action set_vlan_untagged() { + } + @name(".set_vlan_tagged") action set_vlan_tagged(vlan_id_t vid) { + hdr.vlan_tag[0].setValid(); + hdr.vlan_tag[0].ether_type = hdr.ethernet.ether_type; + hdr.vlan_tag[0].dei = 0; + hdr.vlan_tag[0].vid = vid; + hdr.ethernet.ether_type = 0x8100; + } + @name(".port_bd_to_vlan_mapping") table port_bd_to_vlan_mapping { + key = { + local_md.egress_port_lag_index: exact @name("port_lag_index") ; + local_md.bd : exact @name("bd") ; + } + actions = { + set_vlan_untagged; + set_vlan_tagged; + } + const default_action = set_vlan_untagged; + size = port_bd_table_size; + } + @name(".bd_to_vlan_mapping") table bd_to_vlan_mapping { + key = { + local_md.bd: exact @name("bd") ; + } + actions = { + set_vlan_untagged; + set_vlan_tagged; + } + const default_action = set_vlan_untagged; + size = bd_table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + if (!port_bd_to_vlan_mapping.apply().hit) { + bd_to_vlan_mapping.apply(); + } + } + } +} + +action fib_hit(inout switch_local_metadata_t local_md, switch_nexthop_t nexthop_index, switch_fib_label_t fib_label) { + local_md.nexthop = nexthop_index; + local_md.flags.routed = true; +} +action fib_miss(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; +} +action fib_miss_lpm4(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; + local_md.flags.fib_lpm_miss = true; +} +action fib_miss_lpm6(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; + local_md.flags.fib_lpm_miss = true; +} +action fib_drop(inout switch_local_metadata_t local_md) { + local_md.flags.routed = false; + local_md.flags.fib_drop = true; +} +action fib_myip(inout switch_local_metadata_t local_md, switch_myip_type_t myip) { + local_md.flags.myip = myip; +} +control Fibv4(in bit<32> dst_addr, inout switch_local_metadata_t local_md)(switch_uint32_t host_table_size, switch_uint32_t lpm_table_size, bool local_host_enable=false, switch_uint32_t local_host_table_size=1024) { + @pack(2) @name(".ipv4_host") table host { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : exact @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = host_table_size; + } + @name(".ipv4_local_host") table local_host { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : exact @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = local_host_table_size; + } + Alpm(number_partitions = 4096, subtrees_per_partition = 2, atcam_subset_width = 18, shift_granularity = 1) algo_lpm; + @name(".ipv4_lpm") table lpm32 { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : lpm @name("ip_dst_addr") ; + } + actions = { + fib_miss_lpm4(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss_lpm4(local_md); + size = lpm_table_size; + implementation = algo_lpm; + requires_versioning = false; + } + apply { + if (local_host_enable) { + if (!local_host.apply().hit) { + if (!host.apply().hit) { + lpm32.apply(); + } + } + } else { + if (!host.apply().hit) { + lpm32.apply(); + } + } + } +} + +control Fibv6(in bit<128> dst_addr, inout switch_local_metadata_t local_md)(switch_uint32_t host_table_size, switch_uint32_t host64_table_size, switch_uint32_t lpm_table_size, switch_uint32_t lpm64_table_size=1024) { + @immediate(0) @name(".ipv6_host") table host { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : exact @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = host_table_size; + } + @name(".ipv6_lpm_tcam") table lpm_tcam { + key = { + local_md.vrf: exact @name("vrf") ; + dst_addr : lpm @name("ip_dst_addr") ; + } + actions = { + fib_miss(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss(local_md); + size = lpm_table_size; + } + Alpm(number_partitions = 2048, subtrees_per_partition = 2, atcam_subset_width = 32, shift_granularity = 2) algo_lpm64; + @name(".ipv6_lpm64") table lpm64 { + key = { + local_md.vrf : exact @name("vrf") ; + dst_addr[127:64]: lpm @name("ip_dst_addr") ; + } + actions = { + fib_miss_lpm6(local_md); + fib_hit(local_md); + fib_myip(local_md); + fib_drop(local_md); + } + const default_action = fib_miss_lpm6(local_md); + size = lpm64_table_size; + implementation = algo_lpm64; + requires_versioning = false; + } + apply { + if (!host.apply().hit) { + if (!lpm_tcam.apply().hit) { + lpm64.apply(); + } + } + } +} + +control EgressVRF(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".set_vrf_properties") action set_vrf_properties(switch_tunnel_vni_t vni, mac_addr_t smac) { + local_md.tunnel.vni = vni; + hdr.ethernet.src_addr = smac; + } + @use_hash_action(1) @name(".vrf_mapping") table vrf_mapping { + key = { + local_md.vrf: exact @name("vrf") ; + } + actions = { + set_vrf_properties; + } + const default_action = set_vrf_properties(0, 0); + size = VRF_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + vrf_mapping.apply(); + if (hdr.ipv4.isValid()) { + hdr.ipv4.ttl = hdr.ipv4.ttl - 1; + } else if (hdr.ipv6.isValid()) { + hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1; + } + } + } +} + +control MTU(in switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=16) { + action ipv4_mtu_check() { + local_md.checks.mtu = local_md.checks.mtu |-| hdr.ipv4.total_len; + } + action ipv6_mtu_check() { + local_md.checks.mtu = local_md.checks.mtu |-| hdr.ipv6.payload_len; + } + action mtu_miss() { + local_md.checks.mtu = 16w0xffff; + } + table mtu { + key = { + local_md.flags.bypass_egress: exact; + local_md.flags.routed : exact; + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + } + actions = { + ipv4_mtu_check; + ipv6_mtu_check; + mtu_miss; + } + const default_action = mtu_miss; + const entries = { + (false, true, true, false) : ipv4_mtu_check(); + (false, true, false, true) : ipv6_mtu_check(); + } + size = table_size; + } + apply { + mtu.apply(); + } +} + +control Nexthop(inout switch_local_metadata_t local_md)(switch_uint32_t nexthop_table_size, switch_uint32_t ecmp_group_table_size, switch_uint32_t ecmp_selection_table_size, switch_uint32_t ecmp_max_members_per_group=64) { + Hash(HashAlgorithm_t.IDENTITY) selector_hash; + @name(".nexthop_ecmp_action_profile") ActionProfile(ecmp_selection_table_size) ecmp_action_profile; + @name(".nexthop_ecmp_selector") ActionSelector(ecmp_action_profile, selector_hash, SelectorMode_t.FAIR, ecmp_max_members_per_group, ecmp_group_table_size) ecmp_selector; + @name(".nexthop_set_nexthop_properties") action set_nexthop_properties(switch_port_lag_index_t port_lag_index, switch_bd_t bd, switch_nat_zone_t zone) { + local_md.egress_port_lag_index = port_lag_index; + local_md.checks.same_if = local_md.ingress_port_lag_index ^ port_lag_index; + local_md.tunnel_nexthop = local_md.nexthop; + } + @name(".set_ecmp_properties") action set_ecmp_properties(switch_port_lag_index_t port_lag_index, switch_bd_t bd, switch_nexthop_t nexthop_index, switch_nat_zone_t zone) { + local_md.nexthop = nexthop_index; + local_md.tunnel_nexthop = local_md.nexthop; + set_nexthop_properties(port_lag_index, bd, zone); + } + @name(".set_nexthop_properties_post_routed_flood") action set_nexthop_properties_post_routed_flood(switch_bd_t bd, switch_mgid_t mgid, switch_nat_zone_t zone) { + local_md.egress_port_lag_index = 0; + local_md.multicast.id = mgid; + } + @name(".set_ecmp_properties_post_routed_flood") action set_ecmp_properties_post_routed_flood(switch_bd_t bd, switch_mgid_t mgid, switch_nexthop_t nexthop_index, switch_nat_zone_t zone) { + local_md.nexthop = nexthop_index; + set_nexthop_properties_post_routed_flood(bd, mgid, zone); + } + @name(".set_nexthop_properties_glean") action set_nexthop_properties_glean() { + local_md.flags.glean = true; + } + @name(".set_ecmp_properties_glean") action set_ecmp_properties_glean(switch_nexthop_t nexthop_index) { + local_md.nexthop = nexthop_index; + set_nexthop_properties_glean(); + } + @name(".set_nexthop_properties_drop") action set_nexthop_properties_drop() { + local_md.drop_reason = SWITCH_DROP_REASON_NEXTHOP; + } + @name(".set_ecmp_properties_drop") action set_ecmp_properties_drop() { + set_nexthop_properties_drop(); + } + @name(".set_nexthop_properties_tunnel") action set_nexthop_properties_tunnel(switch_tunnel_ip_index_t dip_index) { + local_md.tunnel.dip_index = dip_index; + local_md.egress_port_lag_index = 0; + local_md.tunnel_nexthop = local_md.nexthop; + } + @name(".set_ecmp_properties_tunnel") action set_ecmp_properties_tunnel(switch_tunnel_ip_index_t dip_index, switch_nexthop_t nexthop_index) { + local_md.tunnel.dip_index = dip_index; + local_md.egress_port_lag_index = 0; + local_md.tunnel_nexthop = nexthop_index; + } + @ways(2) @name(".ecmp_table") table ecmp { + key = { + local_md.nexthop : exact; + local_md.hash[15:0]: selector; + } + actions = { + @defaultonly NoAction; + set_ecmp_properties; + set_ecmp_properties_drop; + set_ecmp_properties_glean; + set_ecmp_properties_post_routed_flood; + set_ecmp_properties_tunnel; + } + const default_action = NoAction; + size = ecmp_group_table_size; + implementation = ecmp_selector; + } + @name(".nexthop") table nexthop { + key = { + local_md.nexthop: exact; + } + actions = { + @defaultonly NoAction; + set_nexthop_properties; + set_nexthop_properties_drop; + set_nexthop_properties_glean; + set_nexthop_properties_post_routed_flood; + set_nexthop_properties_tunnel; + } + const default_action = NoAction; + size = nexthop_table_size; + } + apply { + if (local_md.acl_nexthop != 0) { + local_md.flags.fib_lpm_miss = false; + local_md.nexthop = local_md.acl_nexthop; + } + switch (nexthop.apply().action_run) { + NoAction: { + ecmp.apply(); + } + default: { + } + } + } +} + +control OuterFib(inout switch_local_metadata_t local_md)(switch_uint32_t ecmp_max_members_per_group=64) { + Hash(HashAlgorithm_t.IDENTITY) selector_hash; + @name(".outer_fib_ecmp_action_profile") ActionProfile(ECMP_SELECT_TABLE_SIZE) ecmp_action_profile; + @name(".outer_fib_ecmp_selector") ActionSelector(ecmp_action_profile, selector_hash, SelectorMode_t.FAIR, ecmp_max_members_per_group, ECMP_GROUP_TABLE_SIZE) ecmp_selector; + @name(".outer_fib_set_nexthop_properties") action set_nexthop_properties(switch_port_lag_index_t port_lag_index, switch_nexthop_t nexthop_index) { + local_md.nexthop = nexthop_index; + local_md.egress_port_lag_index = port_lag_index; + } + @name(".outer_fib") table fib { + key = { + local_md.tunnel.dip_index: exact; + local_md.hash[31:16] : selector; + } + actions = { + NoAction; + set_nexthop_properties; + } + const default_action = NoAction; + implementation = ecmp_selector; + size = 1 << 13; + } + apply { + fib.apply(); + } +} + +control Neighbor(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".neighbor_rewrite_l2") action rewrite_l2(switch_bd_t bd, mac_addr_t dmac) { + hdr.ethernet.dst_addr = dmac; + } + @use_hash_action(1) @name(".neighbor") table neighbor { + key = { + local_md.nexthop: exact; + } + actions = { + rewrite_l2; + } + const default_action = rewrite_l2(0, 0); + size = NEXTHOP_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + neighbor.apply(); + } + } +} + +control OuterNexthop(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".outer_nexthop_rewrite_l2") action rewrite_l2(switch_bd_t bd) { + local_md.bd = bd; + } + @use_hash_action(1) @name(".outer_nexthop") table outer_nexthop { + key = { + local_md.nexthop: exact; + } + actions = { + rewrite_l2; + } + const default_action = rewrite_l2(0); + size = NEXTHOP_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress && local_md.flags.routed) { + outer_nexthop.apply(); + } + } +} + +parser SwitchIngressParser(packet_in pkt, out switch_header_t hdr, out switch_local_metadata_t local_md, out ingress_intrinsic_metadata_t ig_intr_md) { + Checksum() ipv4_checksum; + Checksum() inner_ipv4_checksum; + Checksum() inner2_ipv4_checksum; + @name(".ingress_udp_port_vxlan") value_set>(1) udp_port_vxlan; + @name(".ingress_cpu_port") value_set(1) cpu_port; + @name(".ingress_nvgre_st_key") value_set(1) nvgre_st_key; + state start { + pkt.extract(ig_intr_md); + local_md.ingress_port = ig_intr_md.ingress_port; + local_md.timestamp = ig_intr_md.ingress_mac_tstamp[31:0]; + transition parse_port_metadata; + } + state parse_resubmit { + transition accept; + } + state parse_port_metadata { + switch_port_metadata_t port_md = port_metadata_unpack(pkt); + local_md.ingress_port_lag_index = port_md.port_lag_index; + local_md.ingress_port_lag_label = port_md.port_lag_label; + transition parse_packet; + } + state parse_packet { + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type, local_md.ingress_port) { + cpu_port: parse_cpu; + (0x800, default): parse_ipv4; + (0x806, default): parse_arp; + (0x86dd, default): parse_ipv6; + (0x8100, default): parse_vlan; + (0x88a8, default): parse_vlan; + default: accept; + } + } + state parse_cpu { + pkt.extract(hdr.fabric); + pkt.extract(hdr.cpu); + local_md.bypass = hdr.cpu.reason_code; + transition select(hdr.cpu.ether_type) { + 0x800: parse_ipv4; + 0x806: parse_arp; + 0x86dd: parse_ipv6; + 0x8100: parse_vlan; + 0x88a8: parse_vlan; + default: accept; + } + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + ipv4_checksum.add(hdr.ipv4); + transition select(hdr.ipv4.ihl) { + 5: parse_ipv4_no_options; + 6: parse_ipv4_options; + default: accept; + } + } + state parse_ipv4_options { + pkt.extract(hdr.ipv4_option); + ipv4_checksum.add(hdr.ipv4_option); + transition parse_ipv4_no_options; + } + state parse_ipv4_no_options { + local_md.flags.ipv4_checksum_err = ipv4_checksum.verify(); + transition select(hdr.ipv4.protocol, hdr.ipv4.frag_offset) { + (1, 0): parse_icmp; + (2, 0): parse_igmp; + (6, 0): parse_tcp; + (17, 0): parse_udp; + (4, 0): parse_ipinip; + (41, 0): parse_ipv6inip; + default: accept; + } + } + state parse_arp { + pkt.extract(hdr.arp); + transition accept; + } + state parse_vlan { + pkt.extract(hdr.vlan_tag.next); + transition select(hdr.vlan_tag.last.ether_type) { + 0x806: parse_arp; + 0x800: parse_ipv4; + 0x8100: parse_vlan; + 0x86dd: parse_ipv6; + default: accept; + } + } + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition select(hdr.ipv6.next_hdr) { + 58: parse_icmp; + 6: parse_tcp; + 17: parse_udp; + 4: parse_ipinip; + 41: parse_ipv6inip; + default: accept; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + 2123: parse_gtp_u; + udp_port_vxlan: parse_vxlan; + 4791: parse_rocev2; + default: accept; + } + } + state parse_tcp { + pkt.extract(hdr.tcp); + transition accept; + } + state parse_icmp { + pkt.extract(hdr.icmp); + transition accept; + } + state parse_igmp { + pkt.extract(hdr.igmp); + transition accept; + } + state parse_gtp_u { + pkt.extract(hdr.gtp); + transition accept; + } + state parse_rocev2 { + transition accept; + } + state parse_vxlan { + pkt.extract(hdr.vxlan); + local_md.tunnel.type = SWITCH_INGRESS_TUNNEL_TYPE_VXLAN; + local_md.tunnel.vni = hdr.vxlan.vni; + transition parse_inner_ethernet; + } + state parse_ipinip { + local_md.tunnel.type = SWITCH_INGRESS_TUNNEL_TYPE_IPINIP; + transition parse_inner_ipv4; + } + state parse_ipv6inip { + local_md.tunnel.type = SWITCH_INGRESS_TUNNEL_TYPE_IPINIP; + transition parse_inner_ipv6; + } + state parse_inner_ethernet { + pkt.extract(hdr.inner_ethernet); + transition select(hdr.inner_ethernet.ether_type) { + 0x800: parse_inner_ipv4; + 0x86dd: parse_inner_ipv6; + default: accept; + } + } + state parse_inner_ipv4 { + pkt.extract(hdr.inner_ipv4); + inner_ipv4_checksum.add(hdr.inner_ipv4); + local_md.flags.inner_ipv4_checksum_err = inner_ipv4_checksum.verify(); + transition select(hdr.inner_ipv4.protocol) { + 1: parse_inner_icmp; + 6: parse_inner_tcp; + 17: parse_inner_udp; + default: accept; + } + } + state parse_inner_ipv6 { + pkt.extract(hdr.inner_ipv6); + transition select(hdr.inner_ipv6.next_hdr) { + 58: parse_inner_icmp; + 6: parse_inner_tcp; + 17: parse_inner_udp; + default: accept; + } + } + state parse_inner_udp { + pkt.extract(hdr.inner_udp); + transition accept; + } + state parse_inner_tcp { + pkt.extract(hdr.inner_tcp); + transition accept; + } + state parse_inner_icmp { + pkt.extract(hdr.inner_icmp); + transition accept; + } +} + +parser SwitchEgressParser(packet_in pkt, out switch_header_t hdr, out switch_local_metadata_t local_md, out egress_intrinsic_metadata_t eg_intr_md) { + @name(".egress_udp_port_vxlan") value_set>(1) udp_port_vxlan; + @name(".egress_cpu_port") value_set(1) cpu_port; + @name(".egress_nvgre_st_key") value_set(1) nvgre_st_key; + @critical state start { + pkt.extract(eg_intr_md); + local_md.pkt_length = eg_intr_md.pkt_length; + local_md.egress_port = eg_intr_md.egress_port; + local_md.qos.qdepth = eg_intr_md.deq_qdepth; + switch_port_mirror_metadata_h mirror_md = pkt.lookahead(); + transition select(eg_intr_md.deflection_flag, mirror_md.src, mirror_md.type) { + (1, default, default): parse_deflected_pkt; + (default, SWITCH_PKT_SRC_BRIDGED, default): parse_bridged_pkt; + (default, default, 1): parse_port_mirrored_metadata; + (default, SWITCH_PKT_SRC_CLONED_EGRESS, 2): parse_cpu_mirrored_metadata; + (default, SWITCH_PKT_SRC_CLONED_INGRESS, 3): parse_dtel_drop_metadata_from_ingress; + (default, default, 3): parse_dtel_drop_metadata_from_egress; + (default, default, 4): parse_dtel_switch_local_metadata; + (default, default, 5): parse_simple_mirrored_metadata; + } + } + state parse_bridged_pkt { + pkt.extract(hdr.bridged_md); + local_md.pkt_src = SWITCH_PKT_SRC_BRIDGED; + local_md.ingress_port = hdr.bridged_md.base.ingress_port; + local_md.egress_port_lag_index = hdr.bridged_md.base.ingress_port_lag_index; + local_md.bd = hdr.bridged_md.base.ingress_bd; + local_md.nexthop = hdr.bridged_md.base.nexthop; + local_md.cpu_reason = hdr.bridged_md.base.cpu_reason; + local_md.flags.routed = hdr.bridged_md.base.routed; + local_md.flags.bypass_egress = hdr.bridged_md.base.bypass_egress; + local_md.lkp.pkt_type = hdr.bridged_md.base.pkt_type; + local_md.ingress_timestamp = hdr.bridged_md.base.timestamp; + local_md.qos.tc = hdr.bridged_md.base.tc; + local_md.qos.qid = hdr.bridged_md.base.qid; + local_md.qos.color = hdr.bridged_md.base.color; + local_md.vrf = hdr.bridged_md.base.vrf; + local_md.lkp.l4_src_port = hdr.bridged_md.acl.l4_src_port; + local_md.lkp.l4_dst_port = hdr.bridged_md.acl.l4_dst_port; + local_md.lkp.tcp_flags = hdr.bridged_md.acl.tcp_flags; + local_md.tunnel_nexthop = hdr.bridged_md.tunnel.tunnel_nexthop; + local_md.tunnel.ttl_mode = hdr.bridged_md.tunnel.ttl_mode; + local_md.tunnel.qos_mode = hdr.bridged_md.tunnel.qos_mode; + local_md.tunnel.terminate = hdr.bridged_md.tunnel.terminate; + local_md.dtel.report_type = hdr.bridged_md.dtel.report_type; + local_md.dtel.hash = hdr.bridged_md.dtel.hash; + local_md.dtel.session_id = hdr.bridged_md.dtel.session_id; + transition parse_ethernet; + } + state parse_deflected_pkt { + pkt.extract(hdr.bridged_md); + local_md.pkt_src = SWITCH_PKT_SRC_DEFLECTED; + local_md.mirror.type = 255; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = hdr.bridged_md.dtel.report_type; + local_md.dtel.hash = hdr.bridged_md.dtel.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = hdr.bridged_md.dtel.session_id; + local_md.qos.qid = hdr.bridged_md.base.qid; + local_md.ingress_timestamp = hdr.bridged_md.base.timestamp; + hdr.dtel_report = { 0, hdr.bridged_md.base.ingress_port, 0, hdr.bridged_md.dtel.egress_port, 0, hdr.bridged_md.base.qid }; + hdr.dtel_drop_report = { SWITCH_DROP_REASON_TRAFFIC_MANAGER, 0 }; + transition accept; + } + state parse_port_mirrored_with_bridged_metadata { + switch_port_mirror_metadata_h port_md; + switch_bridged_metadata_h b_md; + pkt.extract(port_md); + pkt.extract(b_md); + pkt.extract(hdr.ethernet); + local_md.pkt_src = port_md.src; + local_md.mirror.session_id = port_md.session_id; + local_md.ingress_timestamp = port_md.timestamp; + local_md.flags.bypass_egress = true; + local_md.mirror.type = port_md.type; + local_md.dtel.session_id = 0; + transition accept; + } + state parse_port_mirrored_metadata { + switch_port_mirror_metadata_h port_md; + pkt.extract(port_md); + pkt.extract(hdr.ethernet); + local_md.pkt_src = port_md.src; + local_md.mirror.session_id = port_md.session_id; + local_md.ingress_timestamp = port_md.timestamp; + local_md.flags.bypass_egress = true; + local_md.mirror.type = port_md.type; + local_md.dtel.session_id = 0; + transition accept; + } + state parse_cpu_mirrored_metadata { + switch_cpu_mirror_metadata_h cpu_md; + pkt.extract(cpu_md); + pkt.extract(hdr.ethernet); + local_md.pkt_src = cpu_md.src; + local_md.flags.bypass_egress = true; + local_md.bd = cpu_md.bd; + local_md.cpu_reason = cpu_md.reason_code; + local_md.mirror.type = cpu_md.type; + local_md.dtel.session_id = 0; + transition accept; + } + state parse_dtel_drop_metadata_from_egress { + switch_dtel_drop_mirror_metadata_h dtel_md; + pkt.extract(dtel_md); + local_md.pkt_src = dtel_md.src; + local_md.mirror.type = dtel_md.type; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = dtel_md.report_type; + local_md.dtel.hash = dtel_md.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = dtel_md.session_id; + local_md.ingress_timestamp = dtel_md.timestamp; + hdr.dtel_report = { 0, dtel_md.ingress_port, 0, dtel_md.egress_port, 0, dtel_md.qid }; + hdr.dtel_drop_report = { dtel_md.drop_reason, 0 }; + transition accept; + } + state parse_dtel_drop_metadata_from_ingress { + switch_dtel_drop_mirror_metadata_h dtel_md; + pkt.extract(dtel_md); + local_md.pkt_src = dtel_md.src; + local_md.mirror.type = dtel_md.type; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = dtel_md.report_type; + local_md.dtel.hash = dtel_md.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = dtel_md.session_id; + local_md.ingress_timestamp = dtel_md.timestamp; + hdr.dtel_report = { 0, dtel_md.ingress_port, 0, SWITCH_PORT_INVALID, 0, dtel_md.qid }; + hdr.dtel_drop_report = { dtel_md.drop_reason, 0 }; + transition accept; + } + state parse_dtel_switch_local_metadata { + switch_dtel_switch_local_mirror_metadata_h dtel_md; + pkt.extract(dtel_md); + local_md.pkt_src = dtel_md.src; + local_md.mirror.type = dtel_md.type; + local_md.flags.bypass_egress = true; + local_md.dtel.report_type = dtel_md.report_type; + local_md.dtel.hash = dtel_md.hash; + local_md.dtel.session_id = 0; + local_md.mirror.session_id = dtel_md.session_id; + local_md.ingress_timestamp = dtel_md.timestamp; + hdr.dtel_report = { 0, dtel_md.ingress_port, 0, dtel_md.egress_port, 0, dtel_md.qid }; + hdr.dtel_switch_local_report = { 0, dtel_md.qdepth, dtel_md.egress_timestamp }; + transition accept; + } + state parse_simple_mirrored_metadata { + switch_simple_mirror_metadata_h simple_mirror_md; + pkt.extract(simple_mirror_md); + local_md.pkt_src = simple_mirror_md.src; + local_md.mirror.type = simple_mirror_md.type; + local_md.mirror.session_id = simple_mirror_md.session_id; + local_md.flags.bypass_egress = true; + transition parse_ethernet; + } + state parse_packet { + transition parse_ethernet; + } + state parse_ethernet { + pkt.extract(hdr.ethernet); + transition select(hdr.ethernet.ether_type, eg_intr_md.egress_port) { + cpu_port: parse_cpu; + (0x800, default): parse_ipv4; + (0x86dd, default): parse_ipv6; + (0x8100, default): parse_vlan; + (0x88a8, default): parse_vlan; + default: parse_pad; + } + } + state parse_cpu { + local_md.flags.bypass_egress = true; + transition parse_pad; + } + state parse_ipv4 { + pkt.extract(hdr.ipv4); + transition select(hdr.ipv4.protocol, hdr.ipv4.ihl, hdr.ipv4.frag_offset) { + (17, 5, 0): parse_udp; + (4, 5, 0): parse_inner_ipv4; + (41, 5, 0): parse_inner_ipv6; + (default, 6, default): parse_ipv4_options; + default: parse_pad; + } + } + state parse_ipv4_options { + pkt.extract(hdr.ipv4_option); + transition select(hdr.ipv4.protocol, hdr.ipv4.frag_offset) { + (17, 0): parse_udp; + (4, 0): parse_inner_ipv4; + (41, 0): parse_inner_ipv6; + default: parse_pad; + } + } + state parse_vlan { + pkt.extract(hdr.vlan_tag.next); + transition select(hdr.vlan_tag.last.ether_type) { + 0x800: parse_ipv4; + 0x8100: parse_vlan; + 0x86dd: parse_ipv6; + default: parse_pad; + } + } + state parse_ipv6 { + pkt.extract(hdr.ipv6); + transition select(hdr.ipv6.next_hdr) { + default: parse_pad; + } + } + state parse_udp { + pkt.extract(hdr.udp); + transition select(hdr.udp.dst_port) { + udp_port_vxlan: parse_vxlan; + default: parse_pad; + } + } + state egress_parse_sfc_pause { + transition parse_pad; + } + state parse_tcp { + pkt.extract(hdr.tcp); + transition parse_pad; + } + state parse_vxlan { + pkt.extract(hdr.vxlan); + transition parse_inner_ethernet; + } + state parse_inner_ethernet { + pkt.extract(hdr.inner_ethernet); + transition select(hdr.inner_ethernet.ether_type) { + 0x800: parse_inner_ipv4; + 0x86dd: parse_inner_ipv6; + default: parse_pad; + } + } + state parse_inner_ipv4 { + pkt.extract(hdr.inner_ipv4); + transition parse_pad; + } + state parse_inner_ipv6 { + pkt.extract(hdr.inner_ipv6); + transition parse_pad; + } + state parse_pad { + transition accept; + } +} + +control IngressMirror(inout switch_header_t hdr, in switch_local_metadata_t local_md, in ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr) { + Mirror() mirror; + apply { + if (ig_intr_md_for_dprsr.mirror_type == 1) { + mirror.emit(local_md.mirror.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.timestamp, local_md.mirror.session_id }); + } else if (ig_intr_md_for_dprsr.mirror_type == 3) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.timestamp, local_md.dtel.session_id, local_md.lag_hash, local_md.dtel.report_type, 0, local_md.ingress_port, 0, local_md.egress_port, 0, local_md.qos.qid, local_md.drop_reason }); + } else if (ig_intr_md_for_dprsr.mirror_type == 6) { + } + } +} + +control EgressMirror(inout switch_header_t hdr, in switch_local_metadata_t local_md, in egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr) { + Mirror() mirror; + apply { + if (eg_intr_md_for_dprsr.mirror_type == 1) { + mirror.emit(local_md.mirror.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.ingress_timestamp, local_md.mirror.session_id }); + } else if (eg_intr_md_for_dprsr.mirror_type == 2) { + mirror.emit(local_md.mirror.session_id, { local_md.mirror.src, local_md.mirror.type, 0, local_md.ingress_port, local_md.bd, 0, local_md.egress_port_lag_index, local_md.cpu_reason }); + } else if (eg_intr_md_for_dprsr.mirror_type == 4) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.ingress_timestamp, local_md.dtel.session_id, local_md.dtel.hash, local_md.dtel.report_type, 0, local_md.ingress_port, 0, local_md.egress_port, 0, local_md.qos.qid, 0, local_md.qos.qdepth, local_md.egress_timestamp }); + } else if (eg_intr_md_for_dprsr.mirror_type == 3) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.ingress_timestamp, local_md.dtel.session_id, local_md.dtel.hash, local_md.dtel.report_type, 0, local_md.ingress_port, 0, local_md.egress_port, 0, local_md.qos.qid, local_md.drop_reason }); + } else if (eg_intr_md_for_dprsr.mirror_type == 5) { + mirror.emit(local_md.dtel.session_id, { local_md.mirror.src, local_md.mirror.type, local_md.dtel.session_id }); + } + } +} + +control IngressNatChecksum(inout switch_header_t hdr, in switch_local_metadata_t local_md) { + Checksum() tcp_checksum; + Checksum() udp_checksum; + apply { + } +} + +control SwitchIngressDeparser(packet_out pkt, inout switch_header_t hdr, in switch_local_metadata_t local_md, in ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr) { + IngressMirror() mirror; + @name(".learning_digest") Digest() digest; + apply { + mirror.apply(hdr, local_md, ig_intr_md_for_dprsr); + if (ig_intr_md_for_dprsr.digest_type == SWITCH_DIGEST_TYPE_MAC_LEARNING) { + digest.pack({ local_md.ingress_outer_bd, local_md.ingress_port_lag_index, hdr.ethernet.src_addr }); + } + pkt.emit(hdr.bridged_md); + pkt.emit(hdr.ethernet); + pkt.emit(hdr.vlan_tag); + pkt.emit(hdr.arp); + pkt.emit(hdr.ipv4); + pkt.emit(hdr.ipv4_option); + pkt.emit(hdr.ipv6); + pkt.emit(hdr.udp); + pkt.emit(hdr.tcp); + pkt.emit(hdr.icmp); + pkt.emit(hdr.igmp); + pkt.emit(hdr.rocev2_bth); + pkt.emit(hdr.vxlan); + pkt.emit(hdr.gre); + pkt.emit(hdr.inner_ethernet); + pkt.emit(hdr.inner_ipv4); + pkt.emit(hdr.inner_ipv6); + pkt.emit(hdr.inner_udp); + pkt.emit(hdr.inner_tcp); + pkt.emit(hdr.inner_icmp); + } +} + +control SwitchEgressDeparser(packet_out pkt, inout switch_header_t hdr, in switch_local_metadata_t local_md, in egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr) { + EgressMirror() mirror; + Checksum() ipv4_checksum; + Checksum() inner_ipv4_checksum; + apply { + mirror.apply(hdr, local_md, eg_intr_md_for_dprsr); + hdr.ipv4.hdr_checksum = ipv4_checksum.update({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.total_len, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.src_addr, hdr.ipv4.dst_addr, hdr.ipv4_option.type, hdr.ipv4_option.length, hdr.ipv4_option.value }); + if (local_md.inner_ipv4_checksum_update_en) { + hdr.inner_ipv4.hdr_checksum = inner_ipv4_checksum.update({ hdr.inner_ipv4.version, hdr.inner_ipv4.ihl, hdr.inner_ipv4.diffserv, hdr.inner_ipv4.total_len, hdr.inner_ipv4.identification, hdr.inner_ipv4.flags, hdr.inner_ipv4.frag_offset, hdr.inner_ipv4.ttl, hdr.inner_ipv4.protocol, hdr.inner_ipv4.src_addr, hdr.inner_ipv4.dst_addr }); + } + pkt.emit(hdr.ethernet); + pkt.emit(hdr.fabric); + pkt.emit(hdr.cpu); + pkt.emit(hdr.timestamp); + pkt.emit(hdr.vlan_tag); + pkt.emit(hdr.ipv4); + pkt.emit(hdr.ipv4_option); + pkt.emit(hdr.ipv6); + pkt.emit(hdr.udp); + pkt.emit(hdr.dtel); + pkt.emit(hdr.dtel_report); + pkt.emit(hdr.dtel_switch_local_report); + pkt.emit(hdr.dtel_drop_report); + pkt.emit(hdr.vxlan); + pkt.emit(hdr.gre); + pkt.emit(hdr.erspan); + pkt.emit(hdr.erspan_type2); + pkt.emit(hdr.erspan_type3); + pkt.emit(hdr.erspan_platform); + pkt.emit(hdr.inner_ethernet); + pkt.emit(hdr.inner_ipv4); + pkt.emit(hdr.inner_ipv6); + pkt.emit(hdr.inner_udp); + pkt.emit(hdr.pad); + } +} + +control MirrorRewrite(inout switch_header_t hdr, inout switch_local_metadata_t local_md, out egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr)(switch_uint32_t table_size=1024) { + bit<16> length; + action add_ethernet_header(in mac_addr_t src_addr, in mac_addr_t dst_addr, in bit<16> ether_type) { + hdr.ethernet.setValid(); + hdr.ethernet.ether_type = ether_type; + hdr.ethernet.src_addr = src_addr; + hdr.ethernet.dst_addr = dst_addr; + } + action add_vlan_tag(vlan_id_t vid, bit<3> pcp, bit<16> ether_type) { + hdr.vlan_tag[0].setValid(); + hdr.vlan_tag[0].pcp = pcp; + hdr.vlan_tag[0].vid = vid; + hdr.vlan_tag[0].ether_type = ether_type; + } + action add_ipv4_header(in bit<8> diffserv, in bit<8> ttl, in bit<8> protocol, in ipv4_addr_t src_addr, in ipv4_addr_t dst_addr) { + hdr.ipv4.setValid(); + hdr.ipv4.version = 4w4; + hdr.ipv4.ihl = 4w5; + hdr.ipv4.diffserv = diffserv; + hdr.ipv4.identification = 0; + hdr.ipv4.flags = 0; + hdr.ipv4.frag_offset = 0; + hdr.ipv4.ttl = ttl; + hdr.ipv4.protocol = protocol; + hdr.ipv4.src_addr = src_addr; + hdr.ipv4.dst_addr = dst_addr; + hdr.ipv6.setInvalid(); + } + action add_gre_header(in bit<16> proto) { + hdr.gre.setValid(); + hdr.gre.proto = proto; + hdr.gre.flags_version = 0; + } + action add_erspan_common(bit<16> version_vlan, bit<10> session_id) { + hdr.erspan.setValid(); + hdr.erspan.version_vlan = version_vlan; + hdr.erspan.session_id = (bit<16>)session_id; + } + action add_erspan_type2(bit<10> session_id) { + add_erspan_common(0x1000, session_id); + hdr.erspan_type2.setValid(); + hdr.erspan_type2.index = 0; + } + action add_erspan_type3(bit<10> session_id, bit<32> timestamp, bool opt_sub_header) { + add_erspan_common(0x2000, session_id); + hdr.erspan_type3.setValid(); + hdr.erspan_type3.timestamp = timestamp; + hdr.erspan_type3.ft_d_other = 0x4; + if (opt_sub_header) { + } + } + @name(".rewrite_") action rewrite_(switch_qid_t qid) { + local_md.qos.qid = qid; + } + @name(".rewrite_erspan_type2") action rewrite_erspan_type2(switch_qid_t qid, mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type2((bit<10>)local_md.mirror.session_id); + add_gre_header(0x88be); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w32; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, 0x800); + } + @name(".rewrite_erspan_type2_with_vlan") action rewrite_erspan_type2_with_vlan(switch_qid_t qid, bit<16> ether_type, mac_addr_t smac, mac_addr_t dmac, bit<3> pcp, vlan_id_t vid, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type2((bit<10>)local_md.mirror.session_id); + add_gre_header(0x88be); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w32; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, ether_type); + add_vlan_tag(vid, pcp, 0x800); + } + @name(".rewrite_erspan_type3") action rewrite_erspan_type3(switch_qid_t qid, mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type3((bit<10>)local_md.mirror.session_id, (bit<32>)local_md.ingress_timestamp, false); + add_gre_header(0x22eb); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w36; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, 0x800); + } + @name(".rewrite_erspan_type3_with_vlan") action rewrite_erspan_type3_with_vlan(switch_qid_t qid, bit<16> ether_type, mac_addr_t smac, mac_addr_t dmac, bit<3> pcp, vlan_id_t vid, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl) { + local_md.qos.qid = qid; + add_erspan_type3((bit<10>)local_md.mirror.session_id, (bit<32>)local_md.ingress_timestamp, false); + add_gre_header(0x22eb); + add_ipv4_header(tos, ttl, 47, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w36; + hdr.inner_ethernet = hdr.ethernet; + add_ethernet_header(smac, dmac, ether_type); + add_vlan_tag(vid, pcp, 0x800); + } + action rewrite_dtel_report(mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl, bit<16> udp_dst_port, switch_mirror_session_t session_id, bit<16> max_pkt_len) { + hdr.udp.setValid(); + hdr.udp.dst_port = udp_dst_port; + hdr.udp.checksum = 0; + add_ipv4_header(tos, ttl, 17, sip, dip); + hdr.ipv4.total_len = local_md.pkt_length + 16w28; + hdr.udp.length = local_md.pkt_length + 16w8; + hdr.ipv4.flags = 2; + add_ethernet_header(smac, dmac, 0x800); + eg_intr_md_for_dprsr.mtu_trunc_len = (bit<14>)max_pkt_len; + local_md.pkt_length = local_md.pkt_length + DTEL_REPORT_V0_5_OUTER_HEADERS_LENGTH; + } + @name(".rewrite_dtel_report_with_entropy") action rewrite_dtel_report_with_entropy(mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl, bit<16> udp_dst_port, switch_mirror_session_t session_id, bit<16> max_pkt_len) { + rewrite_dtel_report(smac, dmac, sip, dip, tos, ttl, udp_dst_port, session_id, max_pkt_len); + hdr.udp.src_port = local_md.dtel.hash[15:0]; + } + @name(".rewrite_dtel_report_without_entropy") action rewrite_dtel_report_without_entropy(mac_addr_t smac, mac_addr_t dmac, ipv4_addr_t sip, ipv4_addr_t dip, bit<8> tos, bit<8> ttl, bit<16> udp_dst_port, bit<16> udp_src_port, switch_mirror_session_t session_id, bit<16> max_pkt_len) { + rewrite_dtel_report(smac, dmac, sip, dip, tos, ttl, udp_dst_port, session_id, max_pkt_len); + hdr.udp.src_port = udp_src_port; + } + @name(".rewrite_ip_udp_lengths") action rewrite_ip_udp_lengths() { + hdr.ipv4.total_len = local_md.pkt_length - 16w14; + hdr.udp.length = local_md.pkt_length - 16w34; + } + @name(".rewrite_dtel_ifa_clone") action rewrite_dtel_ifa_clone() { + local_md.dtel.ifa_cloned = 1; + } + @name(".mirror_rewrite") table rewrite { + key = { + local_md.mirror.session_id: exact; + } + actions = { + NoAction; + rewrite_; + rewrite_erspan_type2; + rewrite_erspan_type2_with_vlan; + rewrite_erspan_type3; + rewrite_erspan_type3_with_vlan; + rewrite_dtel_report_with_entropy; + rewrite_dtel_report_without_entropy; + } + const default_action = NoAction; + size = table_size; + } + action adjust_length(bit<16> length_offset) { + local_md.pkt_length = local_md.pkt_length + length_offset; + local_md.mirror.type = 0; + } + table pkt_length { + key = { + local_md.mirror.type: exact; + } + actions = { + adjust_length; + } + const entries = { + 2 : adjust_length(0xfff2); + 1 : adjust_length(0xfff5); + 3 : adjust_length(2); + 4 : adjust_length(0x0); + 5 : adjust_length(0xfff9); + 255 : adjust_length(20); + } + } + action rewrite_ipv4_udp_len_truncate() { + hdr.ipv4.total_len = (bit<16>)eg_intr_md_for_dprsr.mtu_trunc_len - 16w18; + hdr.udp.length = (bit<16>)eg_intr_md_for_dprsr.mtu_trunc_len - 16w38; + } + table pkt_len_trunc_adjustment { + key = { + hdr.udp.isValid() : exact; + hdr.ipv4.isValid(): exact; + } + actions = { + NoAction; + rewrite_ipv4_udp_len_truncate; + } + const default_action = NoAction; + const entries = { + (true, true) : rewrite_ipv4_udp_len_truncate(); + } + } + apply { + pkt_length.apply(); + rewrite.apply(); + local_md.pkt_length = local_md.pkt_length |-| (bit<16>)eg_intr_md_for_dprsr.mtu_trunc_len; + if (local_md.pkt_length > 0 && eg_intr_md_for_dprsr.mtu_trunc_len > 0) { + pkt_len_trunc_adjustment.apply(); + } + } +} + +control IngressRmac(inout switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t port_vlan_table_size, switch_uint32_t vlan_table_size=4096) { + @name(".rmac_miss") action rmac_miss() { + local_md.flags.rmac_hit = false; + } + @name(".rmac_hit") action rmac_hit() { + local_md.flags.rmac_hit = true; + } + @name(".pv_rmac") table pv_rmac { + key = { + local_md.ingress_port_lag_index: ternary; + hdr.vlan_tag[0].isValid() : ternary; + hdr.vlan_tag[0].vid : ternary; + hdr.ethernet.dst_addr : ternary; + } + actions = { + rmac_miss; + rmac_hit; + } + const default_action = rmac_miss; + size = port_vlan_table_size; + } + @name(".vlan_rmac") table vlan_rmac { + key = { + hdr.vlan_tag[0].vid : exact; + hdr.ethernet.dst_addr: exact; + } + actions = { + @defaultonly rmac_miss; + rmac_hit; + } + const default_action = rmac_miss; + size = vlan_table_size; + } + apply { + switch (pv_rmac.apply().action_run) { + rmac_miss: { + if (hdr.vlan_tag[0].isValid()) { + vlan_rmac.apply(); + } + } + } + } +} + +control IngressPortMirror(in switch_port_t port, inout switch_mirror_metadata_t mirror_md)(switch_uint32_t table_size=288) { + @name(".set_ingress_mirror_id") action set_ingress_mirror_id(switch_mirror_session_t session_id, switch_mirror_meter_id_t meter_index) { + mirror_md.type = 1; + mirror_md.src = SWITCH_PKT_SRC_CLONED_INGRESS; + mirror_md.session_id = session_id; + } + @name(".ingress_port_mirror") table ingress_port_mirror { + key = { + port: exact; + } + actions = { + NoAction; + set_ingress_mirror_id; + } + const default_action = NoAction; + size = table_size; + } + apply { + ingress_port_mirror.apply(); + } +} + +control EgressPortMirror(in switch_port_t port, inout switch_mirror_metadata_t mirror_md)(switch_uint32_t table_size=288) { + @name(".set_egress_mirror_id") action set_egress_mirror_id(switch_mirror_session_t session_id, switch_mirror_meter_id_t meter_index) { + mirror_md.type = 1; + mirror_md.src = SWITCH_PKT_SRC_CLONED_EGRESS; + mirror_md.session_id = session_id; + } + @name(".egress_port_mirror") table egress_port_mirror { + key = { + port: exact; + } + actions = { + NoAction; + set_egress_mirror_id; + } + const default_action = NoAction; + size = table_size; + } + apply { + egress_port_mirror.apply(); + } +} + +control IngressPortMapping(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr)(switch_uint32_t port_vlan_table_size, switch_uint32_t bd_table_size, switch_uint32_t port_table_size=288, switch_uint32_t vlan_table_size=4096, switch_uint32_t double_tag_table_size=1024) { + IngressPortMirror(port_table_size) port_mirror; + IngressRmac(port_vlan_table_size, vlan_table_size) rmac; + @name(".bd_action_profile") ActionProfile(bd_table_size) bd_action_profile; + Hash>(HashAlgorithm_t.IDENTITY) hash; + const bit<32> vlan_membership_size = 1 << 19; + @name(".vlan_membership") Register, bit<32>>(vlan_membership_size, 0) vlan_membership; + RegisterAction, bit<32>, bit<1>>(vlan_membership) check_vlan_membership = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = ~val; + } + }; + action terminate_cpu_packet() { + local_md.ingress_port = (switch_port_t)hdr.cpu.ingress_port; + local_md.egress_port_lag_index = (switch_port_lag_index_t)hdr.cpu.port_lag_index; + ig_intr_md_for_tm.qid = (switch_qid_t)hdr.cpu.egress_queue; + local_md.flags.bypass_egress = (bool)hdr.cpu.tx_bypass; + hdr.ethernet.ether_type = hdr.cpu.ether_type; + } + @name(".set_cpu_port_properties") action set_cpu_port_properties(switch_port_lag_index_t port_lag_index, switch_ig_port_lag_label_t port_lag_label, switch_yid_t exclusion_id, switch_qos_trust_mode_t trust_mode, switch_qos_group_t qos_group, switch_pkt_color_t color, switch_tc_t tc) { + local_md.ingress_port_lag_index = port_lag_index; + local_md.ingress_port_lag_label = port_lag_label; + local_md.qos.trust_mode = trust_mode; + local_md.qos.group = qos_group; + local_md.qos.color = color; + local_md.qos.tc = tc; + ig_intr_md_for_tm.level2_exclusion_id = exclusion_id; + // terminate_cpu_packet(); + } + @name(".set_port_properties") action set_port_properties(switch_yid_t exclusion_id, switch_learning_mode_t learning_mode, switch_qos_trust_mode_t trust_mode, switch_qos_group_t qos_group, switch_pkt_color_t color, switch_tc_t tc, switch_meter_index_t meter_index, switch_sflow_id_t sflow_session_id, bool mac_pkt_class, switch_ig_port_lag_label_t in_ports_group_label) { + local_md.qos.trust_mode = trust_mode; + local_md.qos.group = qos_group; + local_md.qos.color = color; + local_md.qos.tc = tc; + ig_intr_md_for_tm.level2_exclusion_id = exclusion_id; + local_md.learning.port_mode = learning_mode; + local_md.checks.same_if = SWITCH_FLOOD; + local_md.flags.mac_pkt_class = mac_pkt_class; + local_md.sflow.session_id = sflow_session_id; + } + @placement_priority(2) @name(".ingress_port_mapping") table port_mapping { + key = { + local_md.ingress_port: exact; + } + actions = { + set_port_properties; + set_cpu_port_properties; + } + size = port_table_size; + } + @name(".port_vlan_miss") action port_vlan_miss() { + } + @name(".set_bd_properties") action set_bd_properties(switch_bd_t bd, switch_vrf_t vrf, bool vlan_arp_suppress, switch_packet_action_t vrf_ttl_violation, bool vrf_ttl_violation_valid, switch_packet_action_t vrf_ip_options_violation, bool vrf_unknown_l3_multicast_trap, switch_bd_label_t bd_label, switch_stp_group_t stp_group, switch_learning_mode_t learning_mode, bool ipv4_unicast_enable, bool ipv4_multicast_enable, bool igmp_snooping_enable, bool ipv6_unicast_enable, bool ipv6_multicast_enable, bool mld_snooping_enable, bool mpls_enable, switch_multicast_rpf_group_t mrpf_group, switch_nat_zone_t zone) { + local_md.bd = bd; + local_md.flags.vlan_arp_suppress = vlan_arp_suppress; + local_md.ingress_outer_bd = bd; + local_md.bd_label = bd_label; + local_md.vrf = vrf; + local_md.flags.vrf_ttl_violation = vrf_ttl_violation; + local_md.flags.vrf_ttl_violation_valid = vrf_ttl_violation_valid; + local_md.flags.vrf_ip_options_violation = vrf_ip_options_violation; + local_md.flags.vrf_unknown_l3_multicast_trap = vrf_unknown_l3_multicast_trap; + local_md.stp.group = stp_group; + local_md.multicast.rpf_group = mrpf_group; + local_md.learning.bd_mode = learning_mode; + local_md.ipv4.unicast_enable = ipv4_unicast_enable; + local_md.ipv4.multicast_enable = ipv4_multicast_enable; + local_md.ipv4.multicast_snooping = igmp_snooping_enable; + local_md.ipv6.unicast_enable = ipv6_unicast_enable; + local_md.ipv6.multicast_enable = ipv6_multicast_enable; + local_md.ipv6.multicast_snooping = mld_snooping_enable; + } + @placement_priority(2) @name(".port_vlan_to_bd_mapping") table port_vlan_to_bd_mapping { + key = { + local_md.ingress_port_lag_index: ternary; + hdr.vlan_tag[0].isValid() : ternary; + hdr.vlan_tag[0].vid : ternary; + } + actions = { + NoAction; + port_vlan_miss; + set_bd_properties; + } + const default_action = NoAction; + implementation = bd_action_profile; + size = port_vlan_table_size; + } + @placement_priority(2) @name(".vlan_to_bd_mapping") table vlan_to_bd_mapping { + key = { + hdr.vlan_tag[0].vid: exact; + } + actions = { + NoAction; + port_vlan_miss; + set_bd_properties; + } + const default_action = port_vlan_miss; + implementation = bd_action_profile; + size = vlan_table_size; + } + @name(".cpu_to_bd_mapping") table cpu_to_bd_mapping { + key = { + hdr.cpu.ingress_bd: exact; + } + actions = { + NoAction; + port_vlan_miss; + set_bd_properties; + } + const default_action = port_vlan_miss; + implementation = bd_action_profile; + size = bd_table_size; + } + @name(".set_peer_link_properties") action set_peer_link_properties() { + local_md.flags.peer_link = true; + } + @name(".peer_link") table peer_link { + key = { + local_md.ingress_port_lag_index: exact; + } + actions = { + NoAction; + set_peer_link_properties; + } + const default_action = NoAction; + size = port_table_size; + } + apply { + port_mirror.apply(local_md.ingress_port, local_md.mirror); + switch (port_mapping.apply().action_run) { + set_port_properties: { + if (!port_vlan_to_bd_mapping.apply().hit) { + if (hdr.vlan_tag[0].isValid()) { + vlan_to_bd_mapping.apply(); + } + } + rmac.apply(hdr, local_md); + } + set_cpu_port_properties: { + // Need to check whether the cpu header is valid. + // Otherwise you are setting garbage. + if (hdr.cpu.isValid()) { + terminate_cpu_packet(); + } + } + } + if (hdr.vlan_tag[0].isValid() && !hdr.vlan_tag[1].isValid() && (bit<1>)local_md.flags.port_vlan_miss == 0) { + bit<32> pv_hash_ = hash.get({ local_md.ingress_port[6:0], hdr.vlan_tag[0].vid }); + local_md.flags.port_vlan_miss = (bool)check_vlan_membership.execute(pv_hash_); + } + } +} + +control IngressPortStats(in switch_header_t hdr, in switch_port_t port, in bit<1> drop, in bit<1> copy_to_cpu) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_ip_stats_count") action no_action() { + stats.count(); + } + @name(".ingress_ip_port_stats") table ingress_ip_port_stats { + key = { + port : exact; + hdr.ipv4.isValid() : ternary; + hdr.ipv6.isValid() : ternary; + drop : ternary; + copy_to_cpu : ternary; + hdr.ethernet.dst_addr: ternary; + } + actions = { + no_action; + } + const default_action = no_action; + size = 512; + counters = stats; + } + apply { + ingress_ip_port_stats.apply(); + } +} + +control EgressPortStats(in switch_header_t hdr, in switch_port_t port, in bit<1> drop, in bit<1> copy_to_cpu) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_ip_stats_count") action no_action() { + stats.count(); + } + @name(".egress_ip_port_stats") table egress_ip_port_stats { + key = { + port : exact; + hdr.ipv4.isValid() : ternary; + hdr.ipv6.isValid() : ternary; + drop : ternary; + copy_to_cpu : ternary; + hdr.ethernet.dst_addr: ternary; + } + actions = { + no_action; + } + const default_action = no_action; + size = 512; + counters = stats; + } + apply { + egress_ip_port_stats.apply(); + } +} + +control LAG(inout switch_local_metadata_t local_md, in switch_hash_t hash, out switch_port_t egress_port) { + Hash(HashAlgorithm_t.CRC16) selector_hash; + @name(".lag_action_profile") ActionProfile(LAG_SELECTOR_TABLE_SIZE) lag_action_profile; + @name(".lag_selector") ActionSelector(lag_action_profile, selector_hash, SelectorMode_t.FAIR, LAG_MAX_MEMBERS_PER_GROUP, LAG_GROUP_TABLE_SIZE) lag_selector; + @name(".set_lag_port") action set_lag_port(switch_port_t port) { + egress_port = port; + } + @name(".set_peer_link_port") action set_peer_link_port(switch_port_t port, switch_port_lag_index_t port_lag_index) { + egress_port = port; + local_md.egress_port_lag_index = port_lag_index; + local_md.checks.same_if = local_md.ingress_port_lag_index ^ port_lag_index; + } + @name(".lag_miss") action lag_miss() { + } + @name(".lag_table") table lag { + key = { + local_md.egress_port_lag_index: exact @name("port_lag_index") ; + hash : selector; + } + actions = { + lag_miss; + set_lag_port; + } + // const default_action = set_lag_port(8); + const default_action = lag_miss; + size = LAG_TABLE_SIZE; + implementation = lag_selector; + } + apply { + egress_port = 8; + // lag.apply(); + } +} + +control EgressPortMapping(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, in switch_port_t port)(switch_uint32_t table_size=288) { + @name(".set_port_normal") action set_port_normal(switch_port_lag_index_t port_lag_index, switch_eg_port_lag_label_t port_lag_label, switch_qos_group_t qos_group, switch_meter_index_t meter_index, switch_sflow_id_t sflow_session_id, bool mlag_member, switch_eg_port_lag_label_t acl_out_ports) { + local_md.egress_port_lag_index = port_lag_index; + local_md.egress_port_lag_label = port_lag_label; + local_md.qos.group = qos_group; + } + @name(".egress_port_mapping") table port_mapping { + key = { + port: exact; + } + actions = { + set_port_normal; + } + size = table_size; + } + apply { + port_mapping.apply(); + } +} + +control EgressCpuRewrite(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, in switch_port_t port)(switch_uint32_t table_size=288) { + @name(".cpu_rewrite") action cpu_rewrite() { + hdr.fabric.setValid(); + hdr.fabric.reserved = 0; + hdr.fabric.color = 0; + hdr.fabric.qos = 0; + hdr.fabric.reserved2 = 0; + hdr.cpu.setValid(); + hdr.cpu.egress_queue = 0; + hdr.cpu.tx_bypass = 0; + hdr.cpu.capture_ts = 0; + hdr.cpu.reserved = 0; + hdr.cpu.ingress_port = (bit<16>)local_md.ingress_port; + hdr.cpu.port_lag_index = (bit<16>)local_md.egress_port_lag_index; + hdr.cpu.ingress_bd = (bit<16>)local_md.bd; + hdr.cpu.reason_code = local_md.cpu_reason; + hdr.cpu.ether_type = hdr.ethernet.ether_type; + hdr.ethernet.ether_type = 0x9000; + } + @name(".cpu_port_rewrite") table cpu_port_rewrite { + key = { + port: exact; + } + actions = { + cpu_rewrite; + } + size = table_size; + } + apply { + cpu_port_rewrite.apply(); + } +} + +action set_port_state_properties(inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout switch_local_metadata_t local_md, switch_ig_port_lag_label_t port_lag_label, switch_yid_t exclusion_id, switch_meter_index_t meter_index, switch_sflow_id_t sflow_session_id) { + local_md.ingress_port_lag_label = port_lag_label; + ig_intr_md_for_tm.level2_exclusion_id = exclusion_id; + local_md.checks.same_if = SWITCH_FLOOD; + local_md.sflow.session_id = sflow_session_id; +} +action set_bd_state_properties(inout switch_local_metadata_t local_md, switch_bd_label_t bd_label, bool ipv4_unicast_enable, bool ipv4_multicast_enable, bool igmp_snooping_enable, bool ipv6_unicast_enable, bool ipv6_multicast_enable, bool mld_snooping_enable, bool mpls_enable, switch_packet_action_t vrf_ttl_violation, bool vrf_ttl_violation_valid, switch_packet_action_t vrf_ip_options_violation, switch_nat_zone_t zone) { + local_md.bd_label = bd_label; + local_md.ipv4.unicast_enable = ipv4_unicast_enable; + local_md.ipv4.multicast_enable = ipv4_multicast_enable; + local_md.ipv4.multicast_snooping = igmp_snooping_enable; + local_md.ipv6.unicast_enable = ipv6_unicast_enable; + local_md.ipv6.multicast_enable = ipv6_multicast_enable; + local_md.ipv6.multicast_snooping = mld_snooping_enable; + local_md.flags.vrf_ttl_violation = vrf_ttl_violation; + local_md.flags.vrf_ttl_violation_valid = vrf_ttl_violation_valid; + local_md.flags.vrf_ip_options_violation = vrf_ip_options_violation; +} +control IngressPortBDState(inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, inout switch_local_metadata_t local_md) { + @use_hash_action(0) table port_state { + key = { + local_md.ingress_port: exact; + } + actions = { + set_port_state_properties(ig_intr_md_for_tm, local_md); + } + const default_action = set_port_state_properties(ig_intr_md_for_tm, local_md, 0, 0, 0, 0); + size = 512; + } + @use_hash_action(0) table bd_state { + key = { + local_md.bd[12:0]: exact; + } + actions = { + set_bd_state_properties(local_md); + } + const default_action = set_bd_state_properties(local_md, 0, false, false, false, false, false, false, false, 0, false, 0, 0); + size = 8192; + } + apply { + port_state.apply(); + bd_state.apply(); + } +} + +control PktValidation(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + const switch_uint32_t table_size = MIN_TABLE_SIZE; + action valid_ethernet_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.pkt_type = pkt_type; + local_md.lkp.mac_dst_addr = hdr.ethernet.dst_addr; + } + @name(".malformed_eth_pkt") action malformed_eth_pkt(bit<8> reason) { + local_md.lkp.mac_dst_addr = hdr.ethernet.dst_addr; + local_md.lkp.mac_type = hdr.ethernet.ether_type; + local_md.l2_drop_reason = reason; + } + @name(".valid_pkt_untagged") action valid_pkt_untagged(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_type = hdr.ethernet.ether_type; + valid_ethernet_pkt(pkt_type); + } + @name(".valid_pkt_tagged") action valid_pkt_tagged(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_type = hdr.vlan_tag[0].ether_type; + local_md.lkp.pcp = hdr.vlan_tag[0].pcp; + valid_ethernet_pkt(pkt_type); + } + @name(".validate_ethernet") table validate_ethernet { + key = { + hdr.ethernet.src_addr : ternary; + hdr.ethernet.dst_addr : ternary; + hdr.vlan_tag[0].isValid(): ternary; + } + actions = { + malformed_eth_pkt; + valid_pkt_untagged; + valid_pkt_tagged; + } + size = table_size; + } + @name(".valid_arp_pkt") action valid_arp_pkt() { + local_md.lkp.arp_opcode = hdr.arp.opcode; + } + @name(".valid_ipv6_pkt") action valid_ipv6_pkt(bool is_link_local) { + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV6; + local_md.lkp.ip_tos = hdr.ipv6.traffic_class; + local_md.lkp.ip_proto = hdr.ipv6.next_hdr; + local_md.lkp.ip_ttl = hdr.ipv6.hop_limit; + local_md.lkp.ip_src_addr = hdr.ipv6.src_addr; + local_md.lkp.ip_dst_addr = hdr.ipv6.dst_addr; + local_md.lkp.ipv6_flow_label = hdr.ipv6.flow_label; + local_md.flags.link_local = is_link_local; + } + @name(".valid_ipv4_pkt") action valid_ipv4_pkt(switch_ip_frag_t ip_frag, bool is_link_local) { + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV4; + local_md.lkp.ip_tos = hdr.ipv4.diffserv; + local_md.lkp.ip_proto = hdr.ipv4.protocol; + local_md.lkp.ip_ttl = hdr.ipv4.ttl; + local_md.lkp.ip_src_addr[63:0] = 64w0; + local_md.lkp.ip_src_addr[95:64] = hdr.ipv4.src_addr; + local_md.lkp.ip_src_addr[127:96] = 32w0; + local_md.lkp.ip_dst_addr[63:0] = 64w0; + local_md.lkp.ip_dst_addr[95:64] = hdr.ipv4.dst_addr; + local_md.lkp.ip_dst_addr[127:96] = 32w0; + local_md.lkp.ip_frag = ip_frag; + local_md.flags.link_local = is_link_local; + } + @name(".malformed_ipv4_pkt") action malformed_ipv4_pkt(bit<8> reason, switch_ip_frag_t ip_frag) { + valid_ipv4_pkt(ip_frag, false); + local_md.drop_reason = reason; + } + @name(".malformed_ipv6_pkt") action malformed_ipv6_pkt(bit<8> reason) { + valid_ipv6_pkt(false); + local_md.drop_reason = reason; + } + @name(".validate_ip") table validate_ip { + key = { + hdr.arp.isValid() : ternary; + hdr.ipv4.isValid() : ternary; + local_md.flags.ipv4_checksum_err: ternary; + hdr.ipv4.version : ternary; + hdr.ipv4.ihl : ternary; + hdr.ipv4.flags : ternary; + hdr.ipv4.frag_offset : ternary; + hdr.ipv4.ttl : ternary; + hdr.ipv4.src_addr[31:0] : ternary; + hdr.ipv6.isValid() : ternary; + hdr.ipv6.version : ternary; + hdr.ipv6.hop_limit : ternary; + hdr.ipv6.src_addr[127:0] : ternary; + } + actions = { + malformed_ipv4_pkt; + malformed_ipv6_pkt; + valid_arp_pkt; + valid_ipv4_pkt; + valid_ipv6_pkt; + } + size = table_size; + } + action set_tcp_ports() { + local_md.lkp.l4_src_port = hdr.tcp.src_port; + local_md.lkp.l4_dst_port = hdr.tcp.dst_port; + local_md.lkp.tcp_flags = hdr.tcp.flags; + } + action set_udp_ports() { + local_md.lkp.l4_src_port = hdr.udp.src_port; + local_md.lkp.l4_dst_port = hdr.udp.dst_port; + local_md.lkp.tcp_flags = 0; + } + action set_icmp_type() { + local_md.lkp.l4_src_port[7:0] = hdr.icmp.type; + local_md.lkp.l4_src_port[15:8] = hdr.icmp.code; + local_md.lkp.l4_dst_port = 0; + local_md.lkp.tcp_flags = 0; + } + action set_igmp_type() { + local_md.lkp.l4_src_port[7:0] = hdr.igmp.type; + local_md.lkp.l4_src_port[15:8] = 0; + local_md.lkp.l4_dst_port = 0; + local_md.lkp.tcp_flags = 0; + } + table validate_other { + key = { + hdr.tcp.isValid() : exact; + hdr.udp.isValid() : exact; + hdr.icmp.isValid(): exact; + hdr.igmp.isValid(): exact; + } + actions = { + NoAction; + set_tcp_ports; + set_udp_ports; + set_icmp_type; + } + const default_action = NoAction; + const entries = { + (true, false, false, false) : set_tcp_ports(); + (false, true, false, false) : set_udp_ports(); + (false, false, true, false) : set_icmp_type(); + } + size = 16; + } + apply { + validate_ethernet.apply(); + validate_ip.apply(); + validate_other.apply(); + } +} + +control SameMacCheck(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".compute_same_mac_check") action compute_same_mac_check() { + local_md.drop_reason = SWITCH_DROP_REASON_OUTER_SAME_MAC_CHECK; + } + @ways(1) @name(".same_mac_check") table same_mac_check { + key = { + local_md.same_mac: exact; + } + actions = { + NoAction; + compute_same_mac_check; + } + const default_action = NoAction; + const entries = { + 48w0x0 : compute_same_mac_check(); + } + } + apply { + local_md.same_mac = hdr.ethernet.src_addr ^ hdr.ethernet.dst_addr; + same_mac_check.apply(); + } +} + +control InnerPktValidation(in switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".valid_inner_ethernet_pkt") action valid_ethernet_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_dst_addr = hdr.inner_ethernet.dst_addr; + local_md.lkp.mac_type = hdr.inner_ethernet.ether_type; + local_md.lkp.pkt_type = pkt_type; + } + @name(".valid_inner_ipv4_pkt") action valid_ipv4_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_dst_addr = hdr.inner_ethernet.dst_addr; + local_md.lkp.mac_type = 0x800; + local_md.lkp.pkt_type = pkt_type; + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV4; + local_md.lkp.ip_tos = hdr.inner_ipv4.diffserv; + local_md.lkp.ip_ttl = hdr.inner_ipv4.ttl; + local_md.lkp.ip_proto = hdr.inner_ipv4.protocol; + local_md.lkp.ip_src_addr[63:0] = 64w0; + local_md.lkp.ip_src_addr[95:64] = hdr.inner_ipv4.src_addr; + local_md.lkp.ip_src_addr[127:96] = 32w0; + local_md.lkp.ip_dst_addr[63:0] = 64w0; + local_md.lkp.ip_dst_addr[95:64] = hdr.inner_ipv4.dst_addr; + local_md.lkp.ip_dst_addr[127:96] = 32w0; + } + @name(".valid_inner_ipv6_pkt") action valid_ipv6_pkt(switch_pkt_type_t pkt_type) { + local_md.lkp.mac_dst_addr = hdr.inner_ethernet.dst_addr; + local_md.lkp.mac_type = 0x86dd; + local_md.lkp.pkt_type = pkt_type; + local_md.lkp.ip_type = SWITCH_IP_TYPE_IPV6; + local_md.lkp.ip_tos = hdr.inner_ipv6.traffic_class; + local_md.lkp.ip_ttl = hdr.inner_ipv6.hop_limit; + local_md.lkp.ip_proto = hdr.inner_ipv6.next_hdr; + local_md.lkp.ip_src_addr = hdr.inner_ipv6.src_addr; + local_md.lkp.ip_dst_addr = hdr.inner_ipv6.dst_addr; + local_md.lkp.ipv6_flow_label = hdr.inner_ipv6.flow_label; + local_md.flags.link_local = false; + } + action set_tcp_ports() { + local_md.lkp.l4_src_port = hdr.inner_tcp.src_port; + local_md.lkp.l4_dst_port = hdr.inner_tcp.dst_port; + } + action set_udp_ports() { + local_md.lkp.l4_src_port = hdr.inner_udp.src_port; + local_md.lkp.l4_dst_port = hdr.inner_udp.dst_port; + } + @name(".valid_inner_ipv4_tcp_pkt") action valid_ipv4_tcp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv4_pkt(pkt_type); + set_tcp_ports(); + } + @name(".valid_inner_ipv4_udp_pkt") action valid_ipv4_udp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv4_pkt(pkt_type); + set_udp_ports(); + } + @name(".valid_inner_ipv6_tcp_pkt") action valid_ipv6_tcp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv6_pkt(pkt_type); + set_tcp_ports(); + } + @name(".valid_inner_ipv6_udp_pkt") action valid_ipv6_udp_pkt(switch_pkt_type_t pkt_type) { + valid_ipv6_pkt(pkt_type); + set_udp_ports(); + } + @name(".malformed_l2_inner_pkt") action malformed_l2_pkt(bit<8> reason) { + local_md.l2_drop_reason = reason; + } + @name(".malformed_l3_inner_pkt") action malformed_l3_pkt(bit<8> reason) { + local_md.drop_reason = reason; + } + @name(".validate_inner_ethernet") table validate_ethernet { + key = { + hdr.inner_ethernet.isValid() : ternary; + hdr.inner_ethernet.dst_addr : ternary; + hdr.inner_ipv6.isValid() : ternary; + hdr.inner_ipv6.version : ternary; + hdr.inner_ipv6.hop_limit : ternary; + hdr.inner_ipv4.isValid() : ternary; + local_md.flags.inner_ipv4_checksum_err: ternary; + hdr.inner_ipv4.version : ternary; + hdr.inner_ipv4.ihl : ternary; + hdr.inner_ipv4.ttl : ternary; + hdr.inner_tcp.isValid() : ternary; + hdr.inner_udp.isValid() : ternary; + } + actions = { + NoAction; + valid_ipv4_tcp_pkt; + valid_ipv4_udp_pkt; + valid_ipv4_pkt; + valid_ipv6_tcp_pkt; + valid_ipv6_udp_pkt; + valid_ipv6_pkt; + valid_ethernet_pkt; + malformed_l2_pkt; + malformed_l3_pkt; + } + size = MIN_TABLE_SIZE; + } + apply { + validate_ethernet.apply(); + } +} + +control IngressTunnel(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout switch_lookup_fields_t lkp) { + InnerPktValidation() pkt_validation; + @name(".tunnel_rmac_miss") action rmac_miss() { + local_md.flags.rmac_hit = false; + } + @name(".tunnel_rmac_hit") action rmac_hit() { + local_md.flags.rmac_hit = true; + } + @name(".vxlan_rmac") table vxlan_rmac { + key = { + local_md.tunnel.vni : exact; + hdr.inner_ethernet.dst_addr: exact; + } + actions = { + @defaultonly rmac_miss; + rmac_hit; + } + const default_action = rmac_miss; + size = VNI_MAPPING_TABLE_SIZE; + } + @name(".vxlan_device_rmac") table vxlan_device_rmac { + key = { + hdr.inner_ethernet.dst_addr: exact; + } + actions = { + @defaultonly rmac_miss; + rmac_hit; + } + const default_action = rmac_miss; + size = 128; + } + @name(".dst_vtep_hit") action dst_vtep_hit(switch_tunnel_mode_t ttl_mode, switch_tunnel_mode_t qos_mode) { + local_md.tunnel.ttl_mode = ttl_mode; + local_md.tunnel.qos_mode = qos_mode; + } + @name(".set_inner_bd_properties_base") action set_inner_bd_properties_base(switch_bd_t bd, switch_vrf_t vrf, switch_packet_action_t vrf_ttl_violation, bool vrf_ttl_violation_valid, switch_packet_action_t vrf_ip_options_violation, switch_bd_label_t bd_label, switch_learning_mode_t learning_mode, bool ipv4_unicast_enable, bool ipv6_unicast_enable) { + local_md.ingress_outer_bd = local_md.bd; + local_md.bd = bd; + local_md.bd_label = bd_label; + local_md.vrf = vrf; + local_md.flags.vrf_ttl_violation = vrf_ttl_violation; + local_md.flags.vrf_ttl_violation_valid = vrf_ttl_violation_valid; + local_md.flags.vrf_ip_options_violation = vrf_ip_options_violation; + local_md.learning.bd_mode = learning_mode; + local_md.ipv4.unicast_enable = ipv4_unicast_enable; + local_md.ipv4.multicast_enable = false; + local_md.ipv4.multicast_snooping = false; + local_md.ipv6.unicast_enable = ipv6_unicast_enable; + local_md.ipv6.multicast_enable = false; + local_md.ipv6.multicast_snooping = false; + local_md.tunnel.terminate = true; + } + @name(".set_inner_bd_properties") action set_inner_bd_properties(switch_bd_t bd, switch_vrf_t vrf, switch_packet_action_t vrf_ttl_violation, bool vrf_ttl_violation_valid, switch_packet_action_t vrf_ip_options_violation, switch_bd_label_t bd_label, switch_learning_mode_t learning_mode, bool ipv4_unicast_enable, bool ipv6_unicast_enable, switch_tunnel_mode_t ttl_mode, switch_tunnel_mode_t qos_mode, switch_tunnel_mode_t ecn_mode) { + set_inner_bd_properties_base(bd, vrf, vrf_ttl_violation, vrf_ttl_violation_valid, vrf_ip_options_violation, bd_label, learning_mode, ipv4_unicast_enable, ipv6_unicast_enable); + local_md.tunnel.ttl_mode = ttl_mode; + local_md.tunnel.qos_mode = qos_mode; + } + @name(".dst_vtep") table dst_vtep { + key = { + hdr.ipv4.src_addr : ternary @name("src_addr") ; + hdr.ipv4.dst_addr : ternary @name("dst_addr") ; + local_md.vrf : exact; + local_md.tunnel.type: exact; + } + actions = { + NoAction; + dst_vtep_hit; + set_inner_bd_properties; + } + size = IPV4_DST_VTEP_TABLE_SIZE; + const default_action = NoAction; + requires_versioning = false; + } + action set_ip_tos_outer_ipv4() { + lkp.ip_tos = hdr.ipv4.diffserv; + } + action set_ip_tos_outer_ipv6() { + lkp.ip_tos = hdr.ipv6.traffic_class; + } + table lkp_ip_tos_outer { + key = { + hdr.ipv4.isValid(): exact; + hdr.ipv6.isValid(): exact; + } + actions = { + NoAction; + set_ip_tos_outer_ipv4; + set_ip_tos_outer_ipv6; + } + size = 4; + const entries = { + (true, false) : set_ip_tos_outer_ipv4; + (false, true) : set_ip_tos_outer_ipv6; + } + const default_action = NoAction; + } + @name(".vni_to_bd_mapping") table vni_to_bd_mapping { + key = { + local_md.tunnel.vni: exact; + } + actions = { + NoAction; + set_inner_bd_properties_base; + } + default_action = NoAction; + size = VNI_MAPPING_TABLE_SIZE; + } + apply { + if (local_md.flags.rmac_hit) { + if (lkp.ip_type == SWITCH_IP_TYPE_IPV4) { + switch (dst_vtep.apply().action_run) { + dst_vtep_hit: { + if (vni_to_bd_mapping.apply().hit) { + if (!vxlan_rmac.apply().hit) { + vxlan_device_rmac.apply(); + } + ; + pkt_validation.apply(hdr, local_md); + } + } + set_inner_bd_properties: { + pkt_validation.apply(hdr, local_md); + } + } + } else if (lkp.ip_type == SWITCH_IP_TYPE_IPV6) { + } + } + if (local_md.tunnel.terminate && local_md.tunnel.qos_mode == SWITCH_TUNNEL_MODE_UNIFORM) { + lkp_ip_tos_outer.apply(); + } + } +} + +control TunnelDecap(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + action store_outer_ipv4_fields() { + local_md.tunnel.decap_tos = hdr.ipv4.diffserv; + local_md.tunnel.decap_ttl = hdr.ipv4.ttl; + } + action store_outer_ipv6_fields() { + local_md.tunnel.decap_tos = hdr.ipv6.traffic_class; + local_md.tunnel.decap_ttl = hdr.ipv6.hop_limit; + } + action store_outer_mpls_fields() { + local_md.tunnel.decap_exp = hdr.mpls[0].exp; + local_md.tunnel.decap_ttl = hdr.mpls[0].ttl; + } + action copy_ipv4_header() { + hdr.ipv4.setValid(); + hdr.ipv6.setInvalid(); + hdr.ipv4.version = hdr.inner_ipv4.version; + hdr.ipv4.ihl = hdr.inner_ipv4.ihl; + hdr.ipv4.total_len = hdr.inner_ipv4.total_len; + hdr.ipv4.identification = hdr.inner_ipv4.identification; + hdr.ipv4.flags = hdr.inner_ipv4.flags; + hdr.ipv4.frag_offset = hdr.inner_ipv4.frag_offset; + hdr.ipv4.protocol = hdr.inner_ipv4.protocol; + hdr.ipv4.src_addr = hdr.inner_ipv4.src_addr; + hdr.ipv4.dst_addr = hdr.inner_ipv4.dst_addr; + hdr.ipv4.diffserv = hdr.inner_ipv4.diffserv; + hdr.ipv4.ttl = hdr.inner_ipv4.ttl; + hdr.inner_ipv4.setInvalid(); + } + action copy_ipv6_header() { + hdr.ipv6.setValid(); + hdr.ipv4.setInvalid(); + hdr.ipv6 = hdr.inner_ipv6; + hdr.inner_ipv6.setInvalid(); + } + action invalidate_vxlan_header() { + hdr.vxlan.setInvalid(); + hdr.udp.setInvalid(); + hdr.inner_ethernet.setInvalid(); + } + action invalidate_gre_header() { + } + action invalidate_vlan_tag0() { + hdr.vlan_tag[0].setInvalid(); + } + action decap_v4_inner_ethernet_ipv4() { + store_outer_ipv4_fields(); + invalidate_vlan_tag0(); + hdr.ethernet = hdr.inner_ethernet; + copy_ipv4_header(); + invalidate_vxlan_header(); + } + action decap_v4_inner_ethernet_ipv6() { + store_outer_ipv4_fields(); + invalidate_vlan_tag0(); + hdr.ethernet = hdr.inner_ethernet; + copy_ipv6_header(); + invalidate_vxlan_header(); + } + action decap_v4_inner_ethernet_non_ip() { + store_outer_ipv4_fields(); + invalidate_vlan_tag0(); + hdr.ethernet = hdr.inner_ethernet; + hdr.ipv4.setInvalid(); + hdr.ipv6.setInvalid(); + invalidate_vxlan_header(); + } + action decap_v4_inner_ipv4() { + store_outer_ipv4_fields(); + invalidate_vlan_tag0(); + hdr.ethernet.ether_type = 0x800; + copy_ipv4_header(); + invalidate_gre_header(); + } + action decap_v4_inner_ipv6() { + store_outer_ipv4_fields(); + invalidate_vlan_tag0(); + hdr.ethernet.ether_type = 0x86dd; + copy_ipv6_header(); + invalidate_gre_header(); + } + action decap_v6_inner_ethernet_ipv4() { + store_outer_ipv6_fields(); + invalidate_vlan_tag0(); + hdr.ethernet = hdr.inner_ethernet; + copy_ipv4_header(); + invalidate_vxlan_header(); + } + action decap_v6_inner_ethernet_ipv6() { + store_outer_ipv6_fields(); + invalidate_vlan_tag0(); + hdr.ethernet = hdr.inner_ethernet; + copy_ipv6_header(); + invalidate_vxlan_header(); + } + action decap_v6_inner_ethernet_non_ip() { + store_outer_ipv6_fields(); + invalidate_vlan_tag0(); + hdr.ethernet = hdr.inner_ethernet; + hdr.ipv4.setInvalid(); + hdr.ipv6.setInvalid(); + invalidate_vxlan_header(); + } + action decap_v6_inner_ipv4() { + store_outer_ipv6_fields(); + invalidate_vlan_tag0(); + hdr.ethernet.ether_type = 0x800; + copy_ipv4_header(); + invalidate_gre_header(); + } + action decap_v6_inner_ipv6() { + store_outer_ipv6_fields(); + invalidate_vlan_tag0(); + hdr.ethernet.ether_type = 0x86dd; + copy_ipv6_header(); + invalidate_gre_header(); + } + action decap_mpls_inner_ipv4() { + store_outer_mpls_fields(); + invalidate_vlan_tag0(); + hdr.mpls[0].setInvalid(); + hdr.mpls[1].setInvalid(); + hdr.mpls[2].setInvalid(); + hdr.ethernet.ether_type = 0x800; + copy_ipv4_header(); + } + action decap_mpls_inner_ipv6() { + store_outer_mpls_fields(); + invalidate_vlan_tag0(); + hdr.mpls[0].setInvalid(); + hdr.mpls[1].setInvalid(); + hdr.mpls[2].setInvalid(); + hdr.ethernet.ether_type = 0x86dd; + copy_ipv6_header(); + } + action mpls_pop1() { + store_outer_mpls_fields(); + invalidate_vlan_tag0(); + hdr.mpls.pop_front(1); + } + action mpls_pop2() { + store_outer_mpls_fields(); + invalidate_vlan_tag0(); + hdr.mpls.pop_front(2); + } + table decap_tunnel_hdr { + key = { + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + hdr.udp.isValid() : exact; + hdr.inner_ethernet.isValid(): exact; + hdr.inner_ipv4.isValid() : exact; + hdr.inner_ipv6.isValid() : exact; + } + actions = { + decap_v4_inner_ethernet_ipv4; + decap_v4_inner_ethernet_ipv6; + decap_v4_inner_ethernet_non_ip; + decap_v6_inner_ethernet_ipv4; + decap_v6_inner_ethernet_ipv6; + decap_v6_inner_ethernet_non_ip; + decap_v4_inner_ipv4; + decap_v4_inner_ipv6; + decap_v6_inner_ipv4; + decap_v6_inner_ipv6; + } + const entries = { + (true, false, true, true, true, false) : decap_v4_inner_ethernet_ipv4; + (true, false, true, true, false, true) : decap_v4_inner_ethernet_ipv6; + (true, false, true, true, false, false) : decap_v4_inner_ethernet_non_ip; + (false, true, true, true, true, false) : decap_v6_inner_ethernet_ipv4; + (false, true, true, true, false, true) : decap_v6_inner_ethernet_ipv6; + (false, true, true, true, false, false) : decap_v6_inner_ethernet_non_ip; + (true, false, false, false, true, false) : decap_v4_inner_ipv4; + (true, false, false, false, false, true) : decap_v4_inner_ipv6; + (false, true, false, false, true, false) : decap_v6_inner_ipv4; + (false, true, false, false, false, true) : decap_v6_inner_ipv6; + } + size = MIN_TABLE_SIZE; + } + action decap_len_outer_ipv4_udp() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv4.minSizeInBytes() - hdr.udp.minSizeInBytes() - hdr.vxlan.minSizeInBytes() - hdr.inner_ethernet.minSizeInBytes(); + } + action decap_len_outer_ipv6_udp() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv6.minSizeInBytes() - hdr.udp.minSizeInBytes() - hdr.vxlan.minSizeInBytes() - hdr.inner_ethernet.minSizeInBytes(); + } + action decap_len_outer_ipv4() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv4.minSizeInBytes(); + } + action decap_len_outer_ipv6() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv6.minSizeInBytes(); + } + action decap_len_outer_qtag_ipv4_udp() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv4.minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes() - hdr.udp.minSizeInBytes() - hdr.vxlan.minSizeInBytes() - hdr.inner_ethernet.minSizeInBytes(); + } + action decap_len_outer_qtag_ipv6_udp() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv6.minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes() - hdr.udp.minSizeInBytes() - hdr.vxlan.minSizeInBytes() - hdr.inner_ethernet.minSizeInBytes(); + } + action decap_len_outer_qtag_ipv4() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv4.minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes(); + } + action decap_len_outer_qtag_ipv6() { + local_md.pkt_length = local_md.pkt_length - hdr.ipv6.minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes(); + } + action decap_len_pop1() { + local_md.pkt_length = local_md.pkt_length - hdr.mpls[0].minSizeInBytes(); + } + action decap_len_pop2() { + local_md.pkt_length = local_md.pkt_length - 2 * hdr.mpls[0].minSizeInBytes(); + } + action decap_len_pop3() { + local_md.pkt_length = local_md.pkt_length - 3 * hdr.mpls[0].minSizeInBytes(); + } + action decap_len_qtag_pop1() { + local_md.pkt_length = local_md.pkt_length - hdr.mpls[0].minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes(); + } + action decap_len_qtag_pop2() { + local_md.pkt_length = local_md.pkt_length - 2 * hdr.mpls[0].minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes(); + } + action decap_len_qtag_pop3() { + local_md.pkt_length = local_md.pkt_length - 3 * hdr.mpls[0].minSizeInBytes() - hdr.vlan_tag[0].minSizeInBytes(); + } + table decap_len_adjust { + key = { + hdr.udp.isValid() : exact; + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + hdr.vlan_tag[0].isValid(): exact; + } + actions = { + decap_len_outer_ipv4_udp; + decap_len_outer_ipv6_udp; + decap_len_outer_ipv6; + decap_len_outer_ipv4; + decap_len_outer_qtag_ipv4_udp; + decap_len_outer_qtag_ipv6_udp; + decap_len_outer_qtag_ipv6; + decap_len_outer_qtag_ipv4; + } + const entries = { + (true, true, false, false) : decap_len_outer_ipv4_udp; + (true, false, true, false) : decap_len_outer_ipv6_udp; + (false, true, false, false) : decap_len_outer_ipv4; + (false, false, true, false) : decap_len_outer_ipv6; + (true, true, false, true) : decap_len_outer_qtag_ipv4_udp; + (true, false, true, true) : decap_len_outer_qtag_ipv6_udp; + (false, true, false, true) : decap_len_outer_qtag_ipv4; + (false, false, true, true) : decap_len_outer_qtag_ipv6; + } + size = MIN_TABLE_SIZE; + } + action decap_ttl_inner_v4_uniform() { + hdr.ipv4.ttl = local_md.tunnel.decap_ttl; + } + action decap_ttl_inner_v6_uniform() { + hdr.ipv6.hop_limit = local_md.tunnel.decap_ttl; + } + table decap_ttl { + key = { + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + local_md.tunnel.ttl_mode: exact; + } + actions = { + NoAction; + decap_ttl_inner_v4_uniform; + decap_ttl_inner_v6_uniform; + } + const entries = { + (true, false, SWITCH_TUNNEL_MODE_UNIFORM) : decap_ttl_inner_v4_uniform; + (false, true, SWITCH_TUNNEL_MODE_UNIFORM) : decap_ttl_inner_v6_uniform; + } + const default_action = NoAction; + size = 32; + } + action decap_dscp_inner_v4_uniform() { + @in_hash { + hdr.ipv4.diffserv = local_md.tunnel.decap_tos; + } + } + action decap_dscp_inner_v6_uniform() { + @in_hash { + hdr.ipv6.traffic_class = local_md.tunnel.decap_tos; + } + } + action decap_ecn_inner_v4_from_outer() { + @in_hash { + hdr.ipv4.diffserv[1:0] = local_md.tunnel.decap_tos[1:0]; + } + } + action decap_ecn_inner_v6_from_outer() { + @in_hash { + hdr.ipv6.traffic_class[1:0] = local_md.tunnel.decap_tos[1:0]; + } + } + table decap_dscp { + key = { + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + local_md.tunnel.qos_mode: exact; + } + actions = { + NoAction; + decap_dscp_inner_v4_uniform; + decap_dscp_inner_v6_uniform; + decap_ecn_inner_v4_from_outer; + decap_ecn_inner_v6_from_outer; + } + const entries = { + (true, false, SWITCH_TUNNEL_MODE_UNIFORM) : decap_dscp_inner_v4_uniform; + (false, true, SWITCH_TUNNEL_MODE_UNIFORM) : decap_dscp_inner_v6_uniform; + (true, false, SWITCH_TUNNEL_MODE_PIPE) : decap_ecn_inner_v4_from_outer; + (false, true, SWITCH_TUNNEL_MODE_PIPE) : decap_ecn_inner_v6_from_outer; + } + size = 32; + } + apply { + if (!local_md.flags.bypass_egress) { + decap_len_adjust.apply(); + decap_tunnel_hdr.apply(); + decap_ttl.apply(); + decap_dscp.apply(); + } + } +} + +control TunnelNexthop(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".l2_tunnel_encap") action l2_tunnel_encap(switch_tunnel_type_t type, switch_tunnel_ip_index_t dip_index, switch_tunnel_index_t tunnel_index) { + local_md.tunnel.type = type; + local_md.tunnel.index = tunnel_index; + local_md.tunnel.dip_index = dip_index; + local_md.flags.routed = true; + } + @name(".l3_tunnel_encap") action l3_tunnel_encap(mac_addr_t dmac, switch_tunnel_type_t type, switch_tunnel_ip_index_t dip_index, switch_tunnel_index_t tunnel_index) { + local_md.flags.routed = true; + local_md.tunnel.type = type; + local_md.tunnel.dip_index = dip_index; + local_md.tunnel.index = tunnel_index; + hdr.ethernet.dst_addr = dmac; + } + @name(".l3_tunnel_encap_with_vni") action l3_tunnel_encap_with_vni(mac_addr_t dmac, switch_tunnel_type_t type, switch_tunnel_vni_t vni, switch_tunnel_ip_index_t dip_index, switch_tunnel_index_t tunnel_index) { + local_md.flags.routed = true; + local_md.tunnel.type = type; + local_md.tunnel.index = tunnel_index; + local_md.tunnel.dip_index = dip_index; + hdr.ethernet.dst_addr = dmac; + local_md.tunnel.vni = vni; + } + @name(".srv6_encap") action srv6_encap(switch_tunnel_index_t tunnel_index, bit<3> seg_len) { + local_md.flags.routed = true; + local_md.tunnel.type = SWITCH_EGRESS_TUNNEL_TYPE_SRV6_ENCAP; + local_md.tunnel.index = tunnel_index; + local_md.tunnel.srv6_seg_len = seg_len; + } + @name(".srv6_insert") action srv6_insert(bit<3> seg_len) { + local_md.flags.routed = true; + local_md.tunnel.type = SWITCH_EGRESS_TUNNEL_TYPE_SRV6_INSERT; + local_md.tunnel.srv6_seg_len = seg_len; + } + @name(".tunnel_nexthop") table tunnel_nexthop { + key = { + local_md.tunnel_nexthop: exact; + } + actions = { + NoAction; + l2_tunnel_encap; + l3_tunnel_encap; + l3_tunnel_encap_with_vni; + } + const default_action = NoAction; + size = TUNNEL_NEXTHOP_TABLE_SIZE; + } + apply { + if (!local_md.flags.bypass_egress) { + tunnel_nexthop.apply(); + } + } +} + +control TunnelEncap(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + bit<16> payload_len; + bit<8> ip_proto; + bit<16> gre_proto; + action copy_ipv4_header() { + hdr.inner_ipv4.setValid(); + hdr.inner_ipv6.setInvalid(); + hdr.inner_ipv4.version = hdr.ipv4.version; + hdr.inner_ipv4.ihl = hdr.ipv4.ihl; + hdr.inner_ipv4.diffserv = hdr.ipv4.diffserv; + hdr.inner_ipv4.total_len = hdr.ipv4.total_len; + hdr.inner_ipv4.identification = hdr.ipv4.identification; + hdr.inner_ipv4.flags = hdr.ipv4.flags; + hdr.inner_ipv4.frag_offset = hdr.ipv4.frag_offset; + hdr.inner_ipv4.ttl = hdr.ipv4.ttl; + hdr.inner_ipv4.protocol = hdr.ipv4.protocol; + hdr.inner_ipv4.src_addr = hdr.ipv4.src_addr; + hdr.inner_ipv4.dst_addr = hdr.ipv4.dst_addr; + local_md.inner_ipv4_checksum_update_en = true; + hdr.ipv4.setInvalid(); + } + action copy_inner_ipv4_udp() { + payload_len = hdr.ipv4.total_len; + copy_ipv4_header(); + hdr.inner_udp = hdr.udp; + hdr.udp.setInvalid(); + hdr.inner_udp.setValid(); + ip_proto = 4; + gre_proto = 0x800; + } + action copy_inner_ipv4_unknown() { + payload_len = hdr.ipv4.total_len; + copy_ipv4_header(); + ip_proto = 4; + gre_proto = 0x800; + } + action copy_inner_ipv6_udp() { + payload_len = hdr.ipv6.payload_len + 16w40; + hdr.inner_ipv6 = hdr.ipv6; + hdr.ipv6.setInvalid(); + hdr.inner_ipv4.setInvalid(); + hdr.inner_udp = hdr.udp; + ip_proto = 41; + gre_proto = 0x86dd; + hdr.udp.setInvalid(); + } + action copy_inner_ipv6_unknown() { + payload_len = hdr.ipv6.payload_len + 16w40; + hdr.inner_ipv6 = hdr.ipv6; + ip_proto = 41; + gre_proto = 0x86dd; + hdr.ipv6.setInvalid(); + hdr.inner_ipv4.setInvalid(); + } + action copy_inner_non_ip() { + payload_len = local_md.pkt_length - 16w14; + } + table tunnel_encap_0 { + key = { + hdr.ipv4.isValid(): exact; + hdr.ipv6.isValid(): exact; + hdr.udp.isValid() : exact; + } + actions = { + copy_inner_ipv4_udp; + copy_inner_ipv4_unknown; + copy_inner_ipv6_udp; + copy_inner_ipv6_unknown; + copy_inner_non_ip; + } + const entries = { + (true, false, false) : copy_inner_ipv4_unknown(); + (false, true, false) : copy_inner_ipv6_unknown(); + (true, false, true) : copy_inner_ipv4_udp(); + (false, true, true) : copy_inner_ipv6_udp(); + (false, false, false) : copy_inner_non_ip(); + } + size = 8; + } + action add_udp_header(bit<16> src_port, bit<16> dst_port) { + hdr.udp.setValid(); + hdr.udp.src_port = src_port; + hdr.udp.dst_port = dst_port; + hdr.udp.checksum = 0; + } + action add_vxlan_header(bit<24> vni) { + hdr.vxlan.setValid(); + hdr.vxlan.flags = 8w0x8; + hdr.vxlan.vni = vni; + } + action add_gre_header(bit<16> proto) { + } + action add_ipv4_header(bit<8> proto) { + hdr.ipv4.setValid(); + hdr.ipv4.version = 4w4; + hdr.ipv4.ihl = 4w5; + hdr.ipv4.identification = 0; + hdr.ipv4.flags = 0; + hdr.ipv4.frag_offset = 0; + hdr.ipv4.protocol = proto; + hdr.ipv6.setInvalid(); + } + action add_ipv6_header(bit<8> proto) { + hdr.ipv6.setValid(); + hdr.ipv6.version = 4w6; + hdr.ipv6.flow_label = 0; + hdr.ipv6.next_hdr = proto; + hdr.ipv4.setInvalid(); + } + @name(".encap_ipv4_vxlan") action encap_ipv4_vxlan(bit<16> vxlan_port) { + hdr.inner_ethernet = hdr.ethernet; + add_ipv4_header(17); + hdr.ipv4.flags = 0x2; + hdr.ipv4.total_len = payload_len + hdr.ipv4.minSizeInBytes() + hdr.udp.minSizeInBytes() + hdr.vxlan.minSizeInBytes() + hdr.inner_ethernet.minSizeInBytes(); + add_udp_header(local_md.tunnel.hash, vxlan_port); + hdr.udp.length = payload_len + hdr.udp.minSizeInBytes() + hdr.vxlan.minSizeInBytes() + hdr.inner_ethernet.minSizeInBytes(); + local_md.pkt_length = local_md.pkt_length + hdr.ipv4.minSizeInBytes() + hdr.udp.minSizeInBytes() + hdr.vxlan.minSizeInBytes() + hdr.inner_ethernet.minSizeInBytes(); + add_vxlan_header(local_md.tunnel.vni); + hdr.ethernet.ether_type = 0x800; + } + @name(".encap_ipv6_vxlan") action encap_ipv6_vxlan(bit<16> vxlan_port) { + } + @name(".encap_ipv4_ip") action encap_ipv4_ip() { + add_ipv4_header(ip_proto); + hdr.ipv4.total_len = payload_len + hdr.ipv4.minSizeInBytes(); + local_md.pkt_length = local_md.pkt_length + hdr.ipv4.minSizeInBytes(); + hdr.ethernet.ether_type = 0x800; + } + @name(".encap_ipv6_ip") action encap_ipv6_ip() { + } + @name(".tunnel_encap_1") table tunnel_encap_1 { + key = { + local_md.tunnel.type: exact; + } + actions = { + NoAction; + encap_ipv4_vxlan; + encap_ipv6_vxlan; + encap_ipv4_ip; + encap_ipv6_ip; + } + const default_action = NoAction; + size = MIN_TABLE_SIZE; + } + apply { + if (local_md.tunnel.type != SWITCH_EGRESS_TUNNEL_TYPE_NONE) { + if (local_md.tunnel.type != SWITCH_EGRESS_TUNNEL_TYPE_SRV6_INSERT) { + tunnel_encap_0.apply(); + } + if (local_md.tunnel.type == SWITCH_EGRESS_TUNNEL_TYPE_MPLS) { + } else { + tunnel_encap_1.apply(); + } + } + } +} + +control TunnelRewrite(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".ipv4_sip_rewrite") action ipv4_sip_rewrite(ipv4_addr_t src_addr, bit<8> ttl_val, bit<6> dscp_val) { + hdr.ipv4.src_addr = src_addr; + hdr.ipv4.diffserv[7:2] = dscp_val; + } + @name(".ipv6_sip_rewrite") action ipv6_sip_rewrite(ipv6_addr_t src_addr, bit<8> ttl_val, bit<6> dscp_val) { + hdr.ipv6.src_addr = src_addr; + hdr.ipv6.traffic_class[7:2] = dscp_val; + } + @name(".src_addr_rewrite") table src_addr_rewrite { + key = { + local_md.tunnel.index: exact; + } + actions = { + ipv4_sip_rewrite; + } + size = TUNNEL_OBJECT_SIZE; + } + @name(".encap_ttl_v4_in_v4_pipe") action encap_ttl_v4_in_v4_pipe(bit<8> ttl_val) { + hdr.ipv4.ttl = ttl_val; + } + @name(".encap_ttl_v4_in_v6_pipe") action encap_ttl_v4_in_v6_pipe(bit<8> ttl_val) { + hdr.ipv6.hop_limit = ttl_val; + } + @name(".encap_ttl_v6_in_v4_pipe") action encap_ttl_v6_in_v4_pipe(bit<8> ttl_val) { + hdr.ipv4.ttl = ttl_val; + } + @name(".encap_ttl_v6_in_v6_pipe") action encap_ttl_v6_in_v6_pipe(bit<8> ttl_val) { + hdr.ipv6.hop_limit = ttl_val; + } + @name(".encap_ttl_v4_in_v4_uniform") action encap_ttl_v4_in_v4_uniform() { + hdr.ipv4.ttl = hdr.inner_ipv4.ttl; + } + @name(".encap_ttl_v4_in_v6_uniform") action encap_ttl_v4_in_v6_uniform() { + hdr.ipv6.hop_limit = hdr.inner_ipv4.ttl; + } + @name(".encap_ttl_v6_in_v4_uniform") action encap_ttl_v6_in_v4_uniform() { + hdr.ipv4.ttl = hdr.inner_ipv6.hop_limit; + } + @name(".encap_ttl_v6_in_v6_uniform") action encap_ttl_v6_in_v6_uniform() { + hdr.ipv6.hop_limit = hdr.inner_ipv6.hop_limit; + } + @name(".tunnel_rewrite_encap_ttl") table encap_ttl { + key = { + hdr.inner_ipv4.isValid(): exact; + hdr.inner_ipv6.isValid(): exact; + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + local_md.tunnel.index : exact; + } + actions = { + NoAction; + encap_ttl_v4_in_v4_pipe; + encap_ttl_v4_in_v6_pipe; + encap_ttl_v6_in_v4_pipe; + encap_ttl_v6_in_v6_pipe; + encap_ttl_v4_in_v4_uniform; + encap_ttl_v4_in_v6_uniform; + encap_ttl_v6_in_v4_uniform; + encap_ttl_v6_in_v6_uniform; + } + const default_action = NoAction; + size = TUNNEL_OBJECT_SIZE * 3; + } + @name(".encap_dscp_v4_in_v4_ecn") action encap_dscp_v4_in_v4_ecn() { + } + @name(".encap_dscp_v4_in_v6_ecn") action encap_dscp_v4_in_v6_ecn() { + @in_hash { + hdr.ipv6.traffic_class[1:0] = hdr.inner_ipv4.diffserv[1:0]; + } + } + @name(".encap_dscp_v6_in_v4_ecn") action encap_dscp_v6_in_v4_ecn() { + @in_hash { + hdr.ipv4.diffserv[1:0] = hdr.inner_ipv6.traffic_class[1:0]; + } + } + @name(".encap_dscp_v6_in_v6_ecn") action encap_dscp_v6_in_v6_ecn() { + } + @name(".encap_dscp_v4_in_v4_uniform") action encap_dscp_v4_in_v4_uniform() { + hdr.ipv4.diffserv = hdr.inner_ipv4.diffserv; + } + @name(".encap_dscp_v4_in_v6_uniform") action encap_dscp_v4_in_v6_uniform() { + @in_hash { + hdr.ipv6.traffic_class = hdr.inner_ipv4.diffserv; + } + } + @name(".encap_dscp_v6_in_v4_uniform") action encap_dscp_v6_in_v4_uniform() { + @in_hash { + hdr.ipv4.diffserv = hdr.inner_ipv6.traffic_class; + } + } + @name(".encap_dscp_v6_in_v6_uniform") action encap_dscp_v6_in_v6_uniform() { + hdr.ipv6.traffic_class = hdr.inner_ipv6.traffic_class; + } + @name(".encap_dscp_v4_pipe_mode") action encap_dscp_v4_pipe_mode(bit<6> dscp_val) { + hdr.ipv4.diffserv[1:0] = 0; + hdr.ipv4.diffserv[7:2] = dscp_val; + } + @name(".encap_dscp_v6_pipe_mode") action encap_dscp_v6_pipe_mode(bit<6> dscp_val) { + hdr.ipv6.traffic_class[1:0] = 0; + hdr.ipv6.traffic_class[7:2] = dscp_val; + } + @name(".tunnel_rewrite_encap_dscp") table encap_dscp { + key = { + hdr.inner_ipv4.isValid(): exact; + hdr.inner_ipv6.isValid(): exact; + hdr.ipv4.isValid() : exact; + hdr.ipv6.isValid() : exact; + local_md.tunnel.index : exact; + } + actions = { + NoAction; + encap_dscp_v4_in_v4_ecn; + encap_dscp_v4_in_v6_ecn; + encap_dscp_v6_in_v4_ecn; + encap_dscp_v6_in_v6_ecn; + encap_dscp_v4_in_v4_uniform; + encap_dscp_v4_in_v6_uniform; + encap_dscp_v6_in_v4_uniform; + encap_dscp_v6_in_v6_uniform; + encap_dscp_v4_pipe_mode; + encap_dscp_v6_pipe_mode; + } + const default_action = NoAction; + size = TUNNEL_OBJECT_SIZE * 3; + } + @name(".ipv4_dip_rewrite") action ipv4_dip_rewrite(ipv4_addr_t dst_addr) { + hdr.ipv4.dst_addr = dst_addr; + } + @name(".ipv6_dip_rewrite") action ipv6_dip_rewrite(ipv6_addr_t dst_addr) { + hdr.ipv6.dst_addr = dst_addr; + } + @name(".dst_addr_rewrite") table dst_addr_rewrite { + key = { + local_md.tunnel.dip_index: exact; + } + actions = { + ipv4_dip_rewrite; + } + const default_action = ipv4_dip_rewrite(0); + size = TUNNEL_ENCAP_IP_SIZE; + } + apply { + if (local_md.tunnel.type != SWITCH_EGRESS_TUNNEL_TYPE_NONE) { + if (local_md.tunnel.type == SWITCH_EGRESS_TUNNEL_TYPE_MPLS) { + } else { + dst_addr_rewrite.apply(); + } + if (local_md.tunnel.type != SWITCH_EGRESS_TUNNEL_TYPE_SRV6_INSERT && local_md.tunnel.type != SWITCH_EGRESS_TUNNEL_TYPE_MPLS) { + src_addr_rewrite.apply(); + encap_ttl.apply(); + encap_dscp.apply(); + } + } + } +} + +control TunnelReplication(in egress_intrinsic_metadata_t eg_intr_md, inout switch_local_metadata_t local_md) { + @name(".tunnel_rid_hit") action rid_hit(switch_bd_t bd, switch_nexthop_t nexthop, switch_nexthop_t tunnel_nexthop) { + local_md.nexthop = nexthop; + local_md.tunnel_nexthop = tunnel_nexthop; + } + @name(".tunnel_rid_miss") action rid_miss() { + } + @name(".tunnel_rid") table rid { + key = { + eg_intr_md.egress_rid: exact; + } + actions = { + rid_miss; + rid_hit; + } + size = RID_TABLE_SIZE; + const default_action = rid_miss; + } + apply { + if (eg_intr_md.egress_rid != 0) { + rid.apply(); + } + } +} + +control L2VniMap(inout switch_header_t hdr, inout switch_local_metadata_t local_md) { + @name(".set_l2_vni") action set_l2_vni(switch_tunnel_vni_t vni) { + local_md.tunnel.vni = vni; + } + @name(".bd_to_vni_mapping") table bd_to_vni_mapping { + key = { + local_md.bd: exact; + } + actions = { + NoAction; + set_l2_vni; + } + const default_action = NoAction; + size = 4096; + } + apply { + bd_to_vni_mapping.apply(); + } +} + +control MulticastFlooding(inout switch_local_metadata_t local_md)(switch_uint32_t table_size) { + @name(".mcast_flood") action flood(switch_mgid_t mgid) { + local_md.multicast.id = mgid; + } + @name(".bd_flood") table bd_flood { + key = { + local_md.bd : exact @name("bd") ; + local_md.lkp.pkt_type: exact @name("pkt_type") ; + } + actions = { + flood; + } + size = table_size; + } + apply { + bd_flood.apply(); + } +} + +control MulticastReplication(in egress_intrinsic_metadata_t eg_intr_md, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=4096) { + @name(".multicast_rid_hit") action rid_hit(switch_bd_t bd) { + local_md.checks.same_bd = bd ^ local_md.bd; + local_md.bd = bd; + } + action rid_miss() { + local_md.flags.routed = false; + } + @name(".multicast_rid") table rid { + key = { + eg_intr_md.egress_rid: exact; + } + actions = { + rid_miss; + rid_hit; + } + size = table_size; + const default_action = rid_miss; + } + apply { + if (eg_intr_md.egress_rid != 0) { + rid.apply(); + } else { + local_md.checks.same_bd = 0xff; + } + if (local_md.checks.same_bd == 0) { + local_md.flags.routed = false; + } + } +} + +control ECNAcl(in switch_local_metadata_t local_md, in switch_lookup_fields_t lkp, inout switch_pkt_color_t pkt_color)(switch_uint32_t table_size=512) { + @name(".ecn_acl.set_ingress_color") action set_ingress_color(switch_pkt_color_t color) { + pkt_color = color; + } + @name(".ecn_acl.acl") table acl { + key = { + local_md.ingress_port_lag_label: ternary; + lkp.ip_tos : ternary; + lkp.tcp_flags : ternary; + } + actions = { + NoAction; + set_ingress_color; + } + const default_action = NoAction; + size = table_size; + } + apply { + acl.apply(); + } +} + +control IngressPFCWd(in switch_port_t port, in switch_qid_t qid, out bool flag)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_pfcwd.acl_deny") action acl_deny() { + flag = true; + stats.count(); + } + @ways(2) @name(".ingress_pfcwd.acl") table acl { + key = { + qid : exact; + port: exact; + } + actions = { + @defaultonly NoAction; + acl_deny; + } + const default_action = NoAction; + counters = stats; + size = table_size; + } + apply { + acl.apply(); + } +} + +control EgressPFCWd(in switch_port_t port, in switch_qid_t qid, out bool flag)(switch_uint32_t table_size=512) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_pfcwd.acl_deny") action acl_deny() { + flag = true; + stats.count(); + } + @ways(2) @name(".egress_pfcwd.acl") table acl { + key = { + qid : exact; + port: exact; + } + actions = { + @defaultonly NoAction; + acl_deny; + } + const default_action = NoAction; + counters = stats; + size = table_size; + } + apply { + acl.apply(); + } +} + +control IngressQoSMap(inout switch_header_t hdr, inout switch_local_metadata_t local_md)(switch_uint32_t dscp_map_size=2048, switch_uint32_t pcp_map_size=256) { + @name(".ingress_qos_map.set_ingress_tc") action set_ingress_tc(switch_tc_t tc) { + local_md.qos.tc = tc; + } + @name(".ingress_qos_map.set_ingress_color") action set_ingress_color(switch_pkt_color_t color) { + local_md.qos.color = color; + } + @name(".ingress_qos_map.set_ingress_tc_and_color") action set_ingress_tc_and_color(switch_tc_t tc, switch_pkt_color_t color) { + set_ingress_tc(tc); + set_ingress_color(color); + } + @name(".ingress_qos_map.dscp_tc_map") table dscp_tc_map { + key = { + local_md.qos.group : exact; + local_md.lkp.ip_tos[7:2]: exact; + } + actions = { + NoAction; + set_ingress_tc; + set_ingress_color; + set_ingress_tc_and_color; + } + size = dscp_map_size; + } + @name(".ingress_qos_map.pcp_tc_map") table pcp_tc_map { + key = { + local_md.qos.group: exact; + local_md.lkp.pcp : exact; + } + actions = { + NoAction; + set_ingress_tc; + set_ingress_color; + set_ingress_tc_and_color; + } + size = pcp_map_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_QOS != 0) && local_md.qos.trust_mode & SWITCH_QOS_TRUST_MODE_TRUST_DSCP == SWITCH_QOS_TRUST_MODE_TRUST_DSCP && local_md.lkp.ip_type != SWITCH_IP_TYPE_NONE) { + dscp_tc_map.apply(); + } else if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_QOS != 0) && local_md.qos.trust_mode & SWITCH_QOS_TRUST_MODE_TRUST_PCP == SWITCH_QOS_TRUST_MODE_TRUST_PCP && hdr.vlan_tag[0].isValid()) { + pcp_tc_map.apply(); + } + } +} + +control IngressTC(inout switch_local_metadata_t local_md) { + const bit<32> tc_table_size = 1024; + @name(".ingress_tc.set_icos") action set_icos(switch_cos_t icos) { + local_md.qos.icos = icos; + } + @name(".ingress_tc.set_queue") action set_queue(switch_qid_t qid) { + local_md.qos.qid = qid; + } + @name(".ingress_tc.set_icos_and_queue") action set_icos_and_queue(switch_cos_t icos, switch_qid_t qid) { + set_icos(icos); + set_queue(qid); + } + @name(".ingress_tc.traffic_class") table traffic_class { + key = { + local_md.ingress_port: ternary @name("port") ; + local_md.qos.color : ternary @name("color") ; + local_md.qos.tc : exact @name("tc") ; + } + actions = { + set_icos; + set_queue; + set_icos_and_queue; + } + size = tc_table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_QOS != 0)) { + traffic_class.apply(); + } + } +} + +control PPGStats(inout switch_local_metadata_t local_md) { + const bit<32> ppg_table_size = 1024; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) ppg_stats; + @name(".ppg_stats.count") action count() { + ppg_stats.count(); + } + @ways(2) @name(".ppg_stats.ppg") table ppg { + key = { + local_md.ingress_port: exact @name("port") ; + local_md.qos.icos : exact @name("icos") ; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + size = ppg_table_size; + counters = ppg_stats; + } + apply { + ppg.apply(); + } +} + +control EgressQoS(inout switch_header_t hdr, in switch_port_t port, inout switch_local_metadata_t local_md)(switch_uint32_t table_size=1024) { + @name(".egress_qos.set_ipv4_dscp") action set_ipv4_dscp(bit<6> dscp, bit<3> exp) { + hdr.ipv4.diffserv[7:2] = dscp; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_ipv4_tos") action set_ipv4_tos(switch_uint8_t tos, bit<3> exp) { + hdr.ipv4.diffserv = tos; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_ipv6_dscp") action set_ipv6_dscp(bit<6> dscp, bit<3> exp) { + hdr.ipv6.traffic_class[7:2] = dscp; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_ipv6_tos") action set_ipv6_tos(switch_uint8_t tos, bit<3> exp) { + hdr.ipv6.traffic_class = tos; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.set_vlan_pcp") action set_vlan_pcp(bit<3> pcp, bit<3> exp) { + hdr.vlan_tag[0].pcp = pcp; + local_md.tunnel.mpls_encap_exp = exp; + } + @name(".egress_qos.qos_map") table qos_map { + key = { + local_md.qos.group: ternary @name("group") ; + local_md.qos.tc : ternary @name("tc") ; + local_md.qos.color: ternary @name("color") ; + hdr.ipv4.isValid(): ternary; + hdr.ipv6.isValid(): ternary; + } + actions = { + NoAction; + set_ipv4_dscp; + set_ipv4_tos; + set_ipv6_dscp; + set_ipv6_tos; + set_vlan_pcp; + } + const default_action = NoAction; + size = table_size; + } + apply { + if (!local_md.flags.bypass_egress) { + qos_map.apply(); + } + } +} + +control EgressQueue(in switch_port_t port, inout switch_local_metadata_t local_md)(switch_uint32_t queue_table_size=1024) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) queue_stats; + @name(".egress_queue.count") action count() { + queue_stats.count(); + } + @ways(2) @pack(2) @name(".egress_queue.queue") table queue { + key = { + port : exact; + local_md.qos.qid: exact @name("qid") ; + } + actions = { + @defaultonly NoAction; + count; + } + size = queue_table_size; + const default_action = NoAction; + counters = queue_stats; + } + apply { + queue.apply(); + } +} + +control StormControl(inout switch_local_metadata_t local_md, in switch_pkt_type_t pkt_type, out bool flag)(switch_uint32_t table_size=256, switch_uint32_t meter_size=1024) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) storm_control_stats; + @name(".storm_control.meter") Meter>(meter_size, MeterType_t.BYTES) meter; + @name(".storm_control.count") action count() { + storm_control_stats.count(); + flag = false; + } + @name(".storm_control.drop_and_count") action drop_and_count() { + storm_control_stats.count(); + flag = true; + } + @name(".storm_control.stats") table stats { + key = { + local_md.qos.storm_control_color: exact; + pkt_type : ternary; + local_md.ingress_port : exact; + local_md.flags.dmac_miss : ternary; + } + actions = { + @defaultonly NoAction; + count; + drop_and_count; + } + const default_action = NoAction; + size = table_size * 2; + counters = storm_control_stats; + } + @name(".storm_control.set_meter") action set_meter(bit<16> index) { + local_md.qos.storm_control_color = (bit<2>)meter.execute(index); + } + @name(".storm_control.storm_control") table storm_control { + key = { + local_md.ingress_port : exact; + pkt_type : ternary; + local_md.flags.dmac_miss: ternary; + } + actions = { + @defaultonly NoAction; + set_meter; + } + const default_action = NoAction; + size = table_size; + } + apply { + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_STORM_CONTROL != 0)) { + storm_control.apply(); + } + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_STORM_CONTROL != 0)) { + stats.apply(); + } + } +} + +control IngressMirrorMeter(inout switch_local_metadata_t local_md)(switch_uint32_t table_size=256) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".ingress_mirror_meter.meter") Meter>(table_size, MeterType_t.PACKETS) meter; + switch_pkt_color_t color; + @name(".ingress_mirror_meter.mirror_and_count") action mirror_and_count() { + stats.count(); + } + @name(".ingress_mirror_meter.no_mirror_and_count") action no_mirror_and_count() { + stats.count(); + local_md.mirror.type = 0; + } + @ways(2) @name(".ingress_mirror_meter.meter_action") table meter_action { + key = { + color : exact; + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + mirror_and_count; + no_mirror_and_count; + } + const default_action = NoAction; + size = table_size * 2; + counters = stats; + } + @name(".ingress_mirror_meter.set_meter") action set_meter(bit<9> index) { + color = (bit<2>)meter.execute(index); + } + @name(".ingress_mirror_meter.meter_index") table meter_index { + key = { + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + set_meter; + } + const default_action = NoAction; + size = table_size; + } + apply { + } +} + +control EgressMirrorMeter(inout switch_local_metadata_t local_md)(switch_uint32_t table_size=256) { + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_mirror_meter.meter") Meter>(table_size, MeterType_t.PACKETS) meter; + switch_pkt_color_t color; + @name(".egress_mirror_meter.mirror_and_count") action mirror_and_count() { + stats.count(); + } + @name(".egress_mirror_meter.no_mirror_and_count") action no_mirror_and_count() { + stats.count(); + local_md.mirror.type = 0; + } + @ways(2) @name(".egress_mirror_meter.meter_action") table meter_action { + key = { + color : exact; + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + mirror_and_count; + no_mirror_and_count; + } + const default_action = NoAction; + size = table_size * 2; + counters = stats; + } + @name(".egress_mirror_meter.set_meter") action set_meter(bit<9> index) { + color = (bit<2>)meter.execute(index); + } + @name(".egress_mirror_meter.meter_index") table meter_index { + key = { + local_md.mirror.meter_index: exact; + } + actions = { + @defaultonly NoAction; + set_meter; + } + const default_action = NoAction; + size = table_size; + } + apply { + } +} + +control WRED(inout switch_header_t hdr, in switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, out bool wred_drop) { + switch_wred_index_t index; + bit<1> wred_flag; + const switch_uint32_t wred_size = 1 << 10; + DirectCounter>(CounterType_t.PACKETS_AND_BYTES) stats; + @name(".egress_wred.wred") Wred, switch_wred_index_t>(wred_size, 1, 0) wred; + @name(".egress_wred.set_wred_index") action set_wred_index(switch_wred_index_t wred_index) { + index = wred_index; + wred_flag = (bit<1>)wred.execute(local_md.qos.qdepth, wred_index); + } + @name(".egress_wred.wred_index") table wred_index { + key = { + eg_intr_md.egress_port: exact @name("port") ; + local_md.qos.qid : exact @name("qid") ; + local_md.qos.color : exact @name("color") ; + } + actions = { + NoAction; + set_wred_index; + } + const default_action = NoAction; + size = wred_size; + } + @name(".egress_wred.set_ipv4_ecn") action set_ipv4_ecn() { + hdr.ipv4.diffserv[1:0] = SWITCH_ECN_CODEPOINT_CE; + wred_drop = false; + } + @name(".egress_wred.set_ipv6_ecn") action set_ipv6_ecn() { + hdr.ipv6.traffic_class[1:0] = SWITCH_ECN_CODEPOINT_CE; + wred_drop = false; + } + @name(".egress_wred.drop") action drop() { + wred_drop = true; + } + @name(".egress_wred.v4_wred_action") table v4_wred_action { + key = { + index : exact; + hdr.ipv4.diffserv[1:0]: exact; + } + actions = { + NoAction; + drop; + set_ipv4_ecn; + } + size = 3 * wred_size; + } + @name(".egress_wred.v6_wred_action") table v6_wred_action { + key = { + index : exact; + hdr.ipv6.traffic_class[1:0]: exact; + } + actions = { + NoAction; + drop; + set_ipv6_ecn; + } + size = 3 * wred_size; + } + @name(".egress_wred.count") action count() { + stats.count(); + } + @ways(2) @name(".egress_wred.wred_stats") table wred_stats { + key = { + eg_intr_md.egress_port: exact @name("port") ; + local_md.qos.qid : exact @name("qid") ; + local_md.qos.color : exact @name("color") ; + wred_drop : exact; + } + actions = { + @defaultonly NoAction; + count; + } + const default_action = NoAction; + size = 2 * wred_size; + counters = stats; + } + apply { + if (!local_md.flags.bypass_egress) { + wred_index.apply(); + } + if (!local_md.flags.bypass_egress && wred_flag == 1) { + if (hdr.ipv4.isValid()) { + switch (v4_wred_action.apply().action_run) { + NoAction: { + } + default: { + wred_stats.apply(); + } + } + } else if (hdr.ipv6.isValid()) { + switch (v6_wred_action.apply().action_run) { + NoAction: { + } + default: { + wred_stats.apply(); + } + } + } + } + } +} + +control DeflectOnDrop(in switch_local_metadata_t local_md, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm)(switch_uint32_t table_size=1024) { + @name(".dtel.dod.enable_dod") action enable_dod() { + ig_intr_md_for_tm.deflect_on_drop = 1w1; + } + @name(".dtel.dod.disable_dod") action disable_dod() { + ig_intr_md_for_tm.deflect_on_drop = 1w0; + } + @name(".deflect_on_drop.config") table config { + key = { + local_md.dtel.report_type : ternary; + ig_intr_md_for_tm.ucast_egress_port: ternary @name("egress_port") ; + local_md.qos.qid : ternary @name("qid") ; + local_md.cpu_reason : ternary; + } + actions = { + enable_dod; + disable_dod; + } + size = table_size; + const default_action = disable_dod; + } + apply { + config.apply(); + } +} + +control MirrorOnDrop(in switch_drop_reason_t drop_reason, inout switch_dtel_metadata_t dtel_md, inout switch_mirror_metadata_t mirror_md) { + @name(".dtel.mod.mirror") action mirror() { + mirror_md.type = 3; + mirror_md.src = SWITCH_PKT_SRC_CLONED_INGRESS; + } + @name(".dtel.mod.mirror_and_set_d_bit") action mirror_and_set_d_bit() { + dtel_md.report_type = dtel_md.report_type | SWITCH_DTEL_REPORT_TYPE_DROP; + mirror_md.type = 3; + mirror_md.src = SWITCH_PKT_SRC_CLONED_INGRESS; + } + @name(".mirror_on_drop.config") table config { + key = { + drop_reason : ternary; + dtel_md.report_type: ternary; + } + actions = { + NoAction; + mirror; + mirror_and_set_d_bit; + } + const default_action = NoAction; + } + apply { + config.apply(); + } +} + +control DropReport(in switch_header_t hdr, in switch_local_metadata_t local_md, in bit<32> hash, inout bit<2> flag) { + @name(".drop_report.array1") Register, bit<17>>(1 << 17, 0) array1; + @name(".drop_report.array2") Register, bit<17>>(1 << 17, 0) array2; + RegisterAction, bit<17>, bit<1>>(array1) filter1 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + val = 0b1; + } + }; + RegisterAction, bit<17>, bit<1>>(array2) filter2 = { + void apply(inout bit<1> val, out bit<1> rv) { + rv = val; + val = 0b1; + } + }; + apply { + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_DROP | SWITCH_DTEL_SUPPRESS_REPORT | SWITCH_DTEL_REPORT_TYPE_ETRAP_CHANGE) == SWITCH_DTEL_REPORT_TYPE_DROP && hdr.dtel_drop_report.isValid()) { + flag[0:0] = filter1.execute(hash[17 - 1:0]); + } + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_DROP | SWITCH_DTEL_SUPPRESS_REPORT | SWITCH_DTEL_REPORT_TYPE_ETRAP_CHANGE) == SWITCH_DTEL_REPORT_TYPE_DROP && hdr.dtel_drop_report.isValid()) { + flag[1:1] = filter2.execute(hash[31:32 - 17]); + } + } +} + +struct switch_queue_alert_threshold_t { + bit<32> qdepth; + bit<32> latency; +} + +struct switch_queue_report_quota_t { + bit<32> counter; + bit<32> latency; +} + +control QueueReport(inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, out bit<1> qalert) { + bit<16> quota_; + const bit<32> queue_table_size = 1024; + const bit<32> queue_register_size = 2048; + @name(".queue_report.thresholds") Register>(queue_register_size) thresholds; + RegisterAction, bit<1>>(thresholds) check_thresholds = { + void apply(inout switch_queue_alert_threshold_t reg, out bit<1> flag) { + if (reg.latency <= local_md.dtel.latency || reg.qdepth <= (bit<32>)local_md.qos.qdepth) { + flag = 1; + } + } + }; + @name(".dtel.queue_report.set_qmask") action set_qmask(bit<32> quantization_mask) { + local_md.dtel.latency = local_md.dtel.latency & quantization_mask; + } + @name(".dtel.queue_report.set_qalert") action set_qalert(bit<16> index, bit<16> quota, bit<32> quantization_mask) { + qalert = check_thresholds.execute(index); + quota_ = quota; + set_qmask(quantization_mask); + } + @name(".queue_report.queue_alert") @ways(2) table queue_alert { + key = { + local_md.qos.qid : exact @name("qid") ; + local_md.egress_port: exact @name("port") ; + } + actions = { + set_qalert; + set_qmask; + } + size = queue_table_size; + } + @name(".queue_report.quotas") Register>(queue_register_size) quotas; + RegisterAction, bit<1>>(quotas) reset_quota = { + void apply(inout switch_queue_report_quota_t reg, out bit<1> flag) { + flag = 0; + reg.counter = (bit<32>)quota_[15:0]; + } + }; + RegisterAction, bit<1>>(quotas) check_latency_and_update_quota = { + void apply(inout switch_queue_report_quota_t reg, out bit<1> flag) { + if (reg.counter > 0) { + reg.counter = reg.counter - 1; + flag = 1; + } + if (reg.latency != local_md.dtel.latency) { + reg.latency = local_md.dtel.latency; + flag = 1; + } + } + }; + RegisterAction, bit<1>>(quotas) update_quota = { + void apply(inout switch_queue_report_quota_t reg, out bit<1> flag) { + if (reg.counter > 0) { + reg.counter = reg.counter - 1; + flag = 1; + } + } + }; + @name(".dtel.queue_report.reset_quota_") action reset_quota_(bit<16> index) { + qalert = reset_quota.execute(index); + } + @name(".dtel.queue_report.update_quota_") action update_quota_(bit<16> index) { + qalert = update_quota.execute(index); + } + @name(".dtel.queue_report.check_latency_and_update_quota_") action check_latency_and_update_quota_(bit<16> index) { + qalert = check_latency_and_update_quota.execute(index); + } + @name(".queue_report.check_quota") table check_quota { + key = { + local_md.pkt_src : exact; + qalert : exact; + local_md.qos.qid : exact @name("qid") ; + local_md.egress_port: exact @name("port") ; + } + actions = { + NoAction; + reset_quota_; + update_quota_; + check_latency_and_update_quota_; + } + const default_action = NoAction; + size = 3 * queue_table_size; + } + apply { + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + queue_alert.apply(); + } + check_quota.apply(); + } +} + +control FlowReport(in switch_local_metadata_t local_md, out bit<2> flag) { + bit<16> digest; + Hash>(HashAlgorithm_t.CRC16) hash; + @name(".flow_report.array1") Register, bit<16>>(1 << 16, 0) array1; + @name(".flow_report.array2") Register, bit<16>>(1 << 16, 0) array2; + @reduction_or_group("filter") RegisterAction, bit<16>, bit<2>>(array1) filter1 = { + void apply(inout bit<16> reg, out bit<2> rv) { + if (reg == 16w0) { + rv = 0b10; + } else if (reg == digest) { + rv = 0b1; + } + reg = digest; + } + }; + @reduction_or_group("filter") RegisterAction, bit<16>, bit<2>>(array2) filter2 = { + void apply(inout bit<16> reg, out bit<2> rv) { + if (reg == 16w0) { + rv = 0b10; + } else if (reg == digest) { + rv = 0b1; + } + reg = digest; + } + }; + apply { + digest = hash.get({ local_md.dtel.latency, local_md.ingress_port, local_md.egress_port, local_md.dtel.hash }); + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_FLOW | SWITCH_DTEL_SUPPRESS_REPORT) == SWITCH_DTEL_REPORT_TYPE_FLOW && local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + flag = filter1.execute(local_md.dtel.hash[15:0]); + } + if (local_md.dtel.report_type & (SWITCH_DTEL_REPORT_TYPE_FLOW | SWITCH_DTEL_SUPPRESS_REPORT) == SWITCH_DTEL_REPORT_TYPE_FLOW && local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + flag = flag | filter2.execute(local_md.dtel.hash[31:16]); + } + } +} + +control IngressDtel(in switch_header_t hdr, in switch_lookup_fields_t lkp, inout switch_local_metadata_t local_md, in bit<16> hash, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, inout ingress_intrinsic_metadata_for_tm_t ig_intr_for_tm) { + DeflectOnDrop() dod; + MirrorOnDrop() mod; + Hash(HashAlgorithm_t.IDENTITY) selector_hash; + @name(".dtel.dtel_action_profile") ActionProfile(DTEL_SELECTOR_TABLE_SIZE) dtel_action_profile; + @name(".dtel.session_selector") ActionSelector(dtel_action_profile, selector_hash, SelectorMode_t.FAIR, DTEL_MAX_MEMBERS_PER_GROUP, DTEL_GROUP_TABLE_SIZE) session_selector; + @name(".dtel.set_mirror_session") action set_mirror_session(switch_mirror_session_t session_id) { + local_md.dtel.session_id = session_id; + } + @name(".dtel.mirror_session") table mirror_session { + key = { + hdr.ethernet.isValid(): ternary; + hash : selector; + } + actions = { + NoAction; + set_mirror_session; + } + implementation = session_selector; + } + apply { + dod.apply(local_md, ig_intr_for_tm); + if (local_md.mirror.type == 0) { + mod.apply(local_md.drop_reason, local_md.dtel, local_md.mirror); + } + mirror_session.apply(); + } +} + +control DtelConfig(inout switch_header_t hdr, inout switch_local_metadata_t local_md, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr) { + @name(".dtel_config.seq_number") Register, switch_mirror_session_t>(1024) seq_number; + RegisterAction, switch_mirror_session_t, bit<32>>(seq_number) get_seq_number = { + void apply(inout bit<32> reg, out bit<32> rv) { + reg = reg + 1; + rv = reg; + } + }; + @name(".dtel_config.mirror_switch_local") action mirror_switch_local() { + local_md.mirror.type = 4; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + } + @name(".dtel_config.mirror_switch_local_and_set_q_bit") action mirror_switch_local_and_set_q_bit() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_REPORT_TYPE_QUEUE; + mirror_switch_local(); + } + action mirror_switch_local_and_drop() { + mirror_switch_local(); + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.mirror_switch_local_and_set_f_bit_and_drop") action mirror_switch_local_and_set_f_bit_and_drop() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_REPORT_TYPE_FLOW; + mirror_switch_local(); + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.mirror_switch_local_and_set_q_f_bits_and_drop") action mirror_switch_local_and_set_q_f_bits_and_drop() { + local_md.dtel.report_type = local_md.dtel.report_type | (SWITCH_DTEL_REPORT_TYPE_QUEUE | SWITCH_DTEL_REPORT_TYPE_FLOW); + mirror_switch_local(); + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.mirror_drop") action mirror_drop() { + local_md.mirror.type = 3; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + } + @name(".dtel_config.mirror_drop_and_set_q_bit") action mirror_drop_and_set_q_bit() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_REPORT_TYPE_QUEUE; + mirror_drop(); + } + @name(".dtel_config.mirror_clone") action mirror_clone() { + local_md.mirror.type = 5; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + local_md.dtel.session_id = local_md.dtel.clone_session_id; + } + @name(".dtel_config.drop") action drop() { + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.update") action update(switch_dtel_switch_id_t switch_id, switch_dtel_hw_id_t hw_id, bit<4> next_proto, switch_dtel_report_type_t report_type) { + hdr.dtel.setValid(); + hdr.dtel.hw_id = hw_id; + hdr.dtel.switch_id = switch_id; + hdr.dtel.d_q_f = (bit<3>)report_type; + hdr.dtel.version = 0; + hdr.dtel.next_proto = next_proto; + hdr.dtel.reserved = 0; + hdr.dtel.seq_number = get_seq_number.execute(local_md.mirror.session_id); + hdr.dtel.timestamp = (bit<32>)local_md.ingress_timestamp; + } + @name(".dtel_config.update_and_mirror_truncate") action update_and_mirror_truncate(switch_dtel_switch_id_t switch_id, switch_dtel_hw_id_t hw_id, bit<4> next_proto, bit<8> md_length, bit<16> rep_md_bits, switch_dtel_report_type_t report_type) { + update(switch_id, hw_id, next_proto, report_type); + local_md.mirror.type = 5; + local_md.mirror.src = SWITCH_PKT_SRC_CLONED_EGRESS; + eg_intr_md_for_dprsr.drop_ctl = 0x1; + } + @name(".dtel_config.update_and_set_etrap") action update_and_set_etrap(switch_dtel_switch_id_t switch_id, switch_dtel_hw_id_t hw_id, bit<4> next_proto, bit<8> md_length, bit<16> rep_md_bits, switch_dtel_report_type_t report_type, bit<2> etrap_status) { + hdr.dtel.setValid(); + hdr.dtel.hw_id = hw_id; + hdr.dtel.switch_id = switch_id; + hdr.dtel.d_q_f = (bit<3>)report_type; + hdr.dtel.version = 0; + hdr.dtel.next_proto = next_proto; + hdr.dtel.reserved[14:13] = etrap_status; + hdr.dtel.seq_number = get_seq_number.execute(local_md.mirror.session_id); + hdr.dtel.timestamp = (bit<32>)local_md.ingress_timestamp; + } + @name(".dtel_config.set_ipv4_dscp_all") action set_ipv4_dscp_all(bit<6> dscp) { + hdr.ipv4.diffserv[7:2] = dscp; + } + @name(".dtel_config.set_ipv6_dscp_all") action set_ipv6_dscp_all(bit<6> dscp) { + hdr.ipv6.traffic_class[7:2] = dscp; + } + @name(".dtel_config.set_ipv4_dscp_2") action set_ipv4_dscp_2(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[2:2] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_2") action set_ipv6_dscp_2(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[2:2] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_3") action set_ipv4_dscp_3(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[3:3] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_3") action set_ipv6_dscp_3(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[3:3] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_4") action set_ipv4_dscp_4(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[4:4] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_4") action set_ipv6_dscp_4(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[4:4] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_5") action set_ipv4_dscp_5(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[5:5] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_5") action set_ipv6_dscp_5(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[5:5] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_6") action set_ipv4_dscp_6(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[6:6] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_6") action set_ipv6_dscp_6(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[6:6] = dscp_bit_value; + } + @name(".dtel_config.set_ipv4_dscp_7") action set_ipv4_dscp_7(bit<1> dscp_bit_value) { + hdr.ipv4.diffserv[7:7] = dscp_bit_value; + } + @name(".dtel_config.set_ipv6_dscp_7") action set_ipv6_dscp_7(bit<1> dscp_bit_value) { + hdr.ipv6.traffic_class[7:7] = dscp_bit_value; + } + @name(".dtel_config.config") @ignore_table_dependency("SwitchEgress.system_acl.copp") table config { + key = { + local_md.pkt_src : ternary; + local_md.dtel.report_type : ternary; + local_md.dtel.drop_report_flag : ternary; + local_md.dtel.flow_report_flag : ternary; + local_md.dtel.queue_report_flag: ternary; + local_md.drop_reason : ternary; + local_md.mirror.type : ternary; + hdr.dtel_drop_report.isValid() : ternary; + local_md.lkp.tcp_flags[2:0] : ternary; + } + actions = { + NoAction; + drop; + mirror_switch_local; + mirror_switch_local_and_set_q_bit; + mirror_drop; + mirror_drop_and_set_q_bit; + update; + } + const default_action = NoAction; + } + apply { + config.apply(); + } +} + +control IntEdge(inout switch_local_metadata_t local_md)(switch_uint32_t port_table_size=288) { + @name(".dtel.int_edge.set_clone_mirror_session_id") action set_clone_mirror_session_id(switch_mirror_session_t session_id) { + local_md.dtel.clone_session_id = session_id; + } + @name(".dtel.int_edge.set_ifa_edge") action set_ifa_edge() { + local_md.dtel.report_type = local_md.dtel.report_type | SWITCH_DTEL_IFA_EDGE; + } + @name(".dtel.int_edge.port_lookup") table port_lookup { + key = { + local_md.egress_port: exact; + } + actions = { + NoAction; + set_clone_mirror_session_id; + set_ifa_edge; + } + const default_action = NoAction; + size = port_table_size; + } + apply { + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + port_lookup.apply(); + } + } +} + +control EgressDtel(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, in bit<32> hash) { + DropReport() drop_report; + QueueReport() queue_report; + FlowReport() flow_report; + IntEdge() int_edge; + @name(".dtel.convert_ingress_port") action convert_ingress_port(switch_port_t port) { + hdr.dtel_report.ingress_port = port; + } + @name(".dtel.ingress_port_conversion") table ingress_port_conversion { + key = { + hdr.dtel_report.ingress_port: exact @name("port") ; + hdr.dtel_report.isValid() : exact @name("dtel_report_valid") ; + } + actions = { + NoAction; + convert_ingress_port; + } + const default_action = NoAction; + } + @name(".dtel.convert_egress_port") action convert_egress_port(switch_port_t port) { + hdr.dtel_report.egress_port = port; + } + @name(".dtel.egress_port_conversion") table egress_port_conversion { + key = { + hdr.dtel_report.egress_port: exact @name("port") ; + hdr.dtel_report.isValid() : exact @name("dtel_report_valid") ; + } + actions = { + NoAction; + convert_egress_port; + } + const default_action = NoAction; + } + action update_dtel_timestamps() { + local_md.dtel.latency = eg_intr_md_from_prsr.global_tstamp[31:0] - local_md.ingress_timestamp[31:0]; + local_md.egress_timestamp[31:0] = eg_intr_md_from_prsr.global_tstamp[31:0]; + } + apply { + update_dtel_timestamps(); + if (local_md.pkt_src == SWITCH_PKT_SRC_DEFLECTED && hdr.dtel_drop_report.isValid()) { + local_md.egress_port = hdr.dtel_report.egress_port; + } + ingress_port_conversion.apply(); + egress_port_conversion.apply(); + queue_report.apply(local_md, eg_intr_md, local_md.dtel.queue_report_flag); + flow_report.apply(local_md, local_md.dtel.flow_report_flag); + drop_report.apply(hdr, local_md, hash, local_md.dtel.drop_report_flag); + } +} + +struct switch_sflow_info_t { + bit<32> current; + bit<32> rate; +} + +control IngressSflow(inout switch_local_metadata_t local_md) { + const bit<32> sflow_session_size = 256; + @name(".ingress_sflow_samplers") Register>(sflow_session_size) samplers; + RegisterAction, bit<1>>(samplers) sample_packet = { + void apply(inout switch_sflow_info_t reg, out bit<1> flag) { + if (reg.current > 0) { + reg.current = reg.current - 1; + } else { + reg.current = reg.rate; + flag = 1; + } + } + }; + apply { + if (local_md.sflow.session_id != SWITCH_SFLOW_INVALID_ID) { + local_md.sflow.sample_packet = sample_packet.execute(local_md.sflow.session_id); + } + } +} + +control EgressSflow(inout switch_local_metadata_t local_md) { + const bit<32> sflow_session_size = 256; + Register>(sflow_session_size) samplers; + RegisterAction, bit<1>>(samplers) sample_packet = { + void apply(inout switch_sflow_info_t reg, out bit<1> flag) { + if (reg.current > 0) { + reg.current = reg.current - 1; + } else { + reg.current = reg.rate; + flag = 1; + } + } + }; + apply { + } +} + +@pa_solitary("ingress" , "local_md.flags.ipv4_checksum_err") @pa_no_overlay("ingress" , "smac_src_move") control SwitchIngress(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in ingress_intrinsic_metadata_t ig_intr_md, in ingress_intrinsic_metadata_from_parser_t ig_intr_from_prsr, inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm) { + IngressPortMapping(PORT_VLAN_TABLE_SIZE, BD_TABLE_SIZE) ingress_port_mapping; + PktValidation() pkt_validation; + IngressSTP() stp; + SMAC(MAC_TABLE_SIZE) smac; + DMAC(MAC_TABLE_SIZE) dmac; + IngressTunnel() tunnel; + IngressSflow() sflow; + IngressBd(BD_TABLE_SIZE) bd_stats; + EnableFragHash() enable_frag_hash; + Ipv4Hash() ipv4_hash; + Ipv6Hash() ipv6_hash; + NonIpHash() non_ip_hash; + Lagv4Hash() lagv4_hash; + Lagv6Hash() lagv6_hash; + InnerDtelv4Hash() inner_dtelv4_hash; + InnerDtelv6Hash() inner_dtelv6_hash; + LOU() lou; + Fibv4(IPV4_HOST_TABLE_SIZE, IPV4_LPM_TABLE_SIZE, false) ipv4_fib; + Fibv6(IPV6_HOST_TABLE_SIZE, 64, IPV6_LPM_TABLE_SIZE, IPV6_LPM64_TABLE_SIZE) ipv6_fib; + IngressMacAcl(INGRESS_MAC_ACL_TABLE_SIZE) ingress_mac_acl; + IngressIpv4Acl(INGRESS_IPV4_ACL_TABLE_SIZE) ingress_ipv4_acl; + IngressIpv6Acl(INGRESS_IPV6_ACL_TABLE_SIZE) ingress_ipv6_acl; + IngressIpDtelSampleAcl(INGRESS_IP_DTEL_ACL_TABLE_SIZE) ingress_ip_dtel_acl; + IngressIpAcl(INGRESS_IP_MIRROR_ACL_TABLE_SIZE) ingress_ip_mirror_acl; + IngressIpAcl(INGRESS_IP_QOS_ACL_TABLE_SIZE) ingress_ip_qos_acl; + IngressInnerIpv4Acl(INGRESS_IPV4_DTEL_ACL_TABLE_SIZE) ingress_inner_ipv4_dtel_acl; + IngressInnerIpv6Acl(INGRESS_IPV6_DTEL_ACL_TABLE_SIZE) ingress_inner_ipv6_dtel_acl; + ECNAcl() ecn_acl; + IngressPFCWd(512) pfc_wd; + IngressQoSMap() qos_map; + IngressTC() traffic_class; + PPGStats() ppg_stats; + StormControl() storm_control; + Nexthop(NEXTHOP_TABLE_SIZE, ECMP_GROUP_TABLE_SIZE, ECMP_SELECT_TABLE_SIZE) nexthop; + OuterFib() outer_fib; + LAG() lag; + MulticastFlooding(BD_FLOOD_TABLE_SIZE) flood; + IngressSystemAcl() system_acl; + IngressDtel() dtel; + apply { + pkt_validation.apply(hdr, local_md); + ingress_port_mapping.apply(hdr, local_md, ig_intr_md_for_tm, ig_intr_md_for_dprsr); + ingress_mac_acl.apply(hdr, local_md, local_md.unused_nexthop); + stp.apply(local_md, local_md.stp); + smac.apply(hdr.ethernet.src_addr, hdr.ethernet.dst_addr, local_md, ig_intr_md_for_dprsr.digest_type); + tunnel.apply(hdr, local_md, local_md.lkp); + if (local_md.flags.rmac_hit) { + lou.apply(local_md); + if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_L3 != 0) && local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV6 && local_md.ipv6.unicast_enable) { + ipv6_fib.apply(local_md.lkp.ip_dst_addr, local_md); + } else if (!(local_md.bypass & SWITCH_INGRESS_BYPASS_L3 != 0) && local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV4 && local_md.ipv4.unicast_enable) { + ipv4_fib.apply(local_md.lkp.ip_dst_addr[95:64], local_md); + } else { + dmac.apply(local_md.lkp.mac_dst_addr, local_md); + } + } else { + lou.apply(local_md); + dmac.apply(local_md.lkp.mac_dst_addr, local_md); + } + qos_map.apply(hdr, local_md); + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV4) { + ingress_ipv6_acl.apply(local_md, local_md.acl_nexthop); + } + if (local_md.lkp.ip_type != SWITCH_IP_TYPE_IPV6) { + ingress_ipv4_acl.apply(local_md, local_md.acl_nexthop); + } + ingress_ip_mirror_acl.apply(local_md, local_md.unused_nexthop); + ingress_ip_qos_acl.apply(local_md, local_md.unused_nexthop); + sflow.apply(local_md); + enable_frag_hash.apply(local_md.lkp); + if (local_md.lkp.ip_type == SWITCH_IP_TYPE_NONE) { + non_ip_hash.apply(hdr, local_md, local_md.lag_hash); + } else if (local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV4) { + lagv4_hash.apply(local_md.lkp, local_md.lag_hash); + } else { + lagv6_hash.apply(local_md.lkp, local_md.lag_hash); + } + if (local_md.lkp.ip_type == SWITCH_IP_TYPE_IPV4) { + ipv4_hash.apply(local_md.lkp, local_md.hash[31:0]); + } else { + ipv6_hash.apply(local_md.lkp, local_md.hash[31:0]); + } + nexthop.apply(local_md); + traffic_class.apply(local_md); + storm_control.apply(local_md, local_md.lkp.pkt_type, local_md.flags.storm_control_drop); + bd_stats.apply(local_md.bd, local_md.lkp.pkt_type); + outer_fib.apply(local_md); + if (local_md.egress_port_lag_index == SWITCH_FLOOD) { + flood.apply(local_md); + } else { + lag.apply(local_md, local_md.lag_hash, ig_intr_md_for_tm.ucast_egress_port); + } + if (!local_md.tunnel.terminate) { + if (hdr.inner_ipv4.isValid()) { + inner_dtelv4_hash.apply(hdr, local_md, local_md.lag_hash); + } else if (hdr.inner_ipv4.isValid()) { + inner_dtelv6_hash.apply(hdr, local_md, local_md.lag_hash); + } + } + pfc_wd.apply(local_md.ingress_port, local_md.qos.qid, local_md.flags.pfc_wd_drop); + system_acl.apply(hdr, local_md, ig_intr_md_for_tm, ig_intr_md_for_dprsr); + ppg_stats.apply(local_md); + if (hdr.inner_ipv4.isValid()) { + ingress_inner_ipv4_dtel_acl.apply(hdr, local_md); + } else if (hdr.inner_ipv6.isValid()) { + ingress_inner_ipv6_dtel_acl.apply(hdr, local_md); + } else { + ingress_ip_dtel_acl.apply(local_md, local_md.unused_nexthop); + } + dtel.apply(hdr, local_md.lkp, local_md, local_md.lag_hash[15:0], ig_intr_md_for_dprsr, ig_intr_md_for_tm); + add_bridged_md(hdr.bridged_md, local_md); + set_ig_intr_md(local_md, ig_intr_md_for_dprsr, ig_intr_md_for_tm); + if (local_md.tunnel.terminate) { + ig_intr_md_for_tm.level1_exclusion_id = 16w1; + } + } +} + +control SwitchEgress(inout switch_header_t hdr, inout switch_local_metadata_t local_md, in egress_intrinsic_metadata_t eg_intr_md, in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, inout egress_intrinsic_metadata_for_output_port_t eg_intr_md_for_oport) { + EgressPortMapping() egress_port_mapping; + EgressPortMirror(288) port_mirror; + EgressSTP() stp; + EgressLOU() lou; + EgressQoS() qos; + EgressQueue() queue; + EgressIpv4Acl(EGRESS_IPV4_ACL_TABLE_SIZE) egress_ipv4_acl; + EgressIpv6Acl(EGRESS_IPV6_ACL_TABLE_SIZE) egress_ipv6_acl; + EgressSystemAcl() system_acl; + EgressPFCWd(512) pfc_wd; + EgressVRF() egress_vrf; + EgressBD() egress_bd; + OuterNexthop() outer_nexthop; + EgressBDStats() egress_bd_stats; + MirrorRewrite() mirror_rewrite; + VlanXlate(VLAN_TABLE_SIZE, PORT_VLAN_TABLE_SIZE) vlan_xlate; + VlanDecap() vlan_decap; + TunnelDecap() tunnel_decap; + TunnelNexthop() tunnel_nexthop; + TunnelEncap() tunnel_encap; + TunnelRewrite() tunnel_rewrite; + MTU() mtu; + WRED() wred; + EgressDtel() dtel; + DtelConfig() dtel_config; + EgressCpuRewrite() cpu_rewrite; + Neighbor() neighbor; + TunnelReplication() tunnel_replication; + L2VniMap() l2_vni_map; + SetEgIntrMd() set_eg_intr_md; + apply { + egress_port_mapping.apply(hdr, local_md, eg_intr_md_for_dprsr, eg_intr_md.egress_port); + tunnel_replication.apply(eg_intr_md, local_md); + if (local_md.pkt_src == SWITCH_PKT_SRC_BRIDGED) { + port_mirror.apply(eg_intr_md.egress_port, local_md.mirror); + if (local_md.tunnel.terminate) { + tunnel_decap.apply(hdr, local_md); + } else { + vlan_decap.apply(hdr, local_md); + } + qos.apply(hdr, eg_intr_md.egress_port, local_md); + wred.apply(hdr, local_md, eg_intr_md, local_md.flags.wred_drop); + if (local_md.flags.routed) { + egress_vrf.apply(hdr, local_md); + } else { + l2_vni_map.apply(hdr, local_md); + } + lou.apply(local_md); + if (hdr.ipv4.isValid()) { + egress_ipv4_acl.apply(hdr, local_md); + } else if (hdr.ipv6.isValid()) { + egress_ipv6_acl.apply(hdr, local_md); + } + tunnel_nexthop.apply(hdr, local_md); + outer_nexthop.apply(hdr, local_md); + tunnel_encap.apply(hdr, local_md); + egress_bd.apply(hdr, local_md); + tunnel_rewrite.apply(hdr, local_md); + neighbor.apply(hdr, local_md); + } else { + mirror_rewrite.apply(hdr, local_md, eg_intr_md_for_dprsr); + } + stp.apply(local_md, eg_intr_md.egress_port, local_md.checks.stp); + egress_bd_stats.apply(hdr, local_md); + mtu.apply(hdr, local_md); + vlan_xlate.apply(hdr, local_md); + pfc_wd.apply(eg_intr_md.egress_port, local_md.qos.qid, local_md.flags.pfc_wd_drop); + dtel.apply(hdr, local_md, eg_intr_md, eg_intr_md_from_prsr, local_md.dtel.hash); + system_acl.apply(hdr, local_md, eg_intr_md, eg_intr_md_for_dprsr); + dtel_config.apply(hdr, local_md, eg_intr_md_for_dprsr); + cpu_rewrite.apply(hdr, local_md, eg_intr_md_for_dprsr, eg_intr_md.egress_port); + set_eg_intr_md.apply(hdr, local_md, eg_intr_md_for_dprsr, eg_intr_md_for_oport); + queue.apply(eg_intr_md.egress_port, local_md); + } +} + +Pipeline(SwitchIngressParser(), SwitchIngress(), SwitchIngressDeparser(), SwitchEgressParser(), SwitchEgress(), SwitchEgressDeparser()) pipe; + +Switch(pipe) main; + diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_backend.cpp b/backends/p4tools/modules/testgen/targets/tofino/test_backend.cpp new file mode 100644 index 00000000000..07a70da004a --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_backend.cpp @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend.h" + +#include +#include + +#include + +#include "ir/ir.h" +#include "ir/irutils.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" + +#include "backends/p4tools/modules/testgen/options.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const std::vector TofinoTestBackend::SUPPORTED_BACKENDS = {"PTF", "STF"}; + +TofinoTestBackend::TofinoTestBackend(const TofinoSharedProgramInfo &programInfo, + const TestBackendConfiguration &testBackendConfiguration, + SymbolicExecutor &symbex) + : TestBackEnd(programInfo, testBackendConfiguration, symbex) { + cstring testBackendString = TestgenOptions::get().testBackend; + + if (testBackendString.isNullOrEmpty()) { + ::error( + "No test back end provided. Please provide a test back end using the --test-backend " + "parameter."); + exit(EXIT_FAILURE); + } + if (testBackendString == "PTF") { + testWriter = new PTF(testBackendConfiguration); + } else if (testBackendString == "STF") { + testWriter = new STF(testBackendConfiguration); + } else { + std::stringstream supportedBackendString; + bool isFirst = true; + for (const auto &backend : SUPPORTED_BACKENDS) { + if (!isFirst) { + supportedBackendString << ", "; + } else { + isFirst = false; + } + supportedBackendString << backend; + } + P4C_UNIMPLEMENTED( + "Test back end %1% not implemented for this target. Supported back ends are %2%.", + testBackendString, supportedBackendString.str()); + } +} + +bool TofinoTestBackend::printTestInfo(const ExecutionState *executionState, + const TestBackEnd::TestInfo &testInfo, + const IR::Expression *outputPortExpr) { + // If the port is uninitalized it is not actually tainted. It is dropped instead. + if (outputPortExpr->is()) { + outputPortExpr = IR::Constant::get(outputPortExpr->type, 0); + BUG_CHECK(testInfo.packetIsDropped, "Uninitialized output port that is not dropped."); + } + return TestBackEnd::printTestInfo(executionState, testInfo, outputPortExpr); +} + +TestBackEnd::TestInfo TofinoTestBackend::produceTestInfo( + const ExecutionState *executionState, const Model *finalModel, + const IR::Expression *outputPacketExpr, const IR::Expression *outputPortExpr, + const std::vector> *programTraces) { + auto testInfo = TestBackEnd::produceTestInfo(executionState, finalModel, outputPacketExpr, + outputPortExpr, programTraces); + if (outputPortExpr->is()) { + testInfo.packetIsDropped = true; + } + return testInfo; +} + +const TestSpec *TofinoTestBackend::createTestSpec(const ExecutionState *executionState, + const Model *finalModel, + const TestInfo &testInfo) { + // Create a testSpec. + TestSpec *testSpec = nullptr; + + const auto *ingressPayload = testInfo.inputPacket; + const auto *ingressPayloadMask = IR::Constant::get(IR::Type_Bits::get(1), 1); + const auto ingressPacket = Packet(testInfo.inputPort, ingressPayload, ingressPayloadMask); + + std::optional egressPacket = std::nullopt; + if (!testInfo.packetIsDropped) { + egressPacket = Packet(testInfo.outputPort, testInfo.outputPacket, testInfo.packetTaintMask); + } + testSpec = new TestSpec(ingressPacket, egressPacket, testInfo.programTraces); + // We retrieve the individual table configurations from the execution state. + const auto uninterpretedTableConfigs = executionState->getTestObjectCategory("tableconfigs"_cs); + // Since these configurations are uninterpreted we need to convert them. We launch a + // helper function to solve the variables involved in each table configuration. + for (const auto &tablePair : uninterpretedTableConfigs) { + const auto tableName = tablePair.first; + const auto *uninterpretedTableConfig = tablePair.second->checkedTo(); + const auto *const tableConfig = uninterpretedTableConfig->evaluate(*finalModel, true); + testSpec->addTestObject("tables"_cs, tableName, tableConfig); + } + // TODO: Move this to target specific test specification. + const auto actionProfiles = executionState->getTestObjectCategory("action_profile"_cs); + for (const auto &testObject : actionProfiles) { + const auto profileName = testObject.first; + const auto *actionProfile = testObject.second->checkedTo(); + const auto *evaluatedProfile = actionProfile->evaluate(*finalModel, true); + testSpec->addTestObject("action_profiles"_cs, profileName, evaluatedProfile); + } + + // TODO: Move this to target specific test specification. + const auto actionSelectors = executionState->getTestObjectCategory("action_selector"_cs); + for (const auto &testObject : actionSelectors) { + const auto selectorName = testObject.first; + const auto *actionSelector = testObject.second->checkedTo(); + const auto *evaluatedSelector = actionSelector->evaluate(*finalModel, true); + testSpec->addTestObject("action_selectors"_cs, selectorName, evaluatedSelector); + } + + const auto registerParams = executionState->getTestObjectCategory("registerparams"_cs); + for (const auto &[name, obj] : registerParams) { + const auto *reg = obj->checkedTo(); + const auto *evaluatedReg = reg->evaluate(*finalModel, true); + testSpec->addTestObject("registerparams"_cs, name, evaluatedReg); + } + + const auto registers = executionState->getTestObjectCategory("registervalues"_cs); + for (const auto &[name, obj] : registers) { + const auto *reg = obj->checkedTo(); + const auto *evaluatedReg = reg->evaluate(*finalModel, true); + testSpec->addTestObject("registervalues"_cs, name, evaluatedReg); + } + + const auto directRegisters = executionState->getTestObjectCategory("direct_registervalues"_cs); + for (const auto &[name, obj] : directRegisters) { + const auto *reg = obj->checkedTo(); + const auto *evaluatedReg = reg->evaluate(*finalModel, true); + testSpec->addTestObject("direct_registervalues"_cs, name, evaluatedReg); + } + + return testSpec; +} +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_backend.h b/backends/p4tools/modules/testgen/targets/tofino/test_backend.h new file mode 100644 index 00000000000..1009af64e85 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_backend.h @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_H_ + +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/model.h" +#include "backends/p4tools/common/lib/trace_event.h" +#include "ir/ir.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/core/symbolic_executor/symbolic_executor.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/lib/test_backend.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class TofinoTestBackend : public TestBackEnd { + private: + static const std::vector SUPPORTED_BACKENDS; + + public: + explicit TofinoTestBackend(const TofinoSharedProgramInfo &programInfo, + const TestBackendConfiguration &testBackendConfiguration, + SymbolicExecutor &symbex); + + TestInfo produceTestInfo( + const ExecutionState *executionState, const Model *finalModel, + const IR::Expression *outputPacketExpr, const IR::Expression *outputPortExpr, + const std::vector> *programTraces) override; + + const TestSpec *createTestSpec(const ExecutionState *executionState, const Model *finalModel, + const TestInfo &testInfo) override; + + bool printTestInfo(const ExecutionState *executionState, const TestInfo &testInfo, + const IR::Expression *outputPortExpr) override; +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.cpp b/backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.cpp new file mode 100644 index 00000000000..82b8a59b322 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.cpp @@ -0,0 +1,597 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/format_int.h" +#include "backends/p4tools/common/lib/util.h" +#include "ir/ir.h" +#include "lib/log.h" +#include "nlohmann/json.hpp" + +#include "backends/p4tools/modules/testgen/lib/exceptions.h" +#include "backends/p4tools/modules/testgen/lib/test_backend_configuration.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +PTF::PTF(const TestBackendConfiguration &testBackendConfiguration) + : TestFramework(testBackendConfiguration) {} + +std::vector> PTF::getIgnoreMasks(const IR::Constant *mask) { + std::vector> ignoreMasks; + if (mask == nullptr) { + return ignoreMasks; + } + auto maskBinStr = formatBinExpr(mask, {false, true, false}); + int countZeroes = 0; + size_t offset = 0; + for (; offset < maskBinStr.size(); ++offset) { + if (maskBinStr.at(offset) == '0') { + countZeroes++; + } else { + if (countZeroes > 0) { + ignoreMasks.emplace_back(offset - countZeroes, countZeroes); + countZeroes = 0; + } + } + } + if (countZeroes > 0) { + ignoreMasks.emplace_back(offset - countZeroes, countZeroes); + } + return ignoreMasks; +} + +inja::json PTF::getControlPlane(const TestSpec *testSpec) { + inja::json controlPlaneJson = inja::json::object(); + + // Map of actionProfiles and actionSelectors for easy reference. + std::map apAsMap; + + auto tables = testSpec->getTestObjectCategory("tables"_cs); + if (!tables.empty()) { + controlPlaneJson["tables"] = inja::json::array(); + } + for (auto const &testObject : tables) { + inja::json tblJson; + tblJson["table_name"] = testObject.first.c_str(); + const auto *const tblConfig = testObject.second->checkedTo(); + auto const *tblRules = tblConfig->getRules(); + tblJson["rules"] = inja::json::array(); + for (const auto &tblRules : *tblRules) { + inja::json rule; + auto const *matches = tblRules.getMatches(); + auto const *actionCall = tblRules.getActionCall(); + auto const *actionArgs = actionCall->getArgs(); + rule["action_name"] = actionCall->getActionName().c_str(); + auto j = getControlPlaneForTable(*matches, *actionArgs); + rule["rules"] = std::move(j); + tblJson["rules"].push_back(rule); + } + // Collect action profiles and selectors associated with the table. + checkForTableActionProfile(tblJson, apAsMap, + tblConfig); + + // Check whether the default action is overridden for this table. + checkForDefaultActionOverride(tblJson, tblConfig); + + controlPlaneJson["tables"].push_back(tblJson); + } + + // Collect declarations of action profiles. + collectActionProfileDeclarations(testSpec, controlPlaneJson, apAsMap); + + auto registerParams = testSpec->getTestObjectCategory("registerparams"_cs); + if (!registerParams.empty()) { + controlPlaneJson["register_params"] = inja::json::array(); + } + for (auto const &[objName, obj] : registerParams) { + const auto *reg = obj->checkedTo(); + inja::json r; + auto registerParamName = reg->getRegisterParamDeclaration()->controlPlaneName(); + r["name"] = registerParamName; + r["value"] = formatHexExpr(reg->getEvaluatedInitialValue()); + controlPlaneJson["register_params"].push_back(r); + } + + auto registers = testSpec->getTestObjectCategory("registervalues"_cs); + if (!registers.empty()) { + controlPlaneJson["registers"] = inja::json::array(); + } + for (auto const &[objName, obj] : registers) { + const auto *reg = obj->checkedTo(); + inja::json r; + auto registerName = reg->getRegisterDeclaration()->controlPlaneName(); + r["name"] = registerName; + r["index"] = reg->getEvaluatedInitialIndex()->value.str(); + const auto *evaluatedVal = reg->getEvaluatedInitialValue(); + if (const auto *structExpr = evaluatedVal->to()) { + r["init_val_list"] = inja::json::array(); + for (const auto *structElem : structExpr->components) { + inja::json structElemJson = inja::json::object(); + structElemJson["name"] = registerName + "." + structElem->name; + structElemJson["val"] = formatHexExpr(structElem->expression); + r["init_val_list"].push_back(structElemJson); + } + } else if (const auto *constantVal = evaluatedVal->to()) { + r["init_val"] = formatHexExpr(constantVal); + } else { + P4C_UNIMPLEMENTED("Unsupported initial register value %1% of type %2%", evaluatedVal, + evaluatedVal->node_type_name()); + } + controlPlaneJson["registers"].push_back(r); + } + + auto directRegisters = testSpec->getTestObjectCategory("direct_registervalues"_cs); + if (!directRegisters.empty()) { + controlPlaneJson["direct_registers"] = inja::json::array(); + } + for (auto const &[objName, obj] : directRegisters) { + const auto *reg = obj->checkedTo(); + inja::json r; + auto registerName = reg->getRegisterDeclaration()->controlPlaneName(); + r["name"] = registerName; + r["table"] = reg->getRegisterTable()->controlPlaneName(); + const auto *evaluatedVal = reg->getEvaluatedInitialValue(); + if (const auto *structExpr = evaluatedVal->to()) { + r["init_val_list"] = inja::json::array(); + for (const auto *structElem : structExpr->components) { + inja::json structElemJson = inja::json::object(); + structElemJson["name"] = registerName + "." + structElem->name; + structElemJson["val"] = formatHexExpr(structElem->expression); + r["init_val_list"].push_back(structElemJson); + } + } else if (const auto *constantVal = evaluatedVal->to()) { + r["init_val"] = formatHexExpr(constantVal); + } else { + P4C_UNIMPLEMENTED("Unsupported initial register value %1% of type %2%", evaluatedVal, + evaluatedVal->node_type_name()); + } + controlPlaneJson["direct_registers"].push_back(r); + } + + return controlPlaneJson; +} + +inja::json PTF::getControlPlaneForTable(const TableMatchMap &matches, + const std::vector &args) { + inja::json rulesJson; + + rulesJson["single_exact_matches"] = inja::json::array(); + rulesJson["multiple_exact_matches"] = inja::json::array(); + rulesJson["range_matches"] = inja::json::array(); + rulesJson["ternary_matches"] = inja::json::array(); + rulesJson["lpm_matches"] = inja::json::array(); + + rulesJson["act_args"] = inja::json::array(); + + for (auto const &match : matches) { + auto const fieldName = match.first; + auto const &fieldMatch = match.second; + + inja::json j; + j["field_name"] = fieldName; + if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + rulesJson["single_exact_matches"].push_back(j); + } else if (const auto *elem = fieldMatch->to()) { + j["lo"] = formatHexExpr(elem->getEvaluatedLow()).c_str(); + j["hi"] = formatHexExpr(elem->getEvaluatedHigh()).c_str(); + rulesJson["range_matches"].push_back(j); + } else if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + j["mask"] = formatHexExpr(elem->getEvaluatedMask()).c_str(); + rulesJson["ternary_matches"].push_back(j); + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + j["prefix_len"] = elem->getEvaluatedPrefixLength()->value.str(); + rulesJson["lpm_matches"].push_back(j); + } else { + TESTGEN_UNIMPLEMENTED("Unsupported table key match type \"%1%\"", + fieldMatch->getObjectName()); + } + } + + for (const auto &actArg : args) { + inja::json j; + j["param"] = actArg.getActionParamName().c_str(); + j["value"] = formatHexExpr(actArg.getEvaluatedValue()).c_str(); + rulesJson["act_args"].push_back(j); + } + + return rulesJson; +} + +inja::json PTF::getSend(const TestSpec *testSpec) { + const auto *iPacket = testSpec->getIngressPacket(); + const auto *payload = iPacket->getEvaluatedPayload(); + inja::json sendJson; + sendJson["ig_port"] = iPacket->getPort(); + auto dataStr = formatHexExpr(payload, {false, true, false}); + sendJson["pkt"] = insertHexSeparators(dataStr); + sendJson["pkt_size"] = payload->type->width_bits(); + return sendJson; +} + +inja::json PTF::getVerify(const TestSpec *testSpec) { + inja::json verifyData = inja::json::object(); + if (testSpec->getEgressPacket() != std::nullopt) { + const auto &packet = **testSpec->getEgressPacket(); + verifyData["eg_port"] = packet.getPort(); + const auto *payload = packet.getEvaluatedPayload(); + const auto *payloadMask = packet.getEvaluatedPayloadMask(); + verifyData["ignore_masks"] = getIgnoreMasks(payloadMask); + + auto dataStr = formatHexExpr(payload, {false, true, false}); + verifyData["exp_pkt"] = insertHexSeparators(dataStr); + } + return verifyData; +} + +void PTF::emitPreamble() { + static const std::string PREAMBLE( + R"""(# PTF test for {{test_name}} +# p4testgen seed: {{ default(seed, "none") }} + +import logging +import itertools + +from bfruntime_client_base_tests import BfRuntimeTest +from ptf.mask import Mask +from ptf.testutils import send_packet +from ptf.testutils import verify_packet +from ptf.testutils import verify_no_other_packets + +import bfrt_grpc.bfruntime_pb2 as bfruntime_pb2 +import bfrt_grpc.client as gc + +logger = logging.getLogger('{{test_name}}') +logger.addHandler(logging.StreamHandler()) + +class AbstractTest(BfRuntimeTest): + def setUp(self): + BfRuntimeTest.setUp(self, 0, '{{test_name}}') + self.dev_id = 0 + self.table_entries = [] + self.bfrt_info = None + # Get bfrt_info and set it as part of the test. + self.bfrt_info = self.interface.bfrt_info_get('{{test_name}}') + + # Set target to all pipes on device self.dev_id. + self.target = gc.Target(device_id=0, pipe_id=0xFFFF) + + def tearDown(self): + # Reset tables. + for elt in reversed(self.table_entries): + test_table = self.bfrt_info.table_get(elt[0]) + test_table.entry_del(self.target, elt[1]) + self.table_entries = [] + + # End session. + BfRuntimeTest.tearDown(self) + + def insertTableEntry( + self, table_name, key_fields=None, action_name=None, data_fields=[] + ): + test_table = self.bfrt_info.table_get(table_name) + key_list = [test_table.make_key(key_fields)] + data_list = [test_table.make_data(data_fields, action_name)] + test_table.entry_add(self.target, key_list, data_list) + self.table_entries.append((table_name, key_list)) + + def _responseDumpHelper(self, request): + for response in self.interface.stub.Read(request, timeout=2): + yield response + + def overrideDefaultEntry(self, table_name, action_name=None, data_fields=[]): + test_table = self.bfrt_info.table_get(table_name) + data = test_table.make_data(data_fields, action_name) + test_table.default_entry_set(self.target, data) + + def setRegisterValue(self, reg_name, value, index): + reg_table = self.bfrt_info.table_get(reg_name) + key_list = [reg_table.make_key([gc.KeyTuple("$REGISTER_INDEX", index)])] + value_list = [] + if isinstance(value, list): + for val in value: + value_list.append(gc.DataTuple(val[0], val[1])) + else: + value_list.append(gc.DataTuple("f1", value)) + reg_table.entry_add(self.target, key_list, [reg_table.make_data(value_list)]) + + def entryAdd(self, table_obj, target, table_entry): + req = bfruntime_pb2.WriteRequest() + gc._cpy_target(req, target) + req.atomicity = bfruntime_pb2.WriteRequest.CONTINUE_ON_ERROR + update = req.updates.add() + update.type = bfruntime_pb2.Update.MODIFY + update.entity.table_entry.CopyFrom(table_entry) + resp = self.interface.reader_writer_interface._write(req) + table_obj.get_parser._parse_entry_write_response(resp) + + def setDirectRegisterValue(self, tbl_name, value): + test_table = self.bfrt_info.table_get(tbl_name) + table_id = test_table.info.id + req = bfruntime_pb2.ReadRequest() + req.client_id = self.client_id + gc._cpy_target(req, self.target) + entity = req.entities.add() + table = entity.table_entry + table.table_id = table_id + table_entry = None + for response in self._responseDumpHelper(req): + for entity in response.entities: + assert entity.WhichOneof("entity") == "table_entry" + table_entry = entity.table_entry + break + if table_entry is None: + raise self.failureException( + "No entry in the table that the meter is attached to." + ) + table_entry.ClearField("data") + value_list = [] + if isinstance(value, list): + for val in value: + df = table_entry.data.fields.add() + else: + df = table_entry.data.fields.add() + df.value = gc.DataTuple(gc.DataTuple("f1", value)) + self.entryAdd(test_table, self.target, table_entry) + + def setupCtrlPlane(self): + pass + + def sendPacket(self): + pass + + def verifyPackets(self): + pass + + def runTestImpl(self): + self.setupCtrlPlane() + logger.info("Sending Packet ...") + self.sendPacket() + logger.info("Verifying Packet ...") + self.verifyPackets() + logger.info("Verifying no other packets ...") + verify_no_other_packets(self, self.dev_id, timeout=2) +)"""); + inja::json dataJson; + dataJson["test_name"] = getTestBackendConfiguration().testBaseName; + auto optSeed = getTestBackendConfiguration().seed; + if (optSeed.has_value()) { + dataJson["seed"] = optSeed.value(); + } + + inja::render_to(ptfFileStream, PREAMBLE, dataJson); + ptfFileStream.flush(); +} + +std::string PTF::getTestCaseTemplate() { + static const std::string TEST_CASE( + R"""( +class Test{{test_id}}(AbstractTest): + # Date generated: {{timestamp}} +## if length(selected_branches) > 0 + # {{selected_branches}} +## endif + # Current statement coverage: {{coverage}} + ''' +## for trace_item in trace + {{trace_item}} +##endfor + ''' + + def setupCtrlPlane(self): +## if control_plane +## if existsIn(control_plane, "action_profiles") +## for ap in control_plane.action_profiles +## for action in ap.actions + self.insertTableEntry( + '{{ap.profile}}', + [ + gc.KeyTuple('$ACTION_MEMBER_ID', {{action.action_idx}}), + ], + '{{action.action_name}}', + [ +## for act_param in action.act_args + gc.DataTuple('{{act_param.param}}', {{act_param.value}}), +## endfor + ] + ) +## endfor +## endfor +## endif +## if existsIn(control_plane, "register_params") +## for reg in control_plane.register_params + reg_param = self.bfrt_info.table_get('{{reg.name}}') + reg_param.default_entry_set( + self.target, + reg_param.make_data([gc.DataTuple('value', {{reg.value}})]) + ) +## endfor +## endif +## if existsIn(control_plane, "registers") +## for reg in control_plane.registers +## if existsIn(reg, "init_val_list") + self.setRegisterValue('{{reg.name}}', [{% for r in reg.init_val_list %}('{{r.name}}', {{r.val}}){% if not loop.is_last %}, {% endif %}{% endfor %}], {{reg.index}}) +## else if existsIn(reg, "init_val") + self.setRegisterValue('{{reg.name}}', {{reg.init_val}}, {{reg.index}}) +## endif +## endfor +## endif +## if existsIn(control_plane, "direct_registers") +## for reg in control_plane.direct_registers +## if existsIn(reg, "init_val_list") + self.setDirectRegisterValue('{{reg.table}}', [{% for r in reg.init_val_list %}('{{r.name}}', {{r.val}}){% if not loop.is_last %}, {% endif %}{% endfor %}]) +## else if existsIn(reg, "init_val") + self.setDirectRegisterValue('{{reg.table}}', {{reg.init_val}}) +## endif +## endfor +## endif +## if existsIn(control_plane, "tables") +## for table in control_plane.tables +## if existsIn(table, "default_override") + # Table {{table.table_name}} + self.overrideDefaultEntry( + '{{table.table_name}}', + '{{table.default_override.action_name}}', + [ +## for act_param in table.default_override.act_args + gc.DataTuple('{{act_param.param}}', {{act_param.value}}), +## endfor + ] + ) +## else if existsIn(table, "has_ap") +## for rule in table.rules + self.insertTableEntry( + '{{table.table_name}}', + [ +## for r in rule.rules.single_exact_matches + gc.KeyTuple('{{r.field_name}}', {{r.value}}), +## endfor +## for r in rule.rules.range_matches + gc.KeyTuple('{{r.field_name}}', low={{r.lo}}, high={{r.hi}}), +## endfor +## for r in rule.rules.ternary_matches + gc.KeyTuple('{{r.field_name}}', {{r.value}}, {{r.mask}}), +## endfor +## for r in rule.rules.lpm_matches + gc.KeyTuple('{{r.field_name}}', {{r.value}}, prefix_len={{r.prefix_len}}), +## endfor + ], + None, + [ + gc.DataTuple('$ACTION_MEMBER_ID', {{rule.action_name}}), + ] + ) +## endfor +## else + # Table {{table.table_name}} +## for rule in table.rules + self.insertTableEntry( + '{{table.table_name}}', + [ +## for r in rule.rules.single_exact_matches + gc.KeyTuple('{{r.field_name}}', {{r.value}}), +## endfor +## for r in rule.rules.range_matches + gc.KeyTuple('{{r.field_name}}', low={{r.lo}}, high={{r.hi}}), +## endfor +## for r in rule.rules.ternary_matches + gc.KeyTuple('{{r.field_name}}', {{r.value}}, {{r.mask}}), +## endfor +## for r in rule.rules.lpm_matches + gc.KeyTuple('{{r.field_name}}', {{r.value}}, prefix_len={{r.prefix_len}}), +## endfor + ], + '{{rule.action_name}}', + [ +## for act_param in rule.rules.act_args + gc.DataTuple('{{act_param.param}}', {{act_param.value}}), +## endfor + ] + ) +## endfor +## endif +## endfor +## endif +## else + pass +## endif + + def sendPacket(self): +## if send + ig_port = {{send.ig_port}} + pkt = b'{{send.pkt}}' + send_packet(self, ig_port, pkt) +## else + pass +## endif + + def verifyPackets(self): +## if verify + eg_port = {{verify.eg_port}} + exp_pkt = b'{{verify.exp_pkt}}' + exp_pkt = Mask(exp_pkt) +## for ignore_mask in verify.ignore_masks + exp_pkt.set_do_not_care({{ignore_mask.0}}, {{ignore_mask.1}}) +## endfor + verify_packet(self, exp_pkt, eg_port) +## else + pass +## endif + + def runTest(self): + self.runTestImpl() + +)"""); + return TEST_CASE; +} + +void PTF::emitTestcase(const TestSpec *testSpec, cstring selectedBranches, size_t testIdx, + const std::string &testCase, float currentCoverage) { + inja::json dataJson; + if (selectedBranches != nullptr) { + dataJson["selected_branches"] = selectedBranches.c_str(); + } + + dataJson["test_id"] = testIdx; + dataJson["trace"] = getTrace(testSpec); + dataJson["control_plane"] = getControlPlane(testSpec); + dataJson["send"] = getSend(testSpec); + dataJson["verify"] = getVerify(testSpec); + dataJson["timestamp"] = Utils::getTimeStamp(); + std::stringstream coverageStr; + coverageStr << std::setprecision(2) << currentCoverage; + dataJson["coverage"] = coverageStr.str(); + + LOG5("PTF backend: emitting testcase:" << std::setw(4) << dataJson); + + if (!preambleEmitted) { + BUG_CHECK(getTestBackendConfiguration().fileBasePath.has_value(), "Base path is not set."); + auto ptfFile = getTestBackendConfiguration().fileBasePath.value(); + ptfFile.replace_extension(".py"); + ptfFileStream = std::ofstream(ptfFile); + emitPreamble(); + preambleEmitted = true; + } + inja::render_to(ptfFileStream, testCase, dataJson); + ptfFileStream.flush(); +} + +void PTF::writeTestToFile(const TestSpec *testSpec, cstring selectedBranches, size_t testIdx, + float currentCoverage) { + std::string testCase = getTestCaseTemplate(); + emitTestcase(testSpec, selectedBranches, testIdx, testCase, currentCoverage); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.h b/backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.h new file mode 100644 index 00000000000..d796da68413 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_backend/ptf.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_PTF_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_PTF_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "ir/ir.h" +#include "lib/cstring.h" + +#include "backends/p4tools/modules/testgen/lib/test_framework.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/// Extracts information from the @testSpec to emit a PTF test case. +class PTF : public TestFramework { + /// Has the preamble been generated already? + bool preambleEmitted = false; + + /// The output file. + std::ofstream ptfFileStream; + + public: + ~PTF() override = default; + + PTF(const PTF &) = delete; + + PTF(PTF &&) = delete; + + PTF &operator=(const PTF &) = delete; + + PTF &operator=(PTF &&) = delete; + + explicit PTF(const TestBackendConfiguration &testBackendConfiguration); + + /// Produce a PTF test. + void writeTestToFile(const TestSpec *spec, cstring selectedBranches, size_t testIdx, + float currentCoverage) override; + + private: + /// Emits the test preamble. This is only done once for all generated tests. + /// For the PTF back end this is the test setup Python script.. + void emitPreamble(); + + /// Emits a test case. + /// @param testId specifies the test name. + /// @param selectedBranches enumerates the choices the interpreter made for this path. + /// @param currentCoverage contains statistics about the current coverage of this test and its + /// preceding tests. + void emitTestcase(const TestSpec *testSpec, cstring selectedBranches, size_t testIdx, + const std::string &testCase, float currentCoverage); + + /// @returns the inja test case template as a string. + static std::string getTestCaseTemplate(); + + /// Converts all the control plane objects into Inja format. + static inja::json getControlPlane(const TestSpec *testSpec); + + /// Converts the input packet and port into Inja format. + static inja::json getSend(const TestSpec *testSpec); + + /// Converts the output packet, port, and mask into Inja format. + static inja::json getVerify(const TestSpec *testSpec); + + /// Helper function for @getVerify. Matches the mask value against the input packet value and + /// generates the appropriate ignore ranges. + static std::vector> getIgnoreMasks(const IR::Constant *mask); + + /// Helper function for the control plane table inja objects. + static inja::json getControlPlaneForTable(const TableMatchMap &matches, + const std::vector &args); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_PTF_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.cpp b/backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.cpp new file mode 100644 index 00000000000..92b46e19160 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.cpp @@ -0,0 +1,300 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/format_int.h" +#include "backends/p4tools/common/lib/util.h" +#include "ir/ir.h" +#include "ir/irutils.h" +#include "lib/exceptions.h" +#include "lib/log.h" + +#include "backends/p4tools/modules/testgen/lib/exceptions.h" +#include "backends/p4tools/modules/testgen/lib/test_backend_configuration.h" +#include "backends/p4tools/modules/testgen/lib/test_framework.h" +#include "backends/p4tools/modules/testgen/targets/tofino/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +STF::STF(const TestBackendConfiguration &testBackendConfiguration) + : TestFramework(testBackendConfiguration) {} + +inja::json STF::getControlPlane(const TestSpec *testSpec) { + inja::json controlPlaneJson = inja::json::object(); + + // Map of actionProfiles and actionSelectors for easy reference. + std::map apAsMap; + + auto tables = testSpec->getTestObjectCategory("tables"_cs); + if (!tables.empty()) { + controlPlaneJson["tables"] = inja::json::array(); + } + for (const auto &testObject : tables) { + inja::json tblJson; + tblJson["table_name"] = testObject.first.c_str(); + const auto *const tblConfig = testObject.second->checkedTo(); + const auto *tblRules = tblConfig->getRules(); + tblJson["rules"] = inja::json::array(); + for (const auto &tblRule : *tblRules) { + inja::json rule; + const auto *matches = tblRule.getMatches(); + const auto *actionCall = tblRule.getActionCall(); + const auto *actionArgs = actionCall->getArgs(); + rule["action_name"] = actionCall->getActionName().c_str(); + auto j = getControlPlaneForTable(*matches, *actionArgs); + rule["rules"] = std::move(j); + rule["priority"] = tblRule.getPriority(); + tblJson["rules"].push_back(rule); + } + + // Collect action profiles and selectors associated with the table. + checkForTableActionProfile(tblJson, apAsMap, + tblConfig); + + // Check whether the default action is overridden for this table. + checkForDefaultActionOverride(tblJson, tblConfig); + + controlPlaneJson["tables"].push_back(tblJson); + } + + // Collect declarations of action profiles. + collectActionProfileDeclarations(testSpec, controlPlaneJson, apAsMap); + + return controlPlaneJson; +} + +inja::json STF::getControlPlaneForTable(const TableMatchMap &matches, + const std::vector &args) { + inja::json rulesJson; + + rulesJson["matches"] = inja::json::array(); + rulesJson["act_args"] = inja::json::array(); + rulesJson["needs_priority"] = false; + + for (const auto &match : matches) { + auto fieldName = match.first; + + // Replace slices hdr[:] with hdr. This is output in the context.json + // TODO: This is a limitation of the test_harness. We should fix this. + std::regex sliceRegex(R"(\[([0-9]+:[0-9]+)\])"); + auto sliceName = std::regex_replace(fieldName.c_str(), sliceRegex, ""); + if (sliceName != fieldName.c_str()) { + fieldName = cstring::to_cstring(sliceName); + } + // Removes BAnd operations (" & "). This is output in the context.json + // TODO: This is a limitation of the test_harness. We should fix this. + std::regex bAndRegex(R"( & )"); + auto bandName = std::regex_replace(fieldName.c_str(), bAndRegex, ""); + if (bandName != fieldName.c_str()) { + fieldName = cstring::to_cstring(bandName); + } + + const auto &fieldMatch = match.second; + + inja::json j; + j["field_name"] = fieldName; + if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()); + } else if (const auto *elem = fieldMatch->to()) { + const auto *dataValue = elem->getEvaluatedValue(); + const auto *maskField = elem->getEvaluatedMask(); + BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), + "Data value and its mask should have the same bit width."); + // Using the width from mask - should be same as data + auto dataStr = formatBinExpr(dataValue, {false, true, false}); + auto maskStr = formatBinExpr(maskField, {false, true, false}); + std::string data = "0b"; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) == '0') { + data += "*"; + } else { + data += dataStr.at(dataPos); + } + } + j["value"] = data; + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else if (const auto *elem = fieldMatch->to()) { + const auto *dataValue = elem->getEvaluatedValue(); + auto prefixLen = elem->getEvaluatedPrefixLength()->asInt(); + auto fieldWidth = dataValue->type->width_bits(); + auto maxVal = IR::getMaxBvVal(prefixLen); + const auto *maskField = + IR::Constant::get(dataValue->type, maxVal << (fieldWidth - prefixLen)); + BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), + "Data value and its mask should have the same bit width."); + // Using the width from mask - should be same as data + auto dataStr = formatBinExpr(dataValue, {false, true, false}); + auto maskStr = formatBinExpr(maskField, {false, true, false}); + std::string data = "0b"; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) == '0') { + data += "*"; + } else { + data += dataStr.at(dataPos); + } + } + j["value"] = data; + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else { + TESTGEN_UNIMPLEMENTED("Unsupported table key match type \"%1%\"", + fieldMatch->getObjectName()); + } + rulesJson["matches"].push_back(j); + } + + for (const auto &actArg : args) { + inja::json j; + j["param"] = actArg.getActionParamName().c_str(); + j["value"] = formatHexExpr(actArg.getEvaluatedValue()); + rulesJson["act_args"].push_back(j); + } + + return rulesJson; +} + +inja::json STF::getSend(const TestSpec *testSpec) { + const auto *iPacket = testSpec->getIngressPacket(); + const auto *payload = iPacket->getEvaluatedPayload(); + inja::json sendJson; + sendJson["ig_port"] = iPacket->getPort(); + auto dataStr = formatHexExpr(payload, {false, true, false}); + sendJson["pkt"] = dataStr; + sendJson["pkt_size"] = payload->type->width_bits(); + return sendJson; +} + +inja::json STF::getVerify(const TestSpec *testSpec) { + inja::json verifyData = inja::json::object(); + auto egressPktOpt = testSpec->getEgressPacket(); + if (egressPktOpt.has_value()) { + const auto *packet = egressPktOpt.value(); + verifyData["eg_port"] = packet->getPort(); + const auto *payload = packet->getEvaluatedPayload(); + const auto *payloadMask = packet->getEvaluatedPayloadMask(); + auto dataStr = formatHexExpr(payload, {false, true, false}); + if (payloadMask != nullptr) { + // If a mask is present, construct the packet data with wildcard `*` where there are + // non zero nibbles + auto maskStr = formatHexExpr(payloadMask, {false, true, false}); + std::string packetData; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) != 'F') { + // TODO: We are being conservative here and adding a wildcard for any 0 + // in the 4b nibble + packetData += "*"; + } else { + packetData += dataStr[dataPos]; + } + } + verifyData["exp_pkt"] = packetData; + } else { + verifyData["exp_pkt"] = dataStr; + } + } + return verifyData; +} + +std::string STF::getTestCaseTemplate() { + static std::string TEST_CASE( + R"""(# p4testgen seed: {{ default(seed, "none") }} +# Date generated: {{timestamp}} +## if length(selected_branches) > 0 + # {{selected_branches}} +## endif +# Current statement coverage: {{coverage}} +# Traces +## for trace_item in trace +# {{trace_item}} +##endfor + +## if control_plane +## for table in control_plane.tables +# Table {{table.table_name}} +## if existsIn(table, "default_override") +setdefault {{table.table_name}} {{table.default_override.action_name}}({% for a in table.default_override.act_args %}{{a.param}}:{{a.value}}{% if not loop.is_last %},{% endif %}{% endfor %}) +## else +## for rule in table.rules +add {{table.table_name}} {% if rule.rules.needs_priority %}{{rule.priority}} {% endif %}{% for r in rule.rules.matches %}{{r.field_name}}:{{r.value}} {% endfor %}{{rule.action_name}}({% for a in rule.rules.act_args %}{{a.param}}:{{a.value}}{% if not loop.is_last %},{% endif %}{% endfor %}) +## endfor +## endif + +## endfor +## endif + +packet {{send.ig_port}} {{send.pkt}} +## if verify +expect {{verify.eg_port}} {{verify.exp_pkt}}$ +## endif + +)"""); + return TEST_CASE; +} + +void STF::emitTestcase(const TestSpec *testSpec, cstring selectedBranches, size_t testId, + const std::string &testCase, float currentCoverage) { + inja::json dataJson; + if (selectedBranches != nullptr) { + dataJson["selected_branches"] = selectedBranches.c_str(); + } + auto optSeed = getTestBackendConfiguration().seed; + if (optSeed.has_value()) { + dataJson["seed"] = optSeed.value(); + } + + dataJson["test_id"] = testId; + dataJson["trace"] = getTrace(testSpec); + dataJson["control_plane"] = getControlPlane(testSpec); + dataJson["send"] = getSend(testSpec); + dataJson["verify"] = getVerify(testSpec); + dataJson["timestamp"] = Utils::getTimeStamp(); + std::stringstream coverageStr; + coverageStr << std::setprecision(2) << currentCoverage; + dataJson["coverage"] = coverageStr.str(); + + LOG5("STF test back end: emitting testcase:" << std::setw(4) << dataJson); + + auto optBasePath = getTestBackendConfiguration().fileBasePath; + BUG_CHECK(optBasePath.has_value(), "Base path is not set."); + auto incrementedbasePath = optBasePath.value(); + incrementedbasePath.concat("_" + std::to_string(testId)); + incrementedbasePath.replace_extension(".stf"); + auto stfFileStream = std::ofstream(incrementedbasePath); + inja::render_to(stfFileStream, testCase, dataJson); + stfFileStream.flush(); +} + +void STF::writeTestToFile(const TestSpec *testSpec, cstring selectedBranches, size_t testId, + float currentCoverage) { + std::string testCase = getTestCaseTemplate(); + emitTestcase(testSpec, selectedBranches, testId, testCase, currentCoverage); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.h b/backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.h new file mode 100644 index 00000000000..526e2db7e72 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_backend/stf.h @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_STF_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_STF_H_ + +#include +#include +#include + +#include + +#include "lib/cstring.h" + +#include "backends/p4tools/modules/testgen/lib/test_framework.h" +#include "backends/p4tools/modules/testgen/lib/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/// Extracts information from the @testSpec to emit a STF test case. +class STF : public TestFramework { + public: + ~STF() override = default; + + STF(const STF &) = delete; + + STF(STF &&) = delete; + + STF &operator=(const STF &) = delete; + + STF &operator=(STF &&) = delete; + + explicit STF(const TestBackendConfiguration &testBackendConfiguration); + + /// Produce an STF test. + void writeTestToFile(const TestSpec *spec, cstring selectedBranches, size_t testId, + float currentCoverage) override; + + private: + /// Emits a test case. + /// @param testId specifies the test name. + /// @param selectedBranches enumerates the choices the interpreter made for this path. + /// @param currentCoverage contains statistics about the current coverage of this test and its + /// preceding tests. + void emitTestcase(const TestSpec *testSpec, cstring selectedBranches, size_t testId, + const std::string &testCase, float currentCoverage); + + /// @returns the inja test case template as a string. + static std::string getTestCaseTemplate(); + + /// Converts all the control plane objects into Inja format. + static inja::json getControlPlane(const TestSpec *testSpec); + + /// Converts the input packet and port into Inja format. + static inja::json getSend(const TestSpec *testSpec); + + /// Converts the output packet, port, and mask into Inja format. + static inja::json getVerify(const TestSpec *testSpec); + + /// Helper function for the control plane table inja objects. + static inja::json getControlPlaneForTable(const TableMatchMap &matches, + const std::vector &args); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_BACKEND_STF_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_spec.cpp b/backends/p4tools/modules/testgen/targets/tofino/test_spec.cpp new file mode 100644 index 00000000000..c733dc2370f --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_spec.cpp @@ -0,0 +1,295 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/* ========================================================================================= + * IndexExpression + * ========================================================================================= */ + +IndexExpression::IndexExpression(const IR::Expression *index, const IR::Expression *value) + : index(index), value(value) {} + +const IR::Constant *IndexExpression::getEvaluatedValue() const { + const auto *constant = value->to(); + BUG_CHECK(constant, "Variable is not a constant, has the test object %1% been evaluated?", + getObjectName()); + return constant; +} + +const IR::Constant *IndexExpression::getEvaluatedIndex() const { + const auto *constant = index->to(); + BUG_CHECK(constant, "Variable is not a constant, has the test object %1% been evaluated?", + getObjectName()); + return constant; +} + +const IR::Expression *IndexExpression::getIndex() const { return index; } + +const IR::Expression *IndexExpression::getValue() const { return value; } + +cstring IndexExpression::getObjectName() const { return "IndexExpression"_cs; } + +const IndexExpression *IndexExpression::evaluate(const Model &model, bool doComplete) const { + const auto *evaluatedIndex = model.evaluate(index, doComplete); + const auto *evaluatedValue = model.evaluate(value, doComplete); + return new IndexExpression(evaluatedIndex, evaluatedValue); +} + +std::map> IndexMap::unravelMap() const { + std::map> valueMap; + for (auto it = indexConditions.rbegin(); it != indexConditions.rend(); ++it) { + const auto *storedIndex = it->getEvaluatedIndex(); + const auto *storedVal = it->getEvaluatedValue(); + // Important, if the element already exists in the map, ignore it. + // That index has been overwritten. + valueMap.insert({storedIndex->value, {storedIndex->type->width_bits(), storedVal}}); + } + return valueMap; +} + +/* ========================================================================================= + * IndexMap + * ========================================================================================= */ + +IndexMap::IndexMap(const IR::Expression *initialValue) : initialValue(initialValue) {} + +void IndexMap::writeToIndex(const IR::Expression *index, const IR::Expression *value) { + indexConditions.emplace_back(index, value); +} + +const IR::Expression *IndexMap::getInitialValue() const { return initialValue; } + +const IR::Expression *IndexMap::getValueAtIndex(const IR::Expression *index) const { + const IR::Expression *baseExpr = initialValue; + for (const auto &indexMap : indexConditions) { + const auto *storedIndex = indexMap.getIndex(); + const auto *storedVal = indexMap.getValue(); + baseExpr = + new IR::Mux(baseExpr->type, new IR::Equ(storedIndex, index), storedVal, baseExpr); + } + return baseExpr; +} + +const IR::Expression *IndexMap::getEvaluatedInitialValue() const { + if (const auto *constant = initialValue->to()) { + return constant; + } + if (const auto *initalListExprValue = initialValue->to()) { + // TODO: Validate that this is a list expression of constants. + return initalListExprValue; + } + BUG("Variable is not a constant or a list expression, has the test object %1% been evaluated?", + getObjectName()); +} + +/* ========================================================================================= + * TofinoRegisterValue + * ========================================================================================= */ + +TofinoRegisterValue::TofinoRegisterValue(const IR::Declaration_Instance *decl, + const IR::Expression *initialValue, + const IR::Expression *initialIndex) + : IndexMap(initialValue), decl(decl), initialIndex(initialIndex) {} + +cstring TofinoRegisterValue::getObjectName() const { return "TofinoRegisterValue"_cs; } + +const IR::Declaration_Instance *TofinoRegisterValue::getRegisterDeclaration() const { return decl; } + +const TofinoRegisterValue *TofinoRegisterValue::evaluate(const Model &model, + bool doComplete) const { + const IR::Expression *evaluatedValue = nullptr; + if (const auto *initalListExprValue = initialValue->to()) { + evaluatedValue = model.evaluateStructExpr(initalListExprValue, doComplete); + } else { + evaluatedValue = model.evaluate(initialValue, doComplete); + } + const auto *evaluatedInitialIndex = model.evaluate(initialIndex, doComplete); + return new TofinoRegisterValue(decl, evaluatedValue, evaluatedInitialIndex); +} + +const IR::Constant *TofinoRegisterValue::getEvaluatedInitialIndex() const { + const auto *constant = initialIndex->to(); + BUG_CHECK(constant, "Variable is not a constant, has the test object %1% been evaluated?", + getObjectName()); + return constant; +} + +/* ========================================================================================= + * TofinoDirectRegisterValue + * ========================================================================================= */ + +TofinoDirectRegisterValue::TofinoDirectRegisterValue(const IR::Declaration_Instance *decl, + const IR::Expression *initialValue, + const IR::P4Table *table) + : initialValue(initialValue), decl(decl), table(table) {} + +cstring TofinoDirectRegisterValue::getObjectName() const { return "TofinoDirectRegisterValue"_cs; } + +const IR::Declaration_Instance *TofinoDirectRegisterValue::getRegisterDeclaration() const { + return decl; +} + +const IR::P4Table *TofinoDirectRegisterValue::getRegisterTable() const { return table; } + +const TofinoDirectRegisterValue *TofinoDirectRegisterValue::evaluate(const Model &model, + bool doComplete) const { + const IR::Expression *evaluatedValue = nullptr; + if (const auto *initalListExprValue = initialValue->to()) { + evaluatedValue = model.evaluateStructExpr(initalListExprValue, doComplete); + } else { + evaluatedValue = model.evaluate(initialValue, doComplete); + } + return new TofinoDirectRegisterValue(decl, evaluatedValue, table); +} + +const IR::Expression *TofinoDirectRegisterValue::getInitialValue() const { return initialValue; } + +const IR::Expression *TofinoDirectRegisterValue::getEvaluatedInitialValue() const { + if (const auto *constant = initialValue->to()) { + return constant; + } + if (const auto *initalListExprValue = initialValue->to()) { + // TODO: Validate that this is a list expression of constants. + return initalListExprValue; + } + BUG("Variable is not a constant or a list expression, has the test object %1% been evaluated?", + getObjectName()); +} + +/* ========================================================================================= + * TofinoRegisterParam + * ========================================================================================= */ + +TofinoRegisterParam::TofinoRegisterParam(const IR::Declaration_Instance *decl, + const IR::Expression *initialValue) + : decl(decl), initialValue(initialValue) {} + +cstring TofinoRegisterParam::getObjectName() const { return "TofinoRegisterParam"_cs; } + +const TofinoRegisterParam *TofinoRegisterParam::evaluate(const Model &model, + bool doComplete) const { + return new TofinoRegisterParam(decl, model.evaluate(initialValue, doComplete)); +} + +const IR::Declaration_Instance *TofinoRegisterParam::getRegisterParamDeclaration() const { + return decl; +} + +const IR::Expression *TofinoRegisterParam::getInitialValue() const { return initialValue; } + +const IR::Constant *TofinoRegisterParam::getEvaluatedInitialValue() const { + const auto *constant = initialValue->to(); + BUG_CHECK(constant, "Variable is not a constant, has the test object %1% been evaluated?", + getObjectName()); + return constant; +} + +/* ========================================================================================= + * TofinoActionProfile + * ========================================================================================= */ + +const std::vector>> *TofinoActionProfile::getActions() + const { + return &actions; +} + +TofinoActionProfile::TofinoActionProfile(const IR::IDeclaration *profileDecl) + : profileDecl(profileDecl) {} + +cstring TofinoActionProfile::getObjectName() const { return profileDecl->controlPlaneName(); } + +const IR::IDeclaration *TofinoActionProfile::getProfileDecl() const { return profileDecl; } + +void TofinoActionProfile::addToActionMap(cstring actionName, std::vector actionArgs) { + actions.emplace_back(actionName, actionArgs); +} +size_t TofinoActionProfile::getActionMapSize() const { return actions.size(); } + +const TofinoActionProfile *TofinoActionProfile::evaluate(const Model &model, + bool doComplete) const { + auto *profile = new TofinoActionProfile(profileDecl); + for (const auto &actionTuple : actions) { + auto actionArgs = actionTuple.second; + std::vector evaluatedArgs; + evaluatedArgs.reserve(actionArgs.size()); + for (const auto &actionArg : actionArgs) { + evaluatedArgs.emplace_back(*actionArg.evaluate(model, doComplete)); + } + profile->addToActionMap(actionTuple.first, evaluatedArgs); + } + return profile; +} + +/* ========================================================================================= + * TofinoActionSelector + * ========================================================================================= */ + +cstring TofinoActionSelector::getObjectName() const { return "TofinoActionSelector"_cs; } + +const IR::IDeclaration *TofinoActionSelector::getSelectorDecl() const { return selectorDecl; } + +const TofinoActionProfile *TofinoActionSelector::getActionProfile() const { return actionProfile; } + +TofinoActionSelector::TofinoActionSelector(const IR::IDeclaration *selectorDecl, + const TofinoActionProfile *actionProfile) + : selectorDecl(selectorDecl), actionProfile(actionProfile) {} + +const TofinoActionSelector *TofinoActionSelector::evaluate(const Model &model, + bool doComplete) const { + const auto *evaluatedProfile = actionProfile->evaluate(model, doComplete); + return new TofinoActionSelector(selectorDecl, evaluatedProfile); +} + +/* ========================================================================================= + * Table Key Match Types + * ========================================================================================= */ + +Range::Range(const IR::KeyElement *key, const IR::Expression *low, const IR::Expression *high) + : TableMatch(key), low(low), high(high) {} + +const IR::Constant *Range::getEvaluatedLow() const { + const auto *constant = low->to(); + BUG_CHECK(constant, + "Variable is not a constant. It has type %1% instead. Has the test object %2% " + "been evaluated?", + low->type->node_type_name(), getObjectName()); + return constant; +} + +const IR::Constant *Range::getEvaluatedHigh() const { + const auto *constant = high->to(); + BUG_CHECK(constant, + "Variable is not a constant. It has type %1% instead. Has the test object %2% " + "been evaluated?", + high->type->node_type_name(), getObjectName()); + return constant; +} + +const Range *Range::evaluate(const Model &model, bool doComplete) const { + const auto *evaluatedLow = model.evaluate(low, doComplete); + const auto *evaluatedHigh = model.evaluate(high, doComplete); + return new Range(getKey(), evaluatedLow, evaluatedHigh); +} + +cstring Range::getObjectName() const { return "Range"_cs; } + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/test_spec.h b/backends/p4tools/modules/testgen/targets/tofino/test_spec.h new file mode 100644 index 00000000000..6dbf3495cb4 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/test_spec.h @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_SPEC_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_SPEC_H_ + +#include +#include +#include + +#include "backends/p4tools/common/lib/model.h" +#include "ir/ir.h" +#include "lib/cstring.h" + +#include "backends/p4tools/modules/testgen/lib/test_spec.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +/* ========================================================================================= + * IndexExpression + * ========================================================================================= */ +/// Associates an expression with a particular index. +/// This object is used by all extern object that depend on a particular index to retrieve a value. +/// Examples are registers, meters, or counters. +class IndexExpression : public TestObject { + private: + /// The index of the expression. + const IR::Expression *index; + + /// The value of the expression. + const IR::Expression *value; + + public: + explicit IndexExpression(const IR::Expression *index, const IR::Expression *value); + + /// @returns the evaluated expression index. This means it must be a constant. + /// The function will throw a bug if this is not the case. + [[nodiscard]] const IR::Constant *getEvaluatedIndex() const; + + /// @returns the evaluated condition of the expression. This means it must be a constant. + /// The function will throw a bug if this is not the case. + [[nodiscard]] const IR::Constant *getEvaluatedValue() const; + + /// @returns the index stored in the index expression. + [[nodiscard]] const IR::Expression *getIndex() const; + + /// @returns the value stored in the index expression. + [[nodiscard]] const IR::Expression *getValue() const; + + [[nodiscard]] const IndexExpression *evaluate(const Model &model, + bool doComplete) const override; + + [[nodiscard]] cstring getObjectName() const override; + + DECLARE_TYPEINFO(IndexExpression, TestObject); +}; + +/* ========================================================================================= + * IndexMap + * ========================================================================================= */ +/// Readable and writable symbolic map, which maps indices to particular values. +class IndexMap : public TestObject { + protected: + /// A new IndexMap always requires an initial value. This can be a constant or taint. + const IR::Expression *initialValue; + + /// Each element is an API name paired with a match rule. + std::vector indexConditions; + + public: + explicit IndexMap(const IR::Expression *initialValue); + + /// Write @param value to @param index. + void writeToIndex(const IR::Expression *index, const IR::Expression *value); + + /// @returns the value with which this register has been initialized. + [[nodiscard]] const IR::Expression *getInitialValue() const; + + /// @returns the current value of this register after writes have been performed according to a + /// provided index. + [[nodiscard]] const IR::Expression *getValueAtIndex(const IR::Expression *index) const; + + /// @returns the evaluated register value. This means it must be a constant. + /// The function will throw a bug if this is not the case. + [[nodiscard]] const IR::Expression *getEvaluatedInitialValue() const; + + /// Return the "writes" to this index map as a + [[nodiscard]] std::map> unravelMap() const; + + DECLARE_TYPEINFO(IndexMap, TestObject); +}; + +/* ========================================================================================= + * TofinoRegisterValue + * ========================================================================================= */ +/// This object tracks the list of writes that have been performed to a particular register. The +/// IndexMap represents the pair of the index that was written, and the value that was +/// written to this index. When reading from a register, we can ravel this list starting from the +/// first index into a set of nested Mux expressions (e.g., "readIndex == savedIndex ? savedValue : +/// defaultValue", where defaultValue may be another Mux expression). If the read index matches with +/// the index that was saved in this tuple, we return the value, otherwise we unroll the nested MUX +/// expressions. This implicitly handles overwrites too, as the latest writes to a particular +/// index appear the earliest in this unraveling phase.. +class TofinoRegisterValue : public IndexMap { + private: + /// The register declaration, also contains the control plane name. + const IR::Declaration_Instance *decl; + + /// The index this register is initialized at. + const IR::Expression *initialIndex; + + public: + explicit TofinoRegisterValue(const IR::Declaration_Instance *decl, + const IR::Expression *initialValue, + const IR::Expression *initialIndex); + + [[nodiscard]] cstring getObjectName() const override; + + [[nodiscard]] const TofinoRegisterValue *evaluate(const Model &model, + bool doComplete) const override; + + /// @returns the declaration of this register. + [[nodiscard]] const IR::Declaration_Instance *getRegisterDeclaration() const; + + /// @returns the initial index this register is initialized with. + [[nodiscard]] const IR::Constant *getEvaluatedInitialIndex() const; + + DECLARE_TYPEINFO(TofinoRegisterValue, TestObject); +}; + +/* ========================================================================================= + * TofinoDirectRegisterValue + * ========================================================================================= */ +/// DirectRegisters do not have an index and are attached to tables. +class TofinoDirectRegisterValue : public TestObject { + private: + /// The table this direct register is attached to. + const IR::Expression *initialValue; + + /// The register declaration, also contains the control plane name. + const IR::Declaration_Instance *decl; + + /// The table this direct register is attached to. + const IR::P4Table *table; + + public: + explicit TofinoDirectRegisterValue(const IR::Declaration_Instance *decl, + const IR::Expression *initialValue, + const IR::P4Table *table); + + [[nodiscard]] cstring getObjectName() const override; + + [[nodiscard]] const TofinoDirectRegisterValue *evaluate(const Model &model, + bool doComplete) const override; + + /// @returns the declaration of this register. + [[nodiscard]] const IR::Declaration_Instance *getRegisterDeclaration() const; + + /// @returns the initial value this register is initialized with. + [[nodiscard]] const IR::Expression *getInitialValue() const; + + /// @returns the table this direct register is attached to. + [[nodiscard]] const IR::P4Table *getRegisterTable() const; + + /// @returns the evaluated initial value this register is initialized with. + [[nodiscard]] const IR::Expression *getEvaluatedInitialValue() const; + + DECLARE_TYPEINFO(TofinoDirectRegisterValue, TestObject); +}; + +/* ========================================================================================= + * TofinoRegisterParam + * ========================================================================================= */ +/// Tofino register params have one default value and are read only. +class TofinoRegisterParam : public TestObject { + private: + /// The register param declaration, also contains the control plane name. + const IR::Declaration_Instance *decl; + + /// The initial value of this register parameter. + const IR::Expression *initialValue; + + public: + explicit TofinoRegisterParam(const IR::Declaration_Instance *decl, + const IR::Expression *initialValue); + + [[nodiscard]] cstring getObjectName() const override; + + [[nodiscard]] const TofinoRegisterParam *evaluate(const Model &model, + bool doComplete) const override; + /// @returns the initial value this register is supposed to be initialized with. + [[nodiscard]] const IR::Expression *getInitialValue() const; + + /// @returns the declaration of this register parameter. + [[nodiscard]] const IR::Declaration_Instance *getRegisterParamDeclaration() const; + + /// @returns the evaluated initial value this register is supposed to be initialized with. + [[nodiscard]] const IR::Constant *getEvaluatedInitialValue() const; + + DECLARE_TYPEINFO(TofinoRegisterParam, TestObject); +}; + +/* ========================================================================================= + * TofinoActionProfile + * ========================================================================================= */ +class TofinoActionProfile : public TestObject { + private: + /// The list of actions associated with this profile. + std::vector>> actions; + + /// The associated action profile declaration. + const IR::IDeclaration *profileDecl; + + public: + explicit TofinoActionProfile(const IR::IDeclaration *profileDecl); + + cstring getObjectName() const override; + + /// @returns the action map of this profile. + const std::vector>> *getActions() const; + + /// @returns the associated action profile declaration. + const IR::IDeclaration *getProfileDecl() const; + + /// @returns the current amount of actions associated with this profile. + size_t getActionMapSize() const; + + /// Add an action (its name) and the arguments to the action map of this profile. + void addToActionMap(cstring actionName, std::vector actionArgs); + + const TofinoActionProfile *evaluate(const Model &model, bool doComplete) const override; + + DECLARE_TYPEINFO(TofinoActionProfile, TestObject); +}; + +/* ========================================================================================= + * TofinoActionSelector + * ========================================================================================= */ +class TofinoActionSelector : public TestObject { + private: + /// The associated action selector declaration. + const IR::IDeclaration *selectorDecl; + + /// The associated action profile. + const TofinoActionProfile *actionProfile; + + public: + explicit TofinoActionSelector(const IR::IDeclaration *selectorDecl, + const TofinoActionProfile *actionProfile); + + cstring getObjectName() const override; + + /// @returns the associated action selector declaration. + const IR::IDeclaration *getSelectorDecl() const; + + /// @returns the associated action profile. + const TofinoActionProfile *getActionProfile() const; + + const TofinoActionSelector *evaluate(const Model &model, bool doComplete) const override; + + DECLARE_TYPEINFO(TofinoActionSelector, TestObject); +}; + +/* ========================================================================================= + * Table Key Match Types + * ========================================================================================= */ + +class Range : public TableMatch { + private: + /// The inclusive start of the range. + const IR::Expression *low; + + /// The inclusive end of the range. + const IR::Expression *high; + + public: + explicit Range(const IR::KeyElement *key, const IR::Expression *low, + const IR::Expression *high); + + const Range *evaluate(const Model &model, bool doComplete) const override; + + cstring getObjectName() const override; + + /// @returns the inclusive start of the range. It is expected to be a constant at this point. + /// A BUG is thrown otherwise. + const IR::Constant *getEvaluatedLow() const; + + /// @returns the inclusive end of the range. It is expected to be a constant at this point. + /// A BUG is thrown otherwise. + const IR::Constant *getEvaluatedHigh() const; + + DECLARE_TYPEINFO(Range, TableMatch); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TEST_SPEC_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/testgen_tofino.def b/backends/p4tools/modules/testgen/targets/tofino/testgen_tofino.def new file mode 100644 index 00000000000..d04ac272201 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/testgen_tofino.def @@ -0,0 +1,11 @@ +/// An extension of TaintExpression. This class is used for variables that exhibit special behavior +/// when not initialized. For example, in Tofino, if the port variable has not been written to, the +/// packet will be dropped. However, reading from this uninitialized variable will still produce +/// taint. This behavior must be modeled. So we use this uninitialized IR class, which acts like +/// taint, but is its own type that can be checked for. +class UninitializedTaintExpression : TaintExpression { + toString { return "UninitializedTaintExpression(" + type->toString() + ")"; } + + dbprint { out << "UninitializedTaintExpression(" << type << ")"; } +} + diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.cpp b/backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.cpp new file mode 100644 index 00000000000..309f5f22fc3 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.cpp @@ -0,0 +1,309 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h" + +#include +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/formulae.h" +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/common/lib/variables.h" +#include "backends/tofino/bf-p4c/ir/gress.h" +#include "ir/ir.h" +#include "ir/irutils.h" +#include "lib/exceptions.h" +#include "lib/ordered_map.h" + +#include "backends/p4tools/modules/testgen/core/target.h" +#include "backends/p4tools/modules/testgen/targets/tofino/check_parser_error.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const TofinoProgramInfo &TofinoCmdStepper::getProgramInfo() const { + return *CmdStepper::getProgramInfo().to(); +} + +void TofinoCmdStepper::initializeTargetEnvironment(ExecutionState &nextState) const { + auto programInfo = getProgramInfo(); + const auto &archSpec = programInfo.getArchSpec(); + const auto *pipes = programInfo.getPipes(); + + auto initToTaint = [&](const IR::Member *member) { + nextState.set(member, ToolsVariables::getTaintExpression(member->type)); + }; + + // Initialize all the parameters with their default values. + // TODO: Respect autoinit_metadata annotations. + for (const auto &pipe : *pipes) { + size_t blockIdx = 0; + for (const auto &blockTuple : pipe.pipes) { + const auto *typeDecl = blockTuple.second; + const auto *archMember = archSpec.getArchMember(blockIdx); + nextState.initializeBlockParams(TestgenTarget::get(), typeDecl, + &archMember->blockParams); + blockIdx++; + } + } + + // Set intrinsic metadata fields. Fields that are out of our control are tainted. + const auto *oneBitType = IR::Type_Bits::get(1); + const auto *nineBitType = IR::Type_Bits::get(1); + const auto *sixteenBitType = IR::Type_Bits::get(1); + const auto *queueIdType = IR::Type_Bits::get(5); + + // ingress_intrinsic_metadata_t + const auto *igIntrMd = new IR::PathExpression(new IR::Path("*ig_intr_md")); + // Set the Tofino metadata to valid for initialization. This might be undone in the parser. + // This is because the parser parameter direction for metadata is "out". + nextState.set(ToolsVariables::getHeaderValidity(igIntrMd), IR::BoolLiteral::get(true)); + // Flag distinguishing original packets from resubmitted packets. + // Don't model resubmitted packets for now: set resubmit_flag to 0. + nextState.set(new IR::Member(oneBitType, igIntrMd, "resubmit_flag"), + IR::Constant::get(oneBitType, 0)); + // Ingress IEEE 1588 timestamp (in nsec) + initToTaint(new IR::Member(IR::Type_Bits::get(48), igIntrMd, "ingress_mac_tstamp")); + + // ingress_intrinsic_metadata_from_parser_t + const auto *igIntrPrsMd = new IR::PathExpression(new IR::Path("*ig_intr_md_from_prsr")); + // Global timestamp (ns) taken upon arrival at ingress. + initToTaint(new IR::Member(IR::Type_Bits::get(48), igIntrPrsMd, "global_tstamp")); + // Global version number taken upon arrival at ingress. + initToTaint(new IR::Member(IR::Type_Bits::get(32), igIntrPrsMd, "global_ver")); + + // egress_intrinsic_metadata_t + const auto *egIntrMd = new IR::PathExpression(new IR::Path("*eg_intr_md")); + // Set the Tofino metadata to valid for initialization. This might be undone in the parser. + // This is because the parser parameter direction for metadata is "out". + nextState.set(ToolsVariables::getHeaderValidity(egIntrMd), IR::BoolLiteral::get(true)); + // Queue depth at the packet enqueue time. + initToTaint(new IR::Member(IR::Type_Bits::get(19), egIntrMd, "enq_qdepth")); + // Queue congestion status at the packet enqueue time. + initToTaint(new IR::Member(IR::Type_Bits::get(2), egIntrMd, "enq_congest_stat")); + // Time snapshot taken when the packet is enqueued (in nsec). + initToTaint(new IR::Member(IR::Type_Bits::get(18), egIntrMd, "enq_tstamp")); + // Queue depth at the packet dequeue time. + initToTaint(new IR::Member(IR::Type_Bits::get(19), egIntrMd, "dep_qdepth")); + // Queue congestion status at the packet dequeue time. + initToTaint(new IR::Member(IR::Type_Bits::get(2), egIntrMd, "dep_congest_stat")); + // Dequeue-time application-pool congestion status. 2 bits per pool. + initToTaint(new IR::Member(IR::Type_Bits::get(8), egIntrMd, "app_pool_congest_stat")); + // Time delta between the packet's enqueue and dequeue time. + initToTaint(new IR::Member(IR::Type_Bits::get(18), egIntrMd, "deq_timedelta")); + // L3 replication id for multicast packets. + initToTaint(new IR::Member(sixteenBitType, egIntrMd, "egress_rid")); + // Flag indicating the first replica for the given multicast group. + initToTaint(new IR::Member(oneBitType, egIntrMd, "egress_rid_first")); + // Egress (physical) queue id via which this packet was served. + initToTaint(new IR::Member(queueIdType, egIntrMd, "egress_qid")); + // Egress cos (eCoS) value. + initToTaint(new IR::Member(IR::Type_Bits::get(3), egIntrMd, "egress_cos")); + // Flag indicating whether a packet is deflected due to deflect_on_drop. + // For now, assume that the deflection flag in egress is always 0. + nextState.set(new IR::Member(oneBitType, egIntrMd, "deflection_flag"), + IR::Constant::get(oneBitType, 0)); + // Packet length, in bytes. + // TODO: Fix the packet size width. It should be 16 by default. Then we can calculate a packet. + initToTaint(new IR::Member(IR::Type_Bits::get(16), egIntrMd, "pkt_length")); + + // ingress_intrinsic_metadata_from_parser_t + const auto *egIntrPrsMd = new IR::PathExpression(new IR::Path("*eg_intr_md_from_prsr")); + // Global timestamp (ns) taken upon arrival at egress. + initToTaint(new IR::Member(IR::Type_Bits::get(48), egIntrPrsMd, "global_tstamp")); + // Global version number taken upon arrival at egress. + initToTaint(new IR::Member(IR::Type_Bits::get(32), egIntrPrsMd, "global_ver")); + + // Set the initial drop var to zero. + nextState.set(&TofinoConstants::INGRESS_DROP_VAR, + IR::Constant::get(TofinoConstants::INGRESS_DROP_VAR.type, 0)); + nextState.set(&TofinoConstants::EGRESS_DROP_VAR, + IR::Constant::get(TofinoConstants::EGRESS_DROP_VAR.type, 0)); + + // Initialize the input port. + const auto &ingressPort = programInfo.getTargetInputPortVar(); + // TODO: If egress port is in loopback mode, we should restart Ingress and assign + // egress port to ingress port here. + nextState.set(ingressPort, + ToolsVariables::getSymbolicVariable(ingressPort->type, "tofino_ingress_port"_cs)); + // Initialize the global egress port variable. We initially set it to taint, which means drop. + nextState.set(getProgramInfo().getTargetOutputPortVar(), + new IR::UninitializedTaintExpression(nineBitType)); + + // Tofino appends a frame check sequence to the packet. Initialize it. + nextState.setProperty("fcsLeft"_cs, TofinoConstants::ETH_FCS_SIZE); +} + +std::optional TofinoCmdStepper::startParserImpl( + const IR::P4Parser *parser, ExecutionState &nextState) const { + auto programINfo = getProgramInfo(); + auto gress = programINfo.getGress(parser); + + const auto *nineBitType = IR::Type_Bits::get(9); + const auto *oneBitType = IR::Type_Bits::get(1); + const auto *parserErrorType = programInfo.getParserErrorType(); + + // Check for pa_auto_init_metadata, which sets metadata values to 0. + auto parserParams = parser->getApplyParameters()->parameters; + for (std::size_t idx = 1; idx != parserParams.size(); ++idx) { + const auto *param = parserParams.at(idx); + const auto *paramType = param->type; + if (const auto *typeName = paramType->to()) { + paramType = nextState.resolveType(typeName); + } + const auto *localMdTsType = paramType->checkedTo(); + // Check whether the structure has a "pa_auto_init_metadata" annotation associated with it. + const auto *initMeta = localMdTsType->getAnnotation("pa_auto_init_metadata"_cs); + // If yes, set all values to 0. + if (initMeta != nullptr) { + const auto *paramRef = new IR::PathExpression(paramType, new IR::Path(param->name)); + declareStructLike(nextState, paramRef); + } + } + + switch (gress) { + case INGRESS: { + // bypass_egress is not active at the beginning. + const auto &bypassExpr = + programINfo.getParserParamVar(parser, oneBitType, 4, "bypass_egress"_cs); + nextState.set(bypassExpr, IR::Constant::get(oneBitType, 0)); + + // Check whether the parser error was referenced in this particular pipe. + const auto *pipes = programINfo.getPipes(); + const auto *decl = pipes->at(programINfo.getPipeIdx(parser)).pipes.at("IngressT"_cs); + const auto *p4control = decl->checkedTo(); + CheckParserError checkParserError; + p4control->apply(checkParserError); + nextState.setProperty("parserErrReferenced"_cs, checkParserError.hasParserError()); + // Tofino implicitly drops packets if they are not set during the ingress or egress + // pipeline. + nextState.set( + programINfo.getParserParamVar(parser, nineBitType, 4, "ucast_egress_port"_cs), + new IR::UninitializedTaintExpression(nineBitType)); + // Initialize parser_err with no error and set the parser error label. + // This label is used by some core externs to control target's parser error. + const auto &parserErrorLabelExpr = + programINfo.getParserParamVar(parser, parserErrorType, 5, "parser_err"_cs); + nextState.set(parserErrorLabelExpr, IR::Constant::get(parserErrorType, 0)); + nextState.setParserErrorLabel(parserErrorLabelExpr); + nextState.setProperty("gress"_cs, static_cast(gress_t::INGRESS)); + break; + } + + case EGRESS: { + // Retrieve the egress port associated with this parser. + // Tofino implicitly drops packets if they are not set during the ingress or egress + // pipeline. + nextState.set(programINfo.getParserParamVar(parser, nineBitType, 3, "egress_port"_cs), + new IR::UninitializedTaintExpression(nineBitType)); + // Initialize parser_err with no error and set the parser error label. + // This label is used by some core externs to control target's parser error. + const auto &parserErrorLabelExpr = + programINfo.getParserParamVar(parser, parserErrorType, 3, "parser_err"_cs); + nextState.set(parserErrorLabelExpr, IR::Constant::get(parserErrorType, 0)); + nextState.setParserErrorLabel(parserErrorLabelExpr); + nextState.setProperty("gress"_cs, static_cast(gress_t::EGRESS)); + break; + } + + default: + BUG("Unimplemented thread: %1%", gress); + } + + return std::nullopt; +} + +std::map TofinoCmdStepper::getExceptionHandlers( + const IR::P4Parser *parser, Continuation::Body /*normalContinuation*/, + const ExecutionState &nextState) const { + // According to TNA, the ingress parser drops the packet when any of the following is true: + // - parser_err is non-zero and the ingress MAU doesn't refer to parser_err + // - the PARSER_ERROR_NO_MATCH bit in parser_err is set + // + // The egress parser never drops the packet. + const auto programInfo = getProgramInfo(); + const auto *parserErrorType = programInfo.getParserErrorType(); + + std::map result; + auto gress = programInfo.getGress(parser); + const auto *exitCall = + new IR::MethodCallStatement(Utils::generateInternalMethodCall("tofino_drop", {})); + // Set the parser error label to PARSER_ERROR_NO_MATCH in case of a no-match. + switch (gress) { + case INGRESS: { + // TODO: Implement the conditions above. Currently, this always drops the packet. + // Suggest refactoring `result` so that its values are lists of (path condition, + // continuation) pairs. This would allow us to branch on the value of parser_err. + // If the ingress MAU references the parser error the parser does not drop the + // packet. + if (nextState.getProperty("parserErrReferenced"_cs)) { + result.emplace(Continuation::Exception::PacketTooShort, Continuation::Body({})); + } else { + result.emplace(Continuation::Exception::PacketTooShort, + Continuation::Body({exitCall})); + } + + // Models PARSER_ERROR_NO_TCAM parser error. + // Set the parser error label to PARSER_ERROR_NO_MATCH in case of a no-match. + const auto &parserErrorVariable = + programInfo.getParserParamVar(parser, parserErrorType, 5, "parser_err"_cs); + const auto *noMatchConst = IR::Constant::get(parserErrorVariable->type, + TofinoConstants::PARSER_ERROR_NO_MATCH); + const auto *noMatchAssign = new IR::AssignmentStatement( + parserErrorVariable, + new IR::BOr(parserErrorVariable->type, parserErrorVariable, noMatchConst)); + result.emplace(Continuation::Exception::NoMatch, + Continuation::Body({noMatchAssign, exitCall})); + result.emplace(Continuation::Exception::Reject, Continuation::Body({})); + break; + } + + case EGRESS: { + result.emplace(Continuation::Exception::PacketTooShort, Continuation::Body({})); + const auto &parserErrorVariable = + programInfo.getParserParamVar(parser, parserErrorType, 3, "parser_err"_cs); + const auto *noMatchConst = IR::Constant::get(parserErrorVariable->type, + TofinoConstants::PARSER_ERROR_NO_MATCH); + const auto *noMatchAssign = new IR::AssignmentStatement( + parserErrorVariable, + new IR::BOr(parserErrorVariable->type, parserErrorVariable, noMatchConst)); + result.emplace(Continuation::Exception::NoMatch, Continuation::Body({noMatchAssign})); + result.emplace(Continuation::Exception::Reject, Continuation::Body({})); + break; + } + + case GHOST: + default: + BUG("Unimplemented thread: %1%", gress); + } + + return result; +} + +TofinoCmdStepper::TofinoCmdStepper(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) + : CmdStepper(state, solver, programInfo) {} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h b/backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h new file mode 100644 index 00000000000..9317de638a8 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino/cmd_stepper.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_CMD_STEPPER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_CMD_STEPPER_H_ + +#include +#include +#include + +#include "backends/p4tools/common/lib/formulae.h" +#include "ir/ir.h" +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/core/small_step/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/lib/continuation.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class TofinoCmdStepper : public CmdStepper { + protected: + std::string getClassName() override { return "TofinoCmdStepper"; } + + const TofinoProgramInfo &getProgramInfo() const override; + + std::optional startParserImpl(const IR::P4Parser *parser, + ExecutionState &nextState) const override; + + void initializeTargetEnvironment(ExecutionState &nextState) const override; + + std::map getExceptionHandlers( + const IR::P4Parser *parser, Continuation::Body normalContinuation, + const ExecutionState &nextState) const override; + + public: + TofinoCmdStepper(ExecutionState &state, AbstractSolver &solver, const ProgramInfo &programInfo); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_CMD_STEPPER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.cpp b/backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.cpp new file mode 100644 index 00000000000..5f1976a8f39 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.cpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h" + +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/extern_info.h" +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +Tofino1ExprStepper::Tofino1ExprStepper(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) + : SharedTofinoExprStepper(state, solver, programInfo) {} + +// Provides implementations of JBay externs. +const Tofino1ExprStepper::ExternMethodImpls + Tofino1ExprStepper::TOFINO_EXTERN_METHOD_IMPLS({}); + +void Tofino1ExprStepper::evalExternMethodCall(const ExternInfo &externInfo) { + auto method = TOFINO_EXTERN_METHOD_IMPLS.find(externInfo.externObjectRef, externInfo.methodName, + externInfo.externArguments); + if (method.has_value()) { + return method.value()(externInfo, *this); + } + // Lastly, check whether we are calling an internal extern method. + return SharedTofinoExprStepper::evalExternMethodCall(externInfo); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h b/backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h new file mode 100644 index 00000000000..328275f25e7 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino/expr_stepper.h @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_EXPR_STEPPER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_EXPR_STEPPER_H_ + +#include + +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class Tofino1ExprStepper : public SharedTofinoExprStepper { + protected: + std::string getClassName() override { return "Tofino1ExprStepper"; } + + static const ExternMethodImpls TOFINO_EXTERN_METHOD_IMPLS; + + public: + Tofino1ExprStepper(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo); + + void evalExternMethodCall(const ExternInfo &externInfo) override; +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_EXPR_STEPPER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.cpp b/backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.cpp new file mode 100644 index 00000000000..f3d67405698 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.cpp @@ -0,0 +1,349 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "ir/ir-generated.h" +#include "ir/ir.h" +#include "ir/irutils.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" +#include "lib/map.h" + +#include "backends/p4tools/modules/testgen/core/target.h" +#include "backends/p4tools/modules/testgen/lib/concolic.h" +#include "backends/p4tools/modules/testgen/lib/exceptions.h" +#include "backends/p4tools/modules/testgen/targets/tofino/concolic.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +std::vector> TofinoProgramInfo::ingressCmds() const { + return pipelineCmds(INGRESS); +} + +std::vector> TofinoProgramInfo::egressCmds() const { + return pipelineCmds(EGRESS); +} + +std::optional TofinoProgramInfo::getPipePortRangeConstraint( + const IR::StateVariable &portVar, size_t pipeIdx) const { + // Pipe port ranges are defined Tofino Switch Architecture 3.1: + // Port[8:7] = Pipe number 0,1,2 or 3 + // Port[6:0] = Port number withing a pipe + const auto *pipeNumber = new IR::Slice(portVar, 8, 7); + if (pipes.size() == 2) { + // If we have 2 pipes, pipe ids 0 and 2 are connected to pipe1, 1 and 3 to pipe2. + // The lower bit determines the pipe. + size_t pipeScope = pipeIdx; + return new IR::LOr( + new IR::Equ(pipeNumber, new IR::Constant(pipeNumber->type, pipeScope)), + new IR::Equ(pipeNumber, new IR::Constant(pipeNumber->type, pipeScope | 0x2))); + } + if (pipes.size() > 2) { + return new IR::Equ(pipeNumber, new IR::Constant(pipeNumber->type, pipeIdx)); + } + return {}; +} + +const IR::Expression *TofinoProgramInfo::getValidPortConstraint( + const IR::StateVariable &portVar) const { + const auto &options = TestgenOptions::get(); + // const auto *portNumber = new IR::Slice(portVar, 6, 0); + + // // 0x40-0x47 are recirc ports, 0x48 mirror, 0x49-0x4b reserved + // auto *validMacPort = + // new IR::LAnd(new IR::Lss(portNumber, new IR::Constant(portNumber->type, 0x40)), + // new IR::Leq(new IR::Constant(portNumber->type, 0), portNumber)); + + // auto *allowedPort = + // new IR::Equ(new IR::BAnd(new IR::Neg(new IR::Constant( + // portVar->type, + // SharedTofinoConstants::VALID_INPUT_PORT_MASK)), + // portVar), + // new IR::Constant(portVar->type, 0)); + + // const auto *validPortsCond = new IR::LAnd(validMacPort, allowedPort); + const IR::Expression *validPortsCond = new IR::BoolLiteral(true); + + /// If the the vector of permitted port ranges is not empty, set the restrictions on the + /// possible output port. + if (!options.permittedPortRanges.empty()) { + /// The drop port is also always a valid port. Initialize the condition with it. + const IR::Expression *cond = new IR::BoolLiteral(false); + for (const auto &portRange : options.permittedPortRanges) { + const auto *loVarOut = IR::Constant::get(portVar->type, portRange.first); + const auto *hiVarOut = IR::Constant::get(portVar->type, portRange.second); + cond = new IR::LOr( + cond, new IR::LAnd(new IR::Leq(loVarOut, portVar), new IR::Leq(portVar, hiVarOut))); + } + validPortsCond = new IR::LAnd(validPortsCond, cond); + } + return validPortsCond; +} + +std::vector> TofinoProgramInfo::pipelineCmds( + gress_t gress) const { + /// Compute the series of nodes corresponding to the in-order execution of top-level + /// pipeline-component instantiations. + /// This sequence also includes nodes that handle transitions between the + /// individual component instantiations. + std::vector> pipelineSequences; + const auto &archSpec = getArchSpec(); + + for (const auto &pipeInfo : pipes) { + pipelineSequences.emplace_back(); + auto &pipelineSequence = pipelineSequences.back(); + size_t blockIdx = 0; + + // Keep track of the pipe name throughout execution. + pipelineSequence.emplace_back( + Continuation::PropertyUpdate("pipe_name"_cs, pipeInfo.pipeName)); + + for (const auto &decl : Values(pipeInfo.pipes)) { + // Iterate through the (ordered) pipes of the target architecture. + auto subCmds = processDeclaration(decl, blockIdx); + if (getGress(decl) == gress) { + pipelineSequence.insert(pipelineSequence.end(), subCmds.begin(), subCmds.end()); + } + ++blockIdx; + } + BUG_CHECK(archSpec.getArchVectorSize() == blockIdx, + "The Tofino architecture requires %1% pipes (provided %2% pipes).", + archSpec.getArchVectorSize(), blockIdx); + } + return pipelineSequences; +} + +TofinoProgramInfo::TofinoProgramInfo(const TofinoCompilerResult &compilerResult, + std::vector inputPipes, + std::map declIdToGress, + std::map declIdToPipe) + : TofinoSharedProgramInfo(compilerResult, std::move(inputPipes), std::move(declIdToGress), + std::move(declIdToPipe)) { + concolicMethodImpls.add(*SharedTofinoConcolic::getSharedTofinoConcolicMethodImpls()); + // Restrict egress port to be an allowed valid port. + const auto *validPortsCond = getValidPortConstraint(getTargetOutputPortVar()); + pipelineSequence.emplace_back(Continuation::Guard(validPortsCond)); +} + +std::vector TofinoProgramInfo::processDeclaration( + const IR::Type_Declaration *typeDecl, size_t pipeIdx) const { + // Get the architecture specification for this target. + const auto &archSpec = getArchSpec(); + + BUG_CHECK(pipeIdx < archSpec.getArchVectorSize(), + "Current pipe index %1% exceed the total amount of specified pipes (%2%).", + archSpec.getArchVectorSize(), pipeIdx); + + // Collect parameters. + const auto *applyBlock = typeDecl->to(); + if (applyBlock == nullptr) { + TESTGEN_UNIMPLEMENTED("Constructed type %s of type %s not supported.", typeDecl, + typeDecl->node_type_name()); + } + // Retrieve the current canonical pipe in the architecture spec using the pipe index. + const auto *archMember = archSpec.getArchMember(pipeIdx); + + std::vector cmds; + if ((archMember->blockName == "IngressParserT")) { + // Between the metadata that is prepended to the input packet to the ingress parser and + // the actual packet there is also port metadata of size PORT_METADATA_SIZE. + const auto *portMetadata = createTargetUninitialized( + IR::Type_Bits::get(TofinoConstants::PORT_METADATA_SIZE), false); + const auto *stmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "prepend_to_prog_header", {portMetadata}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("hdr", IR::Direction::In, + IR::Type_Bits::get(TofinoConstants::PORT_METADATA_SIZE))}))); + cmds.emplace_back(stmt); + // Some pipes in Tofino prepend metadata to the input packet. + // In this case, the intrinsic metadata corresponds to the third parameter. + const auto *paramType = resolveProgramType( + &getCompilerResult().getProgram(), new IR::Type_Name("ingress_intrinsic_metadata_t")); + const auto *archPath = new IR::PathExpression(paramType, new IR::Path("*ig_intr_md")); + // We synthesize an argument for the method call. + // The input is the global architecture variable corresponding to the metadata. + const auto *metaPrependStmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "prepend_to_prog_header", {archPath}, IR::Type_Void::get(), + new IR::ParameterList({new IR::Parameter("hdr", IR::Direction::In, paramType)}))); + cmds.emplace_back(metaPrependStmt); + } + // Some pipes in Tofino prepend metadata to the input packet. + // In this case, the intrinsic metadata corresponds to the third parameter. + if ((archMember->blockName == "EgressParserT")) { + const auto *paramType = resolveProgramType( + &getCompilerResult().getProgram(), new IR::Type_Name("egress_intrinsic_metadata_t")); + const auto *archPath = new IR::PathExpression(paramType, new IR::Path("*eg_intr_md")); + // We synthesize an argument for the method call. + // The input is the global architecture variable corresponding to the metadata. + const auto *metaPrependStmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "prepend_to_prog_header", {archPath}, IR::Type_Void::get(), + new IR::ParameterList({new IR::Parameter("hdr", IR::Direction::In, paramType)}))); + cmds.emplace_back(metaPrependStmt); + } + // Copy-in. + const auto *copyInCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_in", {IR::StringLiteral::get(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); + cmds.emplace_back(copyInCall); + // Insert the actual pipeline. + cmds.emplace_back(typeDecl); + // Copy-out. + const auto *copyOutCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_out", {IR::StringLiteral::get(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); + cmds.emplace_back(copyOutCall); + + // After some specific pipelines (deparsers), we have to append the remaining packet + // payload. We use an extern call for this. + if (!((archMember->blockName == "IngressDeparserT") || + (archMember->blockName == "EgressDeparserT"))) { + return cmds; + } + + if ((archMember->blockName == "IngressDeparserT")) { + const auto *nineBitType = IR::Type_Bits::get(9); + // After the deparser, we store the ucast_egress_port as the device output port. + auto *portAssign = new IR::AssignmentStatement( + getTargetOutputPortVar(), + new IR::Member(nineBitType, new IR::PathExpression("*ig_intr_md_for_tm"), + "ucast_egress_port")); + cmds.emplace_back(portAssign); + } + + const auto *stmt = + new IR::MethodCallStatement(Utils::generateInternalMethodCall("prepend_emit_buffer", {})); + cmds.emplace_back(stmt); + + // Also check whether we need to drop the packet. + auto *dropStmt = + new IR::MethodCallStatement(Utils::generateInternalMethodCall("check_tofino_drop", {})); + cmds.emplace_back(dropStmt); + + if ((archMember->blockName == "IngressDeparserT")) { + const auto *oneBitType = IR::Type_Bits::get(1); + + // Check whether ig_intr_md_for_tm.bypass_egress == 1w1 + // If bypass_egress is set, we skip the rest of the pipeline after the ingress deparser. + const IR::Expression *skipCond = + new IR::Equ(IR::Constant::get(oneBitType, 1), + new IR::Member(oneBitType, new IR::PathExpression("*ig_intr_md_for_tm"), + "bypass_egress")); + // Restrict egress port to be an allowed valid port. + const auto *validPortsCond = getValidPortConstraint(getTargetOutputPortVar()); + skipCond = new IR::LAnd(validPortsCond, skipCond); + const auto *skipCheck = new IR::IfStatement(skipCond, new IR::ExitStatement(), nullptr); + cmds.emplace_back(skipCheck); + } + return cmds; +} + +const IR::StateVariable &TofinoProgramInfo::getTargetInputPortVar() const { + return *new IR::StateVariable( + new IR::Member(IR::Type_Bits::get(TofinoConstants::PORT_BIT_WIDTH), + new IR::PathExpression("*ig_intr_md"), "ingress_port")); +} + +const IR::StateVariable &TofinoProgramInfo::getTargetOutputPortVar() const { + return *new IR::StateVariable( + new IR::Member(IR::Type_Bits::get(TofinoConstants::PORT_BIT_WIDTH), + new IR::PathExpression("*eg_intr_md"), "egress_port")); +} + +const IR::Expression *TofinoProgramInfo::dropIsActive() const { + BUG("dropIsActive is not implemented for the Tofino extension. Please use the " + "\"check_tofino_drop\" extern."); +} + +const ArchSpec &TofinoProgramInfo::getArchSpec() const { return ARCH_SPEC; } + +const ArchSpec TofinoProgramInfo::ARCH_SPEC = ArchSpec( + "Switch"_cs, + { + // parser IngressParserT( + // packet_in pkt, + // out H hdr, + // out M ig_md, + // @optional out ingress_intrinsic_metadata_t ig_intr_md, + // @optional out ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, + // @optional out ingress_intrinsic_metadata_from_parser_t ig_intr_md_from_prsr); + {"IngressParserT"_cs, + {nullptr, "*ig_hdr"_cs, "*ig_md"_cs, "*ig_intr_md"_cs, "*ig_intr_md_for_tm"_cs, + "*ig_intr_md_from_prsr"_cs}}, + // control IngressT( + // inout H hdr, + // inout M ig_md, + // @optional in ingress_intrinsic_metadata_t ig_intr_md, + // @optional in ingress_intrinsic_metadata_from_parser_t ig_intr_md_from_prsr, + // @optional inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, + // @optional inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm); + {"IngressT"_cs, + {"*ig_hdr"_cs, "*ig_md"_cs, "*ig_intr_md"_cs, "*ig_intr_md_from_prsr"_cs, + "*ig_intr_md_for_dprsr"_cs, "*ig_intr_md_for_tm"_cs}}, + // control IngressDeparserT( + // packet_out pkt, + // inout H hdr, + // in M metadata, + // @optional in ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, + // @optional in ingress_intrinsic_metadata_t ig_intr_md); + {"IngressDeparserT"_cs, + {nullptr, "*ig_hdr"_cs, "*ig_md"_cs, "*ig_intr_md_for_dprsr"_cs, "*ig_intr_md"_cs}}, + // parser EgressParserT( + // packet_in pkt, + // out H hdr, + // out M eg_md, + // @optional out egress_intrinsic_metadata_t eg_intr_md, + // @optional out egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr); + {"EgressParserT"_cs, + {nullptr, "*eg_hdr"_cs, "*eg_md"_cs, "*eg_intr_md"_cs, "*eg_intr_md_from_prsr"_cs, + "*eg_intr_md_for_dprsr"_cs}}, + // control EgressT( + // inout H hdr, + // inout M eg_md, + // @optional in egress_intrinsic_metadata_t eg_intr_md, + // @optional in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + // @optional inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, + // @optional inout egress_intrinsic_metadata_for_output_port_t + // eg_intr_md_for_oport); + {"EgressT"_cs, + {"*eg_hdr"_cs, "*eg_md"_cs, "*eg_intr_md"_cs, "*eg_intr_md_from_prsr"_cs, + "*eg_intr_md_for_dprsr"_cs, "*eg_intr_md_for_oport"_cs}}, + // control EgressDeparserT( + // packet_out pkt, + // inout H hdr, + // in M metadata, + // @optional in egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, + // @optional in egress_intrinsic_metadata_t eg_intr_md, + // @optional in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr); + {"EgressDeparserT"_cs, + {nullptr, "*eg_hdr"_cs, "*eg_md"_cs, "*eg_intr_md_for_dprsr"_cs, "*eg_intr_md"_cs, + "*eg_intr_md_from_prsr"_cs}}, + }); + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h b/backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h new file mode 100644 index 00000000000..d8470194fe4 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino/program_info.h @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_PROGRAM_INFO_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_PROGRAM_INFO_H_ + +#include +#include +#include + +#include "backends/tofino/bf-p4c/ir/gress.h" +#include "ir/ir.h" + +#include "backends/p4tools/modules/testgen/lib/continuation.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class TofinoProgramInfo : public TofinoSharedProgramInfo { + private: + /// This function contains an imperative specification of the inter-pipe interaction in the + /// target. + std::vector processDeclaration(const IR::Type_Declaration *typeDecl, + size_t pipeIdx) const; + + [[nodiscard]] std::vector> pipelineCmds(gress_t gress) const; + + [[nodiscard]] std::optional getPipePortRangeConstraint( + const IR::StateVariable &portVar, size_t pipeIdx) const override; + + [[nodiscard]] const IR::Expression *getValidPortConstraint( + const IR::StateVariable &portVar) const override; + + public: + TofinoProgramInfo(const TofinoCompilerResult &compilerResult, std::vector inputPipes, + std::map declIdToGress, std::map declIdToPipe); + + /// @see ProgramInfo::getArchSpec + [[nodiscard]] const ArchSpec &getArchSpec() const override; + + [[nodiscard]] const IR::StateVariable &getTargetInputPortVar() const override; + + [[nodiscard]] const IR::StateVariable &getTargetOutputPortVar() const override; + + [[nodiscard]] const IR::Expression *dropIsActive() const override; + + [[nodiscard]] std::vector> ingressCmds() const override; + + [[nodiscard]] std::vector> egressCmds() const override; + + /// @see ProgramInfo::getArchSpec + static const ArchSpec ARCH_SPEC; + + DECLARE_TYPEINFO(TofinoProgramInfo, TofinoSharedProgramInfo); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO_PROGRAM_INFO_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.cpp b/backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.cpp new file mode 100644 index 00000000000..b24f3fc1708 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.cpp @@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h" + +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/common/lib/variables.h" +#include "backends/tofino/bf-p4c/ir/gress.h" +#include "ir/ir-generated.h" +#include "ir/ir.h" +#include "ir/irutils.h" +#include "lib/exceptions.h" +#include "lib/ordered_map.h" + +#include "backends/p4tools/modules/testgen/core/target.h" +#include "backends/p4tools/modules/testgen/targets/tofino/check_parser_error.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +const JBayProgramInfo &JBayCmdStepper::getProgramInfo() const { + return *CmdStepper::getProgramInfo().to(); +} + +void JBayCmdStepper::initializeTargetEnvironment(ExecutionState &nextState) const { + auto programInfo = getProgramInfo(); + const auto &archSpec = programInfo.getArchSpec(); + const auto *pipes = programInfo.getPipes(); + + auto initToTaint = [&](const IR::StateVariable &member) { + nextState.set(member, ToolsVariables::getTaintExpression(member->type)); + }; + + // Initialize all the parameters with their default values. + // TODO: Respect autoinit_metadata annotations. + for (const auto &pipe : *pipes) { + size_t blockIdx = 0; + for (const auto &blockTuple : pipe.pipes) { + const auto *typeDecl = blockTuple.second; + const auto *archMember = archSpec.getArchMember(blockIdx); + nextState.initializeBlockParams(TestgenTarget::get(), typeDecl, + &archMember->blockParams); + blockIdx++; + } + } + // Set intrinsic metadata fields. Fields that are out of our control are tainted. + const auto *oneBitType = IR::Type_Bits::get(1); + const auto *nineBitType = IR::Type_Bits::get(1); + const auto *sixteenBitType = IR::Type_Bits::get(1); + const auto *queueIdType = IR::Type_Bits::get(7); + + // ingress_intrinsic_metadata_t + const auto *igIntrMd = new IR::PathExpression(new IR::Path("*ig_intr_md")); + // Set the Tofino metadata to valid for initialization. This might be undone in the parser. + // This is because the parser parameter direction for metadata is "out". + nextState.set(ToolsVariables::getHeaderValidity(igIntrMd), IR::BoolLiteral::get(true)); + // Flag distinguishing original packets from resubmitted packets. + // Don't model resubmitted packets for now: set resubmit_flag to 0. + nextState.set(new IR::Member(oneBitType, igIntrMd, "resubmit_flag"), + IR::Constant::get(oneBitType, 0)); + // Ingress IEEE 1588 timestamp (in nsec) + initToTaint(new IR::Member(IR::Type_Bits::get(48), igIntrMd, "ingress_mac_tstamp")); + + // ingress_intrinsic_metadata_from_parser_t + const auto *igIntrPrsMd = new IR::PathExpression(new IR::Path("*ig_intr_md_from_prsr")); + // Global timestamp (ns) taken upon arrival at ingress. + initToTaint(new IR::Member(IR::Type_Bits::get(48), igIntrPrsMd, "global_tstamp")); + // Global version number taken upon arrival at ingress. + initToTaint(new IR::Member(IR::Type_Bits::get(32), igIntrPrsMd, "global_ver")); + + // egress_intrinsic_metadata_t + const auto *egIntrMd = new IR::PathExpression(new IR::Path("*eg_intr_md")); + // Set the Tofino metadata to valid for initialization. This might be undone in the parser. + // This is because the parser parameter direction for metadata is "out". + nextState.set(ToolsVariables::getHeaderValidity(egIntrMd), IR::BoolLiteral::get(true)); + // Queue depth at the packet enqueue time. + initToTaint(new IR::Member(IR::Type_Bits::get(19), egIntrMd, "enq_qdepth")); + // Queue congestion status at the packet enqueue time. + initToTaint(new IR::Member(IR::Type_Bits::get(2), egIntrMd, "enq_congest_stat")); + // Time snapshot taken when the packet is enqueued (in nsec). + initToTaint(new IR::Member(IR::Type_Bits::get(32), egIntrMd, "enq_tstamp")); + // Queue depth at the packet dequeue time. + initToTaint(new IR::Member(IR::Type_Bits::get(19), egIntrMd, "dep_qdepth")); + // Queue congestion status at the packet dequeue time. + initToTaint(new IR::Member(IR::Type_Bits::get(2), egIntrMd, "dep_congest_stat")); + // Dequeue-time application-pool congestion status. 2 bits per pool. + initToTaint(new IR::Member(IR::Type_Bits::get(8), egIntrMd, "app_pool_congest_stat")); + // Time delta between the packet's enqueue and dequeue time. + initToTaint(new IR::Member(IR::Type_Bits::get(32), egIntrMd, "deq_timedelta")); + // L3 replication id for multicast packets. + initToTaint(new IR::Member(sixteenBitType, egIntrMd, "egress_rid")); + // Flag indicating the first replica for the given multicast group. + initToTaint(new IR::Member(oneBitType, egIntrMd, "egress_rid_first")); + // Egress (physical) queue id via which this packet was served. + initToTaint(new IR::Member(queueIdType, egIntrMd, "egress_qid")); + // Egress cos (eCoS) value. + initToTaint(new IR::Member(IR::Type_Bits::get(3), egIntrMd, "egress_cos")); + // Flag indicating whether a packet is deflected due to deflect_on_drop. + // For now, assume that the deflection flag in egress is always 0. + nextState.set(new IR::Member(oneBitType, egIntrMd, "deflection_flag"), + IR::Constant::get(oneBitType, 0)); + // Packet length, in bytes. + // TODO: Fix the packet size width. It should be 16 by default. Then we can calculate a packet. + initToTaint(new IR::Member(IR::Type_Bits::get(16), egIntrMd, "pkt_length")); + + // ingress_intrinsic_metadata_from_parser_t + const auto *egIntrPrsMd = new IR::PathExpression(new IR::Path("*eg_intr_md_from_prsr")); + // Global timestamp (ns) taken upon arrival at egress. + initToTaint(new IR::Member(IR::Type_Bits::get(48), egIntrPrsMd, "global_tstamp")); + // Global version number taken upon arrival at egress. + initToTaint(new IR::Member(IR::Type_Bits::get(32), egIntrPrsMd, "global_ver")); + + // Set the initial drop var to zero. + nextState.set(&JBayConstants::INGRESS_DROP_VAR, + IR::Constant::get(JBayConstants::INGRESS_DROP_VAR.type, 0)); + nextState.set(&JBayConstants::EGRESS_DROP_VAR, + IR::Constant::get(JBayConstants::EGRESS_DROP_VAR.type, 0)); + + // Initialize the input port. + const auto &ingressPort = programInfo.getTargetInputPortVar(); + // TODO: If egress port is in loopback mode, we should restart Ingress and assign + // egress port to ingress port here. + nextState.set(ingressPort, + ToolsVariables::getSymbolicVariable(ingressPort->type, "tofino_ingress_port"_cs)); + // Initialize the global egress port variable. We initially set it to taint, which means drop. + nextState.set(programInfo.getTargetOutputPortVar(), + new IR::UninitializedTaintExpression(nineBitType)); + + // Jbay appends a frame check sequence to the packet. Initialize it. + nextState.setProperty("fcsLeft"_cs, JBayConstants::ETH_FCS_SIZE); +} + +std::optional JBayCmdStepper::startParserImpl(const IR::P4Parser *parser, + ExecutionState &nextState) const { + const auto &programInfo = getProgramInfo(); + auto gress = programInfo.getGress(parser); + + const auto *nineBitType = IR::Type_Bits::get(9); + const auto *oneBitType = IR::Type_Bits::get(1); + const auto *parserErrorType = programInfo.getParserErrorType(); + + // Check for pa_auto_init_metadata, which sets metadata values to 0. + auto parserParams = parser->getApplyParameters()->parameters; + for (std::size_t idx = 1; idx != parserParams.size(); ++idx) { + const auto *param = parserParams.at(idx); + const auto *paramType = param->type; + if (const auto *typeName = paramType->to()) { + paramType = nextState.resolveType(typeName); + } + const auto *localMdTsType = paramType->checkedTo(); + // Check whether the structure has a "pa_auto_init_metadata" annotation associated with it. + const auto *initMeta = localMdTsType->getAnnotation("pa_auto_init_metadata"_cs); + // If yes, set all values to 0. + if (initMeta != nullptr) { + const auto *paramRef = new IR::PathExpression(paramType, new IR::Path(param->name)); + declareStructLike(nextState, paramRef); + } + } + + switch (gress) { + case INGRESS: { + // bypass_egress is not active at the beginning. + const auto &bypassExpr = + programInfo.getParserParamVar(parser, oneBitType, 4, "bypass_egress"_cs); + nextState.set(bypassExpr, IR::Constant::get(oneBitType, 0)); + + // Check whether the parser error was referenced in this particular pipe. + const auto *pipes = programInfo.getPipes(); + const auto *decl = pipes->at(programInfo.getPipeIdx(parser)).pipes.at("IngressT"_cs); + const auto *p4control = decl->checkedTo(); + CheckParserError checkParserError; + p4control->apply(checkParserError); + nextState.setProperty("parserErrReferenced"_cs, checkParserError.hasParserError()); + + // Tofino2 implicitly drops packets if they are not set during the ingress or egress + // pipeline. + nextState.set( + programInfo.getParserParamVar(parser, nineBitType, 4, "ucast_egress_port"_cs), + new IR::UninitializedTaintExpression(nineBitType)); + // Initialize parser_err with no error and set the parser error label. + // This label is used by some core externs to control target's parser error. + const auto &parserErrorVariable = + programInfo.getParserParamVar(parser, parserErrorType, 5, "parser_err"_cs); + nextState.set(parserErrorVariable, IR::Constant::get(parserErrorType, 0)); + nextState.setParserErrorLabel(parserErrorVariable); + nextState.setProperty("gress"_cs, static_cast(gress_t::INGRESS)); + break; + } + + case EGRESS: { + // Tofino2 implicitly drops packets if they are not set during the ingress or egress + // pipeline. + nextState.set(programInfo.getParserParamVar(parser, nineBitType, 3, "egress_port"_cs), + new IR::UninitializedTaintExpression(nineBitType)); + // Initialize parser_err with no error and set the parser error label. + // This label is used by some core externs to control target's parser error. + const auto &parserErrorVariable = + programInfo.getParserParamVar(parser, parserErrorType, 3, "parser_err"_cs); + nextState.set(parserErrorVariable, IR::Constant::get(parserErrorType, 0)); + nextState.setParserErrorLabel(parserErrorVariable); + nextState.setProperty("gress"_cs, static_cast(gress_t::EGRESS)); + break; + } + case GHOST: + nextState.setProperty("gress"_cs, static_cast(gress_t::GHOST)); + break; + default: + BUG("Unimplemented thread: %1%", gress); + } + + return std::nullopt; +} + +std::map JBayCmdStepper::getExceptionHandlers( + const IR::P4Parser *parser, Continuation::Body /*normalContinuation*/, + const ExecutionState &nextState) const { + // According to TNA, the ingress parser drops the packet when any of the following is true: + // - parser_err is non-zero and the ingress MAU doesn't refer to parser_err + // - the PARSER_ERROR_NO_MATCH bit in parser_err is set + // + // The egress parser never drops the packet. + const auto &programInfo = getProgramInfo(); + const auto *parserErrorType = programInfo.getParserErrorType(); + + std::map result; + auto gress = programInfo.getGress(parser); + const auto *exitCall = + new IR::MethodCallStatement(Utils::generateInternalMethodCall("tofino_drop", {})); + + switch (gress) { + case INGRESS: { + // TODO: Implement the conditions above. Currently, this always drops the packet. + // Suggest refactoring `result` so that its values are lists of (path condition, + // continuation) pairs. This would allow us to branch on the value of parser_err. + + // If the ingress MAU references the parser error the parser does not drop the packet. + if (nextState.getProperty("parserErrReferenced"_cs)) { + result.emplace(Continuation::Exception::PacketTooShort, Continuation::Body({})); + } else { + result.emplace(Continuation::Exception::PacketTooShort, + Continuation::Body({exitCall})); + } + // Models PARSER_ERROR_NO_TCAM parser error. + // Set the parser error label to PARSER_ERROR_NO_MATCH in case of a no-match. + const auto &parserErrorVariable = + programInfo.getParserParamVar(parser, parserErrorType, 5, "parser_err"_cs); + const auto *noMatchConst = + IR::Constant::get(parserErrorVariable->type, JBayConstants::PARSER_ERROR_NO_MATCH); + const auto *noMatchAssign = new IR::AssignmentStatement( + parserErrorVariable, + new IR::BOr(parserErrorVariable->type, parserErrorVariable, noMatchConst)); + result.emplace(Continuation::Exception::NoMatch, + Continuation::Body({noMatchAssign, exitCall})); + result.emplace(Continuation::Exception::Reject, Continuation::Body({})); + break; + } + + case EGRESS: { + // Models PARSER_ERROR_NO_TCAM parser error. + // Set the parser error label to PARSER_ERROR_NO_MATCH in case of a no-match. + const auto &parserErrorVariable = + programInfo.getParserParamVar(parser, parserErrorType, 5, "parser_err"_cs); + const auto *noMatchConst = + IR::Constant::get(parserErrorVariable->type, JBayConstants::PARSER_ERROR_NO_MATCH); + const auto *noMatchAssign = new IR::AssignmentStatement( + parserErrorVariable, + new IR::BOr(parserErrorVariable->type, parserErrorVariable, noMatchConst)); + result.emplace(Continuation::Exception::PacketTooShort, Continuation::Body({})); + result.emplace(Continuation::Exception::NoMatch, + Continuation::Body({noMatchAssign, exitCall})); + result.emplace(Continuation::Exception::Reject, Continuation::Body({})); + break; + } + + case GHOST: + default: + BUG("Unimplemented thread: %1%", gress); + } + + return result; +} + +JBayCmdStepper::JBayCmdStepper(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) + : CmdStepper(state, solver, programInfo) {} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h b/backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h new file mode 100644 index 00000000000..685e70247c3 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino2/cmd_stepper.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_CMD_STEPPER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_CMD_STEPPER_H_ + +#include +#include +#include + +#include "ir/ir.h" +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/core/small_step/cmd_stepper.h" +#include "backends/p4tools/modules/testgen/lib/continuation.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class JBayCmdStepper : public CmdStepper { + protected: + std::string getClassName() override { return "JBayCmdStepper"; } + + const JBayProgramInfo &getProgramInfo() const override; + + std::optional startParserImpl(const IR::P4Parser *parser, + ExecutionState &nextState) const override; + + void initializeTargetEnvironment(ExecutionState &nextState) const override; + + std::map getExceptionHandlers( + const IR::P4Parser *parser, Continuation::Body normalContinuation, + const ExecutionState &nextState) const override; + + public: + JBayCmdStepper(ExecutionState &state, AbstractSolver &solver, const ProgramInfo &programInfo); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_CMD_STEPPER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.cpp b/backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.cpp new file mode 100644 index 00000000000..1350b88104e --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.cpp @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h" + +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/extern_info.h" +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +JBayExprStepper::JBayExprStepper(ExecutionState &state, AbstractSolver &solver, + const ProgramInfo &programInfo) + : SharedTofinoExprStepper(state, solver, programInfo) {} + +// Provides implementations of JBay externs. +const JBayExprStepper::ExternMethodImpls JBayExprStepper::JBAY_EXTERN_METHOD_IMPLS( + {}); + +void JBayExprStepper::evalExternMethodCall(const ExternInfo &externInfo) { + auto method = JBAY_EXTERN_METHOD_IMPLS.find(externInfo.externObjectRef, externInfo.methodName, + externInfo.externArguments); + if (method.has_value()) { + return method.value()(externInfo, *this); + } + // Lastly, check whether we are calling an internal extern method. + return SharedTofinoExprStepper::evalExternMethodCall(externInfo); +} + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h b/backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h new file mode 100644 index 00000000000..c9f12c9ae23 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino2/expr_stepper.h @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_EXPR_STEPPER_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_EXPR_STEPPER_H_ + +#include + +#include "ir/solver.h" + +#include "backends/p4tools/modules/testgen/core/program_info.h" +#include "backends/p4tools/modules/testgen/lib/execution_state.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_expr_stepper.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class JBayExprStepper : public SharedTofinoExprStepper { + private: + static const ExternMethodImpls JBAY_EXTERN_METHOD_IMPLS; + + protected: + std::string getClassName() override { return "JBayExprStepper"; } + + public: + JBayExprStepper(ExecutionState &state, AbstractSolver &solver, const ProgramInfo &programInfo); + + void evalExternMethodCall(const ExternInfo &externInfo) override; +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_EXPR_STEPPER_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.cpp b/backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.cpp new file mode 100644 index 00000000000..6e35456e59f --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.cpp @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#include "backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h" + +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "ir/ir.h" +#include "ir/irutils.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" +#include "lib/map.h" + +#include "backends/p4tools/modules/testgen/lib/concolic.h" +#include "backends/p4tools/modules/testgen/lib/exceptions.h" +#include "backends/p4tools/modules/testgen/options.h" +#include "backends/p4tools/modules/testgen/targets/tofino/concolic.h" +#include "backends/p4tools/modules/testgen/targets/tofino/constants.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +std::vector> JBayProgramInfo::ingressCmds() const { + return pipelineCmds(INGRESS); +} + +std::vector> JBayProgramInfo::egressCmds() const { + return pipelineCmds(EGRESS); +} + +std::optional JBayProgramInfo::getPipePortRangeConstraint( + const IR::StateVariable &portVar, size_t pipeIdx) const { + // Pipe port ranges are defined Tofino Switch Architecture 3.1: + // Port[8:7] = Pipe number 0,1,2 or 3 + // Port[6:0] = Port number withing a pipe + const auto *pipeNumber = new IR::Slice(portVar, 8, 7); + if (pipes.size() == 2) { + // If we have 2 pipes, pipe ids 0 and 2 are connected to pipe1, 1 and 3 to pipe2. + // The upper bit determines the pipe. + size_t pipeScope = pipeIdx << 1; + return new IR::LOr( + new IR::Equ(pipeNumber, new IR::Constant(pipeNumber->type, pipeScope)), + new IR::Equ(pipeNumber, new IR::Constant(pipeNumber->type, pipeScope | 0x1))); + } + if (pipes.size() > 2) { + return new IR::Equ(pipeNumber, new IR::Constant(pipeNumber->type, pipeIdx)); + } + + return {}; +} + +const IR::Expression *JBayProgramInfo::getValidPortConstraint( + const IR::StateVariable &portVar) const { + // const auto *portNumber = new IR::Slice(portVar, 6, 0); + const auto &options = TestgenOptions::get(); + + // 0-7 are PCIe/CPU/recirc/pktgen ports + // auto *validMacPort = + // new IR::LAnd(new IR::Leq(portNumber, new IR::Constant(portNumber->type, 0x47)), + // new IR::Leq(new IR::Constant(portNumber->type, 0x8), portNumber)); + // auto *allowedPort = + // new IR::Equ(new IR::BAnd(new IR::Neg(new IR::Constant( + // portVar->type, + // SharedTofinoConstants::VALID_INPUT_PORT_MASK)), + // portVar), + // new IR::Constant(portVar->type, 0)); + // const auto *validPortsCond = new IR::LAnd(validMacPort, allowedPort); + + const IR::Expression *validPortsCond = new IR::BoolLiteral(true); + + /// If the the vector of permitted port ranges is not empty, set the restrictions on the + /// possible output port. + if (!options.permittedPortRanges.empty()) { + /// The drop port is also always a valid port. Initialize the condition with it. + const IR::Expression *cond = new IR::BoolLiteral(false); + for (const auto &portRange : options.permittedPortRanges) { + const auto *loVarOut = IR::Constant::get(portVar->type, portRange.first); + const auto *hiVarOut = IR::Constant::get(portVar->type, portRange.second); + cond = new IR::LOr( + cond, new IR::LAnd(new IR::Leq(loVarOut, portVar), new IR::Leq(portVar, hiVarOut))); + } + validPortsCond = new IR::LAnd(validPortsCond, cond); + } + return validPortsCond; +} + +std::vector> JBayProgramInfo::pipelineCmds(gress_t gress) const { + /// Compute the series of nodes corresponding to the in-order execution of top-level + /// pipeline-component instantiations. + /// This sequence also includes nodes that handle transitions between the + /// individual component instantiations. + std::vector> pipelineSequences; + const auto &archSpec = getArchSpec(); + + for (const auto &pipeInfo : pipes) { + pipelineSequences.emplace_back(); + auto &pipelineSequence = pipelineSequences.back(); + size_t blockIdx = 0; + + // Keep track of the pipe name throughout execution. + pipelineSequence.emplace_back( + Continuation::PropertyUpdate("pipe_name"_cs, pipeInfo.pipeName)); + + for (const auto &decl : Values(pipeInfo.pipes)) { + // Iterate through the (ordered) pipes of the target architecture. + auto subCmds = processDeclaration(decl, blockIdx); + if (getGress(decl) == gress) { + pipelineSequence.insert(pipelineSequence.end(), subCmds.begin(), subCmds.end()); + } + ++blockIdx; + } + + /// We are missing the ghost thread in this particular program (it is optional) + // Just advance the pipeIdx to circumvent the check. + // This is only allowed in the case of 6 pipes. + if (blockIdx == 6) { + ++blockIdx; + } + BUG_CHECK(archSpec.getArchVectorSize() >= blockIdx, + "The Tofino architecture requires at most %1% pipes (provided %2% pipes).", + archSpec.getArchVectorSize(), blockIdx); + } + return pipelineSequences; +} + +JBayProgramInfo::JBayProgramInfo(const TofinoCompilerResult &compilerResult, + std::vector inputPipes, + std::map declIdToGress, + std::map declIdToPipe) + : TofinoSharedProgramInfo(compilerResult, std::move(inputPipes), std::move(declIdToGress), + std::move(declIdToPipe)) { + concolicMethodImpls.add(*SharedTofinoConcolic::getSharedTofinoConcolicMethodImpls()); + // Restrict egress port to be an allowed valid port. + const auto *validPortsCond = getValidPortConstraint(getTargetOutputPortVar()); + pipelineSequence.emplace_back(Continuation::Guard(validPortsCond)); +} + +std::vector JBayProgramInfo::processDeclaration( + const IR::Type_Declaration *typeDecl, size_t pipeIdx) const { + // Get the architecture specification for this target. + const auto &archSpec = getArchSpec(); + + BUG_CHECK(pipeIdx < archSpec.getArchVectorSize(), + "Current pipe index %1% exceed the total amount of specified pipes (%2%).", + archSpec.getArchVectorSize(), pipeIdx); + + // Collect parameters. + const auto *applyBlock = typeDecl->to(); + if (applyBlock == nullptr) { + TESTGEN_UNIMPLEMENTED("Constructed type %s of type %s not supported.", typeDecl, + typeDecl->node_type_name()); + } + // Retrieve the current canonical pipe in the architecture spec using the pipe index. + const auto *archMember = archSpec.getArchMember(pipeIdx); + + std::vector cmds; + + if ((archMember->blockName == "IngressParserT")) { + // Between the metadata that is prepended to the input packet to the ingress parser and + // the actual packet there is also port metadata of size PORT_METADATA_SIZE. + const auto *portMetadata = + createTargetUninitialized(IR::Type_Bits::get(JBayConstants::PORT_METADATA_SIZE), false); + const auto *stmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "prepend_to_prog_header", {portMetadata}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("hdr", IR::Direction::In, + IR::Type_Bits::get(JBayConstants::PORT_METADATA_SIZE))}))); + cmds.emplace_back(stmt); + // Some pipes in Tofino prepend metadata to the input packet. + // In this case, the intrinsic metadata corresponds to the third parameter. + const auto *paramType = new IR::Type_Name("ingress_intrinsic_metadata_t"); + const auto *archPath = new IR::PathExpression(paramType, new IR::Path("*ig_intr_md")); + // We synthesize an argument for the method call. + // The input is the global architecture variable corresponding to the metadata. + const auto *metaPrependStmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "prepend_to_prog_header", {archPath}, IR::Type_Void::get(), + new IR::ParameterList({new IR::Parameter("hdr", IR::Direction::In, paramType)}))); + cmds.emplace_back(metaPrependStmt); + } + // Some pipes in Tofino prepend metadata to the input packet. + // In this case, the intrinsic metadata corresponds to the third parameter. + if ((archMember->blockName == "EgressParserT")) { + const auto *paramType = new IR::Type_Name("egress_intrinsic_metadata_t"); + const auto *archPath = new IR::PathExpression(paramType, new IR::Path("*eg_intr_md")); + // We synthesize an argument for the method call. + // The input is the global architecture variable corresponding to the metadata. + const auto *metaPrependStmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "prepend_to_prog_header", {archPath}, IR::Type_Void::get(), + new IR::ParameterList({new IR::Parameter("hdr", IR::Direction::In, paramType)}))); + cmds.emplace_back(metaPrependStmt); + } + // Copy-in. + const auto *copyInCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_in", {IR::StringLiteral::get(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); + cmds.emplace_back(copyInCall); + // Insert the actual pipeline. + cmds.emplace_back(typeDecl); + // Copy-out. + const auto *copyOutCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_out", {IR::StringLiteral::get(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); + cmds.emplace_back(copyOutCall); + + // After some specific pipelines (deparsers), we have to append the remaining packet + // payload. We use an extern call for this. + if (!((archMember->blockName == "IngressDeparserT") || + (archMember->blockName == "EgressDeparserT"))) { + return cmds; + } + + if ((archMember->blockName == "IngressDeparserT")) { + const auto *nineBitType = IR::Type_Bits::get(9); + // After the deparser, we store the ucast_egress_port as the device output port. + auto *portAssign = new IR::AssignmentStatement( + getTargetOutputPortVar(), + new IR::Member(nineBitType, new IR::PathExpression("*ig_intr_md_for_tm"), + "ucast_egress_port")); + cmds.emplace_back(portAssign); + } + + const auto *stmt = + new IR::MethodCallStatement(Utils::generateInternalMethodCall("prepend_emit_buffer", {})); + cmds.emplace_back(stmt); + + // Also check whether we need to drop the packet. + auto *dropStmt = + new IR::MethodCallStatement(Utils::generateInternalMethodCall("check_tofino_drop", {})); + cmds.emplace_back(dropStmt); + + if ((archMember->blockName == "IngressDeparserT")) { + const auto *oneBitType = IR::Type_Bits::get(1); + + // Check whether ig_intr_md_for_tm.bypass_egress == 1w1 + // If bypass_egress is set, we skip the rest of the pipeline after the ingress deparser. + const IR::Expression *skipCond = + new IR::Equ(IR::Constant::get(oneBitType, 1), + new IR::Member(oneBitType, new IR::PathExpression("*ig_intr_md_for_tm"), + "bypass_egress")); + // Restrict egress port to be an allowed valid port. + const auto *validPortsCond = getValidPortConstraint(getTargetOutputPortVar()); + skipCond = new IR::LAnd(validPortsCond, skipCond); + const auto *skipCheck = new IR::IfStatement(skipCond, new IR::ExitStatement(), nullptr); + cmds.emplace_back(skipCheck); + } + + return cmds; +} + +const IR::StateVariable &JBayProgramInfo::getTargetInputPortVar() const { + return *new IR::StateVariable(new IR::Member(IR::Type_Bits::get(JBayConstants::PORT_BIT_WIDTH), + new IR::PathExpression("*ig_intr_md"), + "ingress_port")); +} + +const IR::StateVariable &JBayProgramInfo::getTargetOutputPortVar() const { + return *new IR::StateVariable(new IR::Member(IR::Type_Bits::get(JBayConstants::PORT_BIT_WIDTH), + new IR::PathExpression("*eg_intr_md"), + "egress_port")); +} + +const IR::Expression *JBayProgramInfo::dropIsActive() const { + BUG("dropIsActive is not implemented for the Tofino extension. Please use the " + "\"check_tofino_drop\" extern."); +} + +const ArchSpec &JBayProgramInfo::getArchSpec() const { return ARCH_SPEC; } + +const ArchSpec JBayProgramInfo::ARCH_SPEC = ArchSpec( + "Switch"_cs, + { + // parser IngressParserT( + // packet_in pkt, + // out H hdr, + // out M ig_md, + // out ingress_intrinsic_metadata_t ig_intr_md, + // @optional out ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, + // @optional out ingress_intrinsic_metadata_from_parser_t ig_intr_md_from_prsr); + {"IngressParserT"_cs, + {nullptr, "*ig_hdr"_cs, "*ig_md"_cs, "*ig_intr_md"_cs, "*ig_intr_md_for_tm"_cs, + "*ig_intr_md_from_prsr"_cs}}, + // control IngressT( + // inout H hdr, + // inout M ig_md, + // in ingress_intrinsic_metadata_t ig_intr_md, + // in ingress_intrinsic_metadata_from_parser_t ig_intr_md_from_prsr, + // inout ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, + // inout ingress_intrinsic_metadata_for_tm_t ig_intr_md_for_tm, + // @optional in ghost_intrinsic_metadata_t gh_intr_md); + {"IngressT"_cs, + {"*ig_hdr"_cs, "*ig_md"_cs, "*ig_intr_md"_cs, "*ig_intr_md_from_prsr"_cs, + "*ig_intr_md_for_dprsr"_cs, "*ig_intr_md_for_tm"_cs, "*gh_intr_md"_cs}}, + // control IngressDeparserT( + // packet_out pkt, + // inout H hdr, + // in M metadata, + // in ingress_intrinsic_metadata_for_deparser_t ig_intr_md_for_dprsr, + // @optional in ingress_intrinsic_metadata_t ig_intr_md); + {"IngressDeparserT"_cs, + {nullptr, "*ig_hdr"_cs, "*ig_md"_cs, "*ig_intr_md_for_dprsr"_cs, "*ig_intr_md"_cs}}, + // parser EgressParserT( + // packet_in pkt, + // out H hdr, + // out M eg_md, + // out egress_intrinsic_metadata_t eg_intr_md, + // @optional out egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + // @optional out egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr); + {"EgressParserT"_cs, + {nullptr, "*eg_hdr"_cs, "*eg_md"_cs, "*eg_intr_md"_cs, "*eg_intr_md_from_prsr"_cs, + "*eg_intr_md_for_dprsr"_cs}}, + // control EgressT( + // inout H hdr, + // inout M eg_md, + // in egress_intrinsic_metadata_t eg_intr_md, + // in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr, + // inout egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, + // inout egress_intrinsic_metadata_for_output_port_t eg_intr_md_for_oport); + {"EgressT"_cs, + {"*eg_hdr"_cs, "*eg_md"_cs, "*eg_intr_md"_cs, "*eg_intr_md_from_prsr"_cs, + "*eg_intr_md_for_dprsr"_cs, "*eg_intr_md_for_oport"_cs}}, + // control EgressDeparserT( + // packet_out pkt, + // inout H hdr, + // in M metadata, + // in egress_intrinsic_metadata_for_deparser_t eg_intr_md_for_dprsr, + // @optional in egress_intrinsic_metadata_t eg_intr_md, + // @optional in egress_intrinsic_metadata_from_parser_t eg_intr_md_from_prsr); + {"EgressDeparserT"_cs, + {nullptr, "*eg_hdr"_cs, "*eg_md"_cs, "*eg_intr_md_for_dprsr"_cs, "*eg_intr_md"_cs, + "*eg_intr_md_from_prsr"_cs}}, + // control GhostT(in ghost_intrinsic_metadata_t gh_intr_md); + {"GhostT"_cs, {"*gh_intr_md"_cs}}, + }); + +} // namespace P4::P4Tools::P4Testgen::Tofino diff --git a/backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h b/backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h new file mode 100644 index 00000000000..4c0ea6be293 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/tofino/tofino2/program_info.h @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2024 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_PROGRAM_INFO_H_ +#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_PROGRAM_INFO_H_ + +#include +#include +#include + +#include "backends/tofino/bf-p4c/ir/gress.h" +#include "ir/ir.h" + +#include "backends/p4tools/modules/testgen/lib/continuation.h" +#include "backends/p4tools/modules/testgen/targets/tofino/shared_program_info.h" + +namespace P4::P4Tools::P4Testgen::Tofino { + +class JBayProgramInfo : public TofinoSharedProgramInfo { + private: + /// This function contains an imperative specification of the inter-pipe interaction in the + /// target. + std::vector processDeclaration(const IR::Type_Declaration *typeDecl, + size_t pipeIdx) const; + + [[nodiscard]] std::vector> pipelineCmds(gress_t gress) const; + + [[nodiscard]] const IR::Expression *getValidPortConstraint( + const IR::StateVariable &portVar) const override; + + [[nodiscard]] std::optional getPipePortRangeConstraint( + const IR::StateVariable &portVar, size_t pipeIdx) const override; + + public: + JBayProgramInfo(const TofinoCompilerResult &compilerResult, std::vector inputPipes, + std::map declIdToGress, std::map declIdToPipe); + + /// @see ProgramInfo::getArchSpec + [[nodiscard]] const ArchSpec &getArchSpec() const override; + + [[nodiscard]] const IR::StateVariable &getTargetInputPortVar() const override; + + [[nodiscard]] const IR::StateVariable &getTargetOutputPortVar() const override; + + [[nodiscard]] const IR::Expression *dropIsActive() const override; + + [[nodiscard]] std::vector> ingressCmds() const override; + + [[nodiscard]] std::vector> egressCmds() const override; + + /// @see ProgramInfo::getArchSpec + static const ArchSpec ARCH_SPEC; + + DECLARE_TYPEINFO(JBayProgramInfo, TofinoSharedProgramInfo); +}; + +} // namespace P4::P4Tools::P4Testgen::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_TOFINO_TOFINO2_PROGRAM_INFO_H_ */