From 079649d80e3b5f623f409fa5aca52ab6926975c8 Mon Sep 17 00:00:00 2001 From: VSuryaprasad-hcl <159443973+VSuryaprasad-HCL@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:27:16 +0530 Subject: [PATCH 1/7] [fuzzer] Refactor Fuzzer to use new reference information. (#619) Co-authored-by: kishanps --- p4_fuzzer/BUILD.bazel | 108 +++-- p4_fuzzer/constraints.cc | 24 +- p4_fuzzer/constraints.h | 44 ++ p4_fuzzer/constraints_test.cc | 447 +++++++++++++++++ p4_fuzzer/constraints_util.cc | 45 +- .../constraints_util_integration_test.cc | 49 +- p4_fuzzer/fuzz_util.cc | 453 +++++++++++------- p4_fuzzer/fuzz_util.h | 46 +- p4_fuzzer/fuzz_util_test.cc | 136 +++++- p4_fuzzer/fuzzer_config.cc | 84 ++++ p4_fuzzer/fuzzer_config.h | 73 ++- p4_fuzzer/mutation.cc | 27 +- p4_fuzzer/oracle_util.cc | 5 +- p4_fuzzer/oracle_util_test.cc | 136 +++++- p4_fuzzer/switch_state.cc | 67 --- p4_fuzzer/switch_state.h | 10 - p4_fuzzer/switch_state_test.cc | 366 -------------- p4_fuzzer/test_utils.cc | 44 +- p4_fuzzer/test_utils.h | 28 +- pins_infra_deps.bzl | 6 +- .../tests/p4_constraints_integration_test.cc | 10 +- 21 files changed, 1363 insertions(+), 845 deletions(-) create mode 100644 p4_fuzzer/constraints.h create mode 100644 p4_fuzzer/constraints_test.cc diff --git a/p4_fuzzer/BUILD.bazel b/p4_fuzzer/BUILD.bazel index 0933acfe..429358e3 100644 --- a/p4_fuzzer/BUILD.bazel +++ b/p4_fuzzer/BUILD.bazel @@ -24,22 +24,23 @@ cc_library( name = "switch_state", srcs = ["switch_state.cc"], hdrs = ["switch_state.h"], - deps = [ - "//p4_pdpi:entity_keys", + deps = [ "//gutil:collections", "//gutil:proto", "//gutil:status", + "//p4_pdpi:entity_keys", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", + "//p4_pdpi:translation_options", "//p4_pdpi/internal:ordered_map", "@com_github_google_glog//:glog", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", - "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/types:optional", @@ -51,66 +52,87 @@ cc_library( cc_test( name = "switch_state_test", srcs = ["switch_state_test.cc"], - deps = [ + deps = [ ":switch_state", - ":test_utils", "//gutil:collections", - "//gutil:proto", "//gutil:proto_matchers", "//gutil:status", "//gutil:status_matchers", + "//gutil:testing", "//p4_pdpi:ir", + "//p4_pdpi:ir_cc_proto", + "//p4_pdpi:pd", "//p4_pdpi/testing:main_p4_pd_cc_proto", "//p4_pdpi/testing:test_p4info_cc", - "//p4_pdpi:pd", - "@com_github_google_glog//:glog", + "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_main", ], ) cc_library( name = "fuzzer_config", + srcs = [ + "fuzzer_config.cc", + ], hdrs = [ "fuzzer_config.h", ], deps = [ + "//gutil:status", + "//lib/p4rt:p4rt_port", + "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", + "//p4_pdpi:reference_annotations", + "@com_github_p4lang_p4_constraints//p4_constraints/backend:constraint_info", + "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_protobuf//:protobuf", ], ) cc_library( name = "mutation_and_fuzz_util", - srcs = [ + srcs = [ + "constraints.cc", "fuzz_util.cc", "mutation.cc", - ], - hdrs = [ + ], + hdrs = [ + "constraints.h", "fuzz_util.h", "mutation.h", - ], - deps = [ + ], + deps = [ ":annotation_util", ":fuzzer_cc_proto", ":fuzzer_config", ":switch_state", "//gutil:collections", "//gutil:status", + "//lib/p4rt:p4rt_port", + "//p4_pdpi:entity_keys", "//p4_pdpi:ir_cc_proto", + "//p4_pdpi:references", "//p4_pdpi/internal:ordered_map", "//p4_pdpi/netaddr:ipv6_address", "//p4_pdpi/utils:ir", "@com_github_google_glog//:glog", + "@com_github_p4lang_p4_constraints//p4_constraints/backend:constraint_info", + "@com_github_p4lang_p4_constraints//p4_constraints/backend:symbolic_interpreter", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_github_p4lang_p4runtime//:p4types_cc_proto", + "@com_github_z3prover_z3//:api", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/base", "@com_google_absl//absl/base:endian", @@ -123,7 +145,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", - "@com_google_protobuf//:protobuf_lite", + "@com_google_protobuf//:protobuf", ], ) @@ -135,12 +157,14 @@ cc_test( data = ["acl_table_test.p4info.pb.txt"], deps = [ ":fuzzer_cc_proto", + ":fuzzer_config", ":mutation_and_fuzz_util", ":test_utils", "//gutil:collections", "//gutil:proto_matchers", "//gutil:status_matchers", "//p4_pdpi:ir_cc_proto", + "@com_github_google_glog//:glog", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/container:flat_hash_set", @@ -149,6 +173,7 @@ cc_test( "@com_google_absl//absl/random:seed_sequences", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) @@ -159,25 +184,29 @@ cc_library( hdrs = ["annotation_util.h"], deps = [ ":fuzzer_cc_proto", - "//gutil:status", "//p4_pdpi:ir", + "//p4_pdpi:ir_cc_proto", "//p4_pdpi/utils:ir", + "//thinkit:test_environment", + "@com_github_google_glog//:glog", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", ], ) cc_library( name = "oracle_util", - srcs = [ + srcs = [ "oracle_util.cc", - ], + ], hdrs = ["oracle_util.h"], deps = [ ":fuzzer_cc_proto", ":switch_state", - "//p4_pdpi:entity_keys", "//gutil:status", + "//p4_pdpi:entity_keys", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", "@com_github_p4lang_p4runtime//:p4info_cc_proto", @@ -187,7 +216,7 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", - ], + ], ) cc_test( @@ -197,15 +226,15 @@ cc_test( ":fuzzer_cc_proto", ":mutation_and_fuzz_util", ":oracle_util", + ":switch_state", ":test_utils", - "//gutil:collections", "//gutil:status", "//gutil:status_matchers", "//gutil:testing", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", - "//p4_pdpi:pd", "//p4_pdpi/netaddr:ipv4_address", + "//sai_p4/instantiations/google:instantiations", "//sai_p4/instantiations/google:sai_p4info_cc", "//sai_p4/instantiations/google:sai_pd_cc_proto", "@com_github_p4lang_p4runtime//:p4info_cc_proto", @@ -224,7 +253,6 @@ cc_library( hdrs = ["constraints_util.h"], deps = [ "//gutil:status", - "@com_github_google_glog//:glog", "@com_github_ivmai_cudd//:cudd", "@com_github_p4lang_p4_constraints//p4_constraints:ast_cc_proto", "@com_github_p4lang_p4_constraints//p4_constraints/backend:constraint_info", @@ -251,8 +279,8 @@ cc_test( "//gutil:proto", "@com_github_google_glog//:glog", "@com_github_p4lang_p4_constraints//p4_constraints/backend:constraint_info", - "@com_github_p4lang_p4_constraints//p4_constraints/backend:interpreter", "@com_github_p4lang_p4runtime//:p4info_cc_proto", + "@com_google_absl//absl/types:optional", "@com_google_googletest//:gtest_main", ], ) @@ -264,11 +292,8 @@ cc_library( hdrs = ["test_utils.h"], deps = [ ":fuzzer_config", - ":mutation_and_fuzz_util", ":switch_state", - "//gutil:collections", - "//gutil:testing", - "//p4_pdpi:ir", + "//lib/p4rt:p4rt_port", "//p4_pdpi:ir_cc_proto", "//p4_pdpi/internal:ordered_map", "//p4_pdpi/testing:test_p4info_cc", @@ -282,6 +307,33 @@ cc_library( ], ) +cc_test( + name = "constraints_test", + srcs = ["constraints_test.cc"], + deps = [ + ":fuzzer_config", + ":mutation_and_fuzz_util", + ":switch_state", + ":test_utils", + "//gutil:collections", + "//gutil:status", + "//gutil:status_matchers", + "//gutil:testing", + "//p4_pdpi:ir", + "//p4_pdpi:ir_cc_proto", + "@com_github_google_glog//:glog", + "@com_github_p4lang_p4_constraints//p4_constraints:ast_cc_proto", + "@com_github_p4lang_p4_constraints//p4_constraints/backend:constraint_info", + "@com_github_p4lang_p4_constraints//p4_constraints/backend:interpreter", + "@com_github_p4lang_p4runtime//:p4info_cc_proto", + "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", + "@com_google_absl//absl/random", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + # -- P4 libraries for testing ------------------------------------------------- p4_library( diff --git a/p4_fuzzer/constraints.cc b/p4_fuzzer/constraints.cc index 4f1d052e..8cd28aeb 100644 --- a/p4_fuzzer/constraints.cc +++ b/p4_fuzzer/constraints.cc @@ -16,9 +16,7 @@ #include #include -#include -#include "absl/container/flat_hash_map.h" #include "absl/random/random.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" @@ -53,11 +51,10 @@ absl::StatusOr HasP4RuntimeTranslatedType( } // namespace bool UsesP4Constraints(int table_id, const FuzzerConfig& config) { - auto it = config.GetConstraintInfo().find(table_id); - if (it == config.GetConstraintInfo().end()) { - return false; - } - return it->second.constraint.has_value(); + auto* table_info = + p4_constraints::GetTableInfoOrNull(config.GetConstraintInfo(), table_id); + + return table_info != nullptr && table_info->constraint.has_value(); } bool UsesP4Constraints(const pdpi::IrTableDefinition& table, @@ -68,14 +65,15 @@ bool UsesP4Constraints(const pdpi::IrTableDefinition& table, absl::StatusOr FuzzValidConstrainedTableEntry( const FuzzerConfig& config, const SwitchState& switch_state, const pdpi::IrTableDefinition& table, absl::BitGen& gen) { - auto it = config.GetConstraintInfo().find(table.preamble().id()); - if (it == config.GetConstraintInfo().end()) { + auto* table_info = p4_constraints::GetTableInfoOrNull( + config.GetConstraintInfo(), table.preamble().id()); + + if (table_info == nullptr) { return gutil::InvalidArgumentErrorBuilder() << "given table with ID '" << table.preamble().id() << "' that does not exist in P4Info: " << table.preamble().alias(); } - const p4_constraints::TableInfo& table_info = it->second; - if (!table_info.constraint.has_value()) { + if (!table_info->constraint.has_value()) { return gutil::InvalidArgumentErrorBuilder() << "given table without P4-Constraints: " << table.preamble().alias(); @@ -112,7 +110,7 @@ absl::StatusOr FuzzValidConstrainedTableEntry( ASSIGN_OR_RETURN( p4_constraints::SymbolicEnvironment environment, - p4_constraints::EncodeValidTableEntryInZ3(table_info, solver, skip_key)); + p4_constraints::EncodeValidTableEntryInZ3(*table_info, solver, skip_key)); // Try to add some randomness to get more unique entries by attempting to fuzz // priority, skipping if the initial value yields an unsatisfiable constraint. @@ -149,7 +147,7 @@ absl::StatusOr FuzzValidConstrainedTableEntry( z3::model model = solver.get_model(); ASSIGN_OR_RETURN(TableEntry table_entry, - p4_constraints::ConcretizeEntry(model, table_info, + p4_constraints::ConcretizeEntry(model, *table_info, environment, skip_key)); // Fuzz an action. diff --git a/p4_fuzzer/constraints.h b/p4_fuzzer/constraints.h new file mode 100644 index 00000000..b7244869 --- /dev/null +++ b/p4_fuzzer/constraints.h @@ -0,0 +1,44 @@ +// Copyright 2023 Google LLC +// +// 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. +// ============================================================================= +// Handles p4-constraints support for the P4-Fuzzer. + +#ifndef PINS_INFRA_P4_FUZZER_CONSTRAINTS_H_ +#define PINS_INFRA_P4_FUZZER_CONSTRAINTS_H_ + +#include "absl/random/random.h" +#include "absl/status/statusor.h" +#include "p4/v1/p4runtime.pb.h" +#include "p4_fuzzer/fuzzer_config.h" +#include "p4_fuzzer/switch_state.h" +#include "p4_pdpi/ir.pb.h" + +namespace p4_fuzzer { + +// Checks whether a table uses P4-Constraints. +bool UsesP4Constraints(const pdpi::IrTableDefinition& table, + const FuzzerConfig& config); + +// Checks whether a table uses P4-Constraints. +bool UsesP4Constraints(int table_id, const FuzzerConfig& config); + +// Generates a valid table entry for a table that uses P4-Constraints. Fails if +// given a table for which `!UsesP4Constraints`. +absl::StatusOr FuzzValidConstrainedTableEntry( + const FuzzerConfig& config, const SwitchState& switch_state, + const pdpi::IrTableDefinition& table, absl::BitGen& gen); + +} // namespace p4_fuzzer + +#endif // PINS_INFRA_P4_FUZZER_CONSTRAINTS_H_ diff --git a/p4_fuzzer/constraints_test.cc b/p4_fuzzer/constraints_test.cc new file mode 100644 index 00000000..cbbf9a22 --- /dev/null +++ b/p4_fuzzer/constraints_test.cc @@ -0,0 +1,447 @@ +// Copyright 2023 Google LLC +// +// 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. + +#include "p4_fuzzer/constraints.h" + +#include +#include + +#include "absl/random/random.h" +#include "absl/status/status.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "glog/logging.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gutil/collections.h" +#include "gutil/status.h" // IWYU pragma: keep +#include "gutil/status.h" +#include "gutil/status_matchers.h" // IWYU pragma: keep +#include "gutil/testing.h" +#include "p4/config/v1/p4info.pb.h" +#include "p4/v1/p4runtime.pb.h" +#include "p4_constraints/ast.pb.h" +#include "p4_constraints/backend/constraint_info.h" +#include "p4_constraints/backend/interpreter.h" +#include "p4_fuzzer/fuzz_util.h" +#include "p4_fuzzer/fuzzer_config.h" +#include "p4_fuzzer/switch_state.h" +#include "p4_fuzzer/test_utils.h" +#include "p4_pdpi/ir.h" +#include "p4_pdpi/ir.pb.h" + +namespace p4_fuzzer { +namespace { + +using gutil::IsOk; +using testing::Not; + +using FuzzValidConstrainedTableEntryWithPartialConstraintTest = + testing::TestWithParam; + +// Adds entries that can be referred to by the constrained table. +absl::Status AddReferenceableEntries(absl::BitGen* gen, + const FuzzerConfig& config, + SwitchState& switch_state) { + ASSIGN_OR_RETURN(pdpi::IrTableDefinition referred_table, + gutil::FindOrStatus(config.GetIrP4Info().tables_by_name(), + "one_match_field_table")); + ASSIGN_OR_RETURN( + p4::v1::TableEntry referred_table_entry, + FuzzValidTableEntry(gen, config, switch_state, referred_table)); + p4::v1::Update update; + *update.mutable_entity()->mutable_table_entry() = referred_table_entry; + update.set_type(p4::v1::Update::INSERT); + RETURN_IF_ERROR(switch_state.ApplyUpdate(update)); + return absl::OkStatus(); +} + +// A set of substrings that capture all interesting portions of the test +// P4Info's constrained_table's entry restrictions. Used individually with +// ExtractConstraintLinesContainingString to extract only a subset of the +// P4-constraint. +std::vector GetSupportedConstraintSubstrings() { + return { + "normal", "field10bit", "ipv4", "ipv6", "priority", "mac", "val", + // TODO: P4RT translated values are not yet supported. + // "nonreferring_str", + // "referring_str", + }; +} + +// A set of substrings that are present in only unsupported portions of the test +// P4Info's constrained_table's entry restrictions. Used individually with +// DeleteConstraintLinesContainingString to avoid unsupported P4-constraints. +std::vector GetUnsupportedConstraintPredicates() { + return { + // TODO: P4RT translated values are not yet supported. + "nonreferring_str", + "referring_str", + }; +} + +// Deletes lines containing `string_to_delete` from the constraint on +// `table_id`. +// WARNING: If `string_to_delete` is a subsequence of `entry_restriction(`, this +// function will not work correctly. +absl::Status DeleteConstraintLinesContainingString( + FuzzerConfig& config, int table_id, absl::string_view string_to_delete) { + p4::config::v1::P4Info info = config.GetP4Info(); + for (auto& table : *info.mutable_tables()) { + if (table.preamble().id() == table_id) { + for (int i = 0; i < table.preamble().annotations_size(); i++) { + if (absl::StartsWith(table.preamble().annotations(i), + "@entry_restriction")) { + std::string constraint_without_string_to_delete_line = absl::StrJoin( + // Split by line. + absl::StrSplit(table.preamble().annotations(i), '\n'), "\n", + // Filter out lines with `string_to_delete` when joining. + [&string_to_delete](std::string* out, + absl::string_view constraint) { + if (!absl::StrContains(constraint, string_to_delete)) { + absl::StrAppend(out, constraint); + } + }); + table.mutable_preamble()->set_annotations( + i, constraint_without_string_to_delete_line); + } + } + } + } + + return config.SetP4Info(info); +} + +// Replaces the constraint on `table_id` with only the lines containing +// `string_to_keep`. +// WARNING: If `string_to_keep` is a subsequence of `entry_restriction(`, this +// function will not work correctly. +absl::Status ExtractConstraintLinesContainingString( + FuzzerConfig& config, int table_id, absl::string_view string_to_keep) { + p4::config::v1::P4Info info = config.GetP4Info(); + for (auto& table : *info.mutable_tables()) { + if (table.preamble().id() == table_id) { + for (int i = 0; i < table.preamble().annotations_size(); i++) { + if (absl::StartsWith(table.preamble().annotations(i), + "@entry_restriction")) { + std::string constraint_lines_with_string_to_keep = absl::StrJoin( + // Split by line. + absl::StrSplit(table.preamble().annotations(i), '\n'), "\n", + // Keep only lines with `string_to_keep` when joining. + [&string_to_keep](std::string* out, + absl::string_view constraint) { + if (absl::StrContains(constraint, string_to_keep)) { + absl::StrAppend(out, constraint); + } + }); + table.mutable_preamble()->set_annotations( + i, absl::StrCat("@entry_restriction(\"", + constraint_lines_with_string_to_keep, "\")")); + } + } + } + } + + return config.SetP4Info(info); +} + +// Removes conjuncts from `config` for `table_id` that relate to P4-Constraints +// which are not yet supported by FuzzValidConstrainedTableEntry. These are +// explicitly listed in `GetUnsupportedConstraintPredicates` above. +absl::Status ApplyUnsupportedFeatureWorkaround(FuzzerConfig& config, + int table_id) { + LOG(WARNING) << "Removing conjuncts with substrings: \"" + << absl::StrJoin(GetUnsupportedConstraintPredicates(), "\", \"") + << "\"."; + for (absl::string_view unsupported_constraint_string : + GetUnsupportedConstraintPredicates()) { + RETURN_IF_ERROR(DeleteConstraintLinesContainingString( + config, table_id, unsupported_constraint_string)); + } + return absl::OkStatus(); +} + +TEST(UsesP4ConstraintsTest, ReturnsTrueIfTableHasConstraint) { + p4::config::v1::P4Info p4info = + gutil::ParseProtoOrDie( + R"pb(tables { + preamble { + id: 1 + name: "table1" + annotations: "@entry_restriction(\"true\")" + } + action_refs { id: 1 annotations: "@proto_id(1)" } + } + actions { preamble { id: 1 name: "action1" } } + )pb"); + ASSERT_OK_AND_ASSIGN(FuzzerConfig config, FuzzerConfig::Create(p4info)); + EXPECT_TRUE(UsesP4Constraints(/*table_id=*/1, config)); +} + +TEST(UsesP4ConstraintsTest, ReturnsFalseIfTableDoesNotHaveConstraint) { + p4::config::v1::P4Info p4info = + gutil::ParseProtoOrDie( + R"pb(tables { + preamble { id: 1 name: "table1" } + action_refs { id: 1 annotations: "@proto_id(1)" } + } + actions { preamble { id: 1 name: "action1" } } + )pb"); + ASSERT_OK_AND_ASSIGN(FuzzerConfig config, FuzzerConfig::Create(p4info)); + EXPECT_FALSE(UsesP4Constraints(/*table_id=*/1, config)); +} + +TEST(UsesP4ConstraintsTest, ReturnsTrueIffTableHasConstraint) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + // Used to ensure that the relevant branches for coverage are taken at least + // once. + bool true_branch_taken = false; + bool false_branch_taken = false; + + for (const auto& [table_id, table] : + fuzzer_state.config.GetIrP4Info().tables_by_id()) { + auto* table_info = p4_constraints::GetTableInfoOrNull( + fuzzer_state.config.GetConstraintInfo(), table_id); + ASSERT_NE(table_info, nullptr); + if (table_info->constraint.has_value()) { + true_branch_taken = true; + EXPECT_TRUE(UsesP4Constraints(table, fuzzer_state.config)); + } else { + false_branch_taken = true; + EXPECT_FALSE(UsesP4Constraints(table, fuzzer_state.config)); + } + } + ASSERT_TRUE(true_branch_taken && false_branch_taken); +} + +TEST(UsesP4ConstraintsTest, FalseOnNonExistentTable) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + pdpi::IrTableDefinition bad_table_definition; + EXPECT_FALSE(UsesP4Constraints(bad_table_definition, fuzzer_state.config)); +} + +// Generates a valid entry for a version of 'constrained_table' that does not +// contain unsupported constraints. +TEST(FuzzValidConstrainedTableEntryTest, + GeneratesValidEntryWithAllSupportedConstraints) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + + // Get constrained table. + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition constrained_table, + gutil::FindOrStatus(fuzzer_state.config.GetIrP4Info().tables_by_name(), + "constrained_table")); + + // Removes features that are not yet supported by + // FuzzValidConstrainedTableEntry from `constrained_table`. + ASSERT_OK(ApplyUnsupportedFeatureWorkaround( + fuzzer_state.config, constrained_table.preamble().id())); + + // To ensure valid entries for tables with references even exist. + ASSERT_OK(AddReferenceableEntries(&fuzzer_state.gen, fuzzer_state.config, + fuzzer_state.switch_state)); + + // Generate 10 entries and ensure they all meet the constraints. + for (int i = 0; i < 10; ++i) { + ASSERT_OK_AND_ASSIGN(p4::v1::TableEntry entry, + FuzzValidConstrainedTableEntry( + fuzzer_state.config, fuzzer_state.switch_state, + constrained_table, fuzzer_state.gen)); + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableEntry ir_entry, + pdpi::PiTableEntryToIr(fuzzer_state.config.GetIrP4Info(), entry)); + ASSERT_OK_AND_ASSIGN(std::string failure_reason, + p4_constraints::ReasonEntryViolatesConstraint( + entry, fuzzer_state.config.GetConstraintInfo())); + EXPECT_EQ(failure_reason, "") + << "for ir entry:\n" + << ir_entry.DebugString() << "\nAnd pi entry:\n" + << entry.DebugString(); + } +} + +TEST(FuzzValidConstrainedTableEntryTest, FailsWithNonExistentTable) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + pdpi::IrTableDefinition bad_table_definition; + + EXPECT_THAT(FuzzValidConstrainedTableEntry( + fuzzer_state.config, fuzzer_state.switch_state, + bad_table_definition, fuzzer_state.gen), + Not(IsOk())); +} + +TEST(FuzzValidConstrainedTableEntryTest, FailsWithUnconstrainedTableTest) { + p4::config::v1::P4Info p4info = + gutil::ParseProtoOrDie( + R"pb(tables { + preamble { id: 1 name: "unconstrained_table" } + action_refs { id: 1 annotations: "@proto_id(1)" } + } + actions { preamble { id: 1 name: "action1" } } + )pb"); + ASSERT_OK_AND_ASSIGN(FuzzerConfig config, FuzzerConfig::Create(p4info)); + + // Get the unconstrained table. + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition unconstrained_table, + gutil::FindOrStatus(config.GetIrP4Info().tables_by_id(), 1)); + absl::BitGen gen; + + EXPECT_THAT( + FuzzValidConstrainedTableEntry(config, SwitchState(config.GetIrP4Info()), + unconstrained_table, gen), + Not(IsOk())); +} + +TEST(FuzzValidConstrainedTableEntryTest, + FailsWithUnconstrainedTablesComponentTest) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + // Used to ensure that the branch necessary for coverage is taken at least + // once. + bool test_was_successfully_executed = false; + + for (const auto& [table_id, table] : + fuzzer_state.config.GetIrP4Info().tables_by_id()) { + auto* table_info = p4_constraints::GetTableInfoOrNull( + fuzzer_state.config.GetConstraintInfo(), table_id); + ASSERT_NE(table_info, nullptr); + if (!table_info->constraint.has_value()) { + test_was_successfully_executed = true; + EXPECT_THAT(FuzzValidConstrainedTableEntry(fuzzer_state.config, + fuzzer_state.switch_state, + table, fuzzer_state.gen), + Not(IsOk())); + } + } + ASSERT_TRUE(test_was_successfully_executed); +} + +TEST(FuzzValidConstrainedTableEntryTest, FailsWithUnsatisfiableConstraint) { + p4::config::v1::P4Info p4info = + gutil::ParseProtoOrDie( + R"pb(tables { + preamble { + id: 1 + name: "table1" + annotations: "@entry_restriction(\"false\")" + } + action_refs { id: 1 annotations: "@proto_id(1)" } + } + actions { preamble { id: 1 name: "action1" } } + )pb"); + ASSERT_OK_AND_ASSIGN(FuzzerConfig config, FuzzerConfig::Create(p4info)); + + // Get the table with the unsatisfiable constraint. + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition table1, + gutil::FindOrStatus(config.GetIrP4Info().tables_by_id(), 1)); + absl::BitGen gen; + + EXPECT_THAT(FuzzValidConstrainedTableEntry( + config, SwitchState(config.GetIrP4Info()), table1, gen), + Not(IsOk())); +} + +TEST(FuzzValidConstrainedTableEntryTest, + FailsIfTableHasExactP4runtimeTranslatedMatch) { + p4::config::v1::P4Info p4info = + gutil::ParseProtoOrDie( + R"pb(tables { + preamble { + id: 1 + name: "table1" + annotations: "@entry_restriction(\"true\")" + } + match_fields { + id: 1 + name: "exact_p4runtime_translated" + match_type: EXACT + type_name { name: "p4runtime_translated_type" } + } + action_refs { id: 1 annotations: "@proto_id(1)" } + } + actions { preamble { id: 1 name: "action1" } } + type_info { + new_types { + key: "p4runtime_translated_type" + value { translated_type { sdn_string {} } } + } + } + )pb"); + ASSERT_OK_AND_ASSIGN(FuzzerConfig config, FuzzerConfig::Create(p4info)); + + // Get the table with the P4Runtime translated match. + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition table1, + gutil::FindOrStatus(config.GetIrP4Info().tables_by_id(), 1)); + absl::BitGen gen; + + EXPECT_THAT(FuzzValidConstrainedTableEntry( + config, SwitchState(config.GetIrP4Info()), table1, gen), + Not(IsOk())); +} + +// Generates a valid entry for a version of 'constrained_table' that is only +// constrained by a conjunct containing the given string parameter. +// Provides more granular coverage for easier debugging. +TEST_P(FuzzValidConstrainedTableEntryWithPartialConstraintTest, + GenerateValidEntry) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + + // Get constrained table. + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition constrained_table, + gutil::FindOrStatus(fuzzer_state.config.GetIrP4Info().tables_by_name(), + "constrained_table")); + + // Modify constraints to only contain the sub-constraints we want to test. + ASSERT_OK(ExtractConstraintLinesContainingString( + fuzzer_state.config, constrained_table.preamble().id(), GetParam())); + + // To ensure valid entries for tables with references even exist. + ASSERT_OK(AddReferenceableEntries(&fuzzer_state.gen, fuzzer_state.config, + fuzzer_state.switch_state)); + + // Generate 10 entries and ensure they all meet the constraints. + for (int i = 0; i < 10; ++i) { + ASSERT_OK_AND_ASSIGN(p4::v1::TableEntry entry, + FuzzValidConstrainedTableEntry( + fuzzer_state.config, fuzzer_state.switch_state, + constrained_table, fuzzer_state.gen)); + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableEntry ir_entry, + pdpi::PiTableEntryToIr(fuzzer_state.config.GetIrP4Info(), entry)); + ASSERT_OK_AND_ASSIGN(std::string failure_reason, + p4_constraints::ReasonEntryViolatesConstraint( + entry, fuzzer_state.config.GetConstraintInfo())); + EXPECT_EQ(failure_reason, "") + << "for ir entry:\n" + << ir_entry.DebugString() << "\nAnd pi entry:\n" + << entry.DebugString(); + } +} + +INSTANTIATE_TEST_SUITE_P( + PartialConstraintTestForSupportedConstraints, + FuzzValidConstrainedTableEntryWithPartialConstraintTest, + testing::ValuesIn(GetSupportedConstraintSubstrings()), + [](const testing::TestParamInfo& info) { + return gutil::SnakeCaseToCamelCase(info.param); + }); + +} // namespace +} // namespace p4_fuzzer diff --git a/p4_fuzzer/constraints_util.cc b/p4_fuzzer/constraints_util.cc index da6f076a..08c1bca1 100644 --- a/p4_fuzzer/constraints_util.cc +++ b/p4_fuzzer/constraints_util.cc @@ -13,18 +13,13 @@ // limitations under the License. #include "p4_fuzzer/constraints_util.h" -#include #include #include "absl/status/status.h" -#include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" -#include "cudd.h" #include "cuddObj.hh" -#include "glog/logging.h" #include "gmpxx.h" #include "p4_constraints/ast.pb.h" -#include "p4_constraints/backend/constraint_info.h" namespace p4_fuzzer { namespace { @@ -268,30 +263,32 @@ void PrintBDDAsDotFile(const BDD& bdd, Cudd* mgr) { mgr->DumpDot(/*nodes =*/std::vector{bdd.Add()}); } -absl::StatusOr ConstraintToBddInfo( - const p4_constraints::ConstraintInfo& constraints, Cudd* mgr) { - BDDInfo bdd_info; +// TODO: b/306252845 - clean up P4 Constraint API +// absl::StatusOr ConstraintToBddInfo( +// const p4_constraints::ConstraintInfo& constraints, Cudd* mgr) { +// BDDInfo bdd_info; - for (auto& [id, table_info] : constraints) { - if (!table_info.constraint.has_value()) { - continue; - } +// for (auto& [id, table_info] : constraints) { +// if (!table_info.constraint.has_value()) { +// continue; +// } - const auto& expr = table_info.constraint.value(); +// const auto& expr = table_info.constraint.value(); - MatchKeyToBddVariableMapping mapping; - ASSIGN_OR_RETURN(BDD bdd, ExpressionToBDD(expr, mgr, &mapping)); +// MatchKeyToBddVariableMapping mapping; +// ASSIGN_OR_RETURN(BDD bdd, ExpressionToBDD(expr, mgr, &mapping)); - auto [_, inserted] = - bdd_info.insert({id, SymbolicConstraint{bdd, mapping}}); +// auto [_, inserted] = +// bdd_info.insert({id, SymbolicConstraint{bdd, mapping}}); - if (!inserted) { - return absl::InternalError(absl::StrCat( - "Duplicate constraint for table ID ", id, ": ", expr.DebugString())); - } - } +// if (!inserted) { +// return absl::InternalError(absl::StrCat( +// "Duplicate constraint for table ID ", id, ": ", +// expr.DebugString())); +// } +// } - return bdd_info; -} +// return bdd_info; +// } } // namespace p4_fuzzer diff --git a/p4_fuzzer/constraints_util_integration_test.cc b/p4_fuzzer/constraints_util_integration_test.cc index 4666d0a6..2d6b80a3 100644 --- a/p4_fuzzer/constraints_util_integration_test.cc +++ b/p4_fuzzer/constraints_util_integration_test.cc @@ -11,9 +11,9 @@ // 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. -#include #include +#include "absl/types/optional.h" #include "glog/logging.h" #include "gtest/gtest.h" #include "gutil/proto.h" @@ -43,36 +43,29 @@ p4_constraints::ConstraintInfo GetConstraintInfoFromP4Info( return *constraints; } -p4_constraints::ast::Expression GetFirstExpressionFromConstraintInfo( - const p4_constraints::ConstraintInfo& constraints) { - for (auto& [id, table_info] : constraints) { - if (table_info.constraint) { - return table_info.constraint.value(); - } - } - - LOG(FATAL) << "No expressions in constraint info"; -} - TEST(ConstraintsUtilTest, BDDConversionTest) { Cudd mgr(0, 0); p4_constraints::ConstraintInfo constraints = GetConstraintInfoFromP4Info(kBinaryEqualsP4InfoFile); - p4_constraints::ast::Expression expr = - GetFirstExpressionFromConstraintInfo(constraints); - LOG(INFO) << "Binary equals constraint:\n" << expr.DebugString(); + constexpr int kTblFuzzTableId = 49497980; + auto* table_info = + p4_constraints::GetTableInfoOrNull(constraints, kTblFuzzTableId); + ASSERT_NE(table_info, nullptr) + << "No table_info associated with table_id " << kTblFuzzTableId; + const absl::optional& constraint1 = + table_info->constraint; + ASSERT_TRUE(constraint1.has_value()); + LOG(INFO) << "Binary equals constraint:\n" << constraint1->DebugString(); MatchKeyToBddVariableMapping equals_mapping; - auto status_or_bdd = ExpressionToBDD(expr, &mgr, &equals_mapping); + auto status_or_bdd = ExpressionToBDD(*constraint1, &mgr, &equals_mapping); LOG(INFO) << "Done.."; - if (!status_or_bdd.ok()) { - LOG(ERROR) << status_or_bdd.status(); - } + ASSERT_EQ(status_or_bdd.status(), absl::OkStatus()); BDD bdd = status_or_bdd.value(); @@ -81,15 +74,21 @@ TEST(ConstraintsUtilTest, BDDConversionTest) { constraints = GetConstraintInfoFromP4Info(kBinaryNotEqualsP4InfoFile); - expr = GetFirstExpressionFromConstraintInfo(constraints); - LOG(INFO) << "Binary not equals constraint:\n" << expr.DebugString(); + table_info = p4_constraints::GetTableInfoOrNull(constraints, 49497980); + ASSERT_NE(table_info, nullptr) + << "No table_info associated with table_id " << kTblFuzzTableId; + const absl::optional& constraint2 = + table_info->constraint; + ASSERT_TRUE(constraint2.has_value()); + LOG(INFO) << "Binary not equals constraint:\n" << constraint2->DebugString(); MatchKeyToBddVariableMapping not_equals_mapping; - status_or_bdd = ExpressionToBDD(expr, &mgr, ¬_equals_mapping); - if (!status_or_bdd.ok()) { - LOG(ERROR) << status_or_bdd.status(); - } + status_or_bdd = ExpressionToBDD(*constraint2, &mgr, ¬_equals_mapping); + + LOG(INFO) << "Done.."; + + ASSERT_EQ(status_or_bdd.status(), absl::OkStatus()); bdd = status_or_bdd.value(); diff --git a/p4_fuzzer/fuzz_util.cc b/p4_fuzzer/fuzz_util.cc index ab48ef8e..ba933d1c 100644 --- a/p4_fuzzer/fuzz_util.cc +++ b/p4_fuzzer/fuzz_util.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" @@ -37,17 +38,23 @@ #include "absl/strings/substitute.h" #include "absl/types/span.h" #include "google/protobuf/repeated_field.h" +#include "google/protobuf/repeated_ptr_field.h" #include "gutil/collections.h" #include "gutil/status.h" +#include "lib/p4rt/p4rt_port.h" #include "p4/config/v1/p4info.pb.h" #include "p4/config/v1/p4types.pb.h" #include "p4/v1/p4runtime.pb.h" #include "p4_fuzzer/annotation_util.h" +#include "p4_fuzzer/constraints.h" #include "p4_fuzzer/fuzzer_config.h" #include "p4_fuzzer/mutation.h" +#include "p4_fuzzer/switch_state.h" +#include "p4_pdpi/entity_keys.h" #include "p4_pdpi/internal/ordered_map.h" #include "p4_pdpi/ir.pb.h" #include "p4_pdpi/netaddr/ipv6_address.h" +#include "p4_pdpi/references.h" #include "p4_pdpi/utils/ir.h" #include "p4_pdpi/entity_keys.h" @@ -58,10 +65,13 @@ using ::absl::gntohll; using ::absl::Uniform; using ::pdpi::TableEntryKey; using ::p4::v1::Action; +using ::p4::v1::Entity; using ::p4::v1::FieldMatch; using ::p4::v1::TableEntry; using ::p4::v1::Update; using ::pdpi::IrTableDefinition; +using ::pdpi::IrTableReference; +using ::pdpi::TableEntryKey; constexpr int kBitsInByte = 8; @@ -108,6 +118,10 @@ bool IsDisabledForFuzzing(const FuzzerConfig& config, absl::string_view name) { return config.disabled_fully_qualified_names.contains(name); } +bool IsModifiableTable(const FuzzerConfig& config, absl::string_view name) { + return !config.non_modifiable_tables.contains(name); +} + namespace { // An item and its associated weight when using a discrete distribution. @@ -137,7 +151,7 @@ const T& DiscreteFromSpan(absl::BitGen* gen, return DiscreteFromSpan(gen, absl::MakeConstSpan(vec)); } -std::string FuzzPort(absl::BitGen* gen, const FuzzerConfig& config) { +pins_test::P4rtPortId FuzzPort(absl::BitGen* gen, const FuzzerConfig& config) { return UniformFromSpan(gen, config.ports); } @@ -149,6 +163,125 @@ inline int DivideRoundedUp(const unsigned int n, const unsigned int d) { return (n - 1) / d + 1; } +// Returns a copy of `references` with all non-match field references removed. +// This is done since it is assumed that match field and action references are +// independent an assumption that is enforced by `CheckReferenceAssumptions` +// when constructing `FuzzerConfig`. +absl::StatusOr> +MatchReferenceInfo( + const google::protobuf::RepeatedPtrField& references) { + google::protobuf::RepeatedPtrField result; + for (const IrTableReference& reference : references) { + google::protobuf::RepeatedPtrField + field_references; + for (const auto& field_reference : reference.field_references()) { + if (field_reference.source().has_match_field()) { + *field_references.Add() = field_reference; + } + } + if (!field_references.empty()) { + IrTableReference table_reference; + *table_reference.mutable_source_table() = reference.source_table(); + *table_reference.mutable_destination_table() = + reference.destination_table(); + *table_reference.mutable_field_references() = std::move(field_references); + result.Add(std::move(table_reference)); + } + } + return result; +} + +// Returns a copy of `references` with all field references that do not contain +// `action_id` removed. This is done since it is assumed that match field and +// action references are independent, an assumption that is enforced by +// `CheckReferenceAssumptions` when constructing `FuzzerConfig`. +absl::StatusOr> +ActionReferenceInfo( + const google::protobuf::RepeatedPtrField& references, + int action_id) { + google::protobuf::RepeatedPtrField result; + for (const IrTableReference& reference : references) { + google::protobuf::RepeatedPtrField + field_references; + for (const auto& field_reference : reference.field_references()) { + if (field_reference.source() + .action_field() + .p4_action_field() + .action_id() == action_id) { + *field_references.Add() = field_reference; + } + } + if (!field_references.empty()) { + IrTableReference table_reference; + *table_reference.mutable_source_table() = reference.source_table(); + *table_reference.mutable_destination_table() = + reference.destination_table(); + *table_reference.mutable_field_references() = std::move(field_references); + result.Add(std::move(table_reference)); + } + } + return result; +} + +// Creates a map from entry field to concrete value that is generated using +// `references`. For every table reference, a random entry is pulled from that +// table to produce concrete values. This implementation has assumptions that +// are enforced by `CheckReferenceAssumptions` when constructing `FuzzerConfig`. +absl::StatusOr> +CreateReferenceMapping( + absl::BitGen* gen, const SwitchState& switch_state, + const google::protobuf::RepeatedPtrField& references) { + absl::flat_hash_map reference_map; + for (const IrTableReference& reference : references) { + if (reference.destination_table().has_p4_table()) { + if (reference.destination_table().p4_table().table_name() == + "router_interface_table" && + reference.source_table().p4_table().table_name() != + "neighbor_table") { + // TODO: b/317404235 - Remove once a less "baked-in" way of masking this + // is found. + // This is a mask that is consistent with `CheckReferenceAssumptions`, + // which is used when creating the `FuzzerConfig`. + continue; + } + + if (switch_state.IsTableEmpty( + reference.destination_table().p4_table().table_id())) { + return gutil::FailedPreconditionErrorBuilder() + << "Table with id " + << reference.destination_table().p4_table().table_id() + << " is empty. Cannot currently generate references to table."; + } + + Entity referenced_entity; + *referenced_entity.mutable_table_entry() = UniformValueFromMap( + gen, switch_state.GetTableEntries( + reference.destination_table().p4_table().table_id())); + ASSIGN_OR_RETURN( + absl::flat_hash_set concrete_references, + pdpi::PossibleIncomingConcreteTableReferences(reference, + referenced_entity)); + for (const pdpi::ConcreteTableReference& concrete_reference : + concrete_references) { + for (const auto& field : concrete_reference.fields) { + reference_map[field.source_field] = field.value; + } + } + } + } + return reference_map; +} + +// Returns a randomly generated `bits` long number in network byte order, stored +// in a `bytes` long string. Unused bits are set to 0. +std::string FuzzBits(absl::BitGen* gen, int bits, int bytes) { + std::string data(bytes, 0); + for (int i = 0; i < bytes; ++i) + data[i] = absl::implicit_cast(Uniform(*gen)); + + return SetUnusedBitsToZero(bits, std::move(data)); +} + absl::StatusOr FuzzActionProfileAction( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, @@ -157,12 +290,13 @@ absl::StatusOr FuzzActionProfileAction( ASSIGN_OR_RETURN( *action.mutable_action(), - FuzzAction(gen, config, switch_state, - UniformFromSpan(gen, AllValidActions(config, ir_table_info)) - .action())); + FuzzAction( + gen, config, switch_state, + UniformFromSpan(gen, AllValidActions(config, ir_table_info)).action(), + ir_table_info)); action.set_weight(Uniform(*gen, 1, max_weight)); - action.set_watch_port(FuzzPort(gen, config)); + action.set_watch_port(FuzzPort(gen, config).GetP4rtEncoding()); return action; } @@ -177,7 +311,7 @@ absl::StatusOr FuzzActionProfileAction( bool IsAccidentallyInvalidUpdate( const FuzzerConfig& config, const SwitchState& switch_state, const AnnotatedWriteRequest& request, - const absl::flat_hash_set& entry_keys_in_request, + const absl::flat_hash_set& entry_keys_in_request, const AnnotatedUpdate& candidate_update) { // Mutated updates are never accidentally invalid (they should always be // invalid) so return early if update contains any mutations. @@ -189,7 +323,7 @@ bool IsAccidentallyInvalidUpdate( const TableEntry& candidate_entry = candidate_update.pi().entity().table_entry(); // Checks for duplicates in current write request. - if (entry_keys_in_request.contains(pdpi::TableEntryKey(candidate_entry))) { + if (entry_keys_in_request.contains(TableEntryKey(candidate_entry))) { return true; } // Checks that an insert does not insert an existing entry. @@ -273,7 +407,7 @@ std::vector GetMandatoryMatchTableIds(const FuzzerConfig& config) { std::vector GetDifferentRoleTableIds(const FuzzerConfig& config) { std::vector table_ids; - for (auto& [key, table] : Ordered(config.info.tables_by_id())) { + for (auto& [key, table] : Ordered(config.GetIrP4Info().tables_by_id())) { if (table.role() == config.role) continue; table_ids.push_back(key); } @@ -308,8 +442,21 @@ std::vector GetTableIdsWithValuePredicate( return table_ids; } +bool ModifiableTableExists(const FuzzerConfig& config, + const SwitchState& switch_state) { + for (const IrTableDefinition& table : AllValidTablesForP4RtRole(config)) { + // A table with no entries is not conisdered modifiable. + if (!switch_state.IsTableEmpty(table.preamble().id()) && + IsModifiableTable(config, table.preamble().name())) { + return true; + } + } + return false; +} + // Randomly generates an update type. -Update::Type FuzzUpdateType(absl::BitGen* gen, const SwitchState& state) { +Update::Type FuzzUpdateType(absl::BitGen* gen, const FuzzerConfig& config, + const SwitchState& state) { if (state.AllTablesEmpty()) { // Insert if there are no entries to delete. return Update::INSERT; @@ -322,7 +469,7 @@ Update::Type FuzzUpdateType(absl::BitGen* gen, const SwitchState& state) { return Update::INSERT; } else { // Equally split the rest between modify and delete. - if (absl::Bernoulli(*gen, 0.5)) { + if (ModifiableTableExists(config, state) && absl::Bernoulli(*gen, 0.5)) { return Update::MODIFY; } return Update::DELETE; @@ -344,7 +491,7 @@ void FuzzNonKeyFields(absl::BitGen* gen, const FuzzerConfig& config, // will think the update is no longer well-formed. } else { if (auto table_definition = gutil::FindOrStatus( - config.info.tables_by_id(), table_entry->table_id()); + config.GetIrP4Info().tables_by_id(), table_entry->table_id()); table_definition.ok()) { // Try up to 3 times to create a new action. for (int i = 0; i < 3; ++i) { @@ -399,6 +546,22 @@ std::vector> GenerateNonEmptyWeightedTableIds( return non_empty_weighted_table_ids; } +std::vector> GenerateModifiableWeightedTableIds( + const FuzzerConfig& config, const SwitchState& switch_state) { + std::vector> modifiable_weighted_table_ids; + for (const auto& weighted_table_id : + GenerateNonEmptyWeightedTableIds(config, switch_state)) { + std::string table_name = config.GetIrP4Info() + .tables_by_id() + .at(weighted_table_id.item) + .preamble() + .name(); + if (!IsModifiableTable(config, table_name)) continue; + modifiable_weighted_table_ids.push_back(weighted_table_id); + } + return modifiable_weighted_table_ids; +} + // Randomly generates an INSERT, MODIFY or DELETE update. The update may be // mutated (see go/p4-fuzzer-design for mutation types). AnnotatedUpdate FuzzUpdate(absl::BitGen* gen, const FuzzerConfig& config, @@ -453,7 +616,7 @@ AnnotatedUpdate FuzzUpdate(absl::BitGen* gen, const FuzzerConfig& config, } } - Update::Type type = FuzzUpdateType(gen, switch_state); + Update::Type type = FuzzUpdateType(gen, config, switch_state); Update update; update.set_type(type); @@ -481,7 +644,7 @@ AnnotatedUpdate FuzzUpdate(absl::BitGen* gen, const FuzzerConfig& config, break; } case Update::MODIFY: { - const int table_id = FuzzNonEmptyTableId(gen, config, switch_state); + const int table_id = FuzzModifiableTableId(gen, config, switch_state); TableEntry table_entry = UniformValueFromMap(gen, switch_state.GetTableEntries(table_id)); FuzzNonKeyFields(gen, config, switch_state, &table_entry); @@ -509,11 +672,11 @@ AnnotatedUpdate FuzzUpdate(absl::BitGen* gen, const FuzzerConfig& config, return FuzzUpdate(gen, config, switch_state); } - return GetAnnotatedUpdate(config.info, update, + return GetAnnotatedUpdate(config.GetIrP4Info(), update, /* mutations = */ {mutation}); } - return GetAnnotatedUpdate(config.info, update, /* mutations = */ {}); + return GetAnnotatedUpdate(config.GetIrP4Info(), update, /* mutations = */ {}); } struct VariableWithReferences { @@ -522,75 +685,6 @@ struct VariableWithReferences { references; }; -// TODO: Given that many fuzzed entries share references, there is -// a lot of redundant computation due to recomputing the exact same values -// (switch state never changes while fuzzing). A good improvement would be to -// cache results, keyed by the reference. -absl::StatusOr> -FuzzEntityWithReferences( - absl::BitGen* gen, const SwitchState& switch_state, - const std::vector& variables_with_references) { - absl::flat_hash_map> - table_to_fields_referenced; - absl::flat_hash_map variable_to_assigned_value; - - // STEP 1: Gather global information. - for (const auto& [variable, references] : variables_with_references) { - // TODO: Multiple @refers_to on a single param/field is not - // currently supported. Single existing case of such behavior is masked. - if (references.size() > 1) { - // This is the mask. Some actions contain both @refers_to - // (router_interface_table, router_interface_id) and @refers_to - // (neighbor_table, router_interface_id). Only the latter is used. - if (references.at(0).match_field() == "router_interface_id") { - table_to_fields_referenced["neighbor_table"].insert( - "router_interface_id"); - continue; - } else { - return gutil::UnimplementedErrorBuilder() - << "Multiple @refers_to on a single variable is " - "not currently supported. Variable '" - << variable << "' has '" << references.size() << "' @refers_to."; - } - } - - // This function should not be passed variables that do not contain - // references. - if (references.empty()) { - return gutil::InvalidArgumentErrorBuilder() - << "Variable '" << variable << "' has no references."; - } - - std::string table_reference = references.at(0).table(); - std::string field_reference = references.at(0).match_field(); - table_to_fields_referenced[table_reference].insert(field_reference); - } - - // STEP 2: Get referenced values. - for (auto& [table, fields] : table_to_fields_referenced) { - ASSIGN_OR_RETURN(std::vector possible_assignments, - switch_state.GetReferableEntries(table, fields)); - if (possible_assignments.empty()) { - return gutil::InvalidArgumentErrorBuilder() - << "Could not fuzz entity with references: No values available " - "for some referenced fields among {" - << absl::StrJoin(fields, ",") << "} in table " << table << "."; - } - ReferableEntry field_name_to_value = - UniformFromSpan(gen, possible_assignments); - for (const auto& variable_with_references : variables_with_references) { - for (const auto& reference : variable_with_references.references) { - if (reference.table() == table) { - variable_to_assigned_value[variable_with_references.variable] = - field_name_to_value.at(reference.match_field()); - } - } - } - } - - return variable_to_assigned_value; -} - } // namespace // Gets the action profile corresponding to the given table from the IrP4Info. @@ -615,13 +709,14 @@ const std::vector AllValidTablesForP4RtRole( const FuzzerConfig& config) { std::vector tables; - for (auto& [key, table] : Ordered(config.info.tables_by_id())) { + for (auto& [key, table] : Ordered(config.GetIrP4Info().tables_by_id())) { // Tables with the wrong role can't be modified by the controller. if (table.role() != config.role) continue; // Tables without actions cannot have valid table entries. if (table.entry_actions().empty()) continue; // Skip deprecated, unused, and disallowed tables. if (pdpi::IsElementDeprecated(table.preamble().annotations()) || + table.is_unsupported() || IsDisabledForFuzzing(config, table.preamble().name())) continue; tables.push_back(table); @@ -655,6 +750,7 @@ const std::vector AllValidMatchFields( table.preamble().name(), ".", match_field_info.match_field().name()); if (pdpi::IsElementDeprecated( match_field_info.match_field().annotations()) || + match_field_info.is_unsupported() || IsDisabledForFuzzing(config, fully_qualified_match_field)) continue; @@ -692,6 +788,14 @@ int FuzzNonEmptyTableId(absl::BitGen* gen, const FuzzerConfig& config, gen, GenerateNonEmptyWeightedTableIds(config, switch_state)); } +int FuzzModifiableTableId(absl::BitGen* gen, const FuzzerConfig& config, + const SwitchState& switch_state) { + CHECK(ModifiableTableExists(config, switch_state)) // Crash OK + << "state: " << switch_state.SwitchStateSummary(); + return DiscreteFromSpan( + gen, GenerateModifiableWeightedTableIds(config, switch_state)); +} + Mutation FuzzMutation(absl::BitGen* gen, const FuzzerConfig& config) { std::vector valid_indexes; @@ -750,36 +854,30 @@ uint64_t BitsToUint64(const std::string& data) { return gntohll(reinterpret_cast(data[0])); } -std::string FuzzBits(absl::BitGen* gen, int bits, int bytes) { - std::string data(bytes, 0); - for (int i = 0; i < bytes; ++i) - data[i] = absl::implicit_cast(Uniform(*gen)); - - return SetUnusedBitsToZero(bits, std::move(data)); -} - -std::string FuzzBits(absl::BitGen* gen, int bits) { +absl::StatusOr FuzzBits(absl::BitGen* gen, int bits) { + if (bits <= 0) { + return gutil::InvalidArgumentErrorBuilder() + << "Bytestrings must have a non-zero positive length. Length " + << bits << " was requested."; + } return FuzzBits(gen, bits, DivideRoundedUp(bits, kBitsInByte)); } -std::string FuzzNonZeroBits(absl::BitGen* gen, int bits) { - CHECK_NE(bits, 0); // Crash okay +absl::StatusOr FuzzNonZeroBits(absl::BitGen* gen, int bits) { while (true) { - std::string result = FuzzBits(gen, bits); + ASSIGN_OR_RETURN(std::string result, FuzzBits(gen, bits)); if (!pdpi::IsAllZeros(result)) return result; } } // Fuzzes a value, with special handling for ports and IDs. -// TODO: This will sometimes return an empty string, which is -// always an invalid value. absl::StatusOr FuzzValue( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, - const p4::config::v1::P4NamedType& type_name, int bits, bool non_zero) { + const p4::config::v1::P4NamedType& type_name, int bits) { // A port: pick any valid port randomly. if (IsPort(type_name)) { - return FuzzPort(gen, config); + return FuzzPort(gen, config).GetP4rtEncoding(); } // A qos queue: pick any valid qos queue randomly. @@ -798,11 +896,10 @@ absl::StatusOr FuzzValue( // A string ID (not referring to anything): Pick a fresh random ID. if (bits == 0) { - return FuzzRandomId(gen, /*min_chars=*/non_zero ? 1 : 0); + return FuzzRandomId(gen); } // Some other value: Normally fuzz bits randomly. - if (non_zero) return FuzzNonZeroBits(gen, bits); return FuzzBits(gen, bits); } @@ -810,10 +907,10 @@ uint64_t FuzzUint64(absl::BitGen* gen, int bits) { return BitsToUint64(FuzzBits(gen, bits, sizeof(uint64_t))); } -p4::v1::FieldMatch FuzzTernaryFieldMatch(absl::BitGen* gen, - const FuzzerConfig& config, int bits) { - std::string mask = FuzzNonZeroBits(gen, bits); - std::string value = FuzzBits(gen, bits); +absl::StatusOr FuzzTernaryFieldMatch( + absl::BitGen* gen, const FuzzerConfig& config, int bits) { + ASSIGN_OR_RETURN(std::string mask, FuzzNonZeroBits(gen, bits)); + ASSIGN_OR_RETURN(std::string value, FuzzBits(gen, bits)); // If a mask bit is 0, the corresponding value bit also has to be 0. value = pdpi::Intersection(value, mask).value(); @@ -824,10 +921,9 @@ p4::v1::FieldMatch FuzzTernaryFieldMatch(absl::BitGen* gen, return match; } -p4::v1::FieldMatch FuzzLpmFieldMatch(absl::BitGen* gen, - const FuzzerConfig& config, - const SwitchState& switch_state, - int bits) { +absl::StatusOr FuzzLpmFieldMatch( + absl::BitGen* gen, const FuzzerConfig& config, + const SwitchState& switch_state, int bits) { // Since /8, /16, /24, and /32 are common, we want to bias the fuzzer to // generate more of them. std::vector likely_bits; @@ -843,11 +939,13 @@ p4::v1::FieldMatch FuzzLpmFieldMatch(absl::BitGen* gen, prefix_len = absl::Uniform(*gen, 1, bits + 1); } + ASSIGN_OR_RETURN(std::string value, FuzzBits(gen, bits)); + p4::v1::FieldMatch match; match.mutable_lpm()->set_prefix_len(prefix_len); // We need a value that has prefix_len many random bits, followed by zeros. match.mutable_lpm()->set_value( - ZeroNLeastSignificantBits(bits - prefix_len, FuzzBits(gen, bits))); + ZeroNLeastSignificantBits(bits - prefix_len, value)); return match; } @@ -860,7 +958,7 @@ absl::StatusOr FuzzExactFieldMatch( // Note that exact messages have to be provided, even if the value is 0. ASSIGN_OR_RETURN(std::string value, FuzzValue(gen, config, switch_state, field.type_name(), - field.bitwidth(), /*non_zero=*/true)); + field.bitwidth())); match.mutable_exact()->set_value(value); return match; @@ -874,7 +972,7 @@ absl::StatusOr FuzzOptionalFieldMatch( p4::config::v1::MatchField field = ir_match_field_info.match_field(); ASSIGN_OR_RETURN(std::string value, FuzzValue(gen, config, switch_state, field.type_name(), - field.bitwidth(), /*non_zero=*/true)); + field.bitwidth())); match.mutable_optional()->set_value(value); return match; } @@ -889,12 +987,13 @@ absl::StatusOr FuzzFieldMatch( p4::v1::FieldMatch match; switch (match_field_info.match_type()) { case p4::config::v1::MatchField::TERNARY: { - match = FuzzTernaryFieldMatch(gen, config, match_field_info.bitwidth()); + ASSIGN_OR_RETURN(match, FuzzTernaryFieldMatch( + gen, config, match_field_info.bitwidth())); break; } case p4::config::v1::MatchField::LPM: { - match = FuzzLpmFieldMatch(gen, config, switch_state, - match_field_info.bitwidth()); + ASSIGN_OR_RETURN(match, FuzzLpmFieldMatch(gen, config, switch_state, + match_field_info.bitwidth())); break; } case p4::config::v1::MatchField::EXACT: { @@ -917,42 +1016,37 @@ absl::StatusOr FuzzFieldMatch( absl::StatusOr FuzzAction( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, - const pdpi::IrActionDefinition& ir_action_info) { + const pdpi::IrActionDefinition& ir_action_info, + const pdpi::IrTableDefinition& ir_table_info) { p4::v1::Action action; action.set_action_id(ir_action_info.preamble().id()); - std::vector params_with_references; - for (const auto& [param_name, ir_param] : - Ordered(ir_action_info.params_by_name())) { - if (ir_param.references().empty()) continue; - - params_with_references.push_back(VariableWithReferences{ - .variable = param_name, - .references = ir_param.references(), - }); - } - - absl::flat_hash_map param_to_referenced_value; + // NOTE: The ability to fuzz references for actions and match fields + // independently is based on an assumption enforced in + // `CheckReferenceAssumptions` when constructing `FuzzerConfig`. Should that + // assumption ever be removed, this code should be updated. + ASSIGN_OR_RETURN(auto action_reference_info, + ActionReferenceInfo(ir_table_info.outgoing_references(), + ir_action_info.preamble().id())); + absl::flat_hash_map reference_map; ASSIGN_OR_RETURN( - param_to_referenced_value, - FuzzEntityWithReferences(gen, switch_state, params_with_references), - _.SetPrepend() << "while fuzzing action '" - << ir_action_info.preamble().name() << "': "); - + reference_map, + CreateReferenceMapping(gen, switch_state, action_reference_info)); for (auto& [param_name, ir_param] : Ordered(ir_action_info.params_by_name())) { p4::v1::Action::Param* param = action.add_params(); param->set_param_id(ir_param.param().id()); // Assign referenced value for params with references. Fuzz values for // params without references. - if (auto it = param_to_referenced_value.find(param_name); - it != param_to_referenced_value.end()) { + if (auto it = reference_map.find( + absl::StrCat(ir_action_info.preamble().alias(), ".", param_name)); + it != reference_map.end()) { param->set_value(it->second); } else { ASSIGN_OR_RETURN( std::string value, FuzzValue(gen, config, switch_state, ir_param.param().type_name(), - ir_param.param().bitwidth(), /*non_zero=*/true), + ir_param.param().bitwidth()), _.SetPrepend() << "while fuzzing parameter '" << param_name << "' of action '" << ir_action_info.preamble().name() << "': "); @@ -981,7 +1075,7 @@ absl::StatusOr FuzzActionProfileActionSet( ASSIGN_OR_RETURN( auto action_profile, - GetActionProfile(config.info, ir_table_info.preamble().id())); + GetActionProfile(config.GetIrP4Info(), ir_table_info.preamble().id())); if (action_profile.has_sum_of_members()) { int max_number_of_actions = action_profile.max_group_size() != 0 @@ -1088,7 +1182,8 @@ absl::StatusOr FuzzAction( FuzzAction( gen, config, switch_state, UniformFromSpan(gen, AllValidActions(config, table_definition)) - .action())); + .action(), + table_definition)); } else { ASSIGN_OR_RETURN(*result.mutable_action_profile_action_set(), FuzzActionProfileActionSet(gen, config, switch_state, @@ -1105,35 +1200,27 @@ absl::StatusOr FuzzValidTableEntry( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, const pdpi::IrTableDefinition& ir_table_info) { + // If the table uses p4-constraints, then we call out to a different + // generation function that uses an SMT solver. + if (UsesP4Constraints(ir_table_info, config) && + !config.ignore_constraints_on_tables.contains( + ir_table_info.preamble().name())) { + return FuzzValidConstrainedTableEntry(config, switch_state, ir_table_info, + *gen); + } + TableEntry table_entry; table_entry.set_table_id(ir_table_info.preamble().id()); - std::vector match_fields_with_references; - for (auto& [match_field_name, ir_match_field] : - Ordered(ir_table_info.match_fields_by_name())) { - if (ir_match_field.references().empty()) continue; - - if (ir_match_field.match_field().match_type() != - p4::config::v1::MatchField::EXACT && - ir_match_field.match_field().match_type() != - p4::config::v1::MatchField::OPTIONAL) { - return gutil::InvalidArgumentErrorBuilder() - << "Only match fields with type exact or optional can have " - "references."; - } - - match_fields_with_references.push_back(VariableWithReferences{ - .variable = match_field_name, - .references = ir_match_field.references(), - }); - } - - absl::flat_hash_map match_field_to_referenced_value; - ASSIGN_OR_RETURN( - match_field_to_referenced_value, - FuzzEntityWithReferences(gen, switch_state, match_fields_with_references), - _.SetPrepend() << "while fuzzing entry for table '" - << ir_table_info.preamble().name() << "': "); + // NOTE: The ability to fuzz references for actions and match fields + // independently is based on an assumption enforced in + // `CheckReferenceAssumptions` when constructing `FuzzerConfig`. Should that + // assumption ever be removed, this code should be updated. + ASSIGN_OR_RETURN(auto match_reference_info, + MatchReferenceInfo(ir_table_info.outgoing_references())); + absl::flat_hash_map reference_map; + ASSIGN_OR_RETURN(reference_map, CreateReferenceMapping(gen, switch_state, + match_reference_info)); for (const pdpi::IrMatchFieldDefinition& match_field_info : AllValidMatchFields(config, ir_table_info)) { @@ -1142,20 +1229,16 @@ absl::StatusOr FuzzValidTableEntry( // Assign referenced value for match fields with references. Fuzz values for // match fields without references. - if (auto it = match_field_to_referenced_value.find(match_field_name); - it != match_field_to_referenced_value.end()) { + if (auto it = reference_map.find(match_field_name); + it != reference_map.end()) { p4::v1::FieldMatch match; match.set_field_id(match_field_id); - if (match_field_info.match_field().match_type() == + if (match_field_info.match_field().match_type() != p4::config::v1::MatchField::EXACT) { - match.mutable_exact()->set_value(it->second); - } else if (match_field_info.match_field().match_type() == - p4::config::v1::MatchField::OPTIONAL) { - // Optionals can have wildcards, so generate wildcard (omit field) with - // some probability, regardless of references. - if (absl::Bernoulli(*gen, kFieldMatchWildcardProbability)) continue; - match.mutable_optional()->set_value(it->second); + return gutil::InvalidArgumentErrorBuilder() + << "Only match fields with type exact can have references."; } + match.mutable_exact()->set_value(it->second); *table_entry.add_match() = match; } else { // If the field can have wildcards, we generate a wildcard match with @@ -1194,7 +1277,11 @@ absl::StatusOr FuzzValidTableEntry( table_entry.set_metadata( absl::StrCat("orion_cookie: ", FuzzUint64(gen, /*bits=*/64))); if (ir_table_info.requires_priority()) { - table_entry.set_priority(FuzzUint64(gen, /*bits=*/16)); + uint64_t priority; + do { + priority = FuzzUint64(gen, /*bits=*/16); + } while (priority == 0); + table_entry.set_priority(priority); } // TODO: Fuzz default actions. @@ -1208,13 +1295,13 @@ absl::StatusOr FuzzValidTableEntry(absl::BitGen* gen, const uint32_t table_id) { return FuzzValidTableEntry( gen, config, switch_state, - gutil::FindOrDie(config.info.tables_by_id(), table_id)); + gutil::FindOrDie(config.GetIrP4Info().tables_by_id(), table_id)); } std::vector ValidForwardingEntries( absl::BitGen* gen, const FuzzerConfig& config, const int num_entries) { std::vector entries; - SwitchState state(config.info); + SwitchState state(config.GetIrP4Info()); for (int i = 0; i < num_entries; ++i) { absl::StatusOr entry; @@ -1234,8 +1321,8 @@ std::vector ValidForwardingEntries( CHECK(state.ApplyUpdate(update).ok()); // Crash okay - entries.push_back( - GetAnnotatedTableEntry(config.info, *entry, /*mutations = */ {})); + entries.push_back(GetAnnotatedTableEntry(config.GetIrP4Info(), *entry, + /*mutations = */ {})); } return entries; @@ -1246,7 +1333,7 @@ AnnotatedWriteRequest FuzzWriteRequest(absl::BitGen* gen, const SwitchState& switch_state, absl::optional max_batch_size) { AnnotatedWriteRequest request; - absl::flat_hash_set entry_keys_in_request; + absl::flat_hash_set entry_keys_in_request; while (absl::Bernoulli(*gen, kAddUpdateProbability)) { if (max_batch_size.has_value() && @@ -1271,7 +1358,7 @@ AnnotatedWriteRequest FuzzWriteRequest(absl::BitGen* gen, *request.add_updates() = update; if (update.pi().entity().has_table_entry()) { entry_keys_in_request.insert( - pdpi::TableEntryKey(update.pi().entity().table_entry())); + TableEntryKey(update.pi().entity().table_entry())); } } } diff --git a/p4_fuzzer/fuzz_util.h b/p4_fuzzer/fuzz_util.h index c073068a..9268ca2a 100644 --- a/p4_fuzzer/fuzz_util.h +++ b/p4_fuzzer/fuzz_util.h @@ -31,9 +31,11 @@ #include #include "absl/random/random.h" +#include "absl/status/statusor.h" #include "absl/types/optional.h" #include "absl/types/span.h" #include "glog/logging.h" +#include "p4/v1/p4runtime.pb.h" #include "p4_fuzzer/fuzzer.pb.h" #include "p4_fuzzer/fuzzer_config.h" #include "p4_fuzzer/switch_state.h" @@ -111,13 +113,13 @@ const std::vector AllValidTablesForP4RtRole( // Returns the list of all "valid" actions in the underlying P4 program for // `table`. Valid actions are those that are legal for use in table entries and -// not @deprecated, @unused, or disabled. +// not @deprecated, @unsupported, or disabled. const std::vector AllValidActions( const FuzzerConfig& config, const pdpi::IrTableDefinition& table); // Returns the list of all "valid" match fields in the underlying P4 program for -// `table`. Valid match fields are those that are not @deprecated, @unused, or -// disabled. +// `table`. Valid match fields are those that are not @deprecated, @unsupported, +// or disabled. const std::vector AllValidMatchFields( const FuzzerConfig& config, const pdpi::IrTableDefinition& table); @@ -158,28 +160,25 @@ uint64_t BitsToUint64(const std::string& data); // fuzzer.proto. Mutation FuzzMutation(absl::BitGen* gen, const FuzzerConfig& config); -// Returns a randomly generated `bits` long number in network byte order, stored -// in a `bytes` long string. Unused bits are set to 0. -std::string FuzzBits(absl::BitGen* gen, int bits, int bytes); - -// Just like above, but the returned string is just long enough to hold the -// randomly generated number. -std::string FuzzBits(absl::BitGen* gen, int bits); +// Returns a randomly generated `bits` long number in network byte order. The +// returned string has just enough bytes to hold the randomly generated number. +// Returns an error if `bits` is <= 0, as empty bytestrings are disallowed. +absl::StatusOr FuzzBits(absl::BitGen* gen, int bits); // Generates a `bits` long uint64 in host byte order. uint64_t FuzzUint64(absl::BitGen* gen, int bits); // Returns a random ID with a length in the closed interval // [`min_chars`, `max_chars`]. -std::string FuzzRandomId(absl::BitGen* gen, int min_chars = 0, +std::string FuzzRandomId(absl::BitGen* gen, int min_chars = 1, int max_chars = 10); // Randomly generates a ternary field match with a bitwidth of `bits`. // Does not set the match field id. See "9.1.1. Match Format" in the P4Runtime // specification for details about which FieldMatch values are valid. // Guarantees not to be a wildcard match. -p4::v1::FieldMatch FuzzTernaryFieldMatch(absl::BitGen* gen, - const FuzzerConfig& config, int bits); +absl::StatusOr FuzzTernaryFieldMatch( + absl::BitGen* gen, const FuzzerConfig& config, int bits); // Randomly generates a field match that conforms to the given // match field info. See "9.1.1. Match Format" in the P4Runtime @@ -197,21 +196,22 @@ absl::StatusOr FuzzAction( const SwitchState& switch_state, const pdpi::IrTableDefinition& table_definition); -// Randomly generates an action that conforms to the given `ir_action_info`. -// See "9.1.2. Action Specification" in the P4Runtime specification for details -// about which Action values are valid. -// May fail if a reference to another table is required. +// Randomly generates an action that conforms to the given `ir_action_info` and +// the reference info in `ir_table_info`. See "9.1.2. Action Specification" in +// the P4Runtime specification for details about which Action values are valid. +// Will fail if a reference to an empty table is required. absl::StatusOr FuzzAction( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, - const pdpi::IrActionDefinition& ir_action_info); + const pdpi::IrActionDefinition& ir_action_info, + const pdpi::IrTableDefinition& ir_table_info); // Randomly generates an ActionProfileActionSet that conforms to the given // `ir_table_info` and `ir_p4_info` for tables that support one-shot // action selector programming. Refer to section "9.2.3. One Shot Action // Selector Programming" in the P4Runtime specification for details on // ActionProfileActionSets. -// May fail if a reference to another table is required. +// Will fail if a reference to an empty table is required. absl::StatusOr FuzzActionProfileActionSet( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, @@ -224,6 +224,10 @@ int FuzzTableId(absl::BitGen* gen, const FuzzerConfig& config); int FuzzNonEmptyTableId(absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state); +// Randomly generates the table id of a modifiable table. +int FuzzModifiableTableId(absl::BitGen* gen, const FuzzerConfig& config, + const SwitchState& switch_state); + // Randomly generates a table entry that conforms to the given table info. // The p4 info is used to lookup action references. See go/p4-fuzzer-design for // details about which TableEntry values are valid. @@ -243,7 +247,9 @@ absl::StatusOr FuzzValidTableEntry( std::vector ValidForwardingEntries( absl::BitGen* gen, const FuzzerConfig& config, const int num_entries); -// Randomly generates a set of updates, both valid and invalid. +// Randomly generates a set of updates, both valid and invalid. Optionally takes +// a max_batch_size parameter determining the maximum number of updates in a +// request. AnnotatedWriteRequest FuzzWriteRequest( absl::BitGen* gen, const FuzzerConfig& config, const SwitchState& switch_state, diff --git a/p4_fuzzer/fuzz_util_test.cc b/p4_fuzzer/fuzz_util_test.cc index 68743ddb..187fbe63 100644 --- a/p4_fuzzer/fuzz_util_test.cc +++ b/p4_fuzzer/fuzz_util_test.cc @@ -26,6 +26,7 @@ #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" +#include "glog/logging.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "gutil/collections.h" @@ -34,6 +35,7 @@ #include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" #include "p4_fuzzer/fuzzer.pb.h" +#include "p4_fuzzer/fuzzer_config.h" #include "p4_fuzzer/test_utils.h" #include "p4_pdpi/ir.pb.h" @@ -119,7 +121,7 @@ TEST(FuzzUtilTest, FuzzBitsStringSize) { absl::BitGen gen; for (auto test_case : test_cases) { - std::string data = FuzzBits(&gen, test_case.bits); + ASSERT_OK_AND_ASSIGN(std::string data, FuzzBits(&gen, test_case.bits)); EXPECT_EQ(data.size(), test_case.bytes) << "bits: " << test_case.bits << " data: '" << data << "'"; } @@ -128,7 +130,7 @@ TEST(FuzzUtilTest, FuzzBitsStringSize) { TEST(FuzzUtilTest, FuzzBitsStringPaddingWorks) { absl::BitGen gen; for (int i = 0; i < 100; ++i) { - std::string data = FuzzBits(&gen, /*bits=*/3); + ASSERT_OK_AND_ASSIGN(std::string data, FuzzBits(&gen, /*bits=*/3)); ASSERT_EQ(data.size(), 1); EXPECT_EQ(data[0] & 0xf8, 0); } @@ -148,6 +150,60 @@ TEST(FuzzUtilTest, FuzzUint64LargeInRange) { } } +TEST(FuzzUtilTest, FuzzWriteRequestAreReproducible) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + + // Use the same sequence seed for both generators. + absl::SeedSeq seed; + absl::BitGen gen_0(seed); + absl::BitGen gen_1(seed); + + // Create 20 instances (of, in expectation, ~50 updates each), and verify that + // they are identical. + for (int i = 0; i < 20; ++i) { + ASSERT_THAT(FuzzWriteRequest(&gen_0, fuzzer_state.config, + fuzzer_state.switch_state), + EqualsProto(FuzzWriteRequest(&gen_1, fuzzer_state.config, + fuzzer_state.switch_state))); + } +} + +TEST(FuzzUtilTest, FuzzWriteRequestAreReproducibleWithState) { + FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); + + absl::BitGen init_gen; + // Generate some random table entries: + for (int i = 0; i < 10; ++i) { + auto request = FuzzWriteRequest(&init_gen, fuzzer_state.config, + fuzzer_state.switch_state); + for (auto update : request.updates()) { + // If an update is not successfully added to the state, we ignore it. + fuzzer_state.switch_state.ApplyUpdate(update.pi()).IgnoreError(); + } + } + + LOG(INFO) << "State size = " + << fuzzer_state.switch_state.GetNumTableEntries(); + + // Use the same sequence seed for both generators. + absl::SeedSeq seed; + absl::BitGen gen_0(seed); + absl::BitGen gen_1(seed); + + // TODO: If we could randomize the ordering of maps for the info in the + // second config, we could check that the fuzzer is deterministic regardless. + auto fuzzer_config2 = fuzzer_state.config; + + // Create 20 instances (of, in expectation, ~50 updates each), and verify that + // they are identical. + for (int i = 0; i < 20; ++i) { + ASSERT_THAT(FuzzWriteRequest(&gen_0, fuzzer_state.config, + fuzzer_state.switch_state), + EqualsProto(FuzzWriteRequest(&gen_1, fuzzer_config2, + fuzzer_state.switch_state))); + } +} + // Test that FuzzActionProfileActionSet correctly generates an ActionProfile // Action Set of acceptable weights and size (derived from max_group_size and // kActionProfileActionSetMaxCardinality). @@ -157,11 +213,12 @@ TEST(FuzzActionProfileActionSetTest, // above that would be just as good. Lower is probably worse though. const int kGroupSizeArbitraryUpperBound = 1000; FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); - ASSERT_OK_AND_ASSIGN(const pdpi::IrTableDefinition& table_definition, - GetAOneShotTableDefinition(fuzzer_state.config.info)); + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); ASSERT_OK_AND_ASSIGN( pdpi::IrActionProfileDefinition action_profile_definition, - GetActionProfileImplementingTable(fuzzer_state.config.info, + GetActionProfileImplementingTable(fuzzer_state.config.GetIrP4Info(), table_definition)); for (int i = 0; i < 1000; ++i) { // Tests a broad enough band of max weights to give us interesting @@ -171,8 +228,19 @@ TEST(FuzzActionProfileActionSetTest, fuzzer_state.gen, kActionProfileActionSetMaxCardinality, kGroupSizeArbitraryUpperBound); - SetMaxGroupSizeInActionProfile(fuzzer_state.config.info, - action_profile_definition, max_group_size); + // Modify action profile in config and regenerate table and action profile + // definitions. + ASSERT_OK(SetMaxGroupSizeInActionProfile( + fuzzer_state.config, + action_profile_definition.action_profile().preamble().id(), + max_group_size)); + ASSERT_OK_AND_ASSIGN( + table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); + ASSERT_OK_AND_ASSIGN( + action_profile_definition, + GetActionProfileImplementingTable(fuzzer_state.config.GetIrP4Info(), + table_definition)); // Fuzz an ActionProfileActionSet. ASSERT_OK_AND_ASSIGN(auto action_profile_action_set, @@ -197,15 +265,28 @@ TEST(FuzzActionProfileActionSetTest, // Action Set of acceptable weights and size when max_group_size is set to 0. TEST(FuzzActionProfileActionSetTest, HandlesZeroMaxGroupSizeCorrectly) { FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); - ASSERT_OK_AND_ASSIGN(const pdpi::IrTableDefinition& table_definition, - GetAOneShotTableDefinition(fuzzer_state.config.info)); + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); ASSERT_OK_AND_ASSIGN( pdpi::IrActionProfileDefinition action_profile_definition, - GetActionProfileImplementingTable(fuzzer_state.config.info, + GetActionProfileImplementingTable(fuzzer_state.config.GetIrP4Info(), table_definition)); - SetMaxGroupSizeInActionProfile(fuzzer_state.config.info, - action_profile_definition, - /*max_group_size=*/0); + + // Modify action profile in config and regenerate table and action profile + // definitions. + ASSERT_OK(SetMaxGroupSizeInActionProfile( + fuzzer_state.config, + action_profile_definition.action_profile().preamble().id(), + /*max_group_size=*/0)); + ASSERT_OK_AND_ASSIGN( + table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); + ASSERT_OK_AND_ASSIGN( + action_profile_definition, + GetActionProfileImplementingTable(fuzzer_state.config.GetIrP4Info(), + table_definition)); + for (int i = 0; i < 1000; ++i) { // Fuzz an ActionProfileActionSet. ASSERT_OK_AND_ASSIGN(auto action_profile_action_set, @@ -231,20 +312,33 @@ TEST(FuzzActionProfileActionSetTest, HandlesZeroMaxGroupSizeCorrectly) { // max group size (in particular, lower than the max number of actions). TEST(FuzzActionProfileActionSetTest, HandlesLowMaxGroupSizeCorrectly) { FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); - ASSERT_OK_AND_ASSIGN(const pdpi::IrTableDefinition& table_definition, - GetAOneShotTableDefinition(fuzzer_state.config.info)); + ASSERT_OK_AND_ASSIGN( + pdpi::IrTableDefinition table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); ASSERT_OK_AND_ASSIGN( pdpi::IrActionProfileDefinition action_profile_definition, - GetActionProfileImplementingTable(fuzzer_state.config.info, + GetActionProfileImplementingTable(fuzzer_state.config.GetIrP4Info(), table_definition)); for (int i = 0; i < 1000; ++i) { // Set up. const int max_group_size = absl::Uniform( fuzzer_state.gen, 1, kActionProfileActionSetMaxCardinality); - SetMaxGroupSizeInActionProfile(fuzzer_state.config.info, - action_profile_definition, max_group_size); + // Modify action profile in config and regenerate table and action profile + // definitions. + ASSERT_OK(SetMaxGroupSizeInActionProfile( + fuzzer_state.config, + action_profile_definition.action_profile().preamble().id(), + max_group_size)); + ASSERT_OK_AND_ASSIGN( + table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); + ASSERT_OK_AND_ASSIGN( + action_profile_definition, + GetActionProfileImplementingTable(fuzzer_state.config.GetIrP4Info(), + table_definition)); + // Fuzz an ActionProfileActionSet. ASSERT_OK_AND_ASSIGN(auto action_profile_action_set, FuzzActionProfileActionSet( &fuzzer_state.gen, fuzzer_state.config, @@ -289,7 +383,7 @@ TEST(FuzzUtilTest, FuzzWriteRequestRespectsDisallowList) { std::vector parts = absl::StrSplit(path, '.'); ASSERT_OK_AND_ASSIGN( const pdpi::IrTableDefinition& table, - gutil::FindOrStatus(fuzzer_state.config.info.tables_by_name(), + gutil::FindOrStatus(fuzzer_state.config.GetIrP4Info().tables_by_name(), parts[parts.size() - 1])); disallowed_ids.insert(table.preamble().id()); } @@ -309,14 +403,14 @@ TEST(FuzzUtilTest, FuzzActionRespectsDisallowList) { FuzzerTestState fuzzer_state = ConstructStandardFuzzerTestState(); ASSERT_OK_AND_ASSIGN( pdpi::IrActionDefinition do_thing_action, - gutil::FindOrStatus(fuzzer_state.config.info.actions_by_name(), + gutil::FindOrStatus(fuzzer_state.config.GetIrP4Info().actions_by_name(), "do_thing_1")); fuzzer_state.config.disabled_fully_qualified_names = { do_thing_action.preamble().name()}; ASSERT_OK_AND_ASSIGN( const pdpi::IrTableDefinition& id_test_table, - gutil::FindOrStatus(fuzzer_state.config.info.tables_by_name(), + gutil::FindOrStatus(fuzzer_state.config.GetIrP4Info().tables_by_name(), "id_test_table")); for (int i = 0; i < 1000; i++) { diff --git a/p4_fuzzer/fuzzer_config.cc b/p4_fuzzer/fuzzer_config.cc index f582e7b4..673b44f4 100644 --- a/p4_fuzzer/fuzzer_config.cc +++ b/p4_fuzzer/fuzzer_config.cc @@ -1,15 +1,99 @@ #include "p4_fuzzer/fuzzer_config.h" +#include + +#include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "google/protobuf/repeated_ptr_field.h" #include "gutil/status.h" // IWYU pragma: keep #include "gutil/status.h" #include "p4_pdpi/ir.h" +#include "p4_pdpi/ir.pb.h" +#include "p4_pdpi/reference_annotations.h" namespace p4_fuzzer { +namespace { + +// TODO: b/317402124, b/263309221 - Remove once assumptions are no longer +// needed. Checks the following assumptions made about table references: +// 1) No reference includes both a match field and an action parameter. This +// simplifies the implementation by allowing each to be independently fuzzed. +// 2) No single field contains 2 or more references. This simplifies the +// implementation by avoiding implicit dependencies between fields. +absl::Status CheckReferenceAssumptions( + const google::protobuf::RepeatedPtrField& + references) { + absl::flat_hash_set encountered_fields; + for (const pdpi::IrTableReference& reference : references) { + bool has_match_reference = false; + bool has_action_reference = false; + for (const auto& field_reference : reference.field_references()) { + switch (field_reference.source().field_case()) { + case pdpi::IrField::kMatchField: { + has_match_reference = true; + break; + } + case pdpi::IrField::kActionField: { + has_action_reference = true; + break; + } + default: + return gutil::InvalidArgumentErrorBuilder() + << "Unknown IrField type in field reference source: " + << field_reference.source().DebugString(); + } + ASSIGN_OR_RETURN(std::string field_name, + pdpi::GetNameOfField(field_reference.source())); + auto [it, fresh] = encountered_fields.insert(field_name); + // TODO: b/317404235 - Remove once a less "baked-in" way of masking this + // is found. + // This is a mask used to maintain the second assumption made in + // CheckReferenceAssumptions. Some actions contain both @refers_to + // (router_interface_table, router_interface_id) and @refers_to + // (neighbor_table, router_interface_id). The neighbor table also + // contains the former reference, making the information redundant. + // Therefore only the latter is counted and former is ignored. + bool masked = + reference.destination_table().p4_table().table_name() == + "router_interface_table" && + reference.source_table().p4_table().table_name() != "neighbor_table"; + + // Check the second assumption. + if (!fresh && !masked) { + ASSIGN_OR_RETURN(std::string table_name, + pdpi::GetNameOfTable(reference.source_table())); + return gutil::AlreadyExistsErrorBuilder() + << "Multiple references coming from the same field are not " + "supported. Field '" + << field_name << "' in table '" << table_name + << "' contains multiple references."; + } + } + + // Check the first assumption. + if (has_match_reference && has_action_reference) { + return gutil::UnimplementedErrorBuilder() + << "References to a table involving both match fields and action " + "parameters are not supported."; + } + } + return absl::OkStatus(); +} +} // namespace absl::Status FuzzerConfig::SetP4Info(const p4::config::v1::P4Info& info) { ASSIGN_OR_RETURN(this->ir_info_, pdpi::CreateIrP4Info(info)); + ASSIGN_OR_RETURN(this->constraint_info_, + p4_constraints::P4ToConstraintInfo(info)); + for (const auto& [unused, table_def] : this->ir_info_.tables_by_id()) { + RETURN_IF_ERROR(CheckReferenceAssumptions(table_def.outgoing_references())); + } + + for (const auto& [unused, table_def] : this->ir_info_.built_in_tables()) { + RETURN_IF_ERROR(CheckReferenceAssumptions(table_def.outgoing_references())); + } + this->info_ = info; return absl::OkStatus(); } diff --git a/p4_fuzzer/fuzzer_config.h b/p4_fuzzer/fuzzer_config.h index 07001617..e4a72c67 100644 --- a/p4_fuzzer/fuzzer_config.h +++ b/p4_fuzzer/fuzzer_config.h @@ -11,25 +11,57 @@ // 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. -#ifndef GOOGLE_P4_FUZZER_FUZZER_CONFIG_H_ -#define GOOGLE_P4_FUZZER_FUZZER_CONFIG_H_ +#ifndef PINS_INFRA_P4_FUZZER_FUZZER_CONFIG_H_ +#define PINS_INFRA_P4_FUZZER_FUZZER_CONFIG_H_ +#include #include +#include +#include #include "absl/container/btree_set.h" #include "absl/container/flat_hash_set.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "lib/p4rt/p4rt_port.h" +#include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" +#include "p4_constraints/backend/constraint_info.h" #include "p4_pdpi/ir.pb.h" namespace p4_fuzzer { -struct FuzzerConfig { - // The IrP4Info of the program to be fuzzed. - pdpi::IrP4Info info; - // The set of valid port names. - std::vector ports; - // The set of valid QOS queues. - std::vector qos_queues; +class FuzzerConfig { + public: + static absl::StatusOr Create( + const p4::config::v1::P4Info& info); + + absl::Status SetP4Info(const p4::config::v1::P4Info& info); + + const p4::config::v1::P4Info& GetP4Info() const { return info_; } + const pdpi::IrP4Info& GetIrP4Info() const { return ir_info_; } + const p4_constraints::ConstraintInfo& GetConstraintInfo() const { + return constraint_info_; + } + + // TODO: These should be taken in as parameters and populated + // with Create instead. + // -- Required --------------------------------------------------------------- + // NOTE: These values are required for correct function. All of them are + // initialized to values that should usually work for GPINs switches. + // --------------------------------------------------------------------------- + // The set of valid port names. 1 tends to be mapped on most GPINs switches. + std::vector ports = + pins_test::P4rtPortId::MakeVectorFromOpenConfigEncodings({1}); + // The set of valid QOS queues. CONTROLLER_PRIORITY_5 tends to be mapped on + // most GPINs switches. + std::vector qos_queues = {"CONTROLLER_PRIORITY_5"}; + // The P4RT role the fuzzer should use. + std::string role = "sdn_controller"; + // The probability of performing a mutation on a given table entry. + float mutate_update_probability = 0.1; + + // -- Optional --------------------------------------------------------------- // The set of tables where the fuzzer should treat their resource guarantees // as hard limits rather than trying to go above them. If there are // limitations or bugs on the switch causing it to behave incorrectly when the @@ -46,10 +78,9 @@ struct FuzzerConfig { // valid table entry for a particular table contains the table itself. // TODO: Check the property above instead. absl::flat_hash_set disabled_fully_qualified_names; - // The P4RT role the fuzzer should use. - std::string role; - // The probability of performing a mutation on a given table entry. - float mutate_update_probability; + // TODO: Fully qualified names of tables that do not support + // MODIFY updates. This behaviour is not compliant with p4 runtime spec. + absl::flat_hash_set non_modifiable_tables; // A function for masking inequalities (due to known bugs) between entries // with the same TableEntryKey on the switch and in the fuzzer. std::optional< @@ -57,8 +88,20 @@ struct FuzzerConfig { TreatAsEqualDuringReadDueToKnownBug; // Controls whether empty ActionProfile one-shots should be generated. bool no_empty_action_profile_groups = false; - }; + // Ignores the constraints on tables listed when fuzzing entries. + absl::flat_hash_set ignore_constraints_on_tables; + + private: + explicit FuzzerConfig() {} + + // The P4Info of the program to be fuzzed. + // Invariant: The two P4Infos and ConstraintInfo are always in sync. + p4::config::v1::P4Info info_; + pdpi::IrP4Info ir_info_; + // Used to fuzz table entries for tables with P4-Constraints. + p4_constraints::ConstraintInfo constraint_info_; +}; } // namespace p4_fuzzer -#endif // GOOGLE_P4_FUZZER_FUZZER_CONFIG_H_ +#endif // PINS_INFRA_P4_FUZZER_FUZZER_CONFIG_H_ diff --git a/p4_fuzzer/mutation.cc b/p4_fuzzer/mutation.cc index 6cd55c34..7044750c 100644 --- a/p4_fuzzer/mutation.cc +++ b/p4_fuzzer/mutation.cc @@ -43,7 +43,8 @@ uint32_t UniformNotFromList(BitGen* gen, const std::vector& list) { const std::vector AllTableIds(const FuzzerConfig& config) { std::vector table_ids; - for (auto& [table_id, table_def] : Ordered(config.info.tables_by_id())) { + for (auto& [table_id, table_def] : + Ordered(config.GetIrP4Info().tables_by_id())) { table_ids.push_back(table_id); } @@ -54,7 +55,8 @@ const std::vector AllTableIds(const FuzzerConfig& config) { const std::vector AllActionIds(const FuzzerConfig& config) { std::vector action_ids; - for (auto& [action_id, action_def] : Ordered(config.info.actions_by_id())) { + for (auto& [action_id, action_def] : + Ordered(config.GetIrP4Info().actions_by_id())) { action_ids.push_back(action_id); } @@ -68,7 +70,7 @@ const std::vector AllMatchFieldIds(const FuzzerConfig& config, std::vector match_ids; for (auto& [match_id, match_def] : - Ordered(gutil::FindOrDie(config.info.tables_by_id(), table_id) + Ordered(gutil::FindOrDie(config.GetIrP4Info().tables_by_id(), table_id) .match_fields_by_id())) { match_ids.push_back(match_id); } @@ -178,7 +180,7 @@ absl::Status MutateInvalidTableImplementation(BitGen* gen, TableEntry* entry, } pdpi::IrTableDefinition ir_table_info = - gutil::FindOrDie(config.info.tables_by_id(), entry->table_id()); + gutil::FindOrDie(config.GetIrP4Info().tables_by_id(), entry->table_id()); switch (entry->action().type_case()) { case p4::v1::TableAction::kActionProfileActionSet: { @@ -187,7 +189,8 @@ absl::Status MutateInvalidTableImplementation(BitGen* gen, TableEntry* entry, FuzzAction( gen, config, switch_state, UniformFromSpan(gen, AllValidActions(config, ir_table_info)) - .action())); + .action(), + ir_table_info)); break; } @@ -230,7 +233,7 @@ absl::Status MutateInvalidActionSelectorWeight(BitGen* gen, action_set->mutable_action_profile_actions(action_to_fuzz); ASSIGN_OR_RETURN(auto action_profile, - GetActionProfile(config.info, entry->table_id())); + GetActionProfile(config.GetIrP4Info(), entry->table_id())); if (absl::Bernoulli(*gen, 0.5)) { action_profile_action->set_weight(0); @@ -285,9 +288,9 @@ absl::Status MutateInvalidValue(absl::BitGen* gen, p4::v1::Update* update, // Try to find a match field. auto* table_entry = update->mutable_entity()->mutable_table_entry(); - ASSIGN_OR_RETURN( - auto table_definition, - gutil::FindOrStatus(config.info.tables_by_id(), table_entry->table_id())); + ASSIGN_OR_RETURN(auto table_definition, + gutil::FindOrStatus(config.GetIrP4Info().tables_by_id(), + table_entry->table_id())); for (auto& match : *table_entry->mutable_match()) { ASSIGN_OR_RETURN(auto match_definition, gutil::FindOrStatus(table_definition.match_fields_by_id(), @@ -332,9 +335,9 @@ absl::Status MutateInvalidValue(absl::BitGen* gen, p4::v1::Update* update, // Try to find an action parameter. for (p4::v1::Action* action : actions) { - ASSIGN_OR_RETURN( - const auto& action_definition, - gutil::FindOrStatus(config.info.actions_by_id(), action->action_id())); + ASSIGN_OR_RETURN(const auto& action_definition, + gutil::FindOrStatus(config.GetIrP4Info().actions_by_id(), + action->action_id())); for (auto& param : *action->mutable_params()) { ASSIGN_OR_RETURN(const auto& param_definition, gutil::FindOrStatus(action_definition.params_by_id(), diff --git a/p4_fuzzer/oracle_util.cc b/p4_fuzzer/oracle_util.cc index 8d884f49..0bf18a48 100644 --- a/p4_fuzzer/oracle_util.cc +++ b/p4_fuzzer/oracle_util.cc @@ -130,7 +130,7 @@ absl::optional SequenceOfUpdatesOracle( const pdpi::IrUpdateStatus& status = updates[i].status; const auto& update_oracle_result = - UpdateOracle(ir_p4_info,update,status,state); + UpdateOracle(ir_p4_info, update, status, state); if (!update_oracle_result.ok()) { error += absl::StrCat( "\n- ", "The update with id=", index, @@ -184,7 +184,8 @@ absl::optional> WriteRequestOracle( StatusCode code = static_cast(update.status.code()); if (code == StatusCode::kResourceExhausted) continue; - flowkey_to_updates[pdpi::TableEntryKey(update.update.pi().entity().table_entry())] + flowkey_to_updates[pdpi::TableEntryKey( + update.update.pi().entity().table_entry())] .push_back(update); } diff --git a/p4_fuzzer/oracle_util_test.cc b/p4_fuzzer/oracle_util_test.cc index 585baf71..d254ca9d 100644 --- a/p4_fuzzer/oracle_util_test.cc +++ b/p4_fuzzer/oracle_util_test.cc @@ -13,8 +13,11 @@ // limitations under the License. #include "p4_fuzzer/oracle_util.h" +#include +#include #include #include +#include #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -22,21 +25,18 @@ #include "gmock/gmock.h" #include "google/rpc/code.pb.h" #include "gtest/gtest.h" -#include "gutil/collections.h" -#include "p4/v1/p4runtime.pb.h" #include "gutil/status.h" -#include "google/rpc/code.pb.h" -#include "gutil/status_matchers.h" +#include "gutil/status_matchers.h" // IWYU pragma: keep #include "gutil/testing.h" #include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" #include "p4_fuzzer/fuzz_util.h" #include "p4_fuzzer/fuzzer.pb.h" +#include "p4_fuzzer/switch_state.h" #include "p4_fuzzer/test_utils.h" #include "p4_pdpi/ir.h" #include "p4_pdpi/ir.pb.h" #include "p4_pdpi/netaddr/ipv4_address.h" -#include "p4_pdpi/pd.h" #include "sai_p4/instantiations/google/instantiations.h" #include "sai_p4/instantiations/google/sai_p4info.h" #include "sai_p4/instantiations/google/sai_pd.pb.h" @@ -46,7 +46,6 @@ namespace { using ::absl::StatusCode; using ::p4::v1::TableEntry; -using ::p4::v1::Update; int AclIngressTableSize() { return sai::GetIrP4Info(sai::Instantiation::kMiddleblock) @@ -62,8 +61,9 @@ absl::StatusOr GetValidActionSelectorTableEntry( // If we want two or more actions, the max cardinality better be at least 2. CHECK_GE(kActionProfileActionSetMaxCardinality, 2); - ASSIGN_OR_RETURN(const auto table_definition, - GetAOneShotTableDefinition(fuzzer_state.config.info)); + ASSIGN_OR_RETURN( + const auto table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); ASSIGN_OR_RETURN( TableEntry table_entry, @@ -96,12 +96,13 @@ absl::StatusOr GetInvalidActionSelectorExceedingMaxGroupSize( FuzzerTestState& fuzzer_state) { ASSIGN_OR_RETURN(auto table_entry, GetValidActionSelectorTableEntry(fuzzer_state)); - ASSIGN_OR_RETURN(const auto table_definition, - GetAOneShotTableDefinition(fuzzer_state.config.info)); + ASSIGN_OR_RETURN( + const auto table_definition, + GetAOneShotTableDefinition(fuzzer_state.config.GetIrP4Info())); ASSIGN_OR_RETURN(const auto action_profile, - GetActionProfileImplementingTable(fuzzer_state.config.info, - table_definition)); + GetActionProfileImplementingTable( + fuzzer_state.config.GetIrP4Info(), table_definition)); int max_group_size = action_profile.action_profile().max_group_size(); for (auto& action : *table_entry.mutable_action() @@ -131,7 +132,7 @@ TableEntry GetIngressAclTableEntry(int match, int action) { } priority: 10 action { - name: "acl_mirror" + name: "mirror" params { name: "mirror_session_id" value { str: "session" } @@ -170,8 +171,9 @@ absl::Status Check(const std::vector& updates, ir_update_status.set_code(static_cast(status)); statuses.push_back(ir_update_status); } - absl::optional> oracle = WriteRequestOracle( - fuzzer_state.config.info, request, statuses, fuzzer_state.switch_state); + std::optional> oracle = + WriteRequestOracle(fuzzer_state.config.GetIrP4Info(), request, statuses, + fuzzer_state.switch_state); if (valid) { if (oracle.has_value()) { std::string explanation = absl::StrCat( @@ -245,6 +247,110 @@ TEST(OracleUtilTest, DISABLED_SameKeyInBatch) { fuzzer_state, /*valid=*/true)); } +TEST(OracleUtilTest, ResourcesCanBeExhaustedInFullState) { + FuzzerTestState full_state = ConstructStandardFuzzerTestState(); + + // Get a prototype table entry. + ASSERT_OK_AND_ASSIGN( + const auto table, + GetATableDefinitionWithMatchType( + full_state, p4::config::v1::MatchField_MatchType_EXACT)); + ASSERT_OK_AND_ASSIGN(const auto match_field, + GetAMatchFieldDefinitionWithMatchType( + table, p4::config::v1::MatchField_MatchType_EXACT)); + ASSERT_OK_AND_ASSIGN(auto table_entry_prototype, + FuzzValidTableEntry(&full_state.gen, full_state.config, + full_state.switch_state, table)); + + // Ensure that the parameter number matches up with the field id. This is a + // convenience and can be replaced with a loop if it ever turns out to be + // false. + const int match_field_index = match_field.match_field().id() - 1; + ASSERT_EQ(table_entry_prototype.mutable_match(match_field_index)->field_id(), + match_field.match_field().id()); + + // Fill up the state. + for (int i = 1; i <= table.size(); i++) { + TableEntry table_entry = table_entry_prototype; + table_entry.mutable_match(match_field_index) + ->mutable_exact() + ->set_value(absl::StrCat(i)); + AddTableEntry(table_entry, &full_state.switch_state); + } + + TableEntry next = table_entry_prototype; + next.mutable_match(match_field_index) + ->mutable_exact() + ->set_value(absl::StrCat(table.size() + 1)); + + // Inserting into full table is okay. + EXPECT_OK(Check({MakeInsert(next, absl::StatusCode::kOk)}, full_state, + /*valid=*/true)); + + // Resource exhasted is okay too. + EXPECT_OK(Check({MakeInsert(next, absl::StatusCode::kResourceExhausted)}, + full_state, + /*valid=*/true)); +} + +TEST(OracleUtilTest, ResourcesCanBeExhaustedInAlmostFullStateWithBatch) { + FuzzerTestState almost_full_state = ConstructStandardFuzzerTestState(); + + // Get a prototype table entry. + ASSERT_OK_AND_ASSIGN( + const auto table, + GetATableDefinitionWithMatchType( + almost_full_state, p4::config::v1::MatchField_MatchType_EXACT)); + ASSERT_OK_AND_ASSIGN(const auto match_field, + GetAMatchFieldDefinitionWithMatchType( + table, p4::config::v1::MatchField_MatchType_EXACT)); + ASSERT_OK_AND_ASSIGN( + auto table_entry_prototype, + FuzzValidTableEntry(&almost_full_state.gen, almost_full_state.config, + almost_full_state.switch_state, table)); + + // Ensure that the parameter number matches up with the field id. This is a + // convenience and can be replaced with a loop if it ever turns out to be + // false. + const int match_field_index = match_field.match_field().id() - 1; + ASSERT_TRUE( + table_entry_prototype.mutable_match(match_field_index)->field_id() == + match_field.match_field().id()); + + // Make the state almost full (1 entry remaining). + for (int i = 1; i <= table.size() - 1; i++) { + TableEntry table_entry = table_entry_prototype; + table_entry.mutable_match(match_field_index) + ->mutable_exact() + ->set_value(absl::StrCat(i)); + AddTableEntry(table_entry, &almost_full_state.switch_state); + } + + TableEntry next1 = table_entry_prototype; + TableEntry next2 = table_entry_prototype; + next1.mutable_match(match_field_index) + ->mutable_exact() + ->set_value(absl::StrCat(table.size() + 1)); + next2.mutable_match(match_field_index) + ->mutable_exact() + ->set_value(absl::StrCat(table.size() + 2)); + + // Resource exhausted is not okay. + EXPECT_OK(Check({MakeInsert(next1, absl::StatusCode::kResourceExhausted)}, + almost_full_state, /*valid=*/false)); + + // Inserting two flows, one of them can fail. + EXPECT_OK(Check({MakeInsert(next1, absl::StatusCode::kOk), + MakeInsert(next2, absl::StatusCode::kResourceExhausted)}, + almost_full_state, /*valid=*/true)); + EXPECT_OK(Check({MakeInsert(next1, absl::StatusCode::kResourceExhausted), + MakeInsert(next2, absl::StatusCode::kOk)}, + almost_full_state, /*valid=*/true)); + EXPECT_OK(Check({MakeInsert(next1, absl::StatusCode::kOk), + MakeInsert(next2, absl::StatusCode::kOk)}, + almost_full_state, /*valid=*/true)); +} + // TODO: Enable this test once the Oracle properly rejects empty // strings for values. TEST(OracleUtilTest, DISABLED_EmptyValuesAreInvalid) { diff --git a/p4_fuzzer/switch_state.cc b/p4_fuzzer/switch_state.cc index ab7f0c33..61236594 100644 --- a/p4_fuzzer/switch_state.cc +++ b/p4_fuzzer/switch_state.cc @@ -111,7 +111,6 @@ absl::StatusOr ReasonActionProfileCanAccommodateTableEntry( // If action weight is 0 or less, or if it is greater than the // max_member_weight (if non-zero) for SumOfMembers semantics, the server // MUST return an InvalidArgumentError. - // Ref: http://screen/6TucRSmmLEytHQK if (action.weight() <= 0) { return absl::InvalidArgumentError(absl::StrFormat( "The new entry attempts to program a member with weight %d, which is " @@ -131,7 +130,6 @@ absl::StatusOr ReasonActionProfileCanAccommodateTableEntry( if (action_profile.has_sum_of_members()) { // If the table entry has too many actions then the current table resources // do not matter. The server must return an InvalidArgumentError. - // Ref: http://screen/ANhyq7q6jQHry2W if (needed_resources.actions > action_profile.max_group_size() && action_profile.max_group_size() != 0) { return absl::InvalidArgumentError(absl::StrFormat( @@ -151,7 +149,6 @@ absl::StatusOr ReasonActionProfileCanAccommodateTableEntry( } else { // If the table entry has too much weight then the current table resources // do not matter. The server must return an InvalidArgumentError. - // Ref: http://screen/axLMH8UBcGE6GQD if (needed_resources.total_weight > action_profile.max_group_size() && action_profile.max_group_size() != 0) { return absl::InvalidArgumentError(absl::StrFormat( @@ -200,7 +197,6 @@ SwitchState::SwitchState(IrP4Info ir_p4info) unordered_tables_[table_id] = UnorderedTableEntries(); current_resource_statistics_[table_id] = ResourceStatistics(); peak_resource_statistics_[table_id] = PeakResourceStatistics(); - } current_entries_ = 0; peak_entries_seen_ = 0; @@ -541,7 +537,6 @@ absl::Status SwitchState::SetTableEntries( for (const p4::v1::TableEntry& entry : table_entries) { *update.mutable_entity()->mutable_table_entry() = entry; RETURN_IF_ERROR(ApplyUpdate(update)); - } return absl::OkStatus(); @@ -637,68 +632,6 @@ std::string SwitchState::SwitchStateSummary() const { peak_entries_seen_, "N/A", res); } -absl::StatusOr> SwitchState::GetReferableEntries( - absl::string_view table, - const absl::flat_hash_set& fields) const { - std::vector result; - - ASSIGN_OR_RETURN(const pdpi::IrTableDefinition* table_definition, - FindPtrOrStatus(ir_p4info_.tables_by_name(), table), - _ << "Table '" << table << "'does not exist in p4info."); - - if (fields.empty()) { - return gutil::InvalidArgumentErrorBuilder() - << "Cannot get referable entries if no fields are being referenced."; - } - - // PI representation uses fields ids, so map ids back to names. - absl::flat_hash_map field_id_to_field_name; - for (const std::string& field : fields) { - ASSIGN_OR_RETURN( - const pdpi::IrMatchFieldDefinition* field_definition, - FindPtrOrStatus(table_definition->match_fields_by_name(), field), - _ << "Table '" << table << "' has no field named '" << field << "'."); - p4::config::v1::MatchField match_field = field_definition->match_field(); - // References must only be to fields of type exact or optional. - if (match_field.match_type() != p4::config::v1::MatchField::EXACT && - match_field.match_type() != p4::config::v1::MatchField::OPTIONAL) { - return gutil::InvalidArgumentErrorBuilder() - << "References must only be to fields with type exact or " - "optional. Field '" - << field << "' in table '" << table << "' is of a different type."; - } - field_id_to_field_name.insert({match_field.id(), field}); - } - - // Loop over all table entries to construct ReferableEntries. - ASSIGN_OR_RETURN( - const OrderedTableEntries* ordered_entries, - FindPtrOrStatus(ordered_tables_, table_definition->preamble().id()), - _ << "Table '" << table << "' exists in p4 info but has no ordered " - << "entry in switch state."); - for (const auto& [key, table_entry] : *ordered_entries) { - ReferableEntry result_entry; - // Fill out ReferableEntry mapping. - for (const auto& match : table_entry.match()) { - if (auto it = field_id_to_field_name.find(match.field_id()); - it != field_id_to_field_name.end()) { - std::string field_name = it->second; - if (match.has_exact()) { - result_entry.insert({field_name, match.exact().value()}); - } else if (match.has_optional()) { - result_entry.insert({field_name, match.optional().value()}); - } - } - } - // Only include entries where all referenced fields are present. - if (result_entry.size() == fields.size()) { - result.push_back(result_entry); - } - } - - return result; -} - absl::Status SwitchState::CheckConsistency() const { if (ordered_tables_.size() != unordered_tables_.size()) { return absl::InternalError(absl::StrFormat( diff --git a/p4_fuzzer/switch_state.h b/p4_fuzzer/switch_state.h index e32ba3e9..b38a5986 100644 --- a/p4_fuzzer/switch_state.h +++ b/p4_fuzzer/switch_state.h @@ -162,16 +162,6 @@ class SwitchState { // Returns a summary of the state. std::string SwitchStateSummary() const; - // Returns a vector of `ReferableEntries` in `table`. Used when a match key or - // action refers to a set of `fields` in `table`. Returns an error if: - // 1) `table` does not exist in p4 info. - // 2) `fields` is empty. - // 3) `fields` contains a field that does not exist in `table`. - // 4) `fields` contains a field that is not of type exact or optional. - absl::StatusOr> GetReferableEntries( - absl::string_view table, - const absl::flat_hash_set& fields) const; - pdpi::IrP4Info GetIrP4Info() const { return ir_p4info_; } // Used in testing to check that SwitchState is always consistent by: diff --git a/p4_fuzzer/switch_state_test.cc b/p4_fuzzer/switch_state_test.cc index 2ca6b84a..f514f3b4 100644 --- a/p4_fuzzer/switch_state_test.cc +++ b/p4_fuzzer/switch_state_test.cc @@ -409,372 +409,6 @@ TEST(SwitchStateTest, ASSERT_OK(state.CheckConsistency()); } -absl::StatusOr MakePiReferableEntry( - const IrP4Info& info, const pdpi::TableEntry& entry, - absl::flat_hash_set fields) { - ASSIGN_OR_RETURN(TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(info, entry)); - - ASSIGN_OR_RETURN( - pdpi::IrTableDefinition table_definition, - gutil::FindOrStatus(info.tables_by_id(), pi_entry.table_id())); - - ReferableEntry result; - for (const auto& match : pi_entry.match()) { - ASSIGN_OR_RETURN(pdpi::IrMatchFieldDefinition match_field_definition, - gutil::FindOrStatus(table_definition.match_fields_by_id(), - match.field_id())); - std::string field_name = match_field_definition.match_field().name(); - if (fields.contains(field_name)) { - switch (match.field_match_type_case()) { - case p4::v1::FieldMatch::kExact: - result.insert({field_name, match.exact().value()}); - break; - case p4::v1::FieldMatch::kOptional: - result.insert({field_name, match.optional().value()}); - break; - default: - return gutil::InvalidArgumentErrorBuilder() - << "Only match fields with type exact or optional can be " - "referred to. Referenced field " - << field_name << " has a different match type."; - break; - } - } - } - - for (const auto& field : fields) { - if (!result.contains(field)) { - return gutil::InvalidArgumentErrorBuilder() - << "Could not form referable entry. Entry is missing field " - << field; - } - } - - return result; -} - -TEST(SwitchStateTest, GetReferableEntriesWithNonExistentTableIsNotFound) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "Not_A_Table"; - const absl::flat_hash_set kReferencedFields = {"Not_A_Field"}; - - // Return NotFoundError when referencing non-existent table. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - StatusIs(absl::StatusCode::kNotFound)); -} - -TEST(SwitchStateTest, GetReferableEntriesWithNoFieldsIsInvalid) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "id_test_table"; - - // Return invalid argument when referencing no fields. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, {}), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST(SwitchStateTest, GetReferableEntriesWithNonExistentFieldIsNotFound) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "id_test_table"; - const absl::flat_hash_set kReferencedFields = {"Not_A_Field"}; - - // Return NotFoundError when referencing non-existent field. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - StatusIs(absl::StatusCode::kNotFound)); -} - -TEST(SwitchStateTest, GetReferableEntriesWithNonExactOrOptionalFieldIsInvalid) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "ternary_table"; - const absl::flat_hash_set kReferencedFields = {"ipv4"}; - - // Return invalid argument when referencing ternary field. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST(SwitchStateTest, GetReferableEntriesWithExactFields) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "id_test_table"; - const absl::flat_hash_set kReferencedFields = {"ipv4", "ipv6"}; - - pdpi::TableEntry entry1 = gutil::ParseProtoOrDie( - R"pb( - id_test_table_entry { - match { ipv6: "::1" ipv4: "0.0.0.1" } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - } - )pb"); - ASSERT_OK_AND_ASSIGN(ReferableEntry referable_entry1, - MakePiReferableEntry(info, entry1, kReferencedFields)); - pdpi::TableEntry entry2 = gutil::ParseProtoOrDie( - R"pb( - id_test_table_entry { - match { ipv6: "::2" ipv4: "0.0.0.2" } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - } - )pb"); - ASSERT_OK_AND_ASSIGN(ReferableEntry referable_entry2, - MakePiReferableEntry(info, entry2, kReferencedFields)); - - // Return empty vector for table with no entries. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(IsEmpty())); - - ASSERT_OK(state.ApplyUpdate(MakePiUpdate(info, Update::INSERT, entry1))); - - // Return a single referable entry for table with one entry. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry1))); - - ASSERT_OK(state.ApplyUpdate(MakePiUpdate(info, Update::INSERT, entry2))); - - // Return two referable entries for table with two entries. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry1, referable_entry2))); - - ASSERT_OK(state.ApplyUpdate(MakePiUpdate(info, Update::DELETE, entry1))); - - // Return one referable entry after entry is deleted. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry2))); -} - -TEST(SwitchStateTest, GetReferableEntriesWithOptionalFields) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "optional_table"; - const absl::flat_hash_set kReferencedFields = {"ipv4", "ipv6", - "str"}; - - pdpi::TableEntry entry_with_2_optionals_omitted = - gutil::ParseProtoOrDie( - R"pb( - optional_table_entry { - match { ipv6 { value: "::1" } } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - priority: 10 - } - )pb"); - - pdpi::TableEntry entry_with_all_optionals_present = - gutil::ParseProtoOrDie( - R"pb( - optional_table_entry { - match { - ipv6 { value: "::2" } - ipv4 { value: "0.0.0.2" } - str { value: "hi" } - } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - priority: 20 - } - )pb"); - ASSERT_OK_AND_ASSIGN( - ReferableEntry referable_entry, - MakePiReferableEntry(info, entry_with_all_optionals_present, - kReferencedFields)); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_with_2_optionals_omitted))); - - // Return empty vector for table with entries where optional fields are - // omitted. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(IsEmpty())); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_with_all_optionals_present))); - - // Return referable entry only for entries where all optionals are present. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry))); -} - -TEST(SwitchStateTest, GetReferableEntriesWithOnlyOneOptionalField) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "optional_table"; - const absl::flat_hash_set kReferencedFields = {"str"}; - - pdpi::TableEntry entry_without_referenced_field = - gutil::ParseProtoOrDie( - R"pb( - optional_table_entry { - match { - ipv6 { value: "::1" } - ipv4 { value: "0.0.0.1" } - } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - priority: 10 - } - )pb"); - - pdpi::TableEntry entry_with_only_referenced_field = - gutil::ParseProtoOrDie( - R"pb( - optional_table_entry { - match { str { value: "hi" } } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - priority: 20 - } - )pb"); - ASSERT_OK_AND_ASSIGN( - ReferableEntry referable_entry_1, - MakePiReferableEntry(info, entry_with_only_referenced_field, - kReferencedFields)); - - pdpi::TableEntry entry_with_all_field_set = - gutil::ParseProtoOrDie( - R"pb( - optional_table_entry { - match { - ipv6 { value: "::3" } - ipv4 { value: "0.0.0.3" } - str { value: "bye" } - } - action { do_thing_1 { arg2: "0x00000002" arg1: "0x00000001" } } - priority: 30 - } - )pb"); - ASSERT_OK_AND_ASSIGN( - ReferableEntry referable_entry_2, - MakePiReferableEntry(info, entry_with_all_field_set, kReferencedFields)); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_without_referenced_field))); - - // Return empty vector for table with entries lacking referenced field. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(IsEmpty())); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_with_only_referenced_field))); - - // Return referable entry only for entry with only referenced field. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry_1))); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_with_all_field_set))); - - // Return referable entry only for entries with referenced field. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry_1, referable_entry_2))); -} - -TEST(SwitchStateTest, GetReferableEntriesWithExactAndOptionalFields) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "exact_and_optional_table"; - const absl::flat_hash_set kReferencedFields = {"ipv6", "str"}; - - pdpi::TableEntry entry_with_optional_omitted = - gutil::ParseProtoOrDie( - R"pb( - exact_and_optional_table_entry { - match { ipv6: "::1" ipv4: "0.0.0.1" } - action { do_thing_4 {} } - priority: 10 - } - )pb"); - - pdpi::TableEntry entry_with_optional_present = - gutil::ParseProtoOrDie( - R"pb( - exact_and_optional_table_entry { - match { - ipv6: "::2" - ipv4: "0.0.0.2" - str { value: "hi" } - } - action { do_thing_4 {} } - priority: 20 - } - )pb"); - ASSERT_OK_AND_ASSIGN(ReferableEntry referable_entry, - MakePiReferableEntry(info, entry_with_optional_present, - kReferencedFields)); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_with_optional_omitted))); - - // Return empty vector for table with entry where optional field is missing. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(IsEmpty())); - - ASSERT_OK(state.ApplyUpdate( - MakePiUpdate(info, Update::INSERT, entry_with_optional_present))); - - // Return referable entry where optional field is present. - EXPECT_THAT(state.GetReferableEntries(kReferencedTable, kReferencedFields), - IsOkAndHolds(ElementsAre(referable_entry))); -} - -TEST(SwitchStateTest, GetReferableEntriesWithIdenticalValueOnField) { - const IrP4Info& info = pdpi::GetTestIrP4Info(); - SwitchState state(info); - - const std::string kReferencedTable = "exact_and_optional_table"; - const absl::flat_hash_set kReferencedFields = {"str"}; - - pdpi::TableEntry entry_1 = gutil::ParseProtoOrDie( - R"pb( - exact_and_optional_table_entry { - match { - ipv6: "::1" - ipv4: "0.0.0.1" - str { value: "hi" } - } - action { do_thing_4 {} } - priority: 10 - } - )pb"); - ASSERT_OK_AND_ASSIGN(ReferableEntry referable_entry_1, - MakePiReferableEntry(info, entry_1, kReferencedFields)); - - pdpi::TableEntry entry_2 = gutil::ParseProtoOrDie( - R"pb( - exact_and_optional_table_entry { - match { - ipv6: "::2" - ipv4: "0.0.0.2" - str { value: "hi" } - } - action { do_thing_4 {} } - priority: 20 - } - )pb"); - ASSERT_OK_AND_ASSIGN(ReferableEntry referable_entry_2, - MakePiReferableEntry(info, entry_2, kReferencedFields)); - - ASSERT_OK(state.ApplyUpdate(MakePiUpdate(info, Update::INSERT, entry_1))); - ASSERT_OK(state.ApplyUpdate(MakePiUpdate(info, Update::INSERT, entry_2))); - - ASSERT_OK_AND_ASSIGN( - std::vector referable_entries, - state.GetReferableEntries(kReferencedTable, kReferencedFields)); - - EXPECT_THAT(referable_entries, - ElementsAre(referable_entry_1, referable_entry_2)); - - // Both entries create the same referable entry. - EXPECT_EQ(referable_entries[0], referable_entries[1]); -} - TEST(SwitchStateTest, SetTableEntriesSetsTableEntries) { SwitchState state(GetIrP4Info()); diff --git a/p4_fuzzer/test_utils.cc b/p4_fuzzer/test_utils.cc index 299ff402..f6fa96b2 100644 --- a/p4_fuzzer/test_utils.cc +++ b/p4_fuzzer/test_utils.cc @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,36 +15,28 @@ #include -#include "absl/random/random.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/substitute.h" -#include "gutil/collections.h" #include "gutil/status.h" -#include "gutil/testing.h" +#include "lib/p4rt/p4rt_port.h" #include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" -#include "p4_fuzzer/fuzz_util.h" #include "p4_fuzzer/fuzzer_config.h" #include "p4_fuzzer/switch_state.h" #include "p4_pdpi/internal/ordered_map.h" -#include "p4_pdpi/ir.h" #include "p4_pdpi/ir.pb.h" -#include "sai_p4/instantiations/google/sai_p4info.h" namespace p4_fuzzer { +absl::StatusOr ConstructFuzzerTestState( + const p4::config::v1::P4Info& info, const std::string& role) { + ASSIGN_OR_RETURN(FuzzerConfig config, FuzzerConfig::Create(info)); + config.role = role; + config.mutate_update_probability = 0.0; -FuzzerTestState ConstructFuzzerTestState(const pdpi::IrP4Info& ir_info, - const std::string& role) { - const FuzzerConfig config{ - .info = ir_info, - .ports = {"1"}, - .qos_queues = {"0x1"}, - .role = role, - }; return FuzzerTestState{ .config = config, - .switch_state = SwitchState(ir_info), + .switch_state = SwitchState(config.GetIrP4Info()), }; } @@ -68,7 +60,7 @@ absl::StatusOr GetATableDefinitionWithMatchType( const FuzzerTestState& fuzzer_state, p4::config::v1::MatchField::MatchType match_type) { for (const auto& [unused, table] : - Ordered(fuzzer_state.config.info.tables_by_id())) { + Ordered(fuzzer_state.config.GetIrP4Info().tables_by_id())) { if (GetAMatchFieldDefinitionWithMatchType(table, match_type).ok()) { return table; } @@ -111,16 +103,18 @@ GetActionProfileImplementingTable(const pdpi::IrP4Info& info, } } -void SetMaxGroupSizeInActionProfile( - pdpi::IrP4Info& info, pdpi::IrActionProfileDefinition& action_profile, - const int max_group_size) { - action_profile.mutable_action_profile()->set_max_group_size(max_group_size); +absl::Status SetMaxGroupSizeInActionProfile(FuzzerConfig& config, + int action_profile_id, + int max_group_size) { + p4::config::v1::P4Info info = config.GetP4Info(); - const uint32_t id = action_profile.action_profile().preamble().id(); - const std::string name = action_profile.action_profile().preamble().alias(); + for (auto& action_profile : *info.mutable_action_profiles()) { + if (action_profile.preamble().id() == action_profile_id) { + action_profile.set_max_group_size(max_group_size); + } + } - (*info.mutable_action_profiles_by_id())[id] = action_profile; - (*info.mutable_action_profiles_by_name())[name] = action_profile; + return config.SetP4Info(info); } } // namespace p4_fuzzer diff --git a/p4_fuzzer/test_utils.h b/p4_fuzzer/test_utils.h index ce175307..3babafcd 100644 --- a/p4_fuzzer/test_utils.h +++ b/p4_fuzzer/test_utils.h @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,10 +11,11 @@ // 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. -#ifndef GOOGLE_P4_FUZZER_TEST_UTILS_H_ -#define GOOGLE_P4_FUZZER_TEST_UTILS_H_ +#ifndef PINS_INFRA_P4_FUZZER_TEST_UTILS_H_ +#define PINS_INFRA_P4_FUZZER_TEST_UTILS_H_ #include "absl/random/random.h" +#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "p4/config/v1/p4info.pb.h" @@ -36,12 +37,13 @@ struct FuzzerTestState { // Constructs a FuzzerTestState from an IrP4Info, only fuzzing tables with the // given role. -FuzzerTestState ConstructFuzzerTestState(const pdpi::IrP4Info& ir_info,const std::string& role); +absl::StatusOr ConstructFuzzerTestState( + const p4::config::v1::P4Info& info, const std::string& role); // Constructs a FuzzerTestState from a standard testing P4Info. // By default, this test state should be used for all tests. inline FuzzerTestState ConstructStandardFuzzerTestState() { - return ConstructFuzzerTestState(pdpi::GetTestIrP4Info(), /*role=*/""); + return ConstructFuzzerTestState(pdpi::GetTestP4Info(), /*role=*/"").value(); } // TODO: Deprecated. Do not use. New tests should not depend on a @@ -49,8 +51,9 @@ inline FuzzerTestState ConstructStandardFuzzerTestState() { // function. inline FuzzerTestState ConstructFuzzerTestStateFromSaiMiddleBlock() { return ConstructFuzzerTestState( - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*role=*/"sdn_controller"); + sai::GetP4Info(sai::Instantiation::kMiddleblock), + /*role=*/"sdn_controller") + .value(); } // Gets a MatchField of a given type from a table definition. Deterministic for @@ -77,10 +80,11 @@ absl::StatusOr GetActionProfileImplementingTable(const pdpi::IrP4Info& info, const pdpi::IrTableDefinition& table); -// Helpers to modify specific pieces of the IrP4Info. -void SetMaxGroupSizeInActionProfile( - pdpi::IrP4Info& info, pdpi::IrActionProfileDefinition& action_profile, - int max_group_size); +// Helpers to modify specific pieces of the FuzzerConfig. +absl::Status SetMaxGroupSizeInActionProfile(FuzzerConfig& config, + int action_profile_id, + int max_group_size); + } // namespace p4_fuzzer -#endif // GOOGLE_P4_FUZZER_TEST_UTILS_H_ +#endif // PINS_INFRA_P4_FUZZER_TEST_UTILS_H_ diff --git a/pins_infra_deps.bzl b/pins_infra_deps.bzl index d7afdb9f..c0461776 100644 --- a/pins_infra_deps.bzl +++ b/pins_infra_deps.bzl @@ -163,9 +163,9 @@ def pins_infra_deps(): if not native.existing_rule("com_github_p4lang_p4_constraints"): http_archive( name = "com_github_p4lang_p4_constraints", - urls = ["https://github.com/p4lang/p4-constraints/archive/11a08877ae8e3cf60acfa90c0f020d65b5a91ed5.zip"], - strip_prefix = "p4-constraints-11a08877ae8e3cf60acfa90c0f020d65b5a91ed5", - sha256 = "e85039af2378264f5e980984ab95f28903baebb3f515f1097377e294e681b2b6", + urls = ["https://github.com/p4lang/p4-constraints/archive/3d5196a793f375ccbe1bf38ae6c49e2e65604f4b.zip"], + strip_prefix = "p4-constraints-3d5196a793f375ccbe1bf38ae6c49e2e65604f4b", + sha256 = "bd8aee6cf824797e7c82ad9183b6ef47a2bbedbcab55ddc9480ccf5a174a1b3d", ) if not native.existing_rule("com_github_nlohmann_json"): http_archive( diff --git a/sai_p4/instantiations/google/tests/p4_constraints_integration_test.cc b/sai_p4/instantiations/google/tests/p4_constraints_integration_test.cc index 67ba51f7..433a60e1 100644 --- a/sai_p4/instantiations/google/tests/p4_constraints_integration_test.cc +++ b/sai_p4/instantiations/google/tests/p4_constraints_integration_test.cc @@ -53,8 +53,9 @@ TEST(AclIngressConstraints, TernaryEqualityMustBeExact) { { ASSERT_OK_AND_ASSIGN(p4::v1::TableEntry pi_entry, pdpi::IrTableEntryToPi(ir_info, ir_entry)); - EXPECT_THAT(p4_constraints::EntryMeetsConstraint(pi_entry, constraint_info), - gutil::IsOkAndHolds(false)); + EXPECT_THAT(p4_constraints::ReasonEntryViolatesConstraint(pi_entry, + constraint_info), + gutil::IsOkAndHolds(::testing::Not(""))); ASSERT_OK_AND_ASSIGN(std::string failure_reason, p4_constraints::ReasonEntryViolatesConstraint( pi_entry, constraint_info)); @@ -72,8 +73,9 @@ TEST(AclIngressConstraints, TernaryEqualityMustBeExact) { { ASSERT_OK_AND_ASSIGN(p4::v1::TableEntry pi_entry, pdpi::IrTableEntryToPi(ir_info, ir_entry)); - EXPECT_THAT(p4_constraints::EntryMeetsConstraint(pi_entry, constraint_info), - gutil::IsOkAndHolds(true)); + EXPECT_THAT(p4_constraints::ReasonEntryViolatesConstraint(pi_entry, + constraint_info), + gutil::IsOkAndHolds("")); ASSERT_OK_AND_ASSIGN(std::string failure_reason, p4_constraints::ReasonEntryViolatesConstraint( pi_entry, constraint_info)); From b95076a675c35bb1e335e33396cdb55313840168 Mon Sep 17 00:00:00 2001 From: VSuryaprasad-hcl <159443973+VSuryaprasad-HCL@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:29:27 +0530 Subject: [PATCH 2/7] [P4_Symbolic] Add support for string formatted optional matches. (#622) --------- Co-authored-by: smolkaj Co-authored-by: kishanps Co-authored-by: kheradmandG --- p4_symbolic/BUILD.bazel | 9 + p4_symbolic/main.cc | 17 +- p4_symbolic/symbolic/BUILD.bazel | 23 + .../symbolic/expected/string_optional.smt2 | 382 ++++++++++ .../symbolic/expected/string_optional.txt | 36 + p4_symbolic/symbolic/expected/vrf.smt2 | 696 ++++++++++++++++++ p4_symbolic/symbolic/expected/vrf.txt | 83 +++ p4_symbolic/symbolic/table.cc | 9 +- p4_symbolic/symbolic/test.bzl | 2 + p4_symbolic/symbolic/values.cc | 44 +- p4_symbolic/symbolic/values_test.cc | 41 ++ .../testdata/string-optional/entries.pb.txt | 111 +++ .../testdata/string-optional/program.p4 | 106 +++ p4_symbolic/testdata/vrf-routing/vrf.p4 | 2 +- p4_symbolic/z3_util.cc | 19 + p4_symbolic/z3_util.h | 10 + p4_symbolic/z3_util_test.cc | 31 + 17 files changed, 1574 insertions(+), 47 deletions(-) create mode 100644 p4_symbolic/symbolic/expected/string_optional.smt2 create mode 100644 p4_symbolic/symbolic/expected/string_optional.txt create mode 100644 p4_symbolic/symbolic/expected/vrf.smt2 create mode 100644 p4_symbolic/symbolic/expected/vrf.txt create mode 100644 p4_symbolic/symbolic/values_test.cc create mode 100644 p4_symbolic/testdata/string-optional/entries.pb.txt create mode 100644 p4_symbolic/testdata/string-optional/program.p4 create mode 100644 p4_symbolic/z3_util_test.cc diff --git a/p4_symbolic/BUILD.bazel b/p4_symbolic/BUILD.bazel index 2fb1ac2d..cd5432f8 100644 --- a/p4_symbolic/BUILD.bazel +++ b/p4_symbolic/BUILD.bazel @@ -72,3 +72,12 @@ cc_library( "@com_google_absl//absl/strings", ], ) + +cc_test( + name = "z3_util_test", + srcs = ["z3_util_test.cc"], + deps = [ + ":z3_util", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/p4_symbolic/main.cc b/p4_symbolic/main.cc index 434f0c53..ccf635b1 100644 --- a/p4_symbolic/main.cc +++ b/p4_symbolic/main.cc @@ -42,6 +42,7 @@ ABSL_FLAG(std::string, entries, "", "if the input p4 program contains no (explicit) tables for which " "entries are needed."); ABSL_FLAG(std::string, debug, "", "Dump the SMT program for debugging"); +ABSL_FLAG(int, port_count, 2, "Number of used ports (numbered 0 to N-1)"); ABSL_FLAG(bool, hardcoded_parser, true, "Use the hardcoded parser during symbolic evaluation"); @@ -53,6 +54,7 @@ absl::Status ParseAndEvaluate() { const std::string &bmv2_path = absl::GetFlag(FLAGS_bmv2); const std::string &entries_path = absl::GetFlag(FLAGS_entries); const std::string &debug_path = absl::GetFlag(FLAGS_debug); + const int port_count = absl::GetFlag(FLAGS_port_count); bool hardcoded_parser = absl::GetFlag(FLAGS_hardcoded_parser); RET_CHECK(!p4info_path.empty()); @@ -63,11 +65,14 @@ absl::Status ParseAndEvaluate() { p4_symbolic::symbolic::Dataplane dataplane, p4_symbolic::ParseToIr(bmv2_path, p4info_path, entries_path)); + // Generate port list. + std::vector physical_ports(port_count); + for (int i = 0; i < port_count; i++) physical_ports[i] = i; + // Evaluate program symbolically. ASSIGN_OR_RETURN( const std::unique_ptr &solver_state, - p4_symbolic::symbolic::EvaluateP4Pipeline(dataplane, - std::vector{0, 1})); + p4_symbolic::symbolic::EvaluateP4Pipeline(dataplane, physical_ports)); // Add constraints for parser. if (hardcoded_parser) { ASSIGN_OR_RETURN( @@ -151,6 +156,14 @@ absl::Status ParseAndEvaluate() { "scalars.userMetadata.vrf_is_valid") << std::endl; } + // Custom metadata field defined in testdata/string-optional/program.p4 + if (packet_option.value().egress_headers.contains( + "scalars.userMetadata.string_field")) { + std::cout << "\tscalars.userMetadata.string_field = " + << packet_option.value().egress_headers.at( + "scalars.userMetadata.string_field") + << std::endl; + } } else { std::cout << "Cannot find solution!" << std::endl; } diff --git a/p4_symbolic/symbolic/BUILD.bazel b/p4_symbolic/symbolic/BUILD.bazel index ca48e2b2..608ae6ec 100644 --- a/p4_symbolic/symbolic/BUILD.bazel +++ b/p4_symbolic/symbolic/BUILD.bazel @@ -103,3 +103,26 @@ end_to_end_test( smt_golden_file = "expected/basic.smt2", table_entries = "//p4_symbolic/testdata:ipv4-routing/entries.pb.txt", ) + +end_to_end_test( + name = "string_optional_test", + output_golden_file = "expected/string_optional.txt", + p4_program = "//p4_symbolic/testdata:string-optional/program.p4", + port_count = 3, + smt_golden_file = "expected/string_optional.smt2", + table_entries = "//p4_symbolic/testdata:string-optional/entries.pb.txt", +) + +cc_test( + name = "values_test", + srcs = ["values_test.cc"], + deps = [ + ":symbolic", + "//gutil:status_matchers", + "//gutil:testing", + "//p4_pdpi:ir_cc_proto", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/p4_symbolic/symbolic/expected/string_optional.smt2 b/p4_symbolic/symbolic/expected/string_optional.smt2 new file mode 100644 index 00000000..af847f33 --- /dev/null +++ b/p4_symbolic/symbolic/expected/string_optional.smt2 @@ -0,0 +1,382 @@ +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let ((?x56 (ite (and (and true (and $x43 (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let (($x66 (and true $x65))) + (let ((?x76 (ite $x66 0 (ite (and true true) 1 (- 1))))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (and (and (not $x51) true) (= ?x76 (- 1)))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let ((?x56 (ite (and (and true (and $x43 (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let (($x66 (and true $x65))) + (let ((?x76 (ite $x66 0 (ite (and true true) 1 (- 1))))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (let (($x144 (and (not $x51) true))) + (and $x144 (= ?x76 0)))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let ((?x56 (ite (and (and true (and $x43 (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let (($x66 (and true $x65))) + (let ((?x76 (ite $x66 0 (ite (and true true) 1 (- 1))))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (and (and (not $x51) true) (= ?x76 1))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let (($x41 (and true (= standard_metadata.ingress_port (concat (_ bv0 7) (_ bv2 2)))))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) + (let (($x42 (and true $x32))) + (let ((?x62 (ite $x42 0 (ite (and true $x36) 1 (ite (and true $x41) 2 (- 1)))))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let ((?x56 (ite (and (and true (and (not $x32) (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true (not $x32)) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (and (and (not $x51) true) (= ?x62 (- 1))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let (($x41 (and true (= standard_metadata.ingress_port (concat (_ bv0 7) (_ bv2 2)))))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) + (let (($x42 (and true $x32))) + (let ((?x62 (ite $x42 0 (ite (and true $x36) 1 (ite (and true $x41) 2 (- 1)))))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let ((?x56 (ite (and (and true (and (not $x32) (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true (not $x32)) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (let (($x144 (and (not $x51) true))) + (and $x144 (= ?x62 0))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let (($x41 (and true (= standard_metadata.ingress_port (concat (_ bv0 7) (_ bv2 2)))))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) + (let (($x42 (and true $x32))) + (let ((?x62 (ite $x42 0 (ite (and true $x36) 1 (ite (and true $x41) 2 (- 1)))))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let ((?x56 (ite (and (and true (and (not $x32) (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true (not $x32)) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (and (and (not $x51) true) (= ?x62 1)))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let (($x41 (and true (= standard_metadata.ingress_port (concat (_ bv0 7) (_ bv2 2)))))) + (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) + (let (($x32 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) + (let (($x42 (and true $x32))) + (let ((?x62 (ite $x42 0 (ite (and true $x36) 1 (ite (and true $x41) 2 (- 1)))))) + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let ((?x56 (ite (and (and true (and (not $x32) (not $x36))) $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true (not $x32)) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (and (and (not $x51) true) (= ?x62 2)))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.string_field () (_ BitVec 9)) +(assert + (let (($x85 (= standard_metadata.ingress_port (_ bv2 9)))) + (let (($x80 (= standard_metadata.ingress_port (_ bv1 9)))) + (let (($x86 (or (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x80) $x85))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) $x86))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (or $x51 (or (or (or false (= ?x77 (_ bv0 9))) (= ?x77 (_ bv1 9))) (= ?x77 (_ bv2 9))))))))))))))))))))) +(assert + (let ((?x34 (concat (_ bv0 8) (_ bv1 1)))) + (let ((?x39 (concat (_ bv0 7) (_ bv2 2)))) + (let ((?x29 (concat (_ bv0 8) (_ bv0 1)))) + (let (($x41 (and true (= standard_metadata.ingress_port ?x39)))) + (let (($x32 (and true (= standard_metadata.ingress_port ?x29)))) + (let (($x43 (not $x32))) + (let (($x48 (and true (and $x43 (not (and true (= standard_metadata.ingress_port ?x34))))))) + (let ((?x56 (ite (and $x48 $x41) ?x29 (ite true ?x29 scalars.userMetadata.string_field)))) + (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) + (let (($x42 (and true $x32))) + (let ((?x63 (ite $x42 ?x39 (ite (and (and true $x43) $x36) ?x34 ?x56)))) + (let (($x65 (and true (= ?x63 ?x39)))) + (let ((?x75 (ite (and (and true (not $x65)) true) ?x34 standard_metadata.egress_spec))) + (let (($x66 (and true $x65))) + (let ((?x77 (ite $x66 ?x29 ?x75))) + (let (($x51 (= ?x77 (_ bv511 9)))) + (and (and (not $x51) true) (= (- 1) (- 1)))))))))))))))))))) +(check-sat) + diff --git a/p4_symbolic/symbolic/expected/string_optional.txt b/p4_symbolic/symbolic/expected/string_optional.txt new file mode 100644 index 00000000..a70df7f3 --- /dev/null +++ b/p4_symbolic/symbolic/expected/string_optional.txt @@ -0,0 +1,36 @@ +Finding packet for table MyIngress.optional_match and row -1 +Cannot find solution! + +Finding packet for table MyIngress.optional_match and row 0 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000000 + +Finding packet for table MyIngress.optional_match and row 1 + Dropped = 0 + standard_metadata.ingress_port = #b000000010 + standard_metadata.egress_spec = #b000000001 + +Finding packet for table MyIngress.set_field_table and row -1 +Cannot find solution! + +Finding packet for table MyIngress.set_field_table and row 0 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000000 + +Finding packet for table MyIngress.set_field_table and row 1 + Dropped = 0 + standard_metadata.ingress_port = #b000000001 + standard_metadata.egress_spec = #b000000001 + +Finding packet for table MyIngress.set_field_table and row 2 + Dropped = 0 + standard_metadata.ingress_port = #b000000010 + standard_metadata.egress_spec = #b000000001 + +Finding packet for table tbl_program92 and row -1 + Dropped = 0 + standard_metadata.ingress_port = #b000000010 + standard_metadata.egress_spec = #b000000001 + diff --git a/p4_symbolic/symbolic/expected/vrf.smt2 b/p4_symbolic/symbolic/expected/vrf.smt2 new file mode 100644 index 00000000..5142be5e --- /dev/null +++ b/p4_symbolic/symbolic/expected/vrf.smt2 @@ -0,0 +1,696 @@ +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let ((?x153 (ite (and $x84 $x95) 0 (ite (and $x84 $x101) 3 (ite (and $x84 $x107) 2 (- 1)))))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x108 (and $x84 $x90))) + (let ((?x67 (ite ipv4.$valid$ (ite $x82 (ite $x108 1 ?x153) (- 1)) (- 1)))) + (let (($x66 (ite ipv4.$valid$ (ite $x82 $x84 false) false))) + (let (($x122 (and $x84 (and (and (and (not $x90) (not $x95)) (not $x101)) (not $x107))))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 (and (and (not $x90) (not $x95)) (not $x101))) $x107))) + (let (($x115 (and (and $x84 (and (not $x90) (not $x95))) $x101))) + (let ((?x144 (ite $x115 ?x130 (ite $x119 ?x130 (ite $x122 (_ bv511 9) standard_metadata.egress_spec))))) + (let (($x111 (and (and $x84 (not $x90)) $x95))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) ?x144)))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x66) (= ?x67 (- 1)))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let ((?x153 (ite (and $x84 $x95) 0 (ite (and $x84 $x101) 3 (ite (and $x84 $x107) 2 (- 1)))))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x108 (and $x84 $x90))) + (let ((?x67 (ite ipv4.$valid$ (ite $x82 (ite $x108 1 ?x153) (- 1)) (- 1)))) + (let (($x66 (ite ipv4.$valid$ (ite $x82 $x84 false) false))) + (let (($x122 (and $x84 (and (and (and (not $x90) (not $x95)) (not $x101)) (not $x107))))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 (and (and (not $x90) (not $x95)) (not $x101))) $x107))) + (let (($x115 (and (and $x84 (and (not $x90) (not $x95))) $x101))) + (let ((?x144 (ite $x115 ?x130 (ite $x119 ?x130 (ite $x122 (_ bv511 9) standard_metadata.egress_spec))))) + (let (($x111 (and (and $x84 (not $x90)) $x95))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) ?x144)))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (let (($x341 (and (not $x49) $x66))) + (and $x341 (= ?x67 0)))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let ((?x153 (ite (and $x84 $x95) 0 (ite (and $x84 $x101) 3 (ite (and $x84 $x107) 2 (- 1)))))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x108 (and $x84 $x90))) + (let ((?x67 (ite ipv4.$valid$ (ite $x82 (ite $x108 1 ?x153) (- 1)) (- 1)))) + (let (($x66 (ite ipv4.$valid$ (ite $x82 $x84 false) false))) + (let (($x122 (and $x84 (and (and (and (not $x90) (not $x95)) (not $x101)) (not $x107))))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 (and (and (not $x90) (not $x95)) (not $x101))) $x107))) + (let (($x115 (and (and $x84 (and (not $x90) (not $x95))) $x101))) + (let ((?x144 (ite $x115 ?x130 (ite $x119 ?x130 (ite $x122 (_ bv511 9) standard_metadata.egress_spec))))) + (let (($x111 (and (and $x84 (not $x90)) $x95))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) ?x144)))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x66) (= ?x67 1))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let ((?x153 (ite (and $x84 $x95) 0 (ite (and $x84 $x101) 3 (ite (and $x84 $x107) 2 (- 1)))))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x108 (and $x84 $x90))) + (let ((?x67 (ite ipv4.$valid$ (ite $x82 (ite $x108 1 ?x153) (- 1)) (- 1)))) + (let (($x66 (ite ipv4.$valid$ (ite $x82 $x84 false) false))) + (let (($x122 (and $x84 (and (and (and (not $x90) (not $x95)) (not $x101)) (not $x107))))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 (and (and (not $x90) (not $x95)) (not $x101))) $x107))) + (let (($x115 (and (and $x84 (and (not $x90) (not $x95))) $x101))) + (let ((?x144 (ite $x115 ?x130 (ite $x119 ?x130 (ite $x122 (_ bv511 9) standard_metadata.egress_spec))))) + (let (($x111 (and (and $x84 (not $x90)) $x95))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) ?x144)))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x66) (= ?x67 2))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let ((?x153 (ite (and $x84 $x95) 0 (ite (and $x84 $x101) 3 (ite (and $x84 $x107) 2 (- 1)))))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x108 (and $x84 $x90))) + (let ((?x67 (ite ipv4.$valid$ (ite $x82 (ite $x108 1 ?x153) (- 1)) (- 1)))) + (let (($x66 (ite ipv4.$valid$ (ite $x82 $x84 false) false))) + (let (($x122 (and $x84 (and (and (and (not $x90) (not $x95)) (not $x101)) (not $x107))))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 (and (and (not $x90) (not $x95)) (not $x101))) $x107))) + (let (($x115 (and (and $x84 (and (not $x90) (not $x95))) $x101))) + (let ((?x144 (ite $x115 ?x130 (ite $x119 ?x130 (ite $x122 (_ bv511 9) standard_metadata.egress_spec))))) + (let (($x111 (and (and $x84 (not $x90)) $x95))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) ?x144)))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x66) (= ?x67 3))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x83 (ite ipv4.$valid$ (ite $x62 0 (ite (and $x50 $x61) 1 (- 1))) (- 1)))) + (let (($x68 (ite ipv4.$valid$ $x50 false))) + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let ((?x80 (ite $x62 ?x79 (ite (and (and $x50 (not $x56)) $x61) ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let (($x65 (and (and $x50 (not $x56)) $x61))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x68) (= ?x83 (- 1))))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x83 (ite ipv4.$valid$ (ite $x62 0 (ite (and $x50 $x61) 1 (- 1))) (- 1)))) + (let (($x68 (ite ipv4.$valid$ $x50 false))) + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let ((?x80 (ite $x62 ?x79 (ite (and (and $x50 (not $x56)) $x61) ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let (($x65 (and (and $x50 (not $x56)) $x61))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x68) (= ?x83 0)))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x83 (ite ipv4.$valid$ (ite $x62 0 (ite (and $x50 $x61) 1 (- 1))) (- 1)))) + (let (($x68 (ite ipv4.$valid$ $x50 false))) + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let ((?x80 (ite $x62 ?x79 (ite (and (and $x50 (not $x56)) $x61) ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let (($x65 (and (and $x50 (not $x56)) $x61))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) $x68) (= ?x83 1)))))))))))))))))))))))))))))))))) +(check-sat) + +; +(set-info :status unknown) +(declare-fun standard_metadata.ingress_port () (_ BitVec 9)) +(declare-fun standard_metadata.egress_spec () (_ BitVec 9)) +(declare-fun scalars.userMetadata.vrf () (_ BitVec 10)) +(declare-fun ipv4.srcAddr () (_ BitVec 32)) +(declare-fun ipv4.$valid$ () Bool) +(declare-fun ipv4.dstAddr () (_ BitVec 32)) +(declare-fun scalars.userMetadata.vrf_is_valid () (_ BitVec 1)) +(assert + (let (($x175 (= standard_metadata.ingress_port (_ bv1 9)))) + (and (and (distinct standard_metadata.ingress_port (_ bv511 9)) true) (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x175)))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (or $x49 (or (or false (= ?x164 (_ bv0 9))) (= ?x164 (_ bv1 9)))))))))))))))))))))))))))))))))) +(assert + (let ((?x73 (concat (_ bv0 9) (_ bv0 1)))) + (let (($x61 (and true (= (bvand ipv4.srcAddr (_ bv555813129 32)) (_ bv555810816 32))))) + (let (($x50 (and true ipv4.$valid$))) + (let (($x65 (and (and $x50 (not (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) $x61))) + (let ((?x79 (concat (_ bv0 9) (_ bv1 1)))) + (let (($x56 (and true (= (bvand ipv4.srcAddr (_ bv153159945 32)) (_ bv2162944 32))))) + (let (($x62 (and $x50 $x56))) + (let ((?x80 (ite $x62 ?x79 (ite $x65 ?x73 scalars.userMetadata.vrf)))) + (let (($x89 (= ?x80 ?x73))) + (let (($x107 (and (and true (= ((_ extract 31 24) ipv4.dstAddr) ((_ extract 31 24) (_ bv167772160 32)))) $x89))) + (let (($x101 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv336855040 32)))) (= ?x80 ?x79)))) + (let (($x95 (and (and true (= ((_ extract 31 16) ipv4.dstAddr) ((_ extract 31 16) (_ bv168427520 32)))) $x89))) + (let (($x90 (and (and true (= ipv4.dstAddr (_ bv168427520 32))) $x89))) + (let (($x109 (not $x90))) + (let (($x113 (and $x109 (not $x95)))) + (let (($x117 (and $x113 (not $x101)))) + (let ((?x75 (ite true (_ bv1 1) (_ bv0 1)))) + (let ((?x76 (ite $x65 ?x75 (ite true (ite false (_ bv1 1) (_ bv0 1)) scalars.userMetadata.vrf_is_valid)))) + (let ((?x81 (ite $x62 ?x75 ?x76))) + (let (($x82 (bvuge ?x81 (_ bv1 1)))) + (let (($x84 (and $x50 $x82))) + (let ((?x124 (ite (and $x84 (and $x117 (not $x107))) (_ bv511 9) standard_metadata.egress_spec))) + (let ((?x130 (concat (_ bv0 8) (_ bv1 1)))) + (let (($x119 (and (and $x84 $x117) $x107))) + (let (($x115 (and (and $x84 $x113) $x101))) + (let (($x111 (and (and $x84 $x109) $x95))) + (let (($x108 (and $x84 $x90))) + (let ((?x164 (ite $x108 ?x130 (ite $x111 (concat (_ bv0 8) (_ bv0 1)) (ite $x115 ?x130 (ite $x119 ?x130 ?x124)))))) + (let (($x49 (= ?x164 (_ bv511 9)))) + (and (and (not $x49) true) (= (- 1) (- 1))))))))))))))))))))))))))))))))) +(check-sat) + diff --git a/p4_symbolic/symbolic/expected/vrf.txt b/p4_symbolic/symbolic/expected/vrf.txt new file mode 100644 index 00000000..355f0642 --- /dev/null +++ b/p4_symbolic/symbolic/expected/vrf.txt @@ -0,0 +1,83 @@ +Finding packet for table packet_ingress.ipv4_lpm_table and row -1 +Cannot find solution! + +Finding packet for table packet_ingress.ipv4_lpm_table and row 0 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000000 + ipv4.srcAddr = #x29210000 + ipv4.dstAddr = #x0a0a0002 + ethernet.dstAddr = #x000000000000 + scalars.userMetadata.vrf = VRF1 + scalars.userMetadata.vrf_is_valid = #b1 + +Finding packet for table packet_ingress.ipv4_lpm_table and row 1 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x29210000 + ipv4.dstAddr = #x0a0a0000 + ethernet.dstAddr = #x000000000000 + scalars.userMetadata.vrf = VRF1 + scalars.userMetadata.vrf_is_valid = #b1 + +Finding packet for table packet_ingress.ipv4_lpm_table and row 2 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x29210000 + ipv4.dstAddr = #x0a1a0000 + ethernet.dstAddr = #x00000000000a + scalars.userMetadata.vrf = VRF1 + scalars.userMetadata.vrf_is_valid = #b1 + +Finding packet for table packet_ingress.ipv4_lpm_table and row 3 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x00210100 + ipv4.dstAddr = #x14140000 + ethernet.dstAddr = #x160000000016 + scalars.userMetadata.vrf = VRF2 + scalars.userMetadata.vrf_is_valid = #b1 + +Finding packet for table packet_ingress.set_vrf_table and row -1 + Dropped = 0 + standard_metadata.ingress_port = #b000000000 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x00000909 + ipv4.dstAddr = #x00000000 + ethernet.dstAddr = #x000000000000 + scalars.userMetadata.vrf = VRF1 + scalars.userMetadata.vrf_is_valid = #b0 + +Finding packet for table packet_ingress.set_vrf_table and row 0 + Dropped = 0 + standard_metadata.ingress_port = #b000000001 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x00210100 + ipv4.dstAddr = #x14140000 + ethernet.dstAddr = #x160000000016 + scalars.userMetadata.vrf = VRF2 + scalars.userMetadata.vrf_is_valid = #b1 + +Finding packet for table packet_ingress.set_vrf_table and row 1 + Dropped = 0 + standard_metadata.ingress_port = #b000000001 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x21210000 + ipv4.dstAddr = #x0a0a0000 + ethernet.dstAddr = #x000000000000 + scalars.userMetadata.vrf = VRF1 + scalars.userMetadata.vrf_is_valid = #b1 + +Finding packet for table tbl_vrf133 and row -1 + Dropped = 0 + standard_metadata.ingress_port = #b000000001 + standard_metadata.egress_spec = #b000000001 + ipv4.srcAddr = #x21210000 + ipv4.dstAddr = #x0a0a0000 + ethernet.dstAddr = #x000000000000 + scalars.userMetadata.vrf = VRF1 + scalars.userMetadata.vrf_is_valid = #b1 + diff --git a/p4_symbolic/symbolic/table.cc b/p4_symbolic/symbolic/table.cc index dfbc0ee8..effe5f8d 100644 --- a/p4_symbolic/symbolic/table.cc +++ b/p4_symbolic/symbolic/table.cc @@ -205,8 +205,15 @@ absl::StatusOr EvaluateSingleMatch( case p4::config::v1::MatchField::OPTIONAL: { if (match.match_value_case() != pdpi::IrMatch::kOptional) return mismatch; + // According to the P4Runtime spec, "for don't care matches, the P4Runtime + // client must omit the field's entire FieldMatch entry when building the + // match repeated field of the TableEntry message". Therefore, if the + // match value is present for an optional match type, it must be a + // concrete value. ASSIGN_OR_RETURN(z3::expr value_expression, - values::FormatBmv2Value(match.optional().value())); + values::FormatP4RTValue( + field_name, match_definition.type_name().name(), + match.optional().value(), translator)); return operators::Eq(field_expression, value_expression); } diff --git a/p4_symbolic/symbolic/test.bzl b/p4_symbolic/symbolic/test.bzl index eeb3c13d..8ae621e1 100644 --- a/p4_symbolic/symbolic/test.bzl +++ b/p4_symbolic/symbolic/test.bzl @@ -31,6 +31,7 @@ def end_to_end_test( output_golden_file, smt_golden_file, table_entries = None, + port_count = 2, p4_deps = []): """Macro that defines our end to end tests. Given a p4 program, this macro runs our main binary @@ -103,6 +104,7 @@ def end_to_end_test( ("--p4info=$(location %s)" % p4info_file), ("--entries=$(location %s)" % table_entries if table_entries else ""), ("--debug=$(location %s)" % smt_output_file), + ("--port_count=%d" % port_count), ("&> $(location %s)" % test_output_file), ]), ) diff --git a/p4_symbolic/symbolic/values.cc b/p4_symbolic/symbolic/values.cc index 8dfbfe3c..4056e899 100644 --- a/p4_symbolic/symbolic/values.cc +++ b/p4_symbolic/symbolic/values.cc @@ -56,47 +56,6 @@ unsigned int FindBitsize(uint64_t value) { return (bitsize > 1 ? bitsize : 1); // At least 1 bit. } -// Turns the given z3 extracted value (as a string) to a uint64_t. -// Z3 returns an extracted value as either a binary, hex, or int strings -// dependening on the size of the value and the formatting flags it is -// initialized with. -uint64_t StringToInt(std::string value) { - static std::unordered_map hex_to_bin = { - {'0', "0000"}, {'1', "0001"}, {'2', "0010"}, {'3', "0011"}, - {'4', "0100"}, {'5', "0101"}, {'6', "0110"}, {'7', "0111"}, - {'8', "1000"}, {'9', "1001"}, {'a', "1010"}, {'b', "1011"}, - {'c', "1100"}, {'d', "1101"}, {'e', "1110"}, {'f', "1111"}}; - - bool value_is_hex = absl::StartsWith(value, "#x"); - bool value_is_binary = absl::StartsWith(value, "#b"); - - // Boolean or integer values. - if (!value_is_hex && !value_is_binary) { - if (value == "true") { - return 1; - } else if (value == "false") { - return 0; - } else { - return std::stoull(value); - } - } - - // Make sure value is a binary string without leading base prefix. - std::string binary; - if (value_is_hex) { - // Turn hex to binary. - absl::string_view stripped_value = absl::StripPrefix(value, "#x"); - for (char c : stripped_value) { - absl::StrAppend(&binary, hex_to_bin.at(c)); - } - } else if (value_is_binary) { - // Strip leading #b for binary strings. - binary = absl::StripPrefix(value, "#b"); - } - - return std::stoull(binary); -} - } // namespace absl::StatusOr ParseIrValue(const std::string &value) { @@ -180,7 +139,7 @@ absl::StatusOr TranslateValueToP4RT( translator.p4runtime_translation_allocators.at(field_type_name); // Turn the value from a string to an int. - uint64_t int_value = StringToInt(value); + uint64_t int_value = Z3ValueStringToInt(value); return allocator.IdToString(int_value); } @@ -191,7 +150,6 @@ uint64_t IdAllocator::AllocateId(const std::string &string_value) { if (this->string_to_id_map_.count(string_value)) { return this->string_to_id_map_.at(string_value); } - // Allocate new bitvector value and store it in mapping. uint64_t int_value = this->counter_++; this->string_to_id_map_.insert({string_value, int_value}); diff --git a/p4_symbolic/symbolic/values_test.cc b/p4_symbolic/symbolic/values_test.cc new file mode 100644 index 00000000..14d4a604 --- /dev/null +++ b/p4_symbolic/symbolic/values_test.cc @@ -0,0 +1,41 @@ +#include "p4_symbolic/symbolic/values.h" + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_cat.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gutil/status_matchers.h" +#include "gutil/testing.h" +#include "p4_pdpi/ir.pb.h" + +namespace p4_symbolic::symbolic::values { +namespace { + +TEST(TranslateValueToP4RT, ReverseTranslatedValuesAreEqualToTheOriginalOnes) { + constexpr int kNumIds = 256; + const std::string kFieldName = "dummy_field_name"; + const std::string kFieldType = "dummy_field_type"; + + // Prepare the translator and expected values. + P4RuntimeTranslator translator; + absl::flat_hash_map z3_value_to_id; + for (int i = 0; i < kNumIds; i++) { + const std::string id = absl::StrCat("id-", i); + pdpi::IrValue ir_value; + ir_value.set_str(id); + ASSERT_OK_AND_ASSIGN(z3::expr expr, FormatP4RTValue(kFieldName, kFieldType, + ir_value, &translator)); + z3_value_to_id[expr.to_string()] = id; + } + + // Check that the reverse translation is as expected. + for (const auto& [z3_value, expected_id] : z3_value_to_id) { + ASSERT_OK_AND_ASSIGN( + const std::string actual_id, + TranslateValueToP4RT(kFieldName, z3_value, translator)); + ASSERT_THAT(actual_id, testing::Eq(expected_id)); + } +} + +} // namespace +} // namespace p4_symbolic::symbolic::values diff --git a/p4_symbolic/testdata/string-optional/entries.pb.txt b/p4_symbolic/testdata/string-optional/entries.pb.txt new file mode 100644 index 00000000..13d1f278 --- /dev/null +++ b/p4_symbolic/testdata/string-optional/entries.pb.txt @@ -0,0 +1,111 @@ +updates { + type: INSERT + entity { + table_entry { + table_id: 44279369 + match { + field_id: 1 + exact { + value: "\00" + } + } + action { + action { + action_id: 21593668 + params { + param_id: 1 + value: "VALUE-0" + } + } + } + } + } +} +updates { + type: INSERT + entity { + table_entry { + table_id: 44279369 + match { + field_id: 1 + exact { + value: "\01" + } + } + action { + action { + action_id: 21593668 + params { + param_id: 1 + value: "VALUE-1" + } + } + } + } + } +} +updates { + type: INSERT + entity { + table_entry { + table_id: 44279369 + match { + field_id: 1 + exact { + value: "\02" + } + } + action { + action { + action_id: 21593668 + params { + param_id: 1 + value: "VALUE-2" + } + } + } + } + } +} +updates { + type: INSERT + entity { + table_entry { + table_id: 47867719 + match { + field_id: 1 + optional { + value: "VALUE-0" + } + } + action { + action { + action_id: 21735938 + params { + param_id: 1 + value: "\00" + } + } + } + priority: 2 + } + } +} +updates { + type: INSERT + entity { + table_entry { + table_id: 47867719 + action { + action { + action_id: 21735938 + params { + param_id: 1 + value: "\01" + } + } + } + priority: 1 + } + } +} diff --git a/p4_symbolic/testdata/string-optional/program.p4 b/p4_symbolic/testdata/string-optional/program.p4 new file mode 100644 index 00000000..1f7f65b0 --- /dev/null +++ b/p4_symbolic/testdata/string-optional/program.p4 @@ -0,0 +1,106 @@ +// Copyright 2024 Google LLC +// +// 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. + +/* -*- P4_16 -*- */ +#include +#include + +@p4runtime_translation("", string) +type bit<9> string_field_t; + +struct metadata { + string_field_t string_field; +} +struct headers {} + +parser UselessParser(packet_in packet, + out headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + state start { + transition accept; + } +} + +control UselessChecksum(inout headers hdr, inout metadata meta) { + apply { } +} + +control UselessEgress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + apply { } +} + +control UselessComputeChecksum(inout headers hdr, inout metadata meta) { + apply { } +} + +control UselessDeparser(packet_out packet, in headers hdr) { + apply { } +} + +// Forwarding Code +control MyIngress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + + action set_egress_spec(bit<9> port) { + standard_metadata.egress_spec = port; + } + + action set_field(string_field_t f) { + meta.string_field = f; + } + + table set_field_table { + key = { + standard_metadata.ingress_port : exact; + } + actions = { + @proto_id(1) set_field; + @proto_id(2) NoAction; + } + size = 1024; + default_action = NoAction; + } + + table optional_match { + key = { + meta.string_field: optional; + } + actions = { + @proto_id(1) set_egress_spec; + @proto_id(2) NoAction; + } + size = 1024; + default_action = NoAction; + } + + apply { + meta.string_field = (string_field_t)0; + set_field_table.apply(); + optional_match.apply(); + } +} + +// Switch +V1Switch( + UselessParser(), + UselessChecksum(), + MyIngress(), + UselessEgress(), + UselessComputeChecksum(), + UselessDeparser() +) main; diff --git a/p4_symbolic/testdata/vrf-routing/vrf.p4 b/p4_symbolic/testdata/vrf-routing/vrf.p4 index 10d99600..cb2770e7 100644 --- a/p4_symbolic/testdata/vrf-routing/vrf.p4 +++ b/p4_symbolic/testdata/vrf-routing/vrf.p4 @@ -22,7 +22,7 @@ typedef bit<9> egress_spec_t; typedef bit<48> mac_addr_t; typedef bit<32> ipv4_addr_t; -@p4runtime_translation("", string) +@p4runtime_translation("", string) type bit<10> vrf_t; header ethernet_t { diff --git a/p4_symbolic/z3_util.cc b/p4_symbolic/z3_util.cc index b290e893..391627a7 100644 --- a/p4_symbolic/z3_util.cc +++ b/p4_symbolic/z3_util.cc @@ -47,4 +47,23 @@ absl::StatusOr HexStringToZ3Bitvector(const std::string& hex_string, return Z3Context().bv_val(decimal.c_str(), *bitwidth); } +uint64_t Z3ValueStringToInt(const std::string& value) { + if (absl::StartsWith(value, "#x")) { + return std::stoull(value.substr(2), /*idx=*/nullptr, /*base=*/16); + } + if (absl::StartsWith(value, "#b")) { + return std::stoull(value.substr(2), /*idx=*/nullptr, /*base=*/2); + } + + // Boolean or integer values. + if (value == "true") { + return 1; + } else if (value == "false") { + return 0; + } else { + // Must be a base 10 number. + return std::stoull(value, /*idx=*/nullptr, /*base=*/10); + } +} + } // namespace p4_symbolic diff --git a/p4_symbolic/z3_util.h b/p4_symbolic/z3_util.h index f993babc..16a2179b 100644 --- a/p4_symbolic/z3_util.h +++ b/p4_symbolic/z3_util.h @@ -34,6 +34,16 @@ absl::StatusOr HexStringToZ3Bitvector( const std::string& hex_string, absl::optional bitwidth = absl::nullopt); +// -- Misc. -------------------------------------------------------------------- + +// Turns the given z3 extracted value (as a string) to a uint64_t. +// Z3 returns an extracted value as either a binary, hex, or decimal strings +// dependening on the size of the value and the formatting flags it is +// initialized with. +// Note: This function assumes that the input is well-formatted and the result +// fits in uint64_t (otherwise an exception will be thrown). +uint64_t Z3ValueStringToInt(const std::string& value); + // == END OF PUBLIC INTERFACE ================================================== template diff --git a/p4_symbolic/z3_util_test.cc b/p4_symbolic/z3_util_test.cc new file mode 100644 index 00000000..88de5194 --- /dev/null +++ b/p4_symbolic/z3_util_test.cc @@ -0,0 +1,31 @@ +#include "p4_symbolic/z3_util.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace p4_symbolic { +namespace { + +TEST(Z3ValueStringToInt, WorksForBoolStrings) { + ASSERT_EQ(Z3ValueStringToInt("true"), 1); + ASSERT_EQ(Z3ValueStringToInt("false"), 0); +} + +TEST(Z3ValueStringToInt, WorksForBinaryStrings) { + ASSERT_EQ(Z3ValueStringToInt("#b10"), 2); + ASSERT_EQ(Z3ValueStringToInt("#b01"), 1); +} + +TEST(Z3ValueStringToInt, WorksForHexStrings) { + ASSERT_EQ(Z3ValueStringToInt("#x10"), 16); + ASSERT_EQ(Z3ValueStringToInt("#xff"), 255); + ASSERT_EQ(Z3ValueStringToInt("#x9"), 9); +} + +TEST(Z3ValueStringToInt, WorksForDecimalStrings) { + ASSERT_EQ(Z3ValueStringToInt("10"), 10); + ASSERT_EQ(Z3ValueStringToInt("900"), 900); +} + +} // namespace +} // namespace p4_symbolic From 815cd97ae7988ab3eb333686e218ea1e8f66d0f7 Mon Sep 17 00:00:00 2001 From: divyagayathri-hcl <159437886+divyagayathri-hcl@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:30:52 +0530 Subject: [PATCH 3/7] [SAIP4] Remove yellow actions and add annotations specifying meter type to direct meters, Add multicast reference annotations to p4 programs, Increase the guaranteed size of QoS and mirror & redirect ACL tables, Add match on ipv6_dst in mirror_and_redirect to middleblock as well, Add entry restrictions of mirror_and_redirect to middleblock as well & Add dst_ipv4 match to mirror_and_redirect. (#624) Co-authored-by: kishanps --- sai_p4/fixed/packet_rewrites.p4 | 6 +- sai_p4/fixed/routing.p4 | 3 +- sai_p4/instantiations/google/acl_ingress.p4 | 53 +++++------ .../google/fabric_border_router.p4info.pb.txt | 7 +- .../google/middleblock.p4info.pb.txt | 46 ++++++++-- .../google/minimum_guaranteed_sizes.p4 | 6 ++ sai_p4/instantiations/google/sai_pd.proto | 12 ++- .../instantiations/google/tor.p4info.pb.txt | 50 +++++----- .../google/unioned_p4info.pb.txt | 92 ++++++++++--------- 9 files changed, 161 insertions(+), 114 deletions(-) diff --git a/sai_p4/fixed/packet_rewrites.p4 b/sai_p4/fixed/packet_rewrites.p4 index 2bf3dec3..d8dd277b 100644 --- a/sai_p4/fixed/packet_rewrites.p4 +++ b/sai_p4/fixed/packet_rewrites.p4 @@ -64,12 +64,10 @@ control multicast_rewrites(inout local_metadata_t local_metadata, table multicast_router_interface_table { key = { multicast_replica_port : exact - // TODO: Add this once supported by PDPI and its customers. - // @referenced_by(multicast_group_table, replica.port) + @referenced_by(builtin::multicast_group_table, replica.port) @id(1); multicast_replica_instance : exact - // TODO: Add this once supported by PDPI and its customers. - // @referenced_by(multicast_group_table, replica.instance) + @referenced_by(builtin::multicast_group_table, replica.instance) @id(2); } actions = { diff --git a/sai_p4/fixed/routing.p4 b/sai_p4/fixed/routing.p4 index ba04c247..4acae4af 100644 --- a/sai_p4/fixed/routing.p4 +++ b/sai_p4/fixed/routing.p4 @@ -175,8 +175,7 @@ control routing_lookup(in headers_t headers, ") action set_multicast_group_id( @id(1) - // TODO: Add this once supported by PDPI and its customers. - // @refers_to(multicast_group_table, multicast_group_id) + @refers_to(builtin::multicast_group_table, multicast_group_id) multicast_group_id_t multicast_group_id) { standard_metadata.mcast_grp = multicast_group_id; } diff --git a/sai_p4/instantiations/google/acl_ingress.p4 b/sai_p4/instantiations/google/acl_ingress.p4 index f41434f5..91ff70ba 100644 --- a/sai_p4/instantiations/google/acl_ingress.p4 +++ b/sai_p4/instantiations/google/acl_ingress.p4 @@ -24,9 +24,11 @@ control acl_ingress(in headers_t headers, bool cancel_copy = false; @id(ACL_INGRESS_METER_ID) + @mode(single_rate_two_color) direct_meter(MeterType.bytes) acl_ingress_meter; @id(ACL_INGRESS_QOS_METER_ID) + @mode(single_rate_two_color) direct_meter(MeterType.bytes) acl_ingress_qos_meter; @id(ACL_INGRESS_COUNTER_ID) @@ -53,7 +55,6 @@ control acl_ingress(in headers_t headers, } #else @sai_action(SAI_PACKET_ACTION_COPY, SAI_PACKET_COLOR_GREEN) - @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_YELLOW) @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_RED) action acl_copy(@sai_action_param(QOS_QUEUE) @id(1) qos_queue_t qos_queue) { acl_ingress_counter.count(); @@ -73,7 +74,6 @@ control acl_ingress(in headers_t headers, @sai_action(SAI_PACKET_ACTION_TRAP) #else @sai_action(SAI_PACKET_ACTION_TRAP, SAI_PACKET_COLOR_GREEN) - @sai_action(SAI_PACKET_ACTION_DROP, SAI_PACKET_COLOR_YELLOW) @sai_action(SAI_PACKET_ACTION_DROP, SAI_PACKET_COLOR_RED) #endif action acl_trap(@sai_action_param(QOS_QUEUE) @id(1) qos_queue_t qos_queue) { @@ -94,7 +94,6 @@ control acl_ingress(in headers_t headers, } #else @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_GREEN) - @sai_action(SAI_PACKET_ACTION_DROP, SAI_PACKET_COLOR_YELLOW) @sai_action(SAI_PACKET_ACTION_DROP, SAI_PACKET_COLOR_RED) action acl_forward() { acl_ingress_meter.read(local_metadata.color); @@ -124,7 +123,6 @@ control acl_ingress(in headers_t headers, @id(ACL_INGRESS_SET_QOS_QUEUE_AND_CANCEL_COPY_ABOVE_RATE_LIMIT_ACTION_ID) @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_GREEN) - @sai_action(SAI_PACKET_ACTION_COPY_CANCEL, SAI_PACKET_COLOR_YELLOW) @sai_action(SAI_PACKET_ACTION_COPY_CANCEL, SAI_PACKET_COLOR_RED) // TODO: Rename qos queue to cpu queue, as per action below. action set_qos_queue_and_cancel_copy_above_rate_limit( @@ -143,7 +141,6 @@ control acl_ingress(in headers_t headers, // set depending on packet color. @id(ACL_INGRESS_SET_CPU_AND_MULTICAST_QUEUES_AND_DENY_ABOVE_RATE_LIMIT_ACTION_ID) @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_GREEN) - @sai_action(SAI_PACKET_ACTION_DENY, SAI_PACKET_COLOR_YELLOW) @sai_action(SAI_PACKET_ACTION_DENY, SAI_PACKET_COLOR_RED) // TODO: Remove @unsupported annotation. @unsupported @@ -151,9 +148,8 @@ control acl_ingress(in headers_t headers, @id(1) @sai_action_param(QOS_QUEUE) qos_queue_t cpu_queue, @id(2) @sai_action_param(MULTICAST_QOS_QUEUE, SAI_PACKET_COLOR_GREEN) qos_queue_t green_multicast_queue, - @id(3) @sai_action_param(MULTICAST_QOS_QUEUE, SAI_PACKET_COLOR_YELLOW) - @sai_action_param(MULTICAST_QOS_QUEUE, SAI_PACKET_COLOR_RED) - qos_queue_t red_and_yellow_multicast_queue) { + @id(3) @sai_action_param(MULTICAST_QOS_QUEUE, SAI_PACKET_COLOR_RED) + qos_queue_t red_multicast_queue) { acl_ingress_qos_meter.read(local_metadata.color); // We model the behavior for GREEN packes only here. // TODO: Branch on color and model behavior for all colors. @@ -163,7 +159,6 @@ control acl_ingress(in headers_t headers, // they are not copied to the CPU. @id(ACL_INGRESS_SET_CPU_QUEUE_AND_DENY_ABOVE_RATE_LIMIT_ACTION_ID) @sai_action(SAI_PACKET_ACTION_FORWARD, SAI_PACKET_COLOR_GREEN) - @sai_action(SAI_PACKET_ACTION_DENY, SAI_PACKET_COLOR_YELLOW) @sai_action(SAI_PACKET_ACTION_DENY, SAI_PACKET_COLOR_RED) action set_cpu_queue_and_deny_above_rate_limit( @id(1) @sai_action_param(QOS_QUEUE) qos_queue_t cpu_queue) { @@ -391,7 +386,7 @@ control acl_ingress(in headers_t headers, const default_action = NoAction; meters = acl_ingress_qos_meter; counters = acl_ingress_qos_counter; - size = ACL_INGRESS_TABLE_MINIMUM_GUARANTEED_SIZE; + size = ACL_INGRESS_QOS_TABLE_MINIMUM_GUARANTEED_SIZE; } @p4runtime_role(P4RUNTIME_ROLE_SDN_CONTROLLER) @@ -471,10 +466,10 @@ control acl_ingress(in headers_t headers, @id(ACL_INGRESS_MIRROR_AND_REDIRECT_TABLE_ID) @sai_acl(INGRESS) @p4runtime_role(P4RUNTIME_ROLE_SDN_CONTROLLER) -#if defined(SAI_INSTANTIATION_TOR) @entry_restriction(" // Only allow IP field matches for IP packets. - is_ipv6::mask != 0 -> is_ipv6 == 1; + dst_ip::mask != 0 -> is_ipv4 == 1; + dst_ipv6::mask != 0 -> is_ipv6 == 1; // Forbid illegal combinations of IP_TYPE fields. is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0); is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0); @@ -483,7 +478,6 @@ control acl_ingress(in headers_t headers, is_ipv4::mask != 0 -> (is_ipv4 == 1); is_ipv6::mask != 0 -> (is_ipv6 == 1); ") -#endif table acl_ingress_mirror_and_redirect_table { key = { #if defined(SAI_INSTANTIATION_TOR) @@ -492,6 +486,17 @@ control acl_ingress(in headers_t headers, @sai_field(SAI_ACL_TABLE_ATTR_FIELD_IN_PORT) @id(1); + local_metadata.acl_metadata : ternary + @name("acl_metadata") + @sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META) + @id(6); + + local_metadata.vlan_id : ternary + @name("vlan_id") + @sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID) + @id(7); +#endif + headers.ipv4.isValid() || headers.ipv6.isValid() : optional @name("is_ip") @sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE/IP) @@ -507,6 +512,12 @@ control acl_ingress(in headers_t headers, @sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE/IPV6ANY) @id(4); + headers.ipv4.dst_addr : ternary + @name("dst_ip") + @sai_field(SAI_ACL_TABLE_ATTR_FIELD_DST_IP) + @format(IPV4_ADDRESS) + @id(10); + headers.ipv6.dst_addr[127:64] : ternary @name("dst_ipv6") @composite_field( @@ -515,22 +526,6 @@ control acl_ingress(in headers_t headers, @format(IPV6_ADDRESS) @id(5); - local_metadata.acl_metadata : ternary - @name("acl_metadata") - @sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META) - @id(6); -#endif - - // This field is technically only needed on ToR and only included - // for middleblock because at least 1 match field without `@unsupported` - // annotation is required. - // TODO: Make this field TOR-only once the - // middleblock match fields are no longer `@unsupported`. - local_metadata.vlan_id : ternary - @name("vlan_id") - @sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID) - @id(7); - local_metadata.vrf_id : optional @name("vrf_id") @sai_field(SAI_ACL_TABLE_ATTR_FIELD_VRF_ID) diff --git a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt index da20e1d3..a371bcc9 100644 --- a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt +++ b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt @@ -857,6 +857,7 @@ tables { match_fields { id: 1 name: "multicast_replica_port" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . port)" match_type: EXACT type_name { name: "port_id_t" @@ -865,6 +866,7 @@ tables { match_fields { id: 2 name: "multicast_replica_instance" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . instance)" bitwidth: 16 match_type: EXACT } @@ -1127,6 +1129,7 @@ actions { params { id: 1 name: "multicast_group_id" + annotations: "@refers_to(builtin : : multicast_group_table , multicast_group_id)" bitwidth: 16 } } @@ -1136,7 +1139,6 @@ actions { name: "ingress.acl_ingress.acl_copy" alias: "acl_copy" annotations: "@sai_action(SAI_PACKET_ACTION_COPY , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_RED)" } params { @@ -1154,7 +1156,6 @@ actions { name: "ingress.acl_ingress.acl_trap" alias: "acl_trap" annotations: "@sai_action(SAI_PACKET_ACTION_TRAP , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_RED)" } params { @@ -1172,7 +1173,6 @@ actions { name: "ingress.acl_ingress.acl_forward" alias: "acl_forward" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_RED)" } } @@ -1519,6 +1519,7 @@ direct_meters { id: 352321792 name: "ingress.acl_ingress.acl_ingress_meter" alias: "acl_ingress_meter" + annotations: "@mode(single_rate_two_color)" } spec { unit: BYTES diff --git a/sai_p4/instantiations/google/middleblock.p4info.pb.txt b/sai_p4/instantiations/google/middleblock.p4info.pb.txt index 2d55d77a..26b9a239 100755 --- a/sai_p4/instantiations/google/middleblock.p4info.pb.txt +++ b/sai_p4/instantiations/google/middleblock.p4info.pb.txt @@ -553,12 +553,43 @@ tables { alias: "acl_ingress_mirror_and_redirect_table" annotations: "@sai_acl(INGRESS)" annotations: "@p4runtime_role(\"sdn_controller\")" + annotations: "@entry_restriction(\"\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")" } match_fields { - id: 7 - name: "vlan_id" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID)" - bitwidth: 12 + id: 2 + name: "is_ip" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IP)" + bitwidth: 1 + match_type: OPTIONAL + } + match_fields { + id: 3 + name: "is_ipv4" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IPV4ANY)" + bitwidth: 1 + match_type: OPTIONAL + } + match_fields { + id: 4 + name: "is_ipv6" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IPV6ANY)" + bitwidth: 1 + match_type: OPTIONAL + } + match_fields { + id: 10 + name: "dst_ip" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_DST_IP)" + annotations: "@format(IPV4_ADDRESS)" + bitwidth: 32 + match_type: TERNARY + } + match_fields { + id: 5 + name: "dst_ipv6" + annotations: "@composite_field(@ sai_field ( SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6_WORD3 ) , @ sai_field ( SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6_WORD2 ))" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 64 match_type: TERNARY } match_fields { @@ -920,6 +951,7 @@ tables { match_fields { id: 1 name: "multicast_replica_port" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . port)" match_type: EXACT type_name { name: "port_id_t" @@ -928,6 +960,7 @@ tables { match_fields { id: 2 name: "multicast_replica_instance" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . instance)" bitwidth: 16 match_type: EXACT } @@ -1110,6 +1143,7 @@ actions { params { id: 1 name: "multicast_group_id" + annotations: "@refers_to(builtin : : multicast_group_table , multicast_group_id)" bitwidth: 16 } } @@ -1119,7 +1153,6 @@ actions { name: "ingress.acl_ingress.acl_copy" alias: "acl_copy" annotations: "@sai_action(SAI_PACKET_ACTION_COPY , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_RED)" } params { @@ -1137,7 +1170,6 @@ actions { name: "ingress.acl_ingress.acl_trap" alias: "acl_trap" annotations: "@sai_action(SAI_PACKET_ACTION_TRAP , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_RED)" } params { @@ -1155,7 +1187,6 @@ actions { name: "ingress.acl_ingress.acl_forward" alias: "acl_forward" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_RED)" } } @@ -1522,6 +1553,7 @@ direct_meters { id: 352321792 name: "ingress.acl_ingress.acl_ingress_meter" alias: "acl_ingress_meter" + annotations: "@mode(single_rate_two_color)" } spec { unit: BYTES diff --git a/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 b/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 index bc6d6156..eba3881e 100644 --- a/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 +++ b/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 @@ -89,9 +89,15 @@ #define ACL_TOR_PRE_INGRESS_TABLE_MINIMUM_GUARANTEED_SIZE 127 +#define ACL_INGRESS_QOS_TABLE_MINIMUM_GUARANTEED_SIZE 511 + #define ACL_INGRESS_COUNTING_TABLE_MINIMUM_GUARANTEED_SIZE 255 +#ifdef SAI_INSTANTIATION_EXPERIMENTAL_TOR +#define ACL_INGRESS_MIRROR_AND_REDIRECT_TABLE_MINIMUM_GUARANTEED_SIZE 511 +#else #define ACL_INGRESS_MIRROR_AND_REDIRECT_TABLE_MINIMUM_GUARANTEED_SIZE 255 +#endif #define ACL_EGRESS_TABLE_MINIMUM_GUARANTEED_SIZE 127 diff --git a/sai_p4/instantiations/google/sai_pd.proto b/sai_p4/instantiations/google/sai_pd.proto index 12448963..e1265b39 100755 --- a/sai_p4/instantiations/google/sai_pd.proto +++ b/sai_p4/instantiations/google/sai_pd.proto @@ -724,7 +724,8 @@ message AclIngressSecurityTableEntry { // Table entry restrictions: // ## Only allow IP field matches for IP packets. -// is_ipv6::mask != 0 -> is_ipv6 == 1; +// dst_ip::mask != 0 -> is_ipv4 == 1; +// dst_ipv6::mask != 0 -> is_ipv6 == 1; // ## Forbid illegal combinations of IP_TYPE fields. // is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0); // is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0); @@ -745,7 +746,8 @@ message AclIngressMirrorAndRedirectTableEntry { Optional vrf_id = 8; // optional match / Format::STRING // CAUTION: This match field is not (yet) supported. Optional ipmc_table_hit = - 9; // optional match / Format::HEX_STRING / 1 bits + 9; // optional match / Format::HEX_STRING / 1 bits + Ternary dst_ip = 10; // ternary match / Format::IPV4 } Match match = 1; message Action { @@ -948,9 +950,9 @@ message SetCpuQueueAction { // CAUTION: This action is not (yet) supported. message SetCpuAndMulticastQueuesAndDenyAboveRateLimitAction { - string cpu_queue = 1; // Format::STRING - string green_multicast_queue = 2; // Format::STRING - string red_and_yellow_multicast_queue = 3; // Format::STRING + string cpu_queue = 1; // Format::STRING + string green_multicast_queue = 2; // Format::STRING + string red_multicast_queue = 3; // Format::STRING } message RedirectToNexthopAction { diff --git a/sai_p4/instantiations/google/tor.p4info.pb.txt b/sai_p4/instantiations/google/tor.p4info.pb.txt index 060b95d8..4aa316ef 100644 --- a/sai_p4/instantiations/google/tor.p4info.pb.txt +++ b/sai_p4/instantiations/google/tor.p4info.pb.txt @@ -788,7 +788,7 @@ tables { const_default_action_id: 21257015 direct_resource_ids: 318767367 direct_resource_ids: 352321794 - size: 256 + size: 511 } tables { preamble { @@ -797,7 +797,7 @@ tables { alias: "acl_ingress_mirror_and_redirect_table" annotations: "@sai_acl(INGRESS)" annotations: "@p4runtime_role(\"sdn_controller\")" - annotations: "@entry_restriction(\"\n // Only allow IP field matches for IP packets.\n is_ipv6::mask != 0 -> is_ipv6 == 1;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")" + annotations: "@entry_restriction(\"\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")" } match_fields { id: 1 @@ -808,6 +808,20 @@ tables { name: "port_id_t" } } + match_fields { + id: 6 + name: "acl_metadata" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META)" + bitwidth: 8 + match_type: TERNARY + } + match_fields { + id: 7 + name: "vlan_id" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID)" + bitwidth: 12 + match_type: TERNARY + } match_fields { id: 2 name: "is_ip" @@ -829,6 +843,14 @@ tables { bitwidth: 1 match_type: OPTIONAL } + match_fields { + id: 10 + name: "dst_ip" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_DST_IP)" + annotations: "@format(IPV4_ADDRESS)" + bitwidth: 32 + match_type: TERNARY + } match_fields { id: 5 name: "dst_ipv6" @@ -837,20 +859,6 @@ tables { bitwidth: 64 match_type: TERNARY } - match_fields { - id: 6 - name: "acl_metadata" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META)" - bitwidth: 8 - match_type: TERNARY - } - match_fields { - id: 7 - name: "vlan_id" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID)" - bitwidth: 12 - match_type: TERNARY - } match_fields { id: 8 name: "vrf_id" @@ -1128,6 +1136,7 @@ tables { match_fields { id: 1 name: "multicast_replica_port" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . port)" match_type: EXACT type_name { name: "port_id_t" @@ -1136,6 +1145,7 @@ tables { match_fields { id: 2 name: "multicast_replica_instance" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . instance)" bitwidth: 16 match_type: EXACT } @@ -1477,6 +1487,7 @@ actions { params { id: 1 name: "multicast_group_id" + annotations: "@refers_to(builtin : : multicast_group_table , multicast_group_id)" bitwidth: 16 } } @@ -1543,7 +1554,6 @@ actions { name: "ingress.acl_ingress.set_qos_queue_and_cancel_copy_above_rate_limit" alias: "set_qos_queue_and_cancel_copy_above_rate_limit" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_COPY_CANCEL , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_COPY_CANCEL , SAI_PACKET_COLOR_RED)" } params { @@ -1561,7 +1571,6 @@ actions { name: "ingress.acl_ingress.set_cpu_and_multicast_queues_and_deny_above_rate_limit" alias: "set_cpu_and_multicast_queues_and_deny_above_rate_limit" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_RED)" annotations: "@unsupported" } @@ -1583,8 +1592,7 @@ actions { } params { id: 3 - name: "red_and_yellow_multicast_queue" - annotations: "@sai_action_param(MULTICAST_QOS_QUEUE , SAI_PACKET_COLOR_YELLOW)" + name: "red_multicast_queue" annotations: "@sai_action_param(MULTICAST_QOS_QUEUE , SAI_PACKET_COLOR_RED)" type_name { name: "qos_queue_t" @@ -1597,7 +1605,6 @@ actions { name: "ingress.acl_ingress.set_cpu_queue_and_deny_above_rate_limit" alias: "set_cpu_queue_and_deny_above_rate_limit" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_RED)" } params { @@ -2015,6 +2022,7 @@ direct_meters { id: 352321794 name: "ingress.acl_ingress.acl_ingress_qos_meter" alias: "acl_ingress_qos_meter" + annotations: "@mode(single_rate_two_color)" } spec { unit: BYTES diff --git a/sai_p4/instantiations/google/unioned_p4info.pb.txt b/sai_p4/instantiations/google/unioned_p4info.pb.txt index 10769ef4..d8c81f91 100644 --- a/sai_p4/instantiations/google/unioned_p4info.pb.txt +++ b/sai_p4/instantiations/google/unioned_p4info.pb.txt @@ -867,6 +867,7 @@ tables { match_fields { id: 1 name: "multicast_replica_port" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . port)" match_type: EXACT type_name { name: "port_id_t" @@ -875,6 +876,7 @@ tables { match_fields { id: 2 name: "multicast_replica_instance" + annotations: "@referenced_by(builtin : : multicast_group_table , replica . instance)" bitwidth: 16 match_type: EXACT } @@ -997,13 +999,43 @@ tables { alias: "acl_ingress_mirror_and_redirect_table" annotations: "@sai_acl(INGRESS)" annotations: "@p4runtime_role(\"sdn_controller\")" - annotations: "@entry_restriction(\"\n // Only allow IP field matches for IP packets.\n is_ipv6::mask != 0 -> is_ipv6 == 1;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")" + annotations: "@entry_restriction(\"\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n // Forbid illegal combinations of IP_TYPE fields.\n is_ip::mask != 0 -> (is_ipv4::mask == 0 && is_ipv6::mask == 0);\n is_ipv4::mask != 0 -> (is_ip::mask == 0 && is_ipv6::mask == 0);\n is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);\n // Forbid unsupported combinations of IP_TYPE fields.\n is_ipv4::mask != 0 -> (is_ipv4 == 1);\n is_ipv6::mask != 0 -> (is_ipv6 == 1);\n \")" } match_fields { - id: 7 - name: "vlan_id" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID)" - bitwidth: 12 + id: 2 + name: "is_ip" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IP)" + bitwidth: 1 + match_type: OPTIONAL + } + match_fields { + id: 3 + name: "is_ipv4" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IPV4ANY)" + bitwidth: 1 + match_type: OPTIONAL + } + match_fields { + id: 4 + name: "is_ipv6" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IPV6ANY)" + bitwidth: 1 + match_type: OPTIONAL + } + match_fields { + id: 10 + name: "dst_ip" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_DST_IP)" + annotations: "@format(IPV4_ADDRESS)" + bitwidth: 32 + match_type: TERNARY + } + match_fields { + id: 5 + name: "dst_ipv6" + annotations: "@composite_field(@ sai_field ( SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6_WORD3 ) , @ sai_field ( SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6_WORD2 ))" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 64 match_type: TERNARY } match_fields { @@ -1033,35 +1065,6 @@ tables { name: "port_id_t" } } - match_fields { - id: 2 - name: "is_ip" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IP)" - bitwidth: 1 - match_type: OPTIONAL - } - match_fields { - id: 3 - name: "is_ipv4" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IPV4ANY)" - bitwidth: 1 - match_type: OPTIONAL - } - match_fields { - id: 4 - name: "is_ipv6" - annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE / IPV6ANY)" - bitwidth: 1 - match_type: OPTIONAL - } - match_fields { - id: 5 - name: "dst_ipv6" - annotations: "@composite_field(@ sai_field ( SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6_WORD3 ) , @ sai_field ( SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6_WORD2 ))" - annotations: "@format(IPV6_ADDRESS)" - bitwidth: 64 - match_type: TERNARY - } match_fields { id: 6 name: "acl_metadata" @@ -1069,6 +1072,13 @@ tables { bitwidth: 8 match_type: TERNARY } + match_fields { + id: 7 + name: "vlan_id" + annotations: "@sai_field(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID)" + bitwidth: 12 + match_type: TERNARY + } action_refs { id: 16777476 annotations: "@proto_id(1)" @@ -1431,7 +1441,7 @@ tables { const_default_action_id: 21257015 direct_resource_ids: 318767367 direct_resource_ids: 352321794 - size: 256 + size: 511 } tables { preamble { @@ -1729,6 +1739,7 @@ actions { params { id: 1 name: "multicast_group_id" + annotations: "@refers_to(builtin : : multicast_group_table , multicast_group_id)" bitwidth: 16 } } @@ -1738,7 +1749,6 @@ actions { name: "ingress.acl_ingress.acl_copy" alias: "acl_copy" annotations: "@sai_action(SAI_PACKET_ACTION_COPY , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_RED)" annotations: "@sai_action(SAI_PACKET_ACTION_COPY)" } @@ -1757,7 +1767,6 @@ actions { name: "ingress.acl_ingress.acl_trap" alias: "acl_trap" annotations: "@sai_action(SAI_PACKET_ACTION_TRAP , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_RED)" annotations: "@sai_action(SAI_PACKET_ACTION_TRAP)" } @@ -1776,7 +1785,6 @@ actions { name: "ingress.acl_ingress.acl_forward" alias: "acl_forward" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DROP , SAI_PACKET_COLOR_RED)" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD)" } @@ -2137,7 +2145,6 @@ actions { name: "ingress.acl_ingress.set_qos_queue_and_cancel_copy_above_rate_limit" alias: "set_qos_queue_and_cancel_copy_above_rate_limit" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_COPY_CANCEL , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_COPY_CANCEL , SAI_PACKET_COLOR_RED)" } params { @@ -2155,7 +2162,6 @@ actions { name: "ingress.acl_ingress.set_cpu_and_multicast_queues_and_deny_above_rate_limit" alias: "set_cpu_and_multicast_queues_and_deny_above_rate_limit" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_RED)" annotations: "@unsupported" } @@ -2177,8 +2183,7 @@ actions { } params { id: 3 - name: "red_and_yellow_multicast_queue" - annotations: "@sai_action_param(MULTICAST_QOS_QUEUE , SAI_PACKET_COLOR_YELLOW)" + name: "red_multicast_queue" annotations: "@sai_action_param(MULTICAST_QOS_QUEUE , SAI_PACKET_COLOR_RED)" type_name { name: "qos_queue_t" @@ -2191,7 +2196,6 @@ actions { name: "ingress.acl_ingress.set_cpu_queue_and_deny_above_rate_limit" alias: "set_cpu_queue_and_deny_above_rate_limit" annotations: "@sai_action(SAI_PACKET_ACTION_FORWARD , SAI_PACKET_COLOR_GREEN)" - annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_YELLOW)" annotations: "@sai_action(SAI_PACKET_ACTION_DENY , SAI_PACKET_COLOR_RED)" } params { @@ -2369,6 +2373,7 @@ direct_meters { id: 352321792 name: "ingress.acl_ingress.acl_ingress_meter" alias: "acl_ingress_meter" + annotations: "@mode(single_rate_two_color)" } spec { unit: BYTES @@ -2380,6 +2385,7 @@ direct_meters { id: 352321794 name: "ingress.acl_ingress.acl_ingress_qos_meter" alias: "acl_ingress_qos_meter" + annotations: "@mode(single_rate_two_color)" } spec { unit: BYTES From 1ce0f4956816e78275407c466057700f0a356a66 Mon Sep 17 00:00:00 2001 From: divyagayathri-hcl <159437886+divyagayathri-hcl@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:32:40 +0530 Subject: [PATCH 4/7] [SAIP4] Squash of multiple changes & Rename mirror_with_psamp_encapsulation to mirror_with_ipfix_encapsulation to reflect change in SAI mirror session type to IPFIX. (#625) Co-authored-by: kishanps --- sai_p4/fixed/ids.h | 2 +- sai_p4/fixed/mirroring.p4 | 14 +++++++------- .../google/fabric_border_router.p4info.pb.txt | 4 ++-- .../google/middleblock.p4info.pb.txt | 4 ++-- .../google/minimum_guaranteed_sizes.p4 | 6 ++++-- sai_p4/instantiations/google/sai_pd.proto | 4 ++-- .../google/test_tools/test_entries.cc | 6 +++--- .../google/test_tools/test_entries.h | 2 +- .../google/tests/sai_p4_bmv2_vlan_test.cc | 4 ++-- sai_p4/instantiations/google/tor.p4info.pb.txt | 6 +++--- sai_p4/instantiations/google/unioned_p4info.pb.txt | 6 +++--- 11 files changed, 30 insertions(+), 28 deletions(-) diff --git a/sai_p4/fixed/ids.h b/sai_p4/fixed/ids.h index c8825c41..80c6b392 100644 --- a/sai_p4/fixed/ids.h +++ b/sai_p4/fixed/ids.h @@ -55,7 +55,7 @@ #define ROUTING_SET_MULTICAST_SRC_MAC_ACTION_ID 0x01000019 // 16777241 #define MIRRORING_MIRROR_AS_IPV4_ERSPAN_ACTION_ID 0x01000007 // 16777223 #define CLONING_INGRESS_CLONE_ACTION_ID 0x0100001C // 16777244 -#define CLONING_MIRROR_WITH_PSAMP_ENCAPSULATION_ACTION_ID \ +#define CLONING_MIRROR_WITH_IPFIX_ENCAPSULATION_ACTION_ID \ 0x0100001D // 16777245 #define L3_ADMIT_ACTION_ID 0x01000008 // 16777224 #define MIRRORING_SET_PRE_SESSION_ACTION_ID 0x01000009 // 16777225 diff --git a/sai_p4/fixed/mirroring.p4 b/sai_p4/fixed/mirroring.p4 index beb57057..af564128 100644 --- a/sai_p4/fixed/mirroring.p4 +++ b/sai_p4/fixed/mirroring.p4 @@ -39,16 +39,16 @@ control mirror_session_lookup(inout headers_t headers, } // Sets - // * SAI_MIRROR_SESSION_ATTR_TYPE to SAI_MIRROR_SESSION_TYPE_PSAMP - // * SAI_MIRROR_SESSION_ATTR_PSAMP_ENCAPSULATION_TYPE to - // SAI_PSAMP_ENCAPSULATION_TYPE_EXTENDED + // * SAI_MIRROR_SESSION_ATTR_TYPE to SAI_MIRROR_SESSION_TYPE_IPFIX + // * SAI_MIRROR_SESSION_ATTR_IPFIX_ENCAPSULATION_TYPE to + // SAI_IPFIX_ENCAPSULATION_TYPE_EXTENDED // * SAI_MIRROR_SESSION_ATTR_MONITOR_PORT to `monitor_port` - @id(CLONING_MIRROR_WITH_PSAMP_ENCAPSULATION_ACTION_ID) - // TODO: Add params needed for PSAMP mirroring. + @id(CLONING_MIRROR_WITH_IPFIX_ENCAPSULATION_ACTION_ID) + // TODO: Add params needed for IPFIX mirroring. // TODO: Remove unsupported annotation once the switch stack // supports this table. @unsupported - action mirror_with_psamp_encapsulation(@id(1) port_id_t monitor_port) { + action mirror_with_ipfix_encapsulation(@id(1) port_id_t monitor_port) { local_metadata.mirror_egress_port = monitor_port; } @@ -63,7 +63,7 @@ control mirror_session_lookup(inout headers_t headers, } actions = { @proto_id(1) mirror_as_ipv4_erspan; - @proto_id(2) mirror_with_psamp_encapsulation; + @proto_id(2) mirror_with_ipfix_encapsulation; @defaultonly NoAction; } diff --git a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt index a371bcc9..6f2eb7d2 100644 --- a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt +++ b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt @@ -1422,8 +1422,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_psamp_encapsulation" - alias: "mirror_with_psamp_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" + alias: "mirror_with_ipfix_encapsulation" annotations: "@unsupported" } params { diff --git a/sai_p4/instantiations/google/middleblock.p4info.pb.txt b/sai_p4/instantiations/google/middleblock.p4info.pb.txt index 26b9a239..1ffc0403 100755 --- a/sai_p4/instantiations/google/middleblock.p4info.pb.txt +++ b/sai_p4/instantiations/google/middleblock.p4info.pb.txt @@ -1467,8 +1467,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_psamp_encapsulation" - alias: "mirror_with_psamp_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" + alias: "mirror_with_ipfix_encapsulation" annotations: "@unsupported" } params { diff --git a/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 b/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 index eba3881e..c9a61cff 100644 --- a/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 +++ b/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 @@ -54,6 +54,8 @@ #define ROUTING_IPV4_MULTICAST_TABLE_MINIMUM_GUARANTEED_SIZE 1600 #define ROUTING_IPV6_MULTICAST_TABLE_MINIMUM_GUARANTEED_SIZE 1600 +#define ROUTING_MULTICAST_ROUTER_INTERFACE_TABLE_MINIMUM_GUARANTEED_SIZE 128 + // The maximum number of wcmp groups. #define WCMP_GROUP_TABLE_MINIMUM_GUARANTEED_SIZE 3968 #define WCMP_GROUP_TABLE_MINIMUM_GUARANTEED_SIZE_TOR 960 @@ -85,15 +87,15 @@ #define ACL_DEFAULT_PRE_INGRESS_TABLE_MINIMUM_GUARANTEED_SIZE 254 - #define ACL_TOR_PRE_INGRESS_TABLE_MINIMUM_GUARANTEED_SIZE 127 +#define ACL_INGRESS_QOS_TABLE_MINIMUM_GUARANTEED_SIZE 511 #define ACL_INGRESS_QOS_TABLE_MINIMUM_GUARANTEED_SIZE 511 #define ACL_INGRESS_COUNTING_TABLE_MINIMUM_GUARANTEED_SIZE 255 -#ifdef SAI_INSTANTIATION_EXPERIMENTAL_TOR +#ifdef SAI_INSTANTIATION_TOR #define ACL_INGRESS_MIRROR_AND_REDIRECT_TABLE_MINIMUM_GUARANTEED_SIZE 511 #else #define ACL_INGRESS_MIRROR_AND_REDIRECT_TABLE_MINIMUM_GUARANTEED_SIZE 255 diff --git a/sai_p4/instantiations/google/sai_pd.proto b/sai_p4/instantiations/google/sai_pd.proto index e1265b39..9a264df1 100755 --- a/sai_p4/instantiations/google/sai_pd.proto +++ b/sai_p4/instantiations/google/sai_pd.proto @@ -161,7 +161,7 @@ message MirrorSessionTableEntry { oneof action { MirrorAsIpv4ErspanAction mirror_as_ipv4_erspan = 1; // CAUTION: This action is not (yet) supported. - MirrorWithPsampEncapsulationAction mirror_with_psamp_encapsulation = 2; + MirrorWithIpfixEncapsulationAction mirror_with_ipfix_encapsulation = 2; } } Action action = 2; @@ -892,7 +892,7 @@ message SetPortAndSrcMacAndVlanIdAction { } // CAUTION: This action is not (yet) supported. -message MirrorWithPsampEncapsulationAction { +message MirrorWithIpfixEncapsulationAction { string monitor_port = 1; // Format::STRING } diff --git a/sai_p4/instantiations/google/test_tools/test_entries.cc b/sai_p4/instantiations/google/test_tools/test_entries.cc index 5a7b15b9..3e17ccca 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries.cc +++ b/sai_p4/instantiations/google/test_tools/test_entries.cc @@ -567,11 +567,11 @@ EntryBuilder& EntryBuilder::AddMirrorSessionTableEntry( *pd_entry.mutable_mirror_session_table_entry(); mirror_session_entry.mutable_match()->set_mirror_session_id( params.mirror_session_id); - sai::MirrorWithPsampEncapsulationAction& action = + sai::MirrorWithIpfixEncapsulationAction& action = *mirror_session_entry.mutable_action() - ->mutable_mirror_with_psamp_encapsulation(); + ->mutable_mirror_with_ipfix_encapsulation(); action.set_monitor_port(params.mirror_egress_port); - // TODO: Fill in PSAMP params in table entry's action. + // TODO: Fill in IPFIX params in table entry's action. *entries_.add_entries() = std::move(pd_entry); return *this; diff --git a/sai_p4/instantiations/google/test_tools/test_entries.h b/sai_p4/instantiations/google/test_tools/test_entries.h index 52595b63..f453729b 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries.h +++ b/sai_p4/instantiations/google/test_tools/test_entries.h @@ -82,7 +82,7 @@ void AbslStringify(Sink& sink, const IpVersion& ip_version) { } } -// Parameters for generating a mirror session table entry with PSAMP +// Parameters for generating a mirror session table entry with IPFIX // encapsulation. // TODO: Adds data members with parameters needed for mirror // encap. diff --git a/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc b/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc index ca4170e0..f4d760fd 100644 --- a/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc +++ b/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc @@ -563,7 +563,7 @@ sai::TableEntries EntriesForwardingAndRewritingVlanInRifTable( TEST(VlanTest, SettingNonReservedVidInRifWithoutVlanChecksResultsInPacketWithThatId) { - const sai::Instantiation kInstantiation = sai::Instantiation::kExperimentalTor; + const sai::Instantiation kInstantiation = sai::Instantiation::kTor; const pdpi::IrP4Info kIrP4Info = sai::GetIrP4Info(kInstantiation); ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); @@ -624,7 +624,7 @@ TEST(VlanTest, } TEST(VlanTest, SettingNonReservedVidInRifWithVlanChecksResultsInDrop) { - const sai::Instantiation kInstantiation = sai::Instantiation::kExperimentalTor; + const sai::Instantiation kInstantiation = sai::Instantiation::kTor; const pdpi::IrP4Info kIrP4Info = sai::GetIrP4Info(kInstantiation); ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); diff --git a/sai_p4/instantiations/google/tor.p4info.pb.txt b/sai_p4/instantiations/google/tor.p4info.pb.txt index 4aa316ef..c517243f 100644 --- a/sai_p4/instantiations/google/tor.p4info.pb.txt +++ b/sai_p4/instantiations/google/tor.p4info.pb.txt @@ -895,7 +895,7 @@ tables { scope: DEFAULT_ONLY } const_default_action_id: 21257015 - size: 255 + size: 511 } tables { preamble { @@ -1884,8 +1884,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_psamp_encapsulation" - alias: "mirror_with_psamp_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" + alias: "mirror_with_ipfix_encapsulation" annotations: "@unsupported" } params { diff --git a/sai_p4/instantiations/google/unioned_p4info.pb.txt b/sai_p4/instantiations/google/unioned_p4info.pb.txt index d8c81f91..11fe4078 100644 --- a/sai_p4/instantiations/google/unioned_p4info.pb.txt +++ b/sai_p4/instantiations/google/unioned_p4info.pb.txt @@ -1097,7 +1097,7 @@ tables { scope: DEFAULT_ONLY } const_default_action_id: 21257015 - size: 255 + size: 511 } tables { preamble { @@ -2035,8 +2035,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_psamp_encapsulation" - alias: "mirror_with_psamp_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" + alias: "mirror_with_ipfix_encapsulation" annotations: "@unsupported" } params { From 33cca1acf2c045608332dfc1de91ecfb7ca29ddd Mon Sep 17 00:00:00 2001 From: VSuryaprasad-hcl <159443973+VSuryaprasad-HCL@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:34:05 +0530 Subject: [PATCH 5/7] [P4_symbolic] Enable secure connection for p4rt_route_test. Migrate program to new SAI API for setting VRFs. update comments to use inclusive naming from github and p4.org Add route_metadata field and initializing logic to reflect the changes in SAI-P4 code. Remove program-specific structs mixed in with the generic API. (#626) Co-authored-by: smolkaj Co-authored-by: kishanps Co-authored-by: kheradmandG --- gutil/io.cc | 2 +- gutil/io.h | 2 +- p4_fuzzer/fuzzer_tests.cc | 5 +- p4_symbolic/BUILD.bazel | 6 +- p4_symbolic/bmv2/BUILD.bazel | 4 +- p4_symbolic/bmv2/bmv2.cc | 4 +- p4_symbolic/bmv2/test.cc | 8 +-- p4_symbolic/ir/BUILD.bazel | 2 +- p4_symbolic/ir/ir.proto | 4 +- p4_symbolic/ir/table_entries.cc | 2 +- p4_symbolic/main.cc | 5 +- p4_symbolic/parser.cc | 4 +- p4_symbolic/sai/fields.cc | 5 ++ p4_symbolic/sai/fields.h | 10 ++- p4_symbolic/symbolic/BUILD.bazel | 5 -- p4_symbolic/symbolic/packet.cc | 76 ---------------------- p4_symbolic/symbolic/packet.h | 40 ------------ p4_symbolic/symbolic/symbolic.cc | 6 +- p4_symbolic/symbolic/symbolic.h | 70 ++------------------ p4_symbolic/tests/BUILD.bazel | 1 + p4_symbolic/tests/sai_p4_component_test.cc | 5 +- p4_symbolic/util/BUILD.bazel | 37 ----------- p4_symbolic/util/io.cc | 75 --------------------- p4_symbolic/util/io.h | 36 ---------- 24 files changed, 48 insertions(+), 366 deletions(-) delete mode 100644 p4_symbolic/symbolic/packet.cc delete mode 100644 p4_symbolic/symbolic/packet.h delete mode 100644 p4_symbolic/util/BUILD.bazel delete mode 100644 p4_symbolic/util/io.cc delete mode 100644 p4_symbolic/util/io.h diff --git a/gutil/io.cc b/gutil/io.cc index 3d1b7542..6508f4c3 100644 --- a/gutil/io.cc +++ b/gutil/io.cc @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/gutil/io.h b/gutil/io.h index 34f0f117..b14e23c6 100644 --- a/gutil/io.h +++ b/gutil/io.h @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/p4_fuzzer/fuzzer_tests.cc b/p4_fuzzer/fuzzer_tests.cc index 62254465..9defc445 100644 --- a/p4_fuzzer/fuzzer_tests.cc +++ b/p4_fuzzer/fuzzer_tests.cc @@ -151,12 +151,13 @@ TEST_P(FuzzTest, P4rtWriteAndCheckNoInternalErrors) { ASSERT_OK_AND_ASSIGN( const pdpi::IrTableDefinition& table, gutil::FindOrStatus(info.tables_by_id(), table_id)); - // TODO: acl_lookup_table has a resource limit problem. + // TODO: acl_pre_ingress_table has a resource limit + // problem. // TODO: router_interface_table, ipv4_table and // ipv6_table all have resource limit problems. // TODO: wcmp_group_table has a resource limit problem. if (!(mask_known_failures && - (table.preamble().alias() == "acl_lookup_table" || + (table.preamble().alias() == "acl_pre_ingress_table" || table.preamble().alias() == "router_interface_table" || table.preamble().alias() == "ipv4_table" || table.preamble().alias() == "ipv6_table" || diff --git a/p4_symbolic/BUILD.bazel b/p4_symbolic/BUILD.bazel index cd5432f8..6c044033 100644 --- a/p4_symbolic/BUILD.bazel +++ b/p4_symbolic/BUILD.bazel @@ -26,11 +26,11 @@ cc_binary( ], deps = [ ":parser", + "//gutil:io", "//gutil:status", "//p4_symbolic/bmv2", "//p4_symbolic/sai:parser", "//p4_symbolic/symbolic", - "//p4_symbolic/util", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/flags:usage", @@ -45,7 +45,8 @@ cc_library( srcs = ["parser.cc"], hdrs = ["parser.h"], deps = [ - "//gutil:proto", + "//gutil:io", + "//gutil:proto", "//gutil:status", "//p4_pdpi:ir_cc_proto", "//p4_symbolic/bmv2", @@ -53,7 +54,6 @@ cc_library( "//p4_symbolic/ir:ir_cc_proto", "//p4_symbolic/ir:pdpi_driver", "//p4_symbolic/symbolic", - "//p4_symbolic/util", "@com_google_absl//absl/types:span", ], ) diff --git a/p4_symbolic/bmv2/BUILD.bazel b/p4_symbolic/bmv2/BUILD.bazel index f80130ff..c2aaf3f0 100644 --- a/p4_symbolic/bmv2/BUILD.bazel +++ b/p4_symbolic/bmv2/BUILD.bazel @@ -44,8 +44,8 @@ cc_library( ], deps = [ ":bmv2_cc_proto", + "//gutil:io", "//gutil:status", - "//p4_symbolic/util", "@com_google_protobuf//:protobuf", ], ) @@ -55,8 +55,8 @@ cc_binary( srcs = ["test.cc"], deps = [ ":bmv2", + "//gutil:io", "//gutil:status", - "//p4_symbolic/util", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/flags:usage", diff --git a/p4_symbolic/bmv2/bmv2.cc b/p4_symbolic/bmv2/bmv2.cc index 0c5623da..08dec256 100644 --- a/p4_symbolic/bmv2/bmv2.cc +++ b/p4_symbolic/bmv2/bmv2.cc @@ -17,14 +17,14 @@ #include #include "google/protobuf/util/json_util.h" +#include "gutil/io.h" #include "gutil/status.h" -#include "p4_symbolic/util/io.h" namespace p4_symbolic { namespace bmv2 { absl::StatusOr ParseBmv2JsonFile(const std::string &json_path) { - ASSIGN_OR_RETURN(std::string json, util::ReadFile(json_path)); + ASSIGN_OR_RETURN(std::string json, gutil::ReadFile(json_path)); return ParseBmv2JsonString(json); } diff --git a/p4_symbolic/bmv2/test.cc b/p4_symbolic/bmv2/test.cc index 99dd4d14..0de25489 100644 --- a/p4_symbolic/bmv2/test.cc +++ b/p4_symbolic/bmv2/test.cc @@ -30,9 +30,9 @@ #include "absl/strings/str_format.h" #include "google/protobuf/text_format.h" #include "google/protobuf/util/json_util.h" +#include "gutil/io.h" #include "gutil/status.h" #include "p4_symbolic/bmv2/bmv2.h" -#include "p4_symbolic/util/io.h" ABSL_FLAG(std::string, bmv2, "", "The path to the bmv2 json file (required)"); ABSL_FLAG(std::string, protobuf, "", @@ -55,8 +55,7 @@ absl::Status Test() { p4_symbolic::bmv2::ParseBmv2JsonFile(bmv2_path.c_str())); // Dumping protobuf. - RETURN_IF_ERROR( - p4_symbolic::util::WriteFile(bmv2.DebugString(), protobuf_path.c_str())); + RETURN_IF_ERROR(gutil::WriteFile(bmv2.DebugString(), protobuf_path.c_str())); // Dumping JSON. google::protobuf::util::JsonPrintOptions dumping_options; @@ -68,8 +67,7 @@ absl::Status Test() { RETURN_IF_ERROR( gutil::ToAbslStatus(google::protobuf::util::MessageToJsonString( bmv2, &json_output_str, dumping_options))); - RETURN_IF_ERROR( - p4_symbolic::util::WriteFile(json_output_str, json_path.c_str())); + RETURN_IF_ERROR(gutil::WriteFile(json_output_str, json_path.c_str())); return absl::OkStatus(); } diff --git a/p4_symbolic/ir/BUILD.bazel b/p4_symbolic/ir/BUILD.bazel index be4ec3e5..14674ca3 100644 --- a/p4_symbolic/ir/BUILD.bazel +++ b/p4_symbolic/ir/BUILD.bazel @@ -59,10 +59,10 @@ cc_library( ], deps = [ ":ir_cc_proto", + "//gutil:io", "//gutil:status", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", - "//p4_symbolic/util", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", diff --git a/p4_symbolic/ir/ir.proto b/p4_symbolic/ir/ir.proto index 72d6ce15..590bd0b7 100644 --- a/p4_symbolic/ir/ir.proto +++ b/p4_symbolic/ir/ir.proto @@ -25,7 +25,7 @@ // Additionally, some types of expressions and statements are unsupported, these // are described in the relevant sections of this file. // See the bmv2 JSON format reference for more information: -// https://github.com/p4lang/behavioral-model/blob/master/docs/JSON_format.md +// https://github.com/p4lang/behavioral-model/blob/main/docs/JSON_format.md syntax = "proto3"; @@ -51,7 +51,7 @@ message P4Program { // TODO: If needed in the future, action calls can be added here, for // action calls that are not wrapped in other control constructs. - // https://github.com/p4lang/behavioral-model/blob/master/docs/JSON_format.md#pipelines + // https://github.com/p4lang/behavioral-model/blob/main/docs/JSON_format.md#pipelines } // A header type definition. diff --git a/p4_symbolic/ir/table_entries.cc b/p4_symbolic/ir/table_entries.cc index 714b9266..331ecfb5 100644 --- a/p4_symbolic/ir/table_entries.cc +++ b/p4_symbolic/ir/table_entries.cc @@ -18,11 +18,11 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_split.h" +#include "gutil/io.h" #include "gutil/status.h" #include "p4/v1/p4runtime.pb.h" #include "p4_pdpi/ir.h" #include "p4_pdpi/ir.pb.h" -#include "p4_symbolic/util/io.h" namespace p4_symbolic { namespace ir { diff --git a/p4_symbolic/main.cc b/p4_symbolic/main.cc index ccf635b1..8451f0ec 100644 --- a/p4_symbolic/main.cc +++ b/p4_symbolic/main.cc @@ -27,12 +27,12 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "gutil/io.h" #include "gutil/status.h" #include "p4_symbolic/bmv2/bmv2.h" #include "p4_symbolic/parser.h" #include "p4_symbolic/sai/parser.h" #include "p4_symbolic/symbolic/symbolic.h" -#include "p4_symbolic/util/io.h" ABSL_FLAG(std::string, p4info, "", "The path to the p4info protobuf file (required)"); @@ -173,8 +173,7 @@ absl::Status ParseAndEvaluate() { // Debugging. if (!debug_path.empty()) { - RETURN_IF_ERROR( - p4_symbolic::util::WriteFile(debug_smt_formula, debug_path)); + RETURN_IF_ERROR(gutil::WriteFile(debug_smt_formula, debug_path)); } return absl::OkStatus(); diff --git a/p4_symbolic/parser.cc b/p4_symbolic/parser.cc index 6d58d91b..bf167715 100644 --- a/p4_symbolic/parser.cc +++ b/p4_symbolic/parser.cc @@ -14,11 +14,11 @@ #include "p4_symbolic/parser.h" +#include "gutil/io.h" #include "gutil/proto.h" #include "p4_symbolic/bmv2/bmv2.h" #include "p4_symbolic/ir/ir.h" #include "p4_symbolic/ir/pdpi_driver.h" -#include "p4_symbolic/util/io.h" namespace p4_symbolic { @@ -58,7 +58,7 @@ absl::StatusOr ParseToIr( const std::string &bmv2_json_path, const std::string &p4info_path, absl::Span table_entries) { // Parse bmv2 json file into our initial bmv2 protobuf. - ASSIGN_OR_RETURN(std::string bmv2_json, util::ReadFile(bmv2_json_path)); + ASSIGN_OR_RETURN(std::string bmv2_json, gutil::ReadFile(bmv2_json_path)); // Parse p4info file into pdpi format. ASSIGN_OR_RETURN(pdpi::IrP4Info p4info, diff --git a/p4_symbolic/sai/fields.cc b/p4_symbolic/sai/fields.cc index 02145369..99f20165 100644 --- a/p4_symbolic/sai/fields.cc +++ b/p4_symbolic/sai/fields.cc @@ -100,6 +100,11 @@ absl::StatusOr GetSaiFields(const SymbolicPerPacketState& state) { .admit_to_l3 = get_metadata_field("admit_to_l3"), .vrf_id = get_metadata_field("vrf_id"), .mirror_session_id_valid = get_metadata_field("mirror_session_id_valid"), + .ingress_port = get_metadata_field("ingress_port"), + .route_metadata = get_metadata_field("route_metadata"), + }; + auto standard_metadata = V1ModelStandardMetadata{ + .ingress_port = get_field("standard_metadata.ingress_port"), }; if (!errors.empty()) { diff --git a/p4_symbolic/sai/fields.h b/p4_symbolic/sai/fields.h index 103c2982..25dd70ef 100644 --- a/p4_symbolic/sai/fields.h +++ b/p4_symbolic/sai/fields.h @@ -63,8 +63,14 @@ struct SaiLocalMetadata { z3::expr vrf_id; // TODO: add `packet_rewrites` fields. z3::expr mirror_session_id_valid; - // TODO: add `mirror*` fields. - // TODO: Add `color` field. + z3::expr ingress_port; + z3::expr route_metadata; +}; + +// Symbolic version of `struct standard_metadata_t` in v1model.p4 +// TODO: Add missing fields, as needed. +struct V1ModelStandardMetadata { + z3::expr ingress_port; }; struct SaiFields { diff --git a/p4_symbolic/symbolic/BUILD.bazel b/p4_symbolic/symbolic/BUILD.bazel index 608ae6ec..b601b1f2 100644 --- a/p4_symbolic/symbolic/BUILD.bazel +++ b/p4_symbolic/symbolic/BUILD.bazel @@ -27,7 +27,6 @@ cc_library( "control.cc", "guarded_map.cc", "operators.cc", - "packet.cc", "symbolic.cc", "table.cc", "util.cc", @@ -39,7 +38,6 @@ cc_library( "control.h", "guarded_map.h", "operators.h", - "packet.h", "symbolic.h", "table.h", "util.h", @@ -59,10 +57,7 @@ cc_library( "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_z3prover_z3//:api", "@com_gnu_gmp//:gmp", - "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/cleanup", - "@com_google_absl//absl/container:btree", - "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", diff --git a/p4_symbolic/symbolic/packet.cc b/p4_symbolic/symbolic/packet.cc deleted file mode 100644 index 12c75a74..00000000 --- a/p4_symbolic/symbolic/packet.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2024 Google LLC -// -// 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. - -// Contains helpers for creating, extracting, and managing concerete and -// symbolic packet structs. - -#include "p4_symbolic/symbolic/packet.h" - -#include - -namespace p4_symbolic { -namespace symbolic { -namespace packet { - -namespace { - -// Get the symbolic field value from state or return a default value -// of the given size. -z3::expr GetOrDefault(SymbolicPerPacketState state, const std::string &field, - unsigned int default_value_bit_size) { - if (state.ContainsKey(field)) { - return state.Get(field).value(); - } -} - -} // namespace - -SymbolicPacket ExtractSymbolicPacket(SymbolicPerPacketState state) { - z3::expr ipv6_src = GetOrDefault(state, "ipv6.src_addr", 128); - z3::expr ipv6_dst = GetOrDefault(state, "ipv6.dst_addr", 128); - - return SymbolicPacket{GetOrDefault(state, "ethernet.src_addr", 48), - GetOrDefault(state, "ethernet.dst_addr", 48), - GetOrDefault(state, "ethernet.ether_type", 16), - - GetOrDefault(state, "ipv4.src_addr", 32), - GetOrDefault(state, "ipv4.dst_addr", 32), - ipv6_dst.extract(127, 64), - ipv6_dst.extract(63, 0), - GetOrDefault(state, "ipv4.protocol", 8), - GetOrDefault(state, "ipv4.dscp", 6), - GetOrDefault(state, "ipv4.ttl", 8), - - GetOrDefault(state, "icmp.type", 8)}; -} - -ConcretePacket ExtractConcretePacket(SymbolicPacket packet, z3::model model) { - return {model.eval(packet.eth_src, true).to_string(), - model.eval(packet.eth_dst, true).to_string(), - model.eval(packet.eth_type, true).to_string(), - - model.eval(packet.ipv4_src, true).to_string(), - model.eval(packet.ipv4_dst, true).to_string(), - model.eval(packet.ipv6_dst_upper, true).to_string(), - model.eval(packet.ipv6_dst_lower, true).to_string(), - model.eval(packet.protocol, true).to_string(), - model.eval(packet.dscp, true).to_string(), - model.eval(packet.ttl, true).to_string(), - - model.eval(packet.icmp_type, true).to_string()}; -} - -} // namespace packet -} // namespace symbolic -} // namespace p4_symbolic diff --git a/p4_symbolic/symbolic/packet.h b/p4_symbolic/symbolic/packet.h deleted file mode 100644 index bd18674a..00000000 --- a/p4_symbolic/symbolic/packet.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2024 Google LLC -// -// 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. - -// Contains helpers for creating, extracting, and managing concerete and -// symbolic packet structs. - -#ifndef P4_SYMBOLIC_SYMBOLIC_PACKET_H_ -#define P4_SYMBOLIC_SYMBOLIC_PACKET_H_ - -#include "gutil/status.h" -#include "p4_symbolic/symbolic/symbolic.h" -#include "z3++.h" - -namespace p4_symbolic { -namespace symbolic { -namespace packet { - -// Extract the packet fields from their p4 program counterparts. -SymbolicPacket ExtractSymbolicPacket(SymbolicPerPacketState state); - -// Extract a concrete packet by evaluating every field's corresponding -// expression in the model. -ConcretePacket ExtractConcretePacket(SymbolicPacket packet, z3::model model); - -} // namespace packet -} // namespace symbolic -} // namespace p4_symbolic - -#endif // P4_SYMBOLIC_SYMBOLIC_PACKET_H_ diff --git a/p4_symbolic/symbolic/symbolic.cc b/p4_symbolic/symbolic/symbolic.cc index bee51063..03df3933 100644 --- a/p4_symbolic/symbolic/symbolic.cc +++ b/p4_symbolic/symbolic/symbolic.cc @@ -16,15 +16,12 @@ #include -#include "absl/algorithm/container.h" #include "absl/cleanup/cleanup.h" -#include "absl/memory/memory.h" #include "absl/types/optional.h" #include "glog/logging.h" #include "gutil/status.h" #include "p4_symbolic/symbolic/control.h" #include "p4_symbolic/symbolic/operators.h" -#include "p4_symbolic/symbolic/packet.h" #include "p4_symbolic/symbolic/util.h" #include "p4_symbolic/z3_util.h" @@ -132,11 +129,11 @@ absl::StatusOr> Solve( solver_state->solver->push(); solver_state->solver->add(constraint); + auto pop = absl::Cleanup([&] { solver_state->solver->pop(); }); z3::check_result check_result = solver_state->solver->check(); switch (check_result) { case z3::unsat: case z3::unknown: - solver_state->solver->pop(); return absl::nullopt; case z3::sat: @@ -145,7 +142,6 @@ absl::StatusOr> Solve( ConcreteContext result, util::ExtractFromModel(solver_state->context, packet_model, solver_state->translator)); - solver_state->solver->pop(); return result; } LOG(DFATAL) << "invalid Z3 check() result: " diff --git a/p4_symbolic/symbolic/symbolic.h b/p4_symbolic/symbolic/symbolic.h index 6b46f93b..2ecf3b72 100644 --- a/p4_symbolic/symbolic/symbolic.h +++ b/p4_symbolic/symbolic/symbolic.h @@ -110,7 +110,7 @@ struct ConcreteTrace { absl::StrAppend(&result, "dropped = ", dropped, "\n"); absl::StrAppend(&result, "got cloned = ", got_cloned, "\n"); for (const auto &[table, match] : matched_entries) { - result = absl::StrCat(result, "\n", table, " => ", match.to_string()); + absl::StrAppend(&result, "\n", table, " => ", match.to_string()); } return result; } @@ -126,87 +126,31 @@ struct SymbolicTrace { z3::expr got_cloned; }; -// Specifies the concrete data inside a packet. -// This is a friendly helper struct, all information in this struct -// is extracted from ConcretePerPacketState. -struct ConcretePacket { - std::string eth_src; - std::string eth_dst; - std::string eth_type; - - std::string ipv4_src; - std::string ipv4_dst; - std::string ipv6_dst_upper; - std::string ipv6_dst_lower; - std::string protocol; - std::string dscp; - std::string ttl; - - std::string icmp_type; - - std::string to_string() const { - return absl::StrCat("eth_src = ", eth_src, "\n", "eth_dst = ", eth_dst, - "\n", "eth_type = ", eth_type, "\n", - "ipv4_src = ", ipv4_src, "\n", "ipv4_dst = ", ipv4_dst, - "\n", "ipv6_dst_upper = ", ipv6_dst_upper, "\n", - "ipv6_dst_lower = ", ipv6_dst_lower, "\n", - "protocol = ", protocol, "\n", "dscp = ", dscp, "\n", - "ttl = ", ttl, "\n", "icmp_type = ", icmp_type); - } -}; - -// A helper struct containing symbolic expressions for every field in a packet. -// All expressions in this struct are extracted from SymbolicPerPacketState. -// We explicitly give these fields name in this struct to simplify how the -// client code can impose constraints on them in assertions. -struct SymbolicPacket { - z3::expr eth_src; // 48 bit. - z3::expr eth_dst; // 48 bit. - z3::expr eth_type; // 16 bit. - - z3::expr ipv4_src; // 32 bit, valid if eth_type = 0x0800 - z3::expr ipv4_dst; // 32 bit, valid if eth_type = 0x0800 - z3::expr ipv6_dst_upper; // 64 bit, valid if eth_type = 0x86dd - z3::expr ipv6_dst_lower; // 64 bit, valid if eth_type = 0x86dd - z3::expr protocol; // 8 bit, valid if eth_type is ip - z3::expr dscp; // 6 bit, valid if eth_type is ip - z3::expr ttl; // 8 bit, valid if eth_type is ip - - z3::expr icmp_type; // 8 bit, valid if eth_type is ip -}; - // The result of solving with some assertion. // This contains an input test packet with its predicted flow in the program, // and the predicted output. struct ConcreteContext { std::string ingress_port; std::string egress_port; - ConcretePacket ingress_packet; // Input packet into the program/switch. - ConcretePacket egress_packet; // Expected output packet. ConcretePerPacketState ingress_headers; ConcretePerPacketState egress_headers; ConcreteTrace trace; // Expected trace in the program. std::string to_string() const { return to_string(false); } std::string to_string(bool verbose) const { - auto result = absl::StrCat( - "ingress_port = ", ingress_port, "\n", "egress_port = ", egress_port, - "\n\n", "ingress_packet:\n", ingress_packet.to_string(), "\n\n", - "egress_packet:\n", egress_packet.to_string(), "\n\n", "trace:\n", - trace.to_string()); + auto result = absl::StrCat("ingress_port = ", ingress_port, "\n", + "egress_port = ", egress_port, "\n", "trace:\n", + trace.to_string()); if (verbose) { auto ingress_string = absl::StrCat("ingress_headers", ":"); auto egress_string = absl::StrCat("egress_headers", ":"); for (const auto &[name, ingress_value] : ingress_headers) { - ingress_string = - absl::StrCat(ingress_string, "\n", name, " = ", ingress_value); + absl::StrAppend(&ingress_string, "\n", name, " = ", ingress_value); } for (const auto &[name, egress_value] : egress_headers) { - egress_string = - absl::StrCat(egress_string, "\n", name, " = ", egress_value); + absl::StrAppend(&egress_string, "\n", name, " = ", egress_value); } - result = - absl::StrCat(result, "\n\n", ingress_string, "\n\n", egress_string); + absl::StrAppend(&result, "\n\n", ingress_string, "\n\n", egress_string); } return result; } diff --git a/p4_symbolic/tests/BUILD.bazel b/p4_symbolic/tests/BUILD.bazel index 013dcdcc..9bf1f6c3 100644 --- a/p4_symbolic/tests/BUILD.bazel +++ b/p4_symbolic/tests/BUILD.bazel @@ -13,6 +13,7 @@ cc_test( "//p4_pdpi:ir_cc_proto", "//p4_pdpi:pd", "//p4_symbolic:parser", + "//p4_symbolic:z3_util", "//p4_symbolic/sai:parser", "//p4_symbolic/symbolic", "//sai_p4/instantiations/google:sai_nonstandard_platforms_cc", diff --git a/p4_symbolic/tests/sai_p4_component_test.cc b/p4_symbolic/tests/sai_p4_component_test.cc index 14944e0c..4e973433 100644 --- a/p4_symbolic/tests/sai_p4_component_test.cc +++ b/p4_symbolic/tests/sai_p4_component_test.cc @@ -15,9 +15,10 @@ #include "p4_symbolic/parser.h" #include "p4_symbolic/sai/parser.h" #include "p4_symbolic/symbolic/symbolic.h" +#include "p4_symbolic/z3_util.h" +#include "sai_p4/instantiations/google/instantiations.h" #include "sai_p4/instantiations/google/sai_nonstandard_platforms.h" #include "sai_p4/instantiations/google/sai_pd.pb.h" -#include "sai_p4/instantiations/google/instantiations.h" #include "thinkit/bazel_test_environment.h" namespace p4_symbolic { @@ -30,7 +31,7 @@ using ::testing::Not; constexpr absl::string_view kTableEntries = R"pb( entries { - acl_pre_ingress_table_entry { + acl_pre_ingress_table_entry { match { src_mac { value: "22:22:22:11:11:11" mask: "ff:ff:ff:ff:ff:ff" } dst_ip { value: "10.0.10.0" mask: "255.255.255.255" } diff --git a/p4_symbolic/util/BUILD.bazel b/p4_symbolic/util/BUILD.bazel deleted file mode 100644 index d9f9a016..00000000 --- a/p4_symbolic/util/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2024 Google LLC -# -# 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. - -package( - default_visibility = ["//visibility:public"], - licenses = ["notice"], -) - -cc_library( - name = "util", - srcs = [ - "io.cc", - # "status.cc", - ], - hdrs = [ - "io.h", - # TODO: clean up. - # "status.h", - ], - deps = [ - "//gutil:status", - "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - ], -) diff --git a/p4_symbolic/util/io.cc b/p4_symbolic/util/io.cc deleted file mode 100644 index 000b8a7c..00000000 --- a/p4_symbolic/util/io.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2024 Google LLC -// -// 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. - -#include "p4_symbolic/util/io.h" - -#include -#include -#include -#include - -#include "absl/strings/str_format.h" -#include "gutil/status.h" - -namespace p4_symbolic { -namespace util { - -namespace { - -absl::Status ErrorNoToAbsl(const char *operation, const std::string &path) { - switch (errno) { - case EACCES: - case ENOENT: - return absl::NotFoundError( - absl::StrFormat("%s: %s", strerror(errno), path)); - default: - return absl::UnknownError(absl::StrFormat("Cannot %s file %s, errno = %d", - operation, path, errno)); - } -} - -} // namespace - -absl::StatusOr ReadFile(const std::string &path) { - std::ifstream f; - f.open(path.c_str()); - if (f.fail()) { - return ErrorNoToAbsl("open", path); - } - f >> std::noskipws; // Read whitespaces. - std::string result(std::istreambuf_iterator(f), - (std::istreambuf_iterator())); - if (f.bad()) { - return ErrorNoToAbsl("read", path); - } - f.close(); - return result; -} - -absl::Status WriteFile(const std::string &content, const std::string &path) { - std::ofstream f; - f.open(path.c_str()); - if (f.fail()) { - return ErrorNoToAbsl("open", path); - } - f << content; - f.close(); - if (f.bad()) { - return ErrorNoToAbsl("write", path); - } - return absl::OkStatus(); -} - -} // namespace util -} // namespace p4_symbolic diff --git a/p4_symbolic/util/io.h b/p4_symbolic/util/io.h deleted file mode 100644 index a105b072..00000000 --- a/p4_symbolic/util/io.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2024 Google LLC -// -// 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. - -#ifndef P4_SYMBOLIC_UTIL_IO_H_ -#define P4_SYMBOLIC_UTIL_IO_H_ - -#include - -#include "absl/status/status.h" -#include "absl/strings/string_view.h" -#include "gutil/status.h" - -namespace p4_symbolic { -namespace util { - -// Reads the entire content of the file and returns it (or an error status). -absl::StatusOr ReadFile(const std::string &path); - -// Writes the content of the string to the file. -absl::Status WriteFile(const std::string &content, const std::string &path); - -} // namespace util -} // namespace p4_symbolic - -#endif // P4_SYMBOLIC_UTIL_IO_H_ From eb418f0936c908a8eaafe98329c1a27ccf3ec807 Mon Sep 17 00:00:00 2001 From: VSuryaprasad-hcl <159443973+VSuryaprasad-HCL@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:35:18 +0530 Subject: [PATCH 6/7] [P4_Symbolic] Eliminate extremely long variable names for action selector variables. Eliminate bug in detecting unsupported scenario (conditional on table match result) Eliminate non-determinism in EvaluateTableEntryCondition. Add test for output determinism.Update hard-coded SAI parser constraint to reflect the updated P4 code.Decrease the number of runs for determinism check. Define kDefaultActionEntryIndex. Fix typo. (#627) Co-authored-by: smolkaj Co-authored-by: kishanps Co-authored-by: kheradmandG --- p4_symbolic/symbolic/BUILD.bazel | 3 +- p4_symbolic/symbolic/table.cc | 60 ++++++++++++++++++-------------- p4_symbolic/symbolic/table.h | 3 ++ p4_symbolic/z3_util.cc | 8 ++++- p4_symbolic/z3_util.h | 8 +++-- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/p4_symbolic/symbolic/BUILD.bazel b/p4_symbolic/symbolic/BUILD.bazel index b601b1f2..81fa9701 100644 --- a/p4_symbolic/symbolic/BUILD.bazel +++ b/p4_symbolic/symbolic/BUILD.bazel @@ -46,7 +46,8 @@ cc_library( deps = [ "//gutil:status", "//p4_pdpi:ir_cc_proto", - "//p4_pdpi/netaddr:ipv4_address", + "//p4_pdpi/internal:ordered_map", + "//p4_pdpi/netaddr:ipv4_address", "//p4_pdpi/netaddr:ipv6_address", "//p4_pdpi/netaddr:mac_address", "//p4_pdpi/utils:ir", diff --git a/p4_symbolic/symbolic/table.cc b/p4_symbolic/symbolic/table.cc index effe5f8d..66e36000 100644 --- a/p4_symbolic/symbolic/table.cc +++ b/p4_symbolic/symbolic/table.cc @@ -30,6 +30,7 @@ #include "absl/types/span.h" #include "gutil/status.h" #include "p4/config/v1/p4info.pb.h" +#include "p4_pdpi/internal/ordered_map.h" #include "p4_pdpi/ir.pb.h" #include "p4_symbolic/symbolic/action.h" #include "p4_symbolic/symbolic/operators.h" @@ -77,7 +78,7 @@ int FindMatchWithName(const pdpi::IrTableEntry &entry, // We return the old index so that Symbolic and Concrete TableMatches are // set up against the indices as they appear in the input table entries array, // and not the sorted array. -std::vector> SortEntries( +std::vector> SortEntries( const ir::Table &table, const std::vector &entries) { if (entries.empty()) return {}; // Find which *definition* of priority we should use by looking at the @@ -106,27 +107,26 @@ std::vector> SortEntries( } // The output array. - std::vector> sorted_entries; - for (size_t i = 0; i < entries.size(); i++) { + std::vector> sorted_entries; + for (int i = 0; i < entries.size(); i++) { const pdpi::IrTableEntry &entry = entries.at(i); sorted_entries.push_back(std::make_pair(i, entry)); } // The sort comparator depends on the match types. - std::function &, - const std::pair &)> + std::function &, + const std::pair &)> comparator; if (has_ternary) { // Sort by explicit priority. - comparator = [](const std::pair &entry1, - const std::pair &entry2) { + comparator = [](const std::pair &entry1, + const std::pair &entry2) { return entry1.second.priority() > entry2.second.priority(); }; } else if (lpm_key.has_value()) { // Sort by prefix length. - comparator = [lpm_key]( - const std::pair &entry1, - const std::pair &entry2) { + comparator = [lpm_key](const std::pair &entry1, + const std::pair &entry2) { auto is_lpm_match = [=](const pdpi::IrMatch &match) -> bool { return match.name() == *lpm_key; }; @@ -235,8 +235,13 @@ absl::StatusOr EvaluateTableEntryCondition( z3::expr condition_expression = Z3Context().bool_val(true); const google::protobuf::Map &match_to_fields = table.table_implementation().match_name_to_field(); - for (const auto &[name, match_fields] : - table.table_definition().match_fields_by_name()) { + + // It is desirable for P4Symbolic to produce deterministic outputs. Therefore, + // all containers need to be traversed in a deterministic order. + const auto sorted_match_fields_by_name = + Ordered(table.table_definition().match_fields_by_name()); + + for (const auto &[name, match_fields] : sorted_match_fields_by_name) { p4::config::v1::MatchField match_definition = match_fields.match_field(); int match_field_index = FindMatchWithName(entry, name); @@ -295,7 +300,7 @@ absl::Status EvaluateSingeTableEntryAction( // Constructs a symbolic expressions that represents the action invocation // corresponding to this entry. absl::Status EvaluateTableEntryAction( - const ir::Table &table, const pdpi::IrTableEntry &entry, + const ir::Table &table, const pdpi::IrTableEntry &entry, int entry_index, const google::protobuf::Map &actions, SymbolicPerPacketState *state, values::P4RuntimeTranslator *translator, const z3::expr &guard) { @@ -312,7 +317,8 @@ absl::Status EvaluateTableEntryAction( // whose value determines which action is executed: to a first // approximation, action i is executed iff `selector == i`. std::string selector_name = - absl::StrCat("action selector for ", entry.DebugString()); + absl::StrFormat("action selector for entry #%d of table '%s'", + entry_index, entry.table_name()); z3::expr selector = Z3Context().int_const(selector_name.c_str()); z3::expr unselected = Z3Context().bool_val(true); for (int i = 0; i < action_set.size(); ++i) { @@ -345,7 +351,7 @@ absl::StatusOr EvaluateTable( const z3::expr &guard) { const std::string &table_name = table.table_definition().preamble().name(); // Sort entries by priority deduced from match types. - std::vector> sorted_entries = + std::vector> sorted_entries = SortEntries(table, entries); // The table semantically is just a bunch of if conditions, one per // table entry, we construct this big if-elseif-...-else symbolically. @@ -431,16 +437,17 @@ absl::StatusOr EvaluateTable( } // Start with the default entry - z3::expr match_index = Z3Context().int_val(-1); - RETURN_IF_ERROR(EvaluateTableEntryAction( - table, default_entry, data_plane.program.actions(), state, translator, - default_entry_assignment_guard)); + z3::expr match_index = Z3Context().int_val(kDefaultActionEntryIndex); + RETURN_IF_ERROR( + EvaluateTableEntryAction(table, default_entry, kDefaultActionEntryIndex, + data_plane.program.actions(), state, translator, + default_entry_assignment_guard)); // Continue evaluating each table entry in reverse priority for (int row = sorted_entries.size() - 1; row >= 0; row--) { - size_t old_index = sorted_entries.at(row).first; + int old_index = sorted_entries.at(row).first; const pdpi::IrTableEntry &entry = sorted_entries.at(row).second; - z3::expr row_symbol = Z3Context().int_val(static_cast(old_index)); + z3::expr row_symbol = Z3Context().int_val(old_index); // The condition used in the big if_else_then construct. ASSIGN_OR_RETURN(z3::expr entry_match, @@ -450,9 +457,9 @@ absl::StatusOr EvaluateTable( // Evaluate the entry's action guarded by its complete assignment guard. z3::expr entry_assignment_guard = assignment_guards.at(row); - RETURN_IF_ERROR( - EvaluateTableEntryAction(table, entry, data_plane.program.actions(), - state, translator, entry_assignment_guard)); + RETURN_IF_ERROR(EvaluateTableEntryAction( + table, entry, old_index, data_plane.program.actions(), state, + translator, entry_assignment_guard)); } // This table has been completely evaluated, the result of the evaluation @@ -476,9 +483,8 @@ absl::StatusOr EvaluateTable( table.table_implementation().action_to_next_control()) { if (first) { next_control = control; - continue; - } - if (next_control != control) { + first = false; + } else if (next_control != control) { return absl::UnimplementedError(absl::StrCat( "Table \"", table_name, "\" invokes different control constructs based on matched actions.")); diff --git a/p4_symbolic/symbolic/table.h b/p4_symbolic/symbolic/table.h index 94b27423..09def04b 100644 --- a/p4_symbolic/symbolic/table.h +++ b/p4_symbolic/symbolic/table.h @@ -36,6 +36,9 @@ namespace p4_symbolic { namespace symbolic { namespace table { +// P4-Symbolic models the default action as an entry with index -1. +constexpr int kDefaultActionEntryIndex = -1; + absl::StatusOr EvaluateTable( const Dataplane &data_plane, const ir::Table &table, const std::vector &entries, diff --git a/p4_symbolic/z3_util.cc b/p4_symbolic/z3_util.cc index 391627a7..cccb0174 100644 --- a/p4_symbolic/z3_util.cc +++ b/p4_symbolic/z3_util.cc @@ -8,8 +8,14 @@ namespace p4_symbolic { -z3::context& Z3Context() { +z3::context& Z3Context(bool renew) { static z3::context* z3_context = new z3::context(); + + if (renew) { + delete z3_context; + z3_context = new z3::context(); + } + return *z3_context; } diff --git a/p4_symbolic/z3_util.h b/p4_symbolic/z3_util.h index 16a2179b..59a8d1c9 100644 --- a/p4_symbolic/z3_util.h +++ b/p4_symbolic/z3_util.h @@ -11,9 +11,11 @@ namespace p4_symbolic { -// Global z3::context used for creating symbolic expressions during symbolic -// evaluation. -z3::context& Z3Context(); +// Returns the global z3::context used for creating symbolic expressions during +// symbolic evaluation. If parameter `renew` is set to true, it deletes the +// older context and returns a new one. +// TODO: `renew` is a workaround for using a global context. +z3::context& Z3Context(bool renew = false); // -- Evaluation --------------------------------------------------------------- From b0e15b8266428e7912e871ecbe6de89d2a8cfe84 Mon Sep 17 00:00:00 2001 From: divyagayathri-hcl <159437886+divyagayathri-hcl@users.noreply.github.com> Date: Sat, 19 Oct 2024 07:38:41 +0530 Subject: [PATCH 7/7] [SAIP4] Models IPFIX mirroring encap. (#628) Co-authored-by: kishanps --- sai_p4/fixed/headers.p4 | 5 + sai_p4/fixed/ids.h | 2 +- sai_p4/fixed/metadata.p4 | 26 +- sai_p4/fixed/mirroring.p4 | 90 ++++++- sai_p4/fixed/parser.p4 | 6 + sai_p4/fixed/vlan.p4 | 28 ++- .../google/fabric_border_router.p4info.pb.txt | 50 +++- .../google/middleblock.p4info.pb.txt | 50 +++- .../google/minimum_guaranteed_sizes.p4 | 2 - sai_p4/instantiations/google/sai_pd.proto | 15 +- .../google/test_tools/test_entries.cc | 18 +- .../google/test_tools/test_entries.h | 11 +- .../google/test_tools/test_entries_test.cc | 13 +- .../tests/sai_p4_bmv2_mirroring_test.cc | 236 ++++++++++++++---- .../google/tests/sai_p4_bmv2_vlan_test.cc | 139 ++++++++++- .../instantiations/google/tor.p4info.pb.txt | 50 +++- .../google/unioned_p4info.pb.txt | 50 +++- 17 files changed, 702 insertions(+), 89 deletions(-) diff --git a/sai_p4/fixed/headers.p4 b/sai_p4/fixed/headers.p4 index b115f60a..dfd92e5f 100644 --- a/sai_p4/fixed/headers.p4 +++ b/sai_p4/fixed/headers.p4 @@ -89,6 +89,8 @@ header ipv6_t { ipv6_addr_t dst_addr; } +#define UDP_HEADER_BYTES 8 + header udp_t { bit<16> src_port; bit<16> dst_port; @@ -143,6 +145,8 @@ header gre_t { bit<16> protocol; } +#define IPFIX_HEADER_BYTES 16 + // IP Flow Information Export (IPFIX) header, pursuant to RFC 7011 section 3.1. header ipfix_t { // Version of IPFIX to which this Message conforms. @@ -161,6 +165,7 @@ header ipfix_t { bit<32> observation_domain_id; } +#define PSAMP_EXTENDED_BYTES 28 // PSAMP extended header, pursuant to RFC5476. header psamp_extended_t { bit<16> template_id; diff --git a/sai_p4/fixed/ids.h b/sai_p4/fixed/ids.h index 80c6b392..b169f602 100644 --- a/sai_p4/fixed/ids.h +++ b/sai_p4/fixed/ids.h @@ -55,7 +55,7 @@ #define ROUTING_SET_MULTICAST_SRC_MAC_ACTION_ID 0x01000019 // 16777241 #define MIRRORING_MIRROR_AS_IPV4_ERSPAN_ACTION_ID 0x01000007 // 16777223 #define CLONING_INGRESS_CLONE_ACTION_ID 0x0100001C // 16777244 -#define CLONING_MIRROR_WITH_IPFIX_ENCAPSULATION_ACTION_ID \ +#define CLONING_MIRROR_WITH_VLAN_TAG_AND_IPFIX_ENCAPSULATION_ACTION_ID \ 0x0100001D // 16777245 #define L3_ADMIT_ACTION_ID 0x01000008 // 16777224 #define MIRRORING_SET_PRE_SESSION_ACTION_ID 0x01000009 // 16777225 diff --git a/sai_p4/fixed/metadata.p4 b/sai_p4/fixed/metadata.p4 index aa47df03..39461a2a 100644 --- a/sai_p4/fixed/metadata.p4 +++ b/sai_p4/fixed/metadata.p4 @@ -141,6 +141,15 @@ struct headers_t { // PacketOut header; extracted only for packets received from the controller. packet_out_header_t packet_out_header; + // -- mirroring encap headers ------------------------------------------------ + ethernet_t mirror_encap_ethernet; + vlan_t mirror_encap_vlan; + ipv6_t mirror_encap_ipv6; + udp_t mirror_encap_udp; + ipfix_t ipfix; + psamp_extended_t psamp_extended; + // -- end of mirroring encap headers ----------------------------------------- + ethernet_t ethernet; vlan_t vlan; @@ -214,7 +223,7 @@ struct local_metadata_t { // pipeline. bool marked_to_copy; - // Mirroring related fields. + // -- Mirroring related fields ----------------------------------------------- // We can't group them into a struct as BMv2 doesn't support passing structs // into clone3. // If true, the packet needs to be mirrored at the end of the ingress @@ -224,6 +233,21 @@ struct local_metadata_t { // Valid iff marked_to_mirror is true. mirror_session_id_t mirror_session_id; port_id_t mirror_egress_port; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + ethernet_addr_t mirror_encap_src_mac; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + ethernet_addr_t mirror_encap_dst_mac; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + vlan_id_t mirror_encap_vlan_id; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + ipv6_addr_t mirror_encap_src_ip; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + ipv6_addr_t mirror_encap_dst_ip; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + bit<16> mirror_encap_udp_src_port; + @field_list(PreservedFieldList.MIRROR_AND_PACKET_IN_COPY) + bit<16> mirror_encap_udp_dst_port; + // -- end of mirroring related fields ---------------------------------------- // Packet-in related fields, which we can't group into a struct, because BMv2 // doesn't support passing structs in clone3. diff --git a/sai_p4/fixed/mirroring.p4 b/sai_p4/fixed/mirroring.p4 index af564128..a5d7769d 100644 --- a/sai_p4/fixed/mirroring.p4 +++ b/sai_p4/fixed/mirroring.p4 @@ -24,7 +24,7 @@ control mirror_session_lookup(inout headers_t headers, // SAI_MIRROR_SESSION_ATTR_TTL // SAI_MIRROR_SESSION_ATTR_TOS // TODO: Remove mirror_as_ipv4_erspan once the the switch - // supports mirror_with_psamp_encapsulation. This action is currently needed + // supports mirror_with_ipfix_encapsulation. This action is currently needed // for mirror_session_table since it is the only action by the switch in this // table. @id(MIRRORING_MIRROR_AS_IPV4_ERSPAN_ACTION_ID) @@ -43,13 +43,39 @@ control mirror_session_lookup(inout headers_t headers, // * SAI_MIRROR_SESSION_ATTR_IPFIX_ENCAPSULATION_TYPE to // SAI_IPFIX_ENCAPSULATION_TYPE_EXTENDED // * SAI_MIRROR_SESSION_ATTR_MONITOR_PORT to `monitor_port` - @id(CLONING_MIRROR_WITH_IPFIX_ENCAPSULATION_ACTION_ID) - // TODO: Add params needed for IPFIX mirroring. + // * SAI_MIRROR_SESSION_ATTR_MONITOR_FAILOVER_PORT to + // `monitor_failover_port + // * SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS + // * SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS + // * SAI_MIRROR_SESSION_ATTR_VLAN_TPID + // * SAI_MIRROR_SESSION_ATTR_VLAN_ID + // * SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS + // * SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS + // * SAI_MIRROR_SESSION_ATTR_UDP_SRC_PORT + // * SAI_MIRROR_SESSION_ATTR_UDP_DST_PORT + @id(CLONING_MIRROR_WITH_VLAN_TAG_AND_IPFIX_ENCAPSULATION_ACTION_ID) // TODO: Remove unsupported annotation once the switch stack // supports this table. @unsupported - action mirror_with_ipfix_encapsulation(@id(1) port_id_t monitor_port) { + action mirror_with_vlan_tag_and_ipfix_encapsulation( + @id(1) port_id_t monitor_port, + @id(2) port_id_t monitor_failover_port, + @id(3) @format(MAC_ADDRESS) ethernet_addr_t mirror_encap_src_mac, + @id(4) @format(MAC_ADDRESS) ethernet_addr_t mirror_encap_dst_mac, + @id(6) vlan_id_t mirror_encap_vlan_id, + @id(7) @format(IPV6_ADDRESS) ipv6_addr_t mirror_encap_dst_ip, + @id(8) @format(IPV6_ADDRESS) ipv6_addr_t mirror_encap_src_ip, + @id(9) bit<16> mirror_encap_udp_src_port, + @id(10) bit<16> mirror_encap_udp_dst_port) { local_metadata.mirror_egress_port = monitor_port; + // monitor_failover_port's effect is not modeled. + local_metadata.mirror_encap_src_mac = mirror_encap_src_mac; + local_metadata.mirror_encap_dst_mac = mirror_encap_dst_mac; + local_metadata.mirror_encap_vlan_id = mirror_encap_vlan_id; + local_metadata.mirror_encap_src_ip = mirror_encap_src_ip; + local_metadata.mirror_encap_dst_ip = mirror_encap_dst_ip; + local_metadata.mirror_encap_udp_src_port = mirror_encap_udp_src_port; + local_metadata.mirror_encap_udp_dst_port = mirror_encap_udp_dst_port; } // Corresponding SAI object: SAI_OBJECT_TYPE_MIRROR_SESSION @@ -63,7 +89,7 @@ control mirror_session_lookup(inout headers_t headers, } actions = { @proto_id(1) mirror_as_ipv4_erspan; - @proto_id(2) mirror_with_ipfix_encapsulation; + @proto_id(2) mirror_with_vlan_tag_and_ipfix_encapsulation; @defaultonly NoAction; } @@ -83,8 +109,60 @@ control mirroring_encap(inout headers_t headers, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { apply { + // All mirrored packets are encapped with + // ================================================================== + // | Ethernet + vlan | IPv6 | UDP | IPFIX + PSAMP extended| payload | + // ================================================================== + // headers. Fields for headers mostly come from mirror-related + // local_metadata. if (IS_MIRROR_COPY(standard_metadata)) { - // TODO: Implements mirroring encap. + headers.mirror_encap_ethernet.setValid(); + headers.mirror_encap_ethernet.src_addr = + local_metadata.mirror_encap_src_mac; + headers.mirror_encap_ethernet.dst_addr = + local_metadata.mirror_encap_dst_mac; + headers.mirror_encap_ethernet.ether_type = ETHERTYPE_8021Q; // VLAN + + headers.mirror_encap_vlan.setValid(); + headers.mirror_encap_vlan.ether_type = ETHERTYPE_IPV6; + headers.mirror_encap_vlan.vlan_id = local_metadata.mirror_encap_vlan_id; + + headers.mirror_encap_ipv6.setValid(); + headers.mirror_encap_ipv6.version = 4w6; + // Mirrored packets' traffic class is 0. + headers.mirror_encap_ipv6.dscp = 0; + headers.mirror_encap_ipv6.ecn = 0; + // Mirrored packets' hop_limit is 16. + headers.mirror_encap_ipv6.hop_limit = 16; + headers.mirror_encap_ipv6.flow_label = 0; + // payload_lentgh for ipv6 packets is the byte length of headers after + // ipv6 + payload. in our case, that's the UDP, IPFIX and PSAMP headers. + // The mirror replicated packet becomes the new payload during mirror + // encap, so standard_metadata.packet_length becomes the payload length. + // contains the length of payload + all headers. + headers.mirror_encap_ipv6.payload_length = + (bit<16>)standard_metadata.packet_length + + UDP_HEADER_BYTES + + IPFIX_HEADER_BYTES + + PSAMP_EXTENDED_BYTES; + headers.mirror_encap_ipv6.next_header = IP_PROTOCOL_UDP; + headers.mirror_encap_ipv6.src_addr = local_metadata.mirror_encap_src_ip; + headers.mirror_encap_ipv6.dst_addr = local_metadata.mirror_encap_dst_ip; + + headers.mirror_encap_udp.setValid(); + headers.mirror_encap_udp.src_port = + local_metadata.mirror_encap_udp_src_port; + headers.mirror_encap_udp.dst_port = + local_metadata.mirror_encap_udp_dst_port; + headers.mirror_encap_udp.hdr_length = + headers.mirror_encap_ipv6.payload_length; + // Mirrored packets' UDP checksum is 0. + headers.mirror_encap_udp.checksum = 0; + + // IPFIX and PSAMP fields are opaque to P4 so we only set their headers + // as valid. + headers.ipfix.setValid(); + headers.psamp_extended.setValid(); } } } // control mirroring_encap diff --git a/sai_p4/fixed/parser.p4 b/sai_p4/fixed/parser.p4 index 02bc09a5..d96ff8e3 100644 --- a/sai_p4/fixed/parser.p4 +++ b/sai_p4/fixed/parser.p4 @@ -146,6 +146,12 @@ control packet_deparser(packet_out packet, in headers_t headers) { #if defined(PLATFORM_BMV2) || defined(PLATFORM_P4SYMBOLIC) packet.emit(headers.packet_in_header); #endif + packet.emit(headers.mirror_encap_ethernet); + packet.emit(headers.mirror_encap_vlan); + packet.emit(headers.mirror_encap_ipv6); + packet.emit(headers.mirror_encap_udp); + packet.emit(headers.ipfix); + packet.emit(headers.psamp_extended); packet.emit(headers.ethernet); packet.emit(headers.tunnel_encap_ipv6); packet.emit(headers.tunnel_encap_gre); diff --git a/sai_p4/fixed/vlan.p4 b/sai_p4/fixed/vlan.p4 index d6bea5fd..30dfe8ad 100644 --- a/sai_p4/fixed/vlan.p4 +++ b/sai_p4/fixed/vlan.p4 @@ -66,6 +66,9 @@ control vlan_untag(inout headers_t headers, } } // control vlan_untag +// Apply VLAN checks for packets in ingress pipeline. +// This control block assumes vlan_untag control block has been called +// and VLAN-related information is stored in metadata instead of in headers. control ingress_vlan_checks(inout headers_t headers, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { @@ -77,15 +80,23 @@ control ingress_vlan_checks(inout headers_t headers, } } // control ingress_vlan_checks -// Apply VLAN checks for forwarded packets. -// TODO: Add VLAN checks for mirrored packets. +// Apply VLAN checks for packets in egress pipeline (except for punted packets). +// This control block assumes vlan_tag control block has not been called and +// VLAN-related information is stored in metadata, instead of in headers. control egress_vlan_checks(inout headers_t headers, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { apply { - if (local_metadata.enable_vlan_checks && - !IS_RESERVED_VLAN_ID(local_metadata.vlan_id)) { - mark_to_drop(standard_metadata); + if (local_metadata.enable_vlan_checks) { + // For mirrored-encapped packets, the encapped VLAN header's VLAN ID + // metadata is different from that of normal VLAN header. + if (IS_MIRROR_COPY(standard_metadata) && + !IS_RESERVED_VLAN_ID(local_metadata.mirror_encap_vlan_id)) { + mark_to_drop(standard_metadata); + } else if (!IS_PACKET_IN_COPY(standard_metadata) && + !IS_RESERVED_VLAN_ID(local_metadata.vlan_id)) { + mark_to_drop(standard_metadata); + } } } } // control egress_vlan_checks @@ -95,9 +106,10 @@ control vlan_tag(inout headers_t headers, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { apply { - // TODO: Forward and Multicast packets should be vlan tagged - // but not mirrored packets. - if (!IS_RESERVED_VLAN_ID(local_metadata.vlan_id)) { + if (!IS_RESERVED_VLAN_ID(local_metadata.vlan_id) && + !IS_MIRROR_COPY(standard_metadata)) { + // Mirroring encapsulates a series of headers, including a VLAN header. + // To seperate concerns, vlan encapping for mirroring is skipped here. headers.vlan.setValid(); headers.vlan.priority_code_point = 0; headers.vlan.drop_eligible_indicator = 0; diff --git a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt index 6f2eb7d2..4402123e 100644 --- a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt +++ b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt @@ -1422,8 +1422,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" - alias: "mirror_with_ipfix_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_vlan_tag_and_ipfix_encapsulation" + alias: "mirror_with_vlan_tag_and_ipfix_encapsulation" annotations: "@unsupported" } params { @@ -1433,6 +1433,52 @@ actions { name: "port_id_t" } } + params { + id: 2 + name: "monitor_failover_port" + type_name { + name: "port_id_t" + } + } + params { + id: 3 + name: "mirror_encap_src_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 4 + name: "mirror_encap_dst_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 6 + name: "mirror_encap_vlan_id" + bitwidth: 12 + } + params { + id: 7 + name: "mirror_encap_dst_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 8 + name: "mirror_encap_src_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 9 + name: "mirror_encap_udp_src_port" + bitwidth: 16 + } + params { + id: 10 + name: "mirror_encap_udp_dst_port" + bitwidth: 16 + } } actions { preamble { diff --git a/sai_p4/instantiations/google/middleblock.p4info.pb.txt b/sai_p4/instantiations/google/middleblock.p4info.pb.txt index 1ffc0403..49144381 100755 --- a/sai_p4/instantiations/google/middleblock.p4info.pb.txt +++ b/sai_p4/instantiations/google/middleblock.p4info.pb.txt @@ -1467,8 +1467,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" - alias: "mirror_with_ipfix_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_vlan_tag_and_ipfix_encapsulation" + alias: "mirror_with_vlan_tag_and_ipfix_encapsulation" annotations: "@unsupported" } params { @@ -1478,6 +1478,52 @@ actions { name: "port_id_t" } } + params { + id: 2 + name: "monitor_failover_port" + type_name { + name: "port_id_t" + } + } + params { + id: 3 + name: "mirror_encap_src_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 4 + name: "mirror_encap_dst_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 6 + name: "mirror_encap_vlan_id" + bitwidth: 12 + } + params { + id: 7 + name: "mirror_encap_dst_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 8 + name: "mirror_encap_src_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 9 + name: "mirror_encap_udp_src_port" + bitwidth: 16 + } + params { + id: 10 + name: "mirror_encap_udp_dst_port" + bitwidth: 16 + } } actions { preamble { diff --git a/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 b/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 index c9a61cff..4f10e692 100644 --- a/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 +++ b/sai_p4/instantiations/google/minimum_guaranteed_sizes.p4 @@ -91,8 +91,6 @@ #define ACL_INGRESS_QOS_TABLE_MINIMUM_GUARANTEED_SIZE 511 -#define ACL_INGRESS_QOS_TABLE_MINIMUM_GUARANTEED_SIZE 511 - #define ACL_INGRESS_COUNTING_TABLE_MINIMUM_GUARANTEED_SIZE 255 #ifdef SAI_INSTANTIATION_TOR diff --git a/sai_p4/instantiations/google/sai_pd.proto b/sai_p4/instantiations/google/sai_pd.proto index 9a264df1..1eeecf62 100755 --- a/sai_p4/instantiations/google/sai_pd.proto +++ b/sai_p4/instantiations/google/sai_pd.proto @@ -161,7 +161,8 @@ message MirrorSessionTableEntry { oneof action { MirrorAsIpv4ErspanAction mirror_as_ipv4_erspan = 1; // CAUTION: This action is not (yet) supported. - MirrorWithIpfixEncapsulationAction mirror_with_ipfix_encapsulation = 2; + MirrorWithVlanTagAndIpfixEncapsulationAction + mirror_with_vlan_tag_and_ipfix_encapsulation = 2; } } Action action = 2; @@ -892,8 +893,16 @@ message SetPortAndSrcMacAndVlanIdAction { } // CAUTION: This action is not (yet) supported. -message MirrorWithIpfixEncapsulationAction { - string monitor_port = 1; // Format::STRING +message MirrorWithVlanTagAndIpfixEncapsulationAction { + string monitor_port = 1; // Format::STRING + string monitor_failover_port = 2; // Format::STRING + string mirror_encap_src_mac = 3; // Format::MAC + string mirror_encap_dst_mac = 4; // Format::MAC + string mirror_encap_vlan_id = 6; // Format::HEX_STRING / 12 bits + string mirror_encap_dst_ip = 7; // Format::IPV6 / 128 bits + string mirror_encap_src_ip = 8; // Format::IPV6 / 128 bits + string mirror_encap_udp_src_port = 9; // Format::HEX_STRING / 16 bits + string mirror_encap_udp_dst_port = 10; // Format::HEX_STRING / 16 bits } message SetVrfAction { diff --git a/sai_p4/instantiations/google/test_tools/test_entries.cc b/sai_p4/instantiations/google/test_tools/test_entries.cc index 3e17ccca..dacb4054 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries.cc +++ b/sai_p4/instantiations/google/test_tools/test_entries.cc @@ -567,13 +567,21 @@ EntryBuilder& EntryBuilder::AddMirrorSessionTableEntry( *pd_entry.mutable_mirror_session_table_entry(); mirror_session_entry.mutable_match()->set_mirror_session_id( params.mirror_session_id); - sai::MirrorWithIpfixEncapsulationAction& action = + sai::MirrorWithVlanTagAndIpfixEncapsulationAction& action = *mirror_session_entry.mutable_action() - ->mutable_mirror_with_ipfix_encapsulation(); - action.set_monitor_port(params.mirror_egress_port); - // TODO: Fill in IPFIX params in table entry's action. + ->mutable_mirror_with_vlan_tag_and_ipfix_encapsulation(); + action.set_monitor_port(params.monitor_port); + // monitor_failover_port's effect is not modeled, so use mirror_egress_port + // as a dummy value to satisfy the action param requirement. + action.set_monitor_failover_port(params.monitor_port); + action.set_mirror_encap_src_mac(params.mirror_encap_src_mac); + action.set_mirror_encap_dst_mac(params.mirror_encap_dst_mac); + action.set_mirror_encap_vlan_id(params.mirror_encap_vlan_id); + action.set_mirror_encap_src_ip(params.mirror_encap_src_ip); + action.set_mirror_encap_dst_ip(params.mirror_encap_dst_ip); + action.set_mirror_encap_udp_src_port(params.mirror_encap_udp_src_port); + action.set_mirror_encap_udp_dst_port(params.mirror_encap_udp_dst_port); *entries_.add_entries() = std::move(pd_entry); - return *this; } diff --git a/sai_p4/instantiations/google/test_tools/test_entries.h b/sai_p4/instantiations/google/test_tools/test_entries.h index f453729b..55875e90 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries.h +++ b/sai_p4/instantiations/google/test_tools/test_entries.h @@ -84,11 +84,16 @@ void AbslStringify(Sink& sink, const IpVersion& ip_version) { // Parameters for generating a mirror session table entry with IPFIX // encapsulation. -// TODO: Adds data members with parameters needed for mirror -// encap. struct MirrorSessionParams { std::string mirror_session_id; - std::string mirror_egress_port; + std::string monitor_port; + std::string mirror_encap_src_mac; + std::string mirror_encap_dst_mac; + std::string mirror_encap_vlan_id; + std::string mirror_encap_src_ip; + std::string mirror_encap_dst_ip; + std::string mirror_encap_udp_src_port; + std::string mirror_encap_udp_dst_port; }; // Parameters for generating an ACL table entry to mark packets to be mirrored. diff --git a/sai_p4/instantiations/google/test_tools/test_entries_test.cc b/sai_p4/instantiations/google/test_tools/test_entries_test.cc index 09c43608..b5aec697 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries_test.cc +++ b/sai_p4/instantiations/google/test_tools/test_entries_test.cc @@ -443,8 +443,17 @@ TEST(EntryBuilder, AddMirrorSessionTableEntry) { pdpi::IrEntities entities, EntryBuilder() .AddMirrorSessionTableEntry(MirrorSessionParams{ - .mirror_session_id = "id", - .mirror_egress_port = "0", + .mirror_session_id = "mirror_session_id", + .monitor_port = "monitor_port", + .mirror_encap_src_mac = + netaddr::MacAddress(1, 1, 1, 1, 1, 1).ToString(), + .mirror_encap_dst_mac = + netaddr::MacAddress(1, 2, 3, 4, 5, 6).ToString(), + .mirror_encap_vlan_id = "0x123", + .mirror_encap_src_ip = "::1", + .mirror_encap_dst_ip = "::2", + .mirror_encap_udp_src_port = "0x1234", + .mirror_encap_udp_dst_port = "0x1235", }) .LogPdEntries() // TODO: Remove unsupported once the diff --git a/sai_p4/instantiations/google/tests/sai_p4_bmv2_mirroring_test.cc b/sai_p4/instantiations/google/tests/sai_p4_bmv2_mirroring_test.cc index 4a767297..d0e90e5d 100644 --- a/sai_p4/instantiations/google/tests/sai_p4_bmv2_mirroring_test.cc +++ b/sai_p4/instantiations/google/tests/sai_p4_bmv2_mirroring_test.cc @@ -17,9 +17,11 @@ #include #include "absl/log/check.h" +#include "absl/strings/str_cat.h" #include "glog/logging.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "gutil/proto_matchers.h" #include "gutil/status_matchers.h" #include "gutil/testing.h" #include "p4/v1/p4runtime.pb.h" @@ -27,6 +29,7 @@ #include "p4_pdpi/p4_runtime_session_extras.h" #include "p4_pdpi/packetlib/packetlib.h" #include "p4_pdpi/packetlib/packetlib.pb.h" +#include "p4_pdpi/packetlib/packetlib_matchers.h" #include "p4_pdpi/string_encodings/byte_string.h" #include "platforms/networking/p4/p4_infra/bmv2/bmv2.h" #include "sai_p4/instantiations/google/instantiations.h" @@ -38,13 +41,13 @@ namespace pins { namespace { using ::gutil::IsOkAndHolds; +using ::gutil::Partially; using ::orion::p4::test::Bmv2; using ::pdpi::HasPacketIn; using ::pdpi::ParsedPayloadIs; using ::testing::ElementsAre; using ::testing::EqualsProto; -using ::testing::Pair; -using ::testing::Property; +using ::testing::Key; using ::testing::SizeIs; using ::testing::UnorderedElementsAre; using ::testing::Values; @@ -52,8 +55,8 @@ using ::testing::Values; constexpr int kV1modelPortBitwidth = 9; // Returns an Ipv4 packet. -packetlib::Packet GetIpv4Packet() { - return gutil::ParseProtoOrDie( +packetlib::Packet GetIpv4PacketOrDie() { + packetlib::Packet packet = gutil::ParseProtoOrDie( R"pb( headers { ethernet_header { @@ -87,11 +90,14 @@ packetlib::Packet GetIpv4Packet() { } payload: "packet" )pb"); + CHECK_OK(packetlib::UpdateMissingComputedFields(packet)); // Crash ok + CHECK_OK(packetlib::PadPacketToMinimumSize(packet)); // Crash ok + return packet; } // Returns an Ipv6 packet. -packetlib::Packet GetIpv6Packet() { - return gutil::ParseProtoOrDie(R"pb( +packetlib::Packet GetIpv6PacketOrDie() { + packetlib::Packet packet = gutil::ParseProtoOrDie(R"pb( headers { ethernet_header { ethernet_destination: "02:03:04:05:06:77" @@ -120,24 +126,24 @@ packetlib::Packet GetIpv6Packet() { } payload: "ipv6 packet" )pb"); + CHECK_OK(packetlib::UpdateMissingComputedFields(packet)); // Crash ok + CHECK_OK(packetlib::PadPacketToMinimumSize(packet)); // Crash ok + return packet; } -packetlib::Packet GetIpPacket(sai::IpVersion ip_version) { +packetlib::Packet GetIpPacketOrDie(sai::IpVersion ip_version) { packetlib::Packet packet; switch (ip_version) { case sai::IpVersion::kIpv4: - packet = GetIpv4Packet(); + packet = GetIpv4PacketOrDie(); break; case sai::IpVersion::kIpv6: - packet = GetIpv6Packet(); + packet = GetIpv6PacketOrDie(); break; default: LOG(FATAL) << " Unsupported ip_version: " // Crash ok << static_cast(ip_version); } - - CHECK_OK(packetlib::UpdateMissingComputedFields(packet)); // Crash ok - CHECK_OK(packetlib::PadPacketToMinimumSize(packet)); // Crash ok return packet; } @@ -146,14 +152,14 @@ std::string BMv2Port(int port) { std::bitset(port)); } -struct MirroringTestParams { +struct MirroringTestConstants { int ingress_port; int mirror_egress_port; sai::MarkToMirrorParams marked_to_mirror_params; sai::MirrorSessionParams mirror_session_params; }; -MirroringTestParams GetDefaultMirroringTestParams() { +MirroringTestConstants GetMirroringTestConstants() { constexpr int kIngressPort = 6; constexpr int kMirrorEgressPort = 5; const std::string kMirrorSessionId = "mirror_session_id"; @@ -168,23 +174,115 @@ MirroringTestParams GetDefaultMirroringTestParams() { .mirror_session_params = { .mirror_session_id = kMirrorSessionId, - .mirror_egress_port = BMv2Port(kMirrorEgressPort), + .monitor_port = BMv2Port(kMirrorEgressPort), + .mirror_encap_src_mac = "00:08:08:08:08:08", + .mirror_encap_dst_mac = "01:09:09:09:09:09", + .mirror_encap_vlan_id = "0x123", + .mirror_encap_src_ip = "::1", + .mirror_encap_dst_ip = "::2", + .mirror_encap_udp_src_port = "0x1234", + .mirror_encap_udp_dst_port = "0x1283", }, }; } -class MirroringTest : public ::testing::TestWithParam {}; +// Asserts headers are IPFIX-extended conformant and verify headers fields are +// populated with values that match with `mirror_session_params` (such as IPV6 +// header's dst ip value should be mirror_session_params.mirror_encap_dst_id) or +// match with hardcoded value in p4 modeling (such as IPV6's hop_limit of 16). +// Fields outside of P4-modeling's control, such as PSAMP header's +// observation_time are not checked. +void AssertPacketIsVlanTaggedAndIpfixEncappedWithMirrorSessionParams( + const packetlib::Packet& packet, + const sai::MirrorSessionParams& mirror_session_params) { + using ::packetlib::HasHeaderCase; + ASSERT_THAT(packet.headers(), + ElementsAre(HasHeaderCase(packetlib::Header::kEthernetHeader), + HasHeaderCase(packetlib::Header::kVlanHeader), + HasHeaderCase(packetlib::Header::kIpv6Header), + HasHeaderCase(packetlib::Header::kUdpHeader), + HasHeaderCase(packetlib::Header::kIpfixHeader), + HasHeaderCase(packetlib::Header::kPsampHeader))); + + packetlib::Header ethernet_header; + ethernet_header.mutable_ethernet_header()->set_ethertype("0x8100"); + ethernet_header.mutable_ethernet_header()->set_ethernet_destination( + mirror_session_params.mirror_encap_dst_mac); + ethernet_header.mutable_ethernet_header()->set_ethernet_source( + mirror_session_params.mirror_encap_src_mac); + + packetlib::Header vlan_header; + vlan_header.mutable_vlan_header()->set_priority_code_point("0x0"); + vlan_header.mutable_vlan_header()->set_drop_eligible_indicator("0x0"); + vlan_header.mutable_vlan_header()->set_vlan_identifier( + mirror_session_params.mirror_encap_vlan_id); + vlan_header.mutable_vlan_header()->set_ethertype("0x86dd"); + + packetlib::Header ipv6_header; + ipv6_header.mutable_ipv6_header()->set_dscp("0x00"); + ipv6_header.mutable_ipv6_header()->set_ecn("0x0"); + ipv6_header.mutable_ipv6_header()->set_flow_label("0x00000"); + ipv6_header.mutable_ipv6_header()->set_hop_limit("0x10"); + ipv6_header.mutable_ipv6_header()->set_version("0x6"); + // IP_PROTOCOL for UDP. + ipv6_header.mutable_ipv6_header()->set_next_header("0x11"); + ipv6_header.mutable_ipv6_header()->set_ipv6_source( + mirror_session_params.mirror_encap_src_ip); + ipv6_header.mutable_ipv6_header()->set_ipv6_destination( + mirror_session_params.mirror_encap_dst_ip); + ASSERT_OK_AND_ASSIGN(const int packet_size, + packetlib::PacketSizeInBytes(packet)); + // Payload length field for both IP and UDP headers is the packet length + // subtracted by the header length of IP header and of headers in front of IP + // header (ETHERNET and VLAN). + const int payload_length = packet_size - /*ETHERNET header bytes*/ 14 - + /*VLAN header bytes*/ 4 - + /*IPV6 header bytes*/ 40; + + ipv6_header.mutable_ipv6_header()->set_payload_length( + packetlib::IpPayloadLength(payload_length)); + + packetlib::Header udp_header; + udp_header.mutable_udp_header()->set_source_port( + mirror_session_params.mirror_encap_udp_src_port); + udp_header.mutable_udp_header()->set_destination_port( + mirror_session_params.mirror_encap_udp_dst_port); + // UDP checksum for mirrored packets is set to 0. + udp_header.mutable_udp_header()->set_checksum("0x0000"); + udp_header.mutable_udp_header()->set_length( + packetlib::IpPayloadLength(payload_length)); + ASSERT_THAT( + packet.headers(), + ElementsAre(EqualsProto(ethernet_header), EqualsProto(vlan_header), + EqualsProto(ipv6_header), EqualsProto(udp_header), + // Use Partially matchers to only check the presence + // of IPFIX and PSAMP headers since P4 modeling + // doesn't populate any of the field within these two + // headers. P4BAR also will not check IPFIX and PSAMP + // headers too. See b/318753700 for details. + Partially(EqualsProto(R"pb(ipfix_header {})pb")), + Partially(EqualsProto(R"pb(psamp_header {})pb")))); +} + +// Test fixture to verify packets are mirrored and encapped. +class MirrorAndEncapTest : public ::testing::TestWithParam {}; -TEST_P(MirroringTest, OnePacketEmittedWhenPacketIsMirroredAndDropped) { - constexpr sai::Instantiation kInstantiation = GetParam(); +TEST_P(MirrorAndEncapTest, OnePacketEmittedWhenPacketIsMirroredAndDropped) { + constexpr sai::Instantiation kInstantiation = sai::Instantiation::kTor; const sai::IpVersion ip_version = GetParam(); - MirroringTestParams mirroring_test_params = GetDefaultMirroringTestParams(); + const MirroringTestConstants mirroring_test_params = + GetMirroringTestConstants(); + SCOPED_TRACE(absl::StrCat( + "MirrorAndEncapTest for OnePacketEmittedWhenPacketIsMirroredAndDropped. " + "IP version: ", + ip_version)); ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); ASSERT_OK_AND_ASSIGN( std::vector pi_entities, sai::EntryBuilder() + .AddDisableVlanChecksEntry() .AddMarkToMirrorAclEntry( mirroring_test_params.marked_to_mirror_params) .AddMirrorSessionTableEntry( @@ -195,26 +293,41 @@ TEST_P(MirroringTest, OnePacketEmittedWhenPacketIsMirroredAndDropped) { /*allow_unsupported=*/true)); ASSERT_OK(pdpi::InstallPiEntities(bmv2.P4RuntimeSession(), pi_entities)); - EXPECT_THAT(bmv2.SendPacket(mirroring_test_params.ingress_port, - GetIpPacket(ip_version)), - IsOkAndHolds(UnorderedElementsAre( - Pair(mirroring_test_params.mirror_egress_port, - Property(&packetlib::Packets::packets, SizeIs(1)))))); + packetlib::Packet input_packet = GetIpPacketOrDie(ip_version); + ASSERT_OK_AND_ASSIGN( + const auto outputs, + bmv2.SendPacket(mirroring_test_params.ingress_port, input_packet)); + ASSERT_THAT(outputs, UnorderedElementsAre( + Key(mirroring_test_params.mirror_egress_port))); + ASSERT_THAT(outputs.at(mirroring_test_params.mirror_egress_port).packets(), + SizeIs(1)); + packetlib::Packet mirrored_packet = + outputs.at(mirroring_test_params.mirror_egress_port).packets(0); + + AssertPacketIsVlanTaggedAndIpfixEncappedWithMirrorSessionParams( + mirrored_packet, mirroring_test_params.mirror_session_params); + ASSERT_THAT(packetlib::ParsePacket(mirrored_packet.payload()), + EqualsProto(input_packet)); } -TEST_P(MirroringTest, +TEST_P(MirrorAndEncapTest, TwoPacketsEmittedWhenAPacketHitsMirroringEntryAndIsForwarded) { constexpr sai::Instantiation kInstantiation = GetParam(); const sai::IpVersion ip_version = GetParam(); - const MirroringTestParams mirroring_test_params = - GetDefaultMirroringTestParams(); + const MirroringTestConstants mirroring_test_params = + GetMirroringTestConstants(); constexpr int kForwardEgressPort = 9; - + SCOPED_TRACE( + absl::StrCat("MirrorAndEncapTest for " + "TwoPacketsEmittedWhenAPacketHitsMirroringEntryAndIsForwarde" + "d. IP version: ", + ip_version)); ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); ASSERT_OK_AND_ASSIGN( std::vector pi_entities, sai::EntryBuilder() + .AddDisableVlanChecksEntry() .AddEntriesForwardingIpPacketsToGivenPort( BMv2Port(kForwardEgressPort)) .AddMarkToMirrorAclEntry( @@ -227,29 +340,47 @@ TEST_P(MirroringTest, /*allow_unsupported=*/true)); ASSERT_OK(pdpi::InstallPiEntities(bmv2.P4RuntimeSession(), pi_entities)); - EXPECT_THAT(bmv2.SendPacket(mirroring_test_params.ingress_port, - GetIpPacket(ip_version)), - IsOkAndHolds(UnorderedElementsAre( - Pair(mirroring_test_params.mirror_egress_port, - Property(&packetlib::Packets::packets, SizeIs(1))), - Pair(kForwardEgressPort, - Property(&packetlib::Packets::packets, SizeIs(1)))))); + + packetlib::Packet input_packet = GetIpPacketOrDie(ip_version); + ASSERT_OK_AND_ASSIGN( + const auto outputs, + bmv2.SendPacket(mirroring_test_params.ingress_port, input_packet)); + + ASSERT_THAT(outputs, UnorderedElementsAre( + Key(mirroring_test_params.mirror_egress_port), + Key(kForwardEgressPort))); + ASSERT_THAT(outputs.at(mirroring_test_params.mirror_egress_port).packets(), + SizeIs(1)); + ASSERT_THAT(outputs.at(kForwardEgressPort).packets(), SizeIs(1)); + + packetlib::Packet mirrored_packet = + outputs.at(mirroring_test_params.mirror_egress_port).packets(0); + AssertPacketIsVlanTaggedAndIpfixEncappedWithMirrorSessionParams( + mirrored_packet, mirroring_test_params.mirror_session_params); + ASSERT_THAT(packetlib::ParsePacket(mirrored_packet.payload()), + EqualsProto(input_packet)); } TEST_P( - MirroringTest, + MirrorAndEncapTest, ThreePacketsEmittedWhenAPacketHitsMirroringEntryAndIsForwardedAndPunted) { constexpr sai::Instantiation kInstantiation = GetParam(); const sai::IpVersion ip_version = GetParam(); - const MirroringTestParams mirroring_test_params = - GetDefaultMirroringTestParams(); + const MirroringTestConstants mirroring_test_params = + GetMirroringTestConstants(); constexpr int kForwardEgressPort = 19; - ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); + SCOPED_TRACE( + absl::StrCat("MirrorAndEncapTest for " + "ThreePacketsEmittedWhenAPacketHitsMirroringEntryAndIsForwar" + "dedAndPunted. IP version: ", + ip_version)); + ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); ASSERT_OK_AND_ASSIGN( std::vector pi_entities, sai::EntryBuilder() + .AddDisableVlanChecksEntry() .AddEntriesForwardingIpPacketsToGivenPort( BMv2Port(kForwardEgressPort)) .AddMarkToMirrorAclEntry( @@ -263,20 +394,31 @@ TEST_P( /*allow_unsupported=*/true)); ASSERT_OK(pdpi::InstallPiEntities(bmv2.P4RuntimeSession(), pi_entities)); - packetlib::Packet input_packet = GetIpPacket(ip_version); - EXPECT_THAT(bmv2.SendPacket(mirroring_test_params.ingress_port, input_packet), - IsOkAndHolds(UnorderedElementsAre( - Pair(mirroring_test_params.mirror_egress_port, - Property(&packetlib::Packets::packets, SizeIs(1))), - Pair(kForwardEgressPort, - Property(&packetlib::Packets::packets, SizeIs(1)))))); + packetlib::Packet input_packet = GetIpPacketOrDie(ip_version); + ASSERT_OK_AND_ASSIGN( + const auto outputs, + bmv2.SendPacket(mirroring_test_params.ingress_port, input_packet)); + ASSERT_THAT(outputs, UnorderedElementsAre( + Key(mirroring_test_params.mirror_egress_port), + Key(kForwardEgressPort))); + ASSERT_THAT(outputs.at(mirroring_test_params.mirror_egress_port).packets(), + SizeIs(1)); + ASSERT_THAT(outputs.at(kForwardEgressPort).packets(), SizeIs(1)); + + const packetlib::Packet mirrored_packet = + outputs.at(mirroring_test_params.mirror_egress_port).packets(0); + + AssertPacketIsVlanTaggedAndIpfixEncappedWithMirrorSessionParams( + mirrored_packet, mirroring_test_params.mirror_session_params); + ASSERT_THAT(packetlib::ParsePacket(mirrored_packet.payload()), + EqualsProto(input_packet)); EXPECT_THAT(bmv2.P4RuntimeSession().ReadStreamChannelResponsesAndFinish(), IsOkAndHolds(ElementsAre( HasPacketIn(ParsedPayloadIs(EqualsProto(input_packet)))))); } -INSTANTIATE_TEST_SUITE_P(DifferentIpVersions, MirroringTest, +INSTANTIATE_TEST_SUITE_P(DifferentIpVersions, MirrorAndEncapTest, Values(sai::IpVersion::kIpv4, sai::IpVersion::kIpv6)); } // namespace diff --git a/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc b/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc index f4d760fd..a9a68f46 100644 --- a/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc +++ b/sai_p4/instantiations/google/tests/sai_p4_bmv2_vlan_test.cc @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include #include "absl/container/flat_hash_map.h" @@ -48,16 +49,17 @@ using ::orion::p4::test::Bmv2; using ::packetlib::HasHeaderCase; using ::pdpi::HasPacketIn; using ::pdpi::ParsedPayloadIs; -using ::testing::_; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::Key; +using ::testing::SizeIs; using PacketsByPort = absl::flat_hash_map; using VlanTest = testing::TestWithParam; -constexpr int kIngressPort = 42; +constexpr int kIngressPort = 2; +constexpr absl::string_view kIngressPortProto = "\002"; constexpr int kEgressPort = 1; constexpr absl::string_view kEgressPortProto = "\001"; @@ -661,7 +663,7 @@ TEST(VlanTest, SettingNonReservedVidInRifWithVlanChecksResultsInDrop) { } TEST(VlanTest, SettingVid4095InRifResultsOutputPacketWithNoVlanTag) { - const sai::Instantiation kInstantiation = sai::Instantiation::kExperimentalTor; + const sai::Instantiation kInstantiation = sai::Instantiation::kTor; const pdpi::IrP4Info kIrP4Info = sai::GetIrP4Info(kInstantiation); ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); @@ -712,5 +714,136 @@ INSTANTIATE_TEST_SUITE_P( return InstantiationToString(info.param); }); +sai::MirrorSessionParams GetMirrorSessionParamsForTest() { + return sai::MirrorSessionParams{ + .mirror_session_id = "mirror_session_id", + .monitor_port = std::string(kEgressPortProto), + .mirror_encap_src_mac = "00:08:08:08:08:08", + .mirror_encap_dst_mac = "01:09:09:09:09:09", + .mirror_encap_vlan_id = "0x123", + .mirror_encap_src_ip = "::1", + .mirror_encap_dst_ip = "::2", + .mirror_encap_udp_src_port = "0x1234", + .mirror_encap_udp_dst_port = "0x1283", + }; +} + +// When VLAN checks are enabled, vlan-encapped mirror packets with non-reserved +// vlan id should not be mirrored. +// Only test ToR instantiation since only it supports +// acl_ingress_mirror_and_redirect_table. +TEST(MirrorVlanTest, + IpfixEncappedMirroredPktWithNonReservedVidIsDroppedWhenVlanChecksEnabled) { + const sai::Instantiation kInstantiation = sai::Instantiation::kTor; + ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); + sai::MirrorSessionParams mirror_session_params = + GetMirrorSessionParamsForTest(); + // Encap the mirrored packet with non-reserved vlan id. + mirror_session_params.mirror_encap_vlan_id = "0x123"; + + ASSERT_OK_AND_ASSIGN( + std::vector pi_entities, + sai::EntryBuilder() + .AddMirrorSessionTableEntry(mirror_session_params) + .AddMarkToMirrorAclEntry(sai::MarkToMirrorParams{ + .ingress_port = std::string(kIngressPortProto), + .mirror_session_id = mirror_session_params.mirror_session_id, + }) + .GetDedupedPiEntities(sai::GetIrP4Info(kInstantiation), + /*allow_unsupported=*/true)); + + ASSERT_OK(pdpi::InstallPiEntities(bmv2.P4RuntimeSession(), pi_entities)); + packetlib::Packet input_packet = GetIpv4PacketOrDie(); + + ASSERT_OK_AND_ASSIGN(auto outputs, + bmv2.SendPacket(kIngressPort, input_packet)); + ASSERT_THAT(outputs, IsEmpty()); +} + +// When VLAN checks are enabled, vlan-encapped mirror packets with reserved +// vlan id should be mirrored. +// Only test ToR instantiation since only it supports +// acl_ingress_mirror_and_redirect_table. +TEST( + MirrorVlanTest, + IpfixEncappedMirroredCopyWithVid4095EgressWithoutTagWhenVlanChecksEnabled) { + const sai::Instantiation kInstantiation = sai::Instantiation::kTor; + ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); + sai::MirrorSessionParams mirror_session_params = + GetMirrorSessionParamsForTest(); + // Encap the mirrored packet with reserved vlan id. + mirror_session_params.mirror_encap_vlan_id = "0xfff"; + + ASSERT_OK_AND_ASSIGN( + std::vector pi_entities, + sai::EntryBuilder() + .AddMirrorSessionTableEntry(mirror_session_params) + .AddMarkToMirrorAclEntry(sai::MarkToMirrorParams{ + .ingress_port = std::string(kIngressPortProto), + .mirror_session_id = mirror_session_params.mirror_session_id, + }) + .GetDedupedPiEntities(sai::GetIrP4Info(kInstantiation), + /*allow_unsupported=*/true)); + + ASSERT_OK(pdpi::InstallPiEntities(bmv2.P4RuntimeSession(), pi_entities)); + packetlib::Packet input_packet = GetIpv4PacketOrDie(); + + ASSERT_OK_AND_ASSIGN(auto outputs, + bmv2.SendPacket(kIngressPort, input_packet)); + ASSERT_THAT(outputs, ElementsAre(Key(kEgressPort))); + ASSERT_THAT(outputs[kEgressPort].packets(), SizeIs(1)); + EXPECT_THAT(outputs[kEgressPort] + .packets() + .at(0) + .headers() + .at(1) + .vlan_header() + .vlan_identifier(), + Eq(mirror_session_params.mirror_encap_vlan_id)); +} + +// When VLAN checks are disabled, vlan-encapped mirror packets with VLAN ID are +// mirrored. +// Only test ToR instantiation since only it supports +// acl_ingress_mirror_and_redirect_table. +TEST( + MirrorVlanTest, + EncappedMirroredCopyWithNonReservedVidEgressWithTagWhenVlanChecksDisabled) { + const sai::Instantiation kInstantiation = sai::Instantiation::kTor; + ASSERT_OK_AND_ASSIGN(Bmv2 bmv2, sai::SetUpBmv2ForSaiP4(kInstantiation)); + + sai::MirrorSessionParams mirror_session_params = + GetMirrorSessionParamsForTest(); + // Encap the mirrored packet with non-reserved vlan id. + mirror_session_params.mirror_encap_vlan_id = "0x123"; + + ASSERT_OK_AND_ASSIGN( + std::vector pi_entities, + sai::EntryBuilder() + .AddDisableVlanChecksEntry() + .AddMirrorSessionTableEntry(GetMirrorSessionParamsForTest()) + .AddMarkToMirrorAclEntry(sai::MarkToMirrorParams{ + .ingress_port = std::string(kIngressPortProto), + .mirror_session_id = + GetMirrorSessionParamsForTest().mirror_session_id, + }) + .GetDedupedPiEntities(sai::GetIrP4Info(kInstantiation), + /*allow_unsupported=*/true)); + ASSERT_OK(pdpi::InstallPiEntities(bmv2.P4RuntimeSession(), pi_entities)); + packetlib::Packet input_packet = GetIpv4PacketOrDie(); + + ASSERT_OK_AND_ASSIGN(auto outputs, + bmv2.SendPacket(kIngressPort, input_packet)); + ASSERT_THAT(outputs, ElementsAre(Key(kEgressPort))); + ASSERT_THAT(outputs[kEgressPort].packets(), SizeIs(1)); + EXPECT_THAT(outputs[kEgressPort] + .packets() + .at(0) + .headers() + .at(1) + .vlan_header() + .vlan_identifier(), + Eq(mirror_session_params.mirror_encap_vlan_id)); +} } // namespace } // namespace pins diff --git a/sai_p4/instantiations/google/tor.p4info.pb.txt b/sai_p4/instantiations/google/tor.p4info.pb.txt index c517243f..89ac4245 100644 --- a/sai_p4/instantiations/google/tor.p4info.pb.txt +++ b/sai_p4/instantiations/google/tor.p4info.pb.txt @@ -1884,8 +1884,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" - alias: "mirror_with_ipfix_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_vlan_tag_and_ipfix_encapsulation" + alias: "mirror_with_vlan_tag_and_ipfix_encapsulation" annotations: "@unsupported" } params { @@ -1895,6 +1895,52 @@ actions { name: "port_id_t" } } + params { + id: 2 + name: "monitor_failover_port" + type_name { + name: "port_id_t" + } + } + params { + id: 3 + name: "mirror_encap_src_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 4 + name: "mirror_encap_dst_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 6 + name: "mirror_encap_vlan_id" + bitwidth: 12 + } + params { + id: 7 + name: "mirror_encap_dst_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 8 + name: "mirror_encap_src_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 9 + name: "mirror_encap_udp_src_port" + bitwidth: 16 + } + params { + id: 10 + name: "mirror_encap_udp_dst_port" + bitwidth: 16 + } } actions { preamble { diff --git a/sai_p4/instantiations/google/unioned_p4info.pb.txt b/sai_p4/instantiations/google/unioned_p4info.pb.txt index 11fe4078..12b7e0d2 100644 --- a/sai_p4/instantiations/google/unioned_p4info.pb.txt +++ b/sai_p4/instantiations/google/unioned_p4info.pb.txt @@ -2035,8 +2035,8 @@ actions { actions { preamble { id: 16777245 - name: "ingress.mirror_session_lookup.mirror_with_ipfix_encapsulation" - alias: "mirror_with_ipfix_encapsulation" + name: "ingress.mirror_session_lookup.mirror_with_vlan_tag_and_ipfix_encapsulation" + alias: "mirror_with_vlan_tag_and_ipfix_encapsulation" annotations: "@unsupported" } params { @@ -2046,6 +2046,52 @@ actions { name: "port_id_t" } } + params { + id: 2 + name: "monitor_failover_port" + type_name { + name: "port_id_t" + } + } + params { + id: 3 + name: "mirror_encap_src_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 4 + name: "mirror_encap_dst_mac" + annotations: "@format(MAC_ADDRESS)" + bitwidth: 48 + } + params { + id: 6 + name: "mirror_encap_vlan_id" + bitwidth: 12 + } + params { + id: 7 + name: "mirror_encap_dst_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 8 + name: "mirror_encap_src_ip" + annotations: "@format(IPV6_ADDRESS)" + bitwidth: 128 + } + params { + id: 9 + name: "mirror_encap_udp_src_port" + bitwidth: 16 + } + params { + id: 10 + name: "mirror_encap_udp_dst_port" + bitwidth: 16 + } } actions { preamble {