diff --git a/sai_p4/fixed/tunnel_termination.p4 b/sai_p4/fixed/tunnel_termination.p4 index 13f11aa2..326e3581 100644 --- a/sai_p4/fixed/tunnel_termination.p4 +++ b/sai_p4/fixed/tunnel_termination.p4 @@ -37,9 +37,6 @@ control tunnel_termination(inout headers_t headers, // Models SAI_TUNNEL_TERM_TABLE. // Currently, we only model IPv6 decap of IP-in-IP packets - // TODO: Remove `@unsupported` annotation once the table is - // supported by the switch stack. - @unsupported @p4runtime_role(P4RUNTIME_ROLE_ROUTING) @id(IPV6_TUNNEL_TERMINATION_TABLE_ID) @unsupported 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 43b7485c..cb4863c3 100644 --- a/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt +++ b/sai_p4/instantiations/google/fabric_border_router.p4info.pb.txt @@ -133,7 +133,6 @@ tables { id: 33554507 name: "ingress.tunnel_termination.ipv6_tunnel_termination_table" alias: "ipv6_tunnel_termination_table" - annotations: "@unsupported" annotations: "@p4runtime_role(\"sdn_controller\")" annotations: "@unsupported" } diff --git a/sai_p4/instantiations/google/middleblock.p4info.pb.txt b/sai_p4/instantiations/google/middleblock.p4info.pb.txt index 5111e503..db13c52d 100755 --- a/sai_p4/instantiations/google/middleblock.p4info.pb.txt +++ b/sai_p4/instantiations/google/middleblock.p4info.pb.txt @@ -125,7 +125,6 @@ tables { id: 33554507 name: "ingress.tunnel_termination.ipv6_tunnel_termination_table" alias: "ipv6_tunnel_termination_table" - annotations: "@unsupported" annotations: "@p4runtime_role(\"sdn_controller\")" annotations: "@unsupported" } diff --git a/sai_p4/instantiations/google/test_tools/test_entries.cc b/sai_p4/instantiations/google/test_tools/test_entries.cc index e7e4a4e2..a53699fb 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries.cc +++ b/sai_p4/instantiations/google/test_tools/test_entries.cc @@ -500,6 +500,33 @@ EntryBuilder& EntryBuilder::AddMirrorSessionTableEntry( return *this; } +EntryBuilder& EntryBuilder::AddIpv6TunnelTerminationEntry( + const Ipv6TunnelTerminationParams& params) { + sai::TableEntry pd_entry; + sai::Ipv6TunnelTerminationTableEntry& tunnel_entry = + *pd_entry.mutable_ipv6_tunnel_termination_table_entry(); + if (params.dst_ipv6_value.has_value()) { + tunnel_entry.mutable_match()->mutable_dst_ipv6()->set_value( + params.dst_ipv6_value->ToString()); + } + if (params.dst_ipv6_mask.has_value()) { + tunnel_entry.mutable_match()->mutable_dst_ipv6()->set_mask( + params.dst_ipv6_mask->ToString()); + } + if (params.src_ipv6_value.has_value()) { + tunnel_entry.mutable_match()->mutable_src_ipv6()->set_value( + params.src_ipv6_value->ToString()); + } + if (params.src_ipv6_mask.has_value()) { + tunnel_entry.mutable_match()->mutable_src_ipv6()->set_mask( + params.src_ipv6_mask->ToString()); + } + tunnel_entry.mutable_action()->mutable_tunnel_decap(); + tunnel_entry.set_priority(1); + *entries_.add_entries() = std::move(pd_entry); + return *this; +} + EntryBuilder& EntryBuilder::AddMarkToMirrorAclEntry( const MarkToMirrorParams& params) { sai::TableEntry pd_entry; diff --git a/sai_p4/instantiations/google/test_tools/test_entries.h b/sai_p4/instantiations/google/test_tools/test_entries.h index 72aba8dc..9f295815 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries.h +++ b/sai_p4/instantiations/google/test_tools/test_entries.h @@ -105,6 +105,14 @@ struct MirrorSessionParams { std::string mirror_encap_udp_dst_port; }; +// Parameters for generating an ipv6 tunnel termination table entry. +struct Ipv6TunnelTerminationParams { + std::optional src_ipv6_value; + std::optional src_ipv6_mask; + std::optional dst_ipv6_value; + std::optional dst_ipv6_mask; +}; + // Parameters for generating an ACL table entry to mark packets to be mirrored. struct MarkToMirrorParams { std::string ingress_port; @@ -229,6 +237,8 @@ class EntryBuilder { EntryBuilder& AddIngressAclEntryRedirectingToMulticastGroup( int multicast_group_id, const MirrorAndRedirectMatchFields& match_fields = {}); + EntryBuilder& AddIpv6TunnelTerminationEntry( + const Ipv6TunnelTerminationParams& params); EntryBuilder& AddMirrorSessionTableEntry(const MirrorSessionParams& params); EntryBuilder& AddMarkToMirrorAclEntry(const MarkToMirrorParams& params); 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 4f7d5fd4..8610258b 100644 --- a/sai_p4/instantiations/google/test_tools/test_entries_test.cc +++ b/sai_p4/instantiations/google/test_tools/test_entries_test.cc @@ -245,6 +245,21 @@ TEST(EntryBuilder, AddVrfEntryAddsEntry) { EXPECT_THAT(entities.entities(), SizeIs(3)); } +TEST(EntryBuilder, AddIpv6TunnelTerminationEntryAddsEntry) { + pdpi::IrP4Info kIrP4Info = GetIrP4Info(Instantiation::kFabricBorderRouter); + sai::Ipv6TunnelTerminationParams params{ + .src_ipv6_value = netaddr::Ipv6Address(0x77, 0x4455, 0, 0, 0, 0, 0, 0), + .src_ipv6_mask = netaddr::Ipv6Address(0xFFFF, 0xFFFF, 0, 0, 0, 0, 0, 0), + .dst_ipv6_value = netaddr::Ipv6Address(0x11, 0x2233, 0, 0, 0, 0, 0, 0), + .dst_ipv6_mask = netaddr::Ipv6Address(0xFFFF, 0xFFFF, 0, 0, 0, 0, 0, 0)}; + ASSERT_OK_AND_ASSIGN(pdpi::IrEntities entities, + EntryBuilder() + .AddIpv6TunnelTerminationEntry(params) + .LogPdEntries() + .GetDedupedIrEntities(kIrP4Info)); + EXPECT_THAT(entities.entities(), SizeIs(1)); +} + TEST(EntryBuilder, AddEntryAdmittingAllPacketsToL3AddsEntry) { pdpi::IrP4Info kIrP4Info = GetIrP4Info(Instantiation::kFabricBorderRouter); ASSERT_OK_AND_ASSIGN(pdpi::IrEntities entities, diff --git a/sai_p4/instantiations/google/unioned_p4info.pb.txt b/sai_p4/instantiations/google/unioned_p4info.pb.txt index c69253af..39351752 100644 --- a/sai_p4/instantiations/google/unioned_p4info.pb.txt +++ b/sai_p4/instantiations/google/unioned_p4info.pb.txt @@ -133,7 +133,6 @@ tables { id: 33554507 name: "ingress.tunnel_termination.ipv6_tunnel_termination_table" alias: "ipv6_tunnel_termination_table" - annotations: "@unsupported" annotations: "@p4runtime_role(\"sdn_controller\")" annotations: "@unsupported" } diff --git a/tests/forwarding/tunnel_decap_test.cc b/tests/forwarding/tunnel_decap_test.cc new file mode 100644 index 00000000..fb0c8383 --- /dev/null +++ b/tests/forwarding/tunnel_decap_test.cc @@ -0,0 +1,431 @@ +// 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 +// +// https://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 "tests/forwarding/tunnel_decap_test.h" + +#include +#include +#include +#include + +#include "absl/log/check.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "dvaas/dataplane_validation.h" +#include "dvaas/test_vector.h" +#include "dvaas/validation_result.h" +#include "glog/logging.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gutil/status.h" // IWYU pragma: keep +#include "gutil/status.h" +#include "gutil/status_matchers.h" // IWYU pragma: keep +#include "lib/gnmi/gnmi_helper.h" +#include "net/google::protobuf/contrib/fixtures/proto-fixture-repository.h" +#include "p4/v1/p4runtime.pb.h" +#include "p4_pdpi/ir.pb.h" +#include "p4_pdpi/netaddr/ipv4_address.h" +#include "p4_pdpi/netaddr/ipv6_address.h" +#include "p4_pdpi/netaddr/mac_address.h" +#include "p4_pdpi/p4_runtime_session.h" +#include "p4_pdpi/p4_runtime_session_extras.h" +#include "p4_pdpi/packetlib/packetlib.h" +#include "p4_pdpi/packetlib/packetlib.pb.h" +#include "sai_p4/instantiations/google/sai_pd.pb.h" +#include "sai_p4/instantiations/google/test_tools/test_entries.h" +#include "thinkit/mirror_testbed.h" + +namespace pins_test { +namespace { + +using ::google::protobuf::contrib::fixtures::ProtoFixtureRepository; + +static const auto dst_mac = + netaddr::MacAddress(0x00, 0xaa, 0xbb, 0xcc, 0xcc, 0xdd); +static const auto tunnel_dst_ipv6 = netaddr::Ipv6Address( + 0x11, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff); +static const auto tunnel_src_ipv6 = netaddr::Ipv6Address( + 0x1122, 0x1122, 0x3344, 0x3344, 0x5566, 0x5566, 0x7788, 0x7788); +static const auto inner_dst_ipv4 = netaddr::Ipv4Address(0x10, 0, 0, 0x1); +static const auto incorrect_dst_ipv6 = netaddr::Ipv6Address( + 0x77, 0x2233, 0x4455, 0x5577, 0x8899, 0xaabb, 0xccdd, 0xeeff); +static const auto exact_match_mask = netaddr::Ipv6Address( + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff); +static const auto inner_dst_ipv6 = netaddr::Ipv6Address( + 0x2001, 0xdb8, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888); + +packetlib::Packet ParsePacketAndFillInComputedFields( + const ProtoFixtureRepository& repo, absl::string_view packet_pb) { + packetlib::Packet packet = repo.ParseTextOrDie(packet_pb); + CHECK_OK(packetlib::UpdateMissingComputedFields(packet)); + return packet; +} + +// Helper function to build a Ipv6 in Ipv6 packet +dvaas::PacketTestVector Ipv6InIpv6DecapTestVector( + const TunnelDecapTestVectorParams& packet_vector_param) { + ProtoFixtureRepository repo; + + repo.RegisterValue("@payload", dvaas::MakeTestPacketTagFromUniqueId(1) + + "Testing IPv6-in-Ipv6 packets") + .RegisterValue("@ingress_port", packet_vector_param.in_port) + .RegisterValue("@egress_port", packet_vector_param.out_port) + .RegisterValue("@inner_dst_ipv6", + packet_vector_param.inner_dst_ipv6.ToString()) + .RegisterValue("@dst_ipv6", packet_vector_param.dst_ipv6.ToString()) + .RegisterValue("@dst_mac", packet_vector_param.dst_mac.ToString()); + + dvaas::PacketTestVector test_vector = + repo.RegisterSnippetOrDie("@ethernet", R"pb( + ethernet_header { + ethernet_destination: @dst_mac, + ethernet_source: "00:00:22:22:00:00" + ethertype: "0x86dd" # ipv6 + } + )pb") + .RegisterSnippetOrDie("@ipv6", R"pb( + ipv6_header { + version: "0x6" + dscp: "0x1b" + ecn: "0x1" + flow_label: "0x12345" + # payload_length: filled in automatically. + next_header: "0x29" # next is ipv6 + hop_limit: 0x42 + ipv6_source: "1122:1122:3344:3344:5566:5566:7788:7788" + ipv6_destination: @dst_ipv6 + # ipv6_destination: "11:2233:4455:6677:8899:aabb:ccdd:eeff" + } + )pb") + .RegisterSnippetOrDie("@inner_ipv6", R"pb( + ipv6_header { + version: "0x6" + dscp: "0x1b" + ecn: "0x1" + flow_label: "0x12345" + next_header: "0x11" # UDP + hop_limit: "0x42" + ipv6_source: "2001:db8:3333:4444:5555:6666:7777:2222" + ipv6_destination: @inner_dst_ipv6 + } + )pb") + .RegisterSnippetOrDie("@udp", R"pb( + udp_header { source_port: "0x0014" destination_port: "0x000a" } + )pb") + .RegisterMessage("@input_packet", ParsePacketAndFillInComputedFields( + repo, + R"pb( + headers: @ethernet + headers: @ipv6 + headers: @inner_ipv6 + headers: @udp + payload: @payload + )pb")) + .RegisterMessage( + "@output_packet", ParsePacketAndFillInComputedFields(repo, R"pb( + headers: @ethernet { + ethernet_header { + ethernet_destination: "02:02:02:02:02:02" + ethernet_source: "06:05:04:03:02:01" + ethertype: "0x86dd" + } + } + headers: @inner_ipv6 { ipv6_header { hop_limit: "0x41" } } + headers: @udp + payload: @payload + )pb")) + .ParseTextOrDie(R"pb( + input { + type: DATAPLANE + packet { port: @ingress_port parsed: @input_packet } + } + acceptable_outputs { + packets { port: @egress_port parsed: @output_packet } + } + )pb"); + + return test_vector; +} + +// Helper function to build a Ipv4 in Ipv6 packet +dvaas::PacketTestVector Ipv4InIpv6DecapTestVector( + const TunnelDecapTestVectorParams& packet_vector_param) { + ProtoFixtureRepository repo; + + repo.RegisterValue("@payload", dvaas::MakeTestPacketTagFromUniqueId(1) + + "Testing IPv4-in-Ipv6 packets") + .RegisterValue("@ingress_port", packet_vector_param.in_port) + .RegisterValue("@egress_port", packet_vector_param.out_port) + .RegisterValue("@dst_ip", packet_vector_param.inner_dst_ipv4.ToString()) + .RegisterValue("@dst_ipv6", packet_vector_param.dst_ipv6.ToString()) + .RegisterValue("@dst_mac", packet_vector_param.dst_mac.ToString()) + .RegisterValue("@ttl", "0x10") + .RegisterValue("@decremented_ttl", "0x0f"); + + dvaas::PacketTestVector test_vector = + repo.RegisterSnippetOrDie("@ethernet", R"pb( + ethernet_header { + ethernet_destination: @dst_mac, + ethernet_source: "00:00:22:22:00:00" + ethertype: "0x86dd" # Udp + } + )pb") + .RegisterSnippetOrDie("@ipv6", R"pb( + ipv6_header { + version: "0x6" + dscp: "0x1b" + ecn: "0x1" + flow_label: "0x12345" + # payload_length: filled in automatically. + next_header: "0x04" # next is ipv4 + hop_limit: 0x42 + ipv6_source: "1122:1122:3344:3344:5566:5566:7788:7788" + ipv6_destination: @dst_ipv6 + } + )pb") + .RegisterSnippetOrDie("@ipv4", R"pb( + ipv4_header { + version: "0x4" + dscp: "0x1b" + ecn: "0x1" + ihl: "0x5" + identification: "0x0000" + flags: "0x0" + ttl: @ttl + fragment_offset: "0x0000" + # payload_length: filled in automatically. + protocol: "0x11" + ipv4_source: "10.0.0.8" + ipv4_destination: @dst_ip + } + )pb") + .RegisterSnippetOrDie("@udp", R"pb( + udp_header { source_port: "0x0014" destination_port: "0x000a" } + )pb") + .RegisterMessage("@input_packet", ParsePacketAndFillInComputedFields( + repo, + R"pb( + headers: @ethernet + headers: @ipv6 + headers: @ipv4 + headers: @udp + payload: @payload + )pb")) + .RegisterMessage( + "@output_packet", ParsePacketAndFillInComputedFields(repo, R"pb( + headers: @ethernet { + ethernet_header { + ethernet_destination: "02:02:02:02:02:02" + ethernet_source: "06:05:04:03:02:01" + ethertype: "0x0800" + } + } + headers: @ipv4 { ipv4_header { ttl: @decremented_ttl } } + headers: @udp + payload: @payload + )pb")) + .ParseTextOrDie(R"pb( + input { + type: DATAPLANE + packet { port: @ingress_port parsed: @input_packet } + } + acceptable_outputs { + packets { port: @egress_port parsed: @output_packet } + } + )pb"); + + return test_vector; +} + +// Helper routine to install L3 route +absl::StatusOr> InstallTunnelTermTable( + pdpi::P4RuntimeSession& switch_session, pdpi::IrP4Info& ir_p4info) { + std::vector pi_entities; + LOG(INFO) << "Installing Tunnel term table"; + sai::Ipv6TunnelTerminationParams params; + params.dst_ipv6_value = tunnel_dst_ipv6; + params.dst_ipv6_mask = exact_match_mask; + params.src_ipv6_value = tunnel_src_ipv6; + params.src_ipv6_mask = exact_match_mask; + + sai::EntryBuilder entry_builder = + sai::EntryBuilder().AddIpv6TunnelTerminationEntry(params); + + ASSIGN_OR_RETURN( + pi_entities, + entry_builder.LogPdEntries().GetDedupedPiEntities(ir_p4info)); + RETURN_IF_ERROR(pdpi::InstallPiEntities(switch_session, pi_entities)); + return pi_entities; +} + +// Helper routine to install L3 route +absl::Status InstallL3Route(pdpi::P4RuntimeSession& switch_session, + pdpi::IrP4Info& ir_p4info, std::string given_port, + sai::IpVersion ip_version) { + std::vector pi_entities; + LOG(INFO) << "Installing L3 route"; + + sai::EntryBuilder entry_builder = + sai::EntryBuilder() + .AddVrfEntry("vrf-1") + .AddPreIngressAclEntryAssigningVrfForGivenIpType( + "vrf-1", sai::IpVersion::kIpv6) + .AddDefaultRouteForwardingAllPacketsToGivenPort(given_port, + ip_version, "vrf-1") + .AddEntryAdmittingAllPacketsToL3(); + + ASSIGN_OR_RETURN( + pi_entities, + entry_builder.LogPdEntries().GetDedupedPiEntities(ir_p4info)); + RETURN_IF_ERROR(pdpi::InstallPiEntities(switch_session, pi_entities)); + return absl::OkStatus(); +} + +TEST_P(TunnelDecapTestFixture, BasicTunnelTermDecapv4Inv6) { + dvaas::DataplaneValidationParams dvaas_params = GetParam().dvaas_params; + + thinkit::MirrorTestbed& testbed = + GetParam().mirror_testbed->GetMirrorTestbed(); + + // Initialize the connection, clear all entities, and (for the SUT) push + // P4Info. + ASSERT_OK_AND_ASSIGN(std::unique_ptr sut_p4rt_session, + pdpi::P4RuntimeSession::Create(testbed.Sut())); + + ASSERT_OK(pdpi::ClearEntities(*sut_p4rt_session)); + + ASSERT_OK_AND_ASSIGN(pdpi::IrP4Info sut_ir_p4info, + pdpi::GetIrP4Info(*sut_p4rt_session)); + + // Get control ports to test on. + ASSERT_OK_AND_ASSIGN( + auto gnmi_stub_control, + GetParam().mirror_testbed->GetMirrorTestbed().Sut().CreateGnmiStub()); + ASSERT_OK_AND_ASSIGN(std::string in_port, + pins_test::GetAnyUpInterfacePortId(*gnmi_stub_control)); + ASSERT_OK_AND_ASSIGN(std::string out_port, + pins_test::GetAnyUpInterfacePortId(*gnmi_stub_control)); + + // Install L3 route entry on SUT + ASSERT_OK(InstallL3Route(*sut_p4rt_session.get(), sut_ir_p4info, out_port, + sai::IpVersion::kIpv4)); + + // Install tunnel term table entry on SUT + ASSERT_OK_AND_ASSIGN( + const auto tunnel_entities, + InstallTunnelTermTable(*sut_p4rt_session.get(), sut_ir_p4info)); + + LOG(INFO) << "Sending IPv4-in-IPv6 Packet from ing:" << in_port + << " to eng:" << out_port; + // Send IPv4-in-IPv6 Packet and Verify positive testcase dst-ipv6 is matching + pins_test::TunnelDecapTestVectorParams tunnel_v6_match{ + .in_port = in_port, + .out_port = out_port, + .dst_mac = dst_mac, + .inner_dst_ipv4 = inner_dst_ipv4, + .dst_ipv6 = tunnel_dst_ipv6}; + dvaas_params.packet_test_vector_override = { + Ipv4InIpv6DecapTestVector(tunnel_v6_match)}; + + ASSERT_OK_AND_ASSIGN( + dvaas::ValidationResult validation_result, + GetParam().dvaas->ValidateDataplane(testbed, dvaas_params)); + + // Log statistics and check that things succeeded. + validation_result.LogStatistics(); + EXPECT_OK(validation_result.HasSuccessRateOfAtLeast(1.0)); + + // Send IPv4-in-IPv6 with wrong dst_ipv6 Packet and Verify + pins_test::TunnelDecapTestVectorParams tunel_v6_mismatch{ + .in_port = in_port, + .out_port = out_port, + .dst_mac = dst_mac, + .inner_dst_ipv4 = inner_dst_ipv4, + .dst_ipv6 = incorrect_dst_ipv6}; + dvaas::PacketTestVector test_vector = + Ipv4InIpv6DecapTestVector(tunel_v6_mismatch); + + for (dvaas::SwitchOutput& output : + *test_vector.mutable_acceptable_outputs()) { + output.clear_packet_ins(); + output.clear_packets(); + } + + dvaas_params.packet_test_vector_override = {test_vector}; + + ASSERT_OK_AND_ASSIGN( + dvaas::ValidationResult validation_result1, + GetParam().dvaas->ValidateDataplane(testbed, dvaas_params)); + + // Log statistics and check that things succeeded. + validation_result1.LogStatistics(); + EXPECT_OK(validation_result1.HasSuccessRateOfAtLeast(1.0)); +} + +TEST_P(TunnelDecapTestFixture, BasicTunnelTermDecapv6Inv6) { + dvaas::DataplaneValidationParams dvaas_params = GetParam().dvaas_params; + thinkit::MirrorTestbed& testbed = + GetParam().mirror_testbed->GetMirrorTestbed(); + + // Initialize the connection, clear all entities, and (for the SUT) push + // P4Info. + ASSERT_OK_AND_ASSIGN(std::unique_ptr sut_p4rt_session, + pdpi::P4RuntimeSession::Create(testbed.Sut())); + + ASSERT_OK(pdpi::ClearEntities(*sut_p4rt_session)); + + ASSERT_OK_AND_ASSIGN(pdpi::IrP4Info sut_ir_p4info, + pdpi::GetIrP4Info(*sut_p4rt_session)); + + // Get control ports to test on. + ASSERT_OK_AND_ASSIGN( + auto gnmi_stub_control, + GetParam().mirror_testbed->GetMirrorTestbed().Sut().CreateGnmiStub()); + ASSERT_OK_AND_ASSIGN(std::string in_port, + pins_test::GetAnyUpInterfacePortId(*gnmi_stub_control)); + ASSERT_OK_AND_ASSIGN(std::string out_port, + pins_test::GetAnyUpInterfacePortId(*gnmi_stub_control)); + + // Install L3 route entry on SUT + ASSERT_OK(InstallL3Route(*sut_p4rt_session.get(), sut_ir_p4info, out_port, + sai::IpVersion::kIpv6)); + + // Install tunnel term table entry on SUT + ASSERT_OK_AND_ASSIGN( + const auto tunnel_entities, + InstallTunnelTermTable(*sut_p4rt_session.get(), sut_ir_p4info)); + + LOG(INFO) << "Sending IPv6-in-IPv6 Packet for packet match from in:" + << in_port << " to out:" << out_port; + // Send IPv6-in-IPv6 Packet and Verify positive testcase dst-ipv6 is matching + pins_test::TunnelDecapTestVectorParams tunnel_v6_match{ + .in_port = in_port, + .out_port = out_port, + .dst_mac = dst_mac, + .inner_dst_ipv6 = inner_dst_ipv6, + .dst_ipv6 = tunnel_dst_ipv6}; + dvaas_params.packet_test_vector_override = { + Ipv6InIpv6DecapTestVector(tunnel_v6_match)}; + + ASSERT_OK_AND_ASSIGN( + dvaas::ValidationResult validation_result, + GetParam().dvaas->ValidateDataplane(testbed, dvaas_params)); + + // Log statistics and check that things succeeded. + validation_result.LogStatistics(); + EXPECT_OK(validation_result.HasSuccessRateOfAtLeast(1.0)); +} + +} // namespace +} // namespace pins_test diff --git a/tests/forwarding/tunnel_decap_test.h b/tests/forwarding/tunnel_decap_test.h new file mode 100644 index 00000000..341ea9b0 --- /dev/null +++ b/tests/forwarding/tunnel_decap_test.h @@ -0,0 +1,65 @@ +// 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 +// +// https://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 PINS_TESTS_FORWARDING_TUNNEL_DECAP_TEST_H_ +#define PINS_TESTS_FORWARDING_TUNNEL_DECAP_TEST_H_ + +#include +#include +#include + +#include "dvaas/dataplane_validation.h" +#include "dvaas/test_vector.pb.h" +#include "gtest/gtest.h" +#include "p4/config/v1/p4info.pb.h" +#include "p4_pdpi/netaddr/ipv4_address.h" +#include "p4_pdpi/netaddr/ipv6_address.h" +#include "p4_pdpi/netaddr/mac_address.h" +#include "sai_p4/instantiations/google/test_tools/test_entries.h" +#include "thinkit/mirror_testbed_fixture.h" + +namespace pins_test { + +struct TunnelDecapTestParams { + // Using a shared_ptr because parameterized tests require objects to be + // copyable. + std::shared_ptr mirror_testbed; + // Pushed to the SUT if given, otherwise assumes the correct one is already + // configured. + std::optional sut_p4info; + std::string test_name; + dvaas::DataplaneValidationParams dvaas_params; + std::shared_ptr dvaas; +}; + +struct TunnelDecapTestVectorParams { + std::string in_port; + std::string out_port; + netaddr::MacAddress dst_mac; + netaddr::Ipv4Address inner_dst_ipv4; + netaddr::Ipv6Address inner_dst_ipv6; + netaddr::Ipv6Address dst_ipv6; +}; + +class TunnelDecapTestFixture + : public testing::TestWithParam { + public: + void SetUp() override { GetParam().mirror_testbed->SetUp(); } + + void TearDown() override { GetParam().mirror_testbed->TearDown(); } +}; + +} // namespace pins_test + +#endif // PINS_TESTS_FORWARDING_TUNNEL_DECAP_TEST_H_