diff --git a/tests/forwarding/BUILD.bazel b/tests/forwarding/BUILD.bazel index 281e623c..25e06638 100644 --- a/tests/forwarding/BUILD.bazel +++ b/tests/forwarding/BUILD.bazel @@ -12,6 +12,7 @@ cc_library( "//gutil:status", "//gutil:status_matchers", "//gutil:testing", + "//lib/gnmi:gnmi_helper", "//p4_pdpi:ir", "//p4_pdpi:p4_runtime_session", "//tests/lib:p4info_helper", @@ -80,36 +81,19 @@ cc_library( alwayslink = True, ) -cc_library( - name = "p4_blackbox_fixture", - testonly = True, - hdrs = ["p4_blackbox_fixture.h"], - deps = [ - "//gutil:status_matchers", - "//lib/gnmi:gnmi_helper", - "//p4_pdpi:p4_runtime_session", - "//p4_pdpi:pd", - "//sai_p4/instantiations/google:sai_p4info_cc", - "//sai_p4/instantiations/google:sai_pd_cc_proto", - "//thinkit:mirror_testbed", - "//thinkit:mirror_testbed_fixture", - "@com_github_p4lang_p4runtime//:p4info_cc_proto", - "@com_google_googletest//:gtest", - ], -) - cc_library( name = "smoke_test", testonly = True, srcs = ["smoke_test.cc"], hdrs = ["smoke_test.h"], deps = [ - ":p4_blackbox_fixture", + ":mirror_blackbox_test_fixture", ":test_data", "//gutil:proto_matchers", "//gutil:status_matchers", "//gutil:testing", - "//p4_pdpi:p4_runtime_session", + "//lib/gnmi:gnmi_helper", + "//p4_pdpi:p4_runtime_session", "//p4_pdpi:pd", "//sai_p4/instantiations/google:sai_p4info_cc", "//sai_p4/instantiations/google:sai_pd_cc_proto", @@ -174,6 +158,7 @@ cc_library( srcs = ["l3_admit_test.cc"], hdrs = ["l3_admit_test.h"], deps = [ + ":mirror_blackbox_test_fixture", ":util", "//gutil:proto", "//gutil:status_matchers", @@ -185,7 +170,8 @@ cc_library( "//p4_pdpi/packetlib:packetlib_cc_proto", "//sai_p4/instantiations/google:instantiations", "//sai_p4/instantiations/google:sai_p4info_cc", - "//tests/lib:p4rt_fixed_table_programming_helper", + "//tests/lib:p4info_helper", + "//tests/lib:p4rt_fixed_table_programming_helper", "//tests/lib:packet_in_helper", "//thinkit:mirror_testbed_fixture", "@com_github_google_glog//:glog", @@ -296,3 +282,20 @@ cc_library( ], alwayslink = True, ) + +cc_library( + name = "mirror_blackbox_test_fixture", + testonly = True, + srcs = ["mirror_blackbox_test_fixture.cc"], + hdrs = ["mirror_blackbox_test_fixture.h"], + deps = [ + "//gutil:status_matchers", + "//p4_pdpi:p4_runtime_session", + "//tests/lib:switch_test_setup_helpers", + "//thinkit:mirror_testbed", + "//thinkit:mirror_testbed_fixture", + "//sai_p4/instantiations/google:sai_p4info_cc", + "//sai_p4/instantiations/google:sai_pd_cc_proto", + "@com_google_googletest//:gtest", + ], +) diff --git a/tests/forwarding/l3_admit_test.cc b/tests/forwarding/l3_admit_test.cc index 97d6c8a1..c756e4e0 100644 --- a/tests/forwarding/l3_admit_test.cc +++ b/tests/forwarding/l3_admit_test.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include "absl/status/status.h" @@ -27,6 +28,7 @@ #include "glog/logging.h" #include "gmock/gmock.h" #include "gutil/proto.h" +#include "gtest/gtest.h" #include "gutil/status_matchers.h" #include "lib/gnmi/gnmi_helper.h" #include "p4/v1/p4runtime.pb.h" @@ -36,6 +38,7 @@ #include "p4_pdpi/packetlib/packetlib.h" #include "p4_pdpi/packetlib/packetlib.pb.h" #include "tests/forwarding/util.h" +#include "tests/lib/p4info_helper.h" #include "tests/lib/p4rt_fixed_table_programming_helper.h" #include "tests/lib/packet_in_helper.h" #include "thinkit/mirror_testbed_fixture.h" @@ -46,53 +49,658 @@ namespace { absl::Status AddAndSetDefaultVrf(pdpi::P4RuntimeSession& session, const pdpi::IrP4Info& ir_p4info, const std::string& vrf_id) { - pdpi::IrWriteRequest ir_write_request; + LOG(INFO) << "Assigning all packets to VRF " << vrf_id << "."; + pdpi::IrUpdate set_vrf_ir_update; RETURN_IF_ERROR(gutil::ReadProtoFromString( absl::Substitute(R"pb( - updates { - type: INSERT - table_entry { - table_name: "vrf_table" - matches { + type: INSERT + table_entry { + table_name: "acl_pre_ingress_table" + priority: 2000 + action { + name: "set_vrf" + params { name: "vrf_id" - exact { str: "$0" } + value { str: "$0" } } - action { name: "no_action" } } } - updates { - type: INSERT - table_entry { - table_name: "acl_pre_ingress_table" - priority: 2000 - action { - name: "set_vrf" - params { - name: "vrf_id" - value { str: "$0" } - } + )pb", + vrf_id), + &set_vrf_ir_update)); + p4::v1::WriteRequest pi_write_request; + ASSIGN_OR_RETURN(*pi_write_request.add_updates(), + VrfTableUpdate(ir_p4info, p4::v1::Update::INSERT, vrf_id)); + ASSIGN_OR_RETURN(*pi_write_request.add_updates(), + pdpi::IrUpdateToPi(ir_p4info, set_vrf_ir_update)); + return pdpi::SetMetadataAndSendPiWriteRequest(&session, pi_write_request); +} + +absl::Status AllowVrfTrafficToDstMac(pdpi::P4RuntimeSession& session, + const pdpi::IrP4Info& ir_p4info, + const std::string& dst_mac, + const std::string& vrf_id) { + LOG(INFO) << "Assigning " << dst_mac << " packets with to " << vrf_id << "."; + pdpi::IrUpdate set_vrf_ir_update; + RETURN_IF_ERROR(gutil::ReadProtoFromString( + absl::Substitute(R"pb( + type: INSERT + table_entry { + table_name: "acl_pre_ingress_table" + matches { + name: "dst_mac" + ternary { + value { mac: "$0" } + mask { mac: "ff:ff:ff:ff:ff:ff" } + } + } + priority: 2000 + action { + name: "set_vrf" + params { + name: "vrf_id" + value { str: "$1" } } } } )pb", - vrf_id), + dst_mac, vrf_id), + &set_vrf_ir_update)); + + p4::v1::WriteRequest pi_write_request; + ASSIGN_OR_RETURN(*pi_write_request.add_updates(), + VrfTableUpdate(ir_p4info, p4::v1::Update::INSERT, vrf_id)); + ASSIGN_OR_RETURN(*pi_write_request.add_updates(), + pdpi::IrUpdateToPi(ir_p4info, set_vrf_ir_update)); + return pdpi::SetMetadataAndSendPiWriteRequest(&session, pi_write_request); +} + +absl::Status PuntAllPacketsToController(pdpi::P4RuntimeSession& session, + const pdpi::IrP4Info& ir_p4info) { + pdpi::IrWriteRequest ir_write_request; + RETURN_IF_ERROR(gutil::ReadProtoFromString( + R"pb( + updates { + type: INSERT + table_entry { + table_name: "acl_ingress_table" + priority: 2 + action { + name: "acl_trap", + params { + name: "qos_queue" + value { str: "0x1" } + } + } + } + } + )pb", &ir_write_request)); + ASSIGN_OR_RETURN(p4::v1::WriteRequest pi_write_request, pdpi::IrWriteRequestToPi(ir_p4info, ir_write_request)); return pdpi::SetMetadataAndSendPiWriteRequest(&session, pi_write_request); } + +// L3 routing configurations that can be shared when generating the L3 routing +// flows. +struct L3Route { + std::string vrf_id; + + std::string switch_mac; + std::pair switch_ip; + + std::string peer_port; + std::string peer_mac; + std::string peer_ip; + + std::string router_interface_id; + std::string nexthop_id; +}; + +absl::Status AddL3Route(pdpi::P4RuntimeSession& session, + const pdpi::IrP4Info& ir_p4info, + const L3Route& options) { + LOG(INFO) << absl::StreamFormat( + "Adding L3 route for %s, %s to port %s.", options.vrf_id, + absl::StrCat(options.switch_ip.first, "/", options.switch_ip.second), + options.peer_port); + + p4::v1::WriteRequest write_request; + LOG(INFO) << "Adding router interface for " << options.switch_mac + << " on port " << options.peer_port << "."; + ASSIGN_OR_RETURN( + *write_request.add_updates(), + RouterInterfaceTableUpdate(ir_p4info, p4::v1::Update::INSERT, + options.router_interface_id, options.peer_port, + options.switch_mac)); + ASSIGN_OR_RETURN(*write_request.add_updates(), + NeighborTableUpdate(ir_p4info, p4::v1::Update::INSERT, + options.router_interface_id, + options.peer_ip, options.peer_mac)); + ASSIGN_OR_RETURN( + *write_request.add_updates(), + NexthopTableUpdate(ir_p4info, p4::v1::Update::INSERT, options.nexthop_id, + options.router_interface_id, options.peer_ip)); + ASSIGN_OR_RETURN( + *write_request.add_updates(), + Ipv4TableUpdate(ir_p4info, p4::v1::Update::INSERT, + IpTableOptions{ + .vrf_id = options.vrf_id, + .dst_addr_lpm = options.switch_ip, + .action = IpTableOptions::Action::kSetNextHopId, + .nexthop_id = options.nexthop_id, + })); + + return pdpi::SetMetadataAndSendPiWriteRequest(&session, write_request); +} + +absl::Status AdmitL3Route(pdpi::P4RuntimeSession& session, + const pdpi::IrP4Info& ir_p4info, + const L3AdmitOptions& options) { + LOG(INFO) << "Admiting L3 packets with DST MAC: " << options.dst_mac.first + << " & " << options.dst_mac.second; + p4::v1::WriteRequest write_request; + ASSIGN_OR_RETURN( + *write_request.add_updates(), + L3AdmitTableUpdate(ir_p4info, p4::v1::Update::INSERT, options)); + return pdpi::SetMetadataAndSendPiWriteRequest(&session, write_request); +} + +absl::StatusOr UdpPacket(absl::string_view dst_mac, + absl::string_view dst_ip, + absl::string_view payload) { + packetlib::Packet packet; + RETURN_IF_ERROR(gutil::ReadProtoFromString( + absl::Substitute( + R"pb( + headers { + ethernet_header { + ethernet_destination: "$0" + ethernet_source: "00:00:22:22:00:00" + ethertype: "0x0800" + } + } + headers { + ipv4_header { + version: "0x4" + ihl: "0x5" + dscp: "0x1b" + ecn: "0x1" + identification: "0x0000" + flags: "0x0" + fragment_offset: "0x0000" + ttl: "0x10" + protocol: "0x11" # UDP + ipv4_source: "10.0.0.1" + ipv4_destination: "$1" + } + } + headers { + udp_header { source_port: "0x0014" destination_port: "0x000a" } + } + payload: "$2" + )pb", + dst_mac, dst_ip, payload), + &packet)); + return packetlib::SerializePacket(packet); +} + +absl::Status SendUdpPacket(pdpi::P4RuntimeSession& session, + const pdpi::IrP4Info& ir_p4info, + const std::string& port_id, int packet_count, + absl::string_view dst_mac, absl::string_view dst_ip, + absl::string_view payload) { + LOG(INFO) << absl::StreamFormat("Sending %d packets with %s, %s to port %s.", + packet_count, dst_mac, dst_ip, port_id); + ASSIGN_OR_RETURN(std::string packet, UdpPacket(dst_mac, dst_ip, payload)); + for (int i = 0; i < packet_count; ++i) { + RETURN_IF_ERROR(InjectEgressPacket(port_id, packet, ir_p4info, &session)); + } + return absl::OkStatus(); +} + } // namespace -TEST_P(L3AdmitTestFixture, L3PacketsAreRoutedWhenMacAddressIsInMyStation) { - LOG(INFO) << "Starting test."; +TEST_P(L3AdmitTestFixture, + DISABLED_L3PacketsAreRoutedOnlyWhenMacAddressIsInMyStation) { + // Punt all traffic arriving at the control switch, and collect them to verify + // forwarding. + std::unique_ptr packetio_control = + std::make_unique(&GetControlP4RuntimeSession(), + PacketInHelper::NoFilter); + ASSERT_OK( + PuntAllPacketsToController(GetControlP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + + // Add an L3 route to enable forwarding. + L3Route l3_route{ + .vrf_id = "vrf-1", + .switch_mac = "00:00:00:00:00:01", + .switch_ip = std::make_pair("10.0.0.1", 32), + .peer_port = "1", + .peer_mac = "00:00:00:00:00:02", + .peer_ip = "10.0.0.2", + .router_interface_id = "rif-1", + .nexthop_id = "nexthop-1", + }; + ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route.vrf_id)); + ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), l3_route)); + + // Admit only 1 MAC address to the forwaring pipeline. + ASSERT_OK(AdmitL3Route( + GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + L3AdmitOptions{ + .priority = 2070, + .dst_mac = std ::make_pair("00:01:02:03:04:05", "FF:FF:FF:FF:FF:FF"), + })); + + // Send 2 sets of packets to the switch. The first set of packets should not + // match the L3 admit MAC and therefore will be dropped. The second set of + // packet should match the L3 admit MAC and therefore get forwarded. + const int kNumberOfTestPacket = 100; + + // Send the "bad" packets first to give them the most time. + const std::string kBadPayload = + "Testing L3 forwarding. This packet should be dropped."; + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, + /*dst_mac=*/"00:aa:bb:cc:cc:dd", + /*dst_ip=*/"10.0.0.1", kBadPayload)); + + // Then send the "good" packets. + const std::string kGoodPayload = + "Testing L3 forwarding. This packet should arrive to packet in."; + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, + /*dst_mac=*/"00:01:02:03:04:05", + /*dst_ip=*/"10.0.0.1", kGoodPayload)); + + absl::Time timeout = absl::Now() + absl::Minutes(1); + int good_packet_count = 0; + int bad_packet_count = 0; + while (good_packet_count < kNumberOfTestPacket) { + if (packetio_control->HasPacketInMessage()) { + ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, + packetio_control->GetNextPacketInMessage()); + // Verify this is the packet we expect. + packetlib::Packet packet_in = + packetlib::ParsePacket(response.packet().payload()); + if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kGoodPayload) { + ++good_packet_count; + } else if (response.update_case() == + p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kBadPayload) { + ++bad_packet_count; + } else { + LOG(WARNING) << "Unexpected response: " << response.DebugString(); + } + } + + if (absl::Now() > timeout) { + LOG(ERROR) << "Reached timeout waiting on packets to arrive."; + break; + } + } + LOG(INFO) << "Done collecting packets."; + + EXPECT_EQ(good_packet_count, kNumberOfTestPacket); + EXPECT_EQ(bad_packet_count, 0); +} - // PacketIO handlers for both the SUT and control switch. - std::unique_ptr packetio_sut = - std::make_unique(p4rt_sut_switch_session_.get(), +TEST_P(L3AdmitTestFixture, L3AdmitCanUseMaskToAllowMultipleMacAddresses) { + // Punt all traffic arriving at the control switch, and collect them to verify + // forwarding. + std::unique_ptr packetio_control = + std::make_unique(&GetControlP4RuntimeSession(), PacketInHelper::NoFilter); + ASSERT_OK( + PuntAllPacketsToController(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + + // Add an L3 route to enable forwarding. + L3Route l3_route{ + .vrf_id = "vrf-1", + .switch_mac = "00:00:00:00:00:01", + .switch_ip = std::make_pair("10.0.0.1", 32), + .peer_port = "1", + .peer_mac = "00:00:00:00:00:02", + .peer_ip = "10.0.0.2", + .router_interface_id = "rif-1", + .nexthop_id = "nexthop-1", + }; + + ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route.vrf_id)); + ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route)); + + // Admit multiple MAC addresses into L3 routing with a single L3 admit rule. + ASSERT_OK(AdmitL3Route( + GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + L3AdmitOptions{ + .priority = 2070, + .dst_mac = std ::make_pair("00:01:02:03:00:05", "FF:FF:FF:FF:F0:FF"), + })); + + // Send 5 sets of packets to the switch each with a different MAC address that + // matches the L3Admit rule's mask. + const int kNumberOfTestPacket = 20; + const std::string kGoodPayload = + "Testing L3 forwarding. This packet should arrive to packet in."; + for (int i = 0; i < 5; ++i) { + std::string dst_mac = absl::StrFormat("00:01:02:03:%02d:05", i); + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, dst_mac, + /*dst_ip=*/"10.0.0.1", kGoodPayload)); + } + + absl::Time timeout = absl::Now() + absl::Minutes(1); + int good_packet_count = 0; + while (good_packet_count < 5 * kNumberOfTestPacket) { + if (packetio_control->HasPacketInMessage()) { + ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, + packetio_control->GetNextPacketInMessage()); + + // Verify this is the packet we expect. + packetlib::Packet packet_in = + packetlib::ParsePacket(response.packet().payload()); + if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kGoodPayload) { + ++good_packet_count; + } else { + LOG(WARNING) << "Unexpected response: " << response.DebugString(); + } + } + + if (absl::Now() > timeout) { + LOG(ERROR) << "Reached timeout waiting on packets to arrive."; + break; + } + } + + LOG(INFO) << "Done collecting packets."; + + EXPECT_EQ(good_packet_count, 5 * kNumberOfTestPacket); +} + +TEST_P(L3AdmitTestFixture, DISABLED_L3AdmitCanUseInPortToRestrictMacAddresses) { + // Punt all traffic arriving at the control switch, and collect them to verify + // forwarding. + std::unique_ptr packetio_control = + std::make_unique(&GetControlP4RuntimeSession(), + PacketInHelper::NoFilter); + ASSERT_OK( + PuntAllPacketsToController(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + + // Add an L3 route to enable forwarding. + L3Route l3_route{ + .vrf_id = "vrf-1", + .switch_mac = "00:00:00:00:00:01", + .switch_ip = std::make_pair("10.0.0.1", 32), + .peer_port = "1", + .peer_mac = "00:00:00:00:00:02", + .peer_ip = "10.0.0.2", + .router_interface_id = "rif-1", + .nexthop_id = "nexthop-1", + }; + ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route.vrf_id)); + ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route)); + + // Admit the MAC addresses only on port XYZ + ASSERT_OK(AdmitL3Route( + GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + L3AdmitOptions{ + .priority = 2070, + .dst_mac = std ::make_pair("00:01:02:03:00:05", "FF:FF:FF:FF:F0:FF"), + .in_port = "2", + })); + + // Send 2 sets of packets to the switch. The first set of packets should not + // match the L3 admit port and therefore will be dropped. The second set of + // packet should match the L3 admit port and therefore get forwarded. + const int kNumberOfTestPacket = 100; + + // Send the "bad" packets first to give them the most time. + const std::string kBadPayload = + "Testing L3 forwarding. This packet should be dropped."; + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, + /*dst_mac=*/"00:01:02:03:04:05", + /*dst_ip=*/"10.0.0.1", kBadPayload)); + + // Then send the "good" packets. + const std::string kGoodPayload = + "Testing L3 forwarding. This packet should arrive to packet in."; + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"2", kNumberOfTestPacket, + /*dst_mac=*/"00:01:02:03:04:05", + /*dst_ip=*/"10.0.0.1", kGoodPayload)); + + absl::Time timeout = absl::Now() + absl::Minutes(1); + int good_packet_count = 0; + int bad_packet_count = 0; + while (good_packet_count < kNumberOfTestPacket) { + if (packetio_control->HasPacketInMessage()) { + ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, + packetio_control->GetNextPacketInMessage()); + + // Verify this is the packet we expect. + packetlib::Packet packet_in = + packetlib::ParsePacket(response.packet().payload()); + if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kGoodPayload) { + ++good_packet_count; + } else if (response.update_case() == + p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kBadPayload) { + ++bad_packet_count; + } else { + LOG(WARNING) << "Unexpected response: " << response.DebugString(); + } + } + + if (absl::Now() > timeout) { + LOG(ERROR) << "Reached timeout waiting on packets to arrive."; + break; + } + } + LOG(INFO) << "Done collecting packets."; + + EXPECT_EQ(good_packet_count, kNumberOfTestPacket); + EXPECT_EQ(bad_packet_count, 0); +} + +TEST_P(L3AdmitTestFixture, L3PacketsCanBeRoutedWithOnlyARouterInterface) { + // Punt all traffic arriving at the control switch, and collect them to verify + // forwarding. std::unique_ptr packetio_control = - std::make_unique(p4rt_control_switch_session_.get(), + std::make_unique(&GetControlP4RuntimeSession(), PacketInHelper::NoFilter); +ASSERT_OK( + PuntAllPacketsToController(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + +// Add an L3 route to enable forwarding, but do not add an explicit L3Admit + // rule. + L3Route l3_route{ + .vrf_id = "vrf-1", + .switch_mac = "00:00:00:00:00:01", + .switch_ip = std::make_pair("10.0.0.1", 32), + .peer_port = "1", + .peer_mac = "00:00:00:00:00:02", + .peer_ip = "10.0.0.2", + .router_interface_id = "rif-1", + .nexthop_id = "nexthop-1", + }; + ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route.vrf_id)); + ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + l3_route)); + + // Send 1 set of packets to the switch using the switch's MAC address from the + // L3 route. + const int kNumberOfTestPacket = 100; + const std::string kGoodPayload = + "Testing L3 forwarding. This packet should arrive to packet in."; + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, + /*dst_mac=*/"00:00:00:00:00:01", + /*dst_ip=*/"10.0.0.1", kGoodPayload)); + + absl::Time timeout = absl::Now() + absl::Minutes(1); + int good_packet_count = 0; + while (good_packet_count < kNumberOfTestPacket) { + if (packetio_control->HasPacketInMessage()) { + ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, + packetio_control->GetNextPacketInMessage()); + + // Verify this is the packet we expect. + packetlib::Packet packet_in = + packetlib::ParsePacket(response.packet().payload()); + if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kGoodPayload) { + ++good_packet_count; + } else { + LOG(WARNING) << "Unexpected response: " << response.DebugString(); + } + } + + if (absl::Now() > timeout) { + LOG(ERROR) << "Reached timeout waiting on packets to arrive."; + break; + } + } + LOG(INFO) << "Done collecting packets."; + + EXPECT_EQ(good_packet_count, kNumberOfTestPacket); +} + +TEST_P(L3AdmitTestFixture, L3PacketsCanBeClassifiedByDestinationMac) { + // Only run this test if the ACL_PRE_INGRESS table supports matching on + // DST_MAC. + if (!TableHasMatchField(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + "acl_pre_ingress_table", "dst_mac")) { + GTEST_SKIP() + << "Skipping because ACL_PRE_INGRESS table can not match on DST_MAC."; + } + + // Punt all traffic arriving at the control switch, and collect them to verify + // forwarding. + std::unique_ptr packetio_control = + std::make_unique(&GetControlP4RuntimeSession(), + PacketInHelper::NoFilter); + ASSERT_OK( + PuntAllPacketsToController(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + + // This test uses 2 MAC addresses. Both will be admitted to L3 routing, but + // only one will get assigned a VRF ID. We expect packets receiving the + // VRF ID (i.e. good) to get routed, and the others (i.e. bad/drop) to get + // dropped. + std::string vrf_id = "vrf-1"; + std::string good_dst_mac = "00:00:00:00:00:03"; + std::string drop_dst_mac = "00:00:00:00:00:04"; + ASSERT_OK(AllowVrfTrafficToDstMac(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + good_dst_mac, vrf_id)); + ASSERT_OK(AdmitL3Route( + GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + L3AdmitOptions{ + .priority = 2070, + .dst_mac = std ::make_pair(good_dst_mac, "FF:FF:FF:FF:FF:FF"), + })); + ASSERT_OK(AdmitL3Route( + GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + L3AdmitOptions{ + .priority = 2070, + .dst_mac = std ::make_pair(drop_dst_mac, "FF:FF:FF:FF:FF:FF"), + })); + + // Add an L3 route to enable forwarding for 10.0.0.1/32 packets. + L3Route l3_route{ + .vrf_id = vrf_id, + .switch_mac = "00:00:00:00:00:01", + .switch_ip = std::make_pair("10.0.0.1", 32), + .peer_port = "1", + .peer_mac = "00:00:00:00:00:02", + .peer_ip = "10.0.0.2", + .router_interface_id = "rif-1", + .nexthop_id = "nexthop-1", + }; + ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), l3_route)); + + // Send 2 set of packets to the switch. One using the expected destination + // MAC (gets forwarded), and another using an unexpected destination MAC + // (gets dropped). + const int kNumberOfTestPacket = 100; + const std::string kGoodPayload = + "Testing L3 forwarding. This packet should arrive to packet in."; + const std::string kBadPayload = + "Testing L3 forwarding. This packet should be dropped."; + + // Send the "bad" packets first to give them the most time. + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, drop_dst_mac, + /*dst_ip=*/"10.0.0.1", kBadPayload)); + ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + /*port_id=*/"1", kNumberOfTestPacket, good_dst_mac, + /*dst_ip=*/"10.0.0.1", kGoodPayload)); + + // Wait for all the good packets to get punted back on the control switch. + absl::Time timeout = absl::Now() + absl::Minutes(1); + int good_packet_count = 0; + int bad_packet_count = 0; + while (good_packet_count < kNumberOfTestPacket) { + if (packetio_control->HasPacketInMessage()) { + ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, + packetio_control->GetNextPacketInMessage()); + + // Verify this is the packet we expect. + packetlib::Packet packet_in = + packetlib::ParsePacket(response.packet().payload()); + if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kGoodPayload) { + ++good_packet_count; + } else if (response.update_case() == + p4::v1::StreamMessageResponse::kPacket && + packet_in.payload() == kBadPayload) { + ++bad_packet_count; + } else { + LOG(WARNING) << "Unexpected response: " << response.DebugString(); + } + } + + if (absl::Now() > timeout) { + LOG(ERROR) << "Reached timeout waiting on packets to arrive."; + break; + } + } + LOG(INFO) << "Done collecting packets."; + EXPECT_EQ(good_packet_count, kNumberOfTestPacket); + EXPECT_EQ(bad_packet_count, 0); } } // namespace pins diff --git a/tests/forwarding/l3_admit_test.h b/tests/forwarding/l3_admit_test.h index b64889ff..17e4db11 100644 --- a/tests/forwarding/l3_admit_test.h +++ b/tests/forwarding/l3_admit_test.h @@ -21,18 +21,22 @@ #include "p4_pdpi/p4_runtime_session.h" #include "sai_p4/instantiations/google/instantiations.h" #include "sai_p4/instantiations/google/sai_p4info.h" +#include "tests/forwarding/mirror_blackbox_test_fixture.h" #include "tests/lib/packet_in_helper.h" #include "thinkit/mirror_testbed_fixture.h" namespace pins { -class L3AdmitTestFixture : public thinkit::MirrorTestbedFixture { +class L3AdmitTestFixture : public pins_test::MirrorBlackboxTestFixture { protected: - - // This test runs on a mirror testbed setup so we open a P4RT connection to - // both switches. - std::unique_ptr p4rt_sut_switch_session_; - std::unique_ptr p4rt_control_switch_session_; + void TearDown() override { + // MirrorBlackboxTestFixture unnecessarily clears tables at TearDown. This + // is not harmful for other tests but is problematic for l3_admit_tests + // since the P4RT session to the controller is closed during the tests (see + // lib/packet_in_helper.h). Therefore, We bypass table clearance in + // TearDown. + MirrorTestbedFixture::TearDown(); + } }; } // namespace pins diff --git a/tests/forwarding/mirror_blackbox_test_fixture.cc b/tests/forwarding/mirror_blackbox_test_fixture.cc new file mode 100644 index 00000000..3c3cc2f9 --- /dev/null +++ b/tests/forwarding/mirror_blackbox_test_fixture.cc @@ -0,0 +1,36 @@ +#include "tests/forwarding/mirror_blackbox_test_fixture.h" + +#include "gtest/gtest.h" +#include "gutil/status_matchers.h" +#include "sai_p4/instantiations/google/sai_p4info.h" +#include "sai_p4/instantiations/google/sai_pd.pb.h" +#include "tests/lib/switch_test_setup_helpers.h" +#include "thinkit/mirror_testbed_fixture.h" +#include "proto/gnmi/gnmi.pb.h" + +namespace pins_test { + +void MirrorBlackboxTestFixture::SetUp() { + thinkit::MirrorTestbedFixture::SetUp(); + + // Initialize the connection, clear table entries, and push GNMI + // configuration for the SUT and Control switch. + std::string sut_gnmi_config; + ASSERT_OK_AND_ASSIGN( + std::tie(sut_p4rt_session_, control_switch_p4rt_session_), + pins_test::ConfigureSwitchPairAndReturnP4RuntimeSessionPair( + GetMirrorTestbed().Sut(), GetMirrorTestbed().ControlSwitch(), + gnmi_config(), p4_info())); + +} + +void MirrorBlackboxTestFixture::TearDown() { + // Clear all table entries to leave the sut and control switch in a clean + // state. + EXPECT_OK(pdpi::ClearTableEntries(&GetSutP4RuntimeSession())); + EXPECT_OK(pdpi::ClearTableEntries(&GetControlP4RuntimeSession())); + + MirrorTestbedFixture::TearDown(); +} + +} // namespace pins_test diff --git a/tests/forwarding/mirror_blackbox_test_fixture.h b/tests/forwarding/mirror_blackbox_test_fixture.h new file mode 100644 index 00000000..c10d3e95 --- /dev/null +++ b/tests/forwarding/mirror_blackbox_test_fixture.h @@ -0,0 +1,53 @@ +// 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_MIRROR_BLACKBOX_TEST_FIXTURE_H_ +#define PINS_TESTS_FORWARDING_MIRROR_BLACKBOX_TEST_FIXTURE_H_ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "p4_pdpi/p4_runtime_session.h" +#include "thinkit/mirror_testbed.h" +#include "thinkit/mirror_testbed_fixture.h" + +namespace pins_test { + +// Fixture for mirror blackbox testing. It performs test specific setup and +// teardown: Creates and initializes a P4RT channel, clears the switch of all +// table entries, and pushes a GNMI config before every test. This gets the +// switch ready to accept programming operations. +class MirrorBlackboxTestFixture : public thinkit::MirrorTestbedFixture { + public: + void SetUp() override; + + void TearDown() override; + + pdpi::P4RuntimeSession& GetSutP4RuntimeSession() const { + return *sut_p4rt_session_; + } + + pdpi::P4RuntimeSession& GetControlP4RuntimeSession() const { + return *control_switch_p4rt_session_; + } + + private: + // This test runs on a mirror testbed setup so we open a P4RT connection to + // both switches. + std::unique_ptr sut_p4rt_session_; + std::unique_ptr control_switch_p4rt_session_; +}; + +} // namespace pins_test + +#endif // PINS_TESTS_FORWARDING_MIRROR_BLACKBOX_TEST_FIXTURE_H_ diff --git a/tests/forwarding/p4_blackbox_fixture.h b/tests/forwarding/p4_blackbox_fixture.h deleted file mode 100644 index 812550ac..00000000 --- a/tests/forwarding/p4_blackbox_fixture.h +++ /dev/null @@ -1,86 +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 -// -// 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_P4_BLACKBOX_FIXTURE_H_ -#define PINS_TESTS_FORWARDING_P4_BLACKBOX_FIXTURE_H_ - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "gutil/status_matchers.h" -#include "lib/gnmi/gnmi_helper.h" -#include "p4/config/v1/p4info.pb.h" -#include "p4_pdpi/p4_runtime_session.h" -#include "p4_pdpi/pd.h" -#include "sai_p4/instantiations/google/sai_p4info.h" -#include "sai_p4/instantiations/google/sai_pd.pb.h" -#include "thinkit/mirror_testbed.h" -#include "thinkit/mirror_testbed_fixture.h" - -namespace pins { - -// Fixture for P4 blackbox testing. It performs test specific setup and -// teardown: Creates an initializes a P4RT channel, to get the switch ready to -// accept programming operations. Clears the switch of all table entries before -// every test. -class P4BlackboxFixture : public thinkit::MirrorTestbedFixture { - public: - void SetUp() override { - MirrorTestbedFixture::SetUp(); - - thinkit::MirrorTestbed& testbed = - GetParam().mirror_testbed->GetMirrorTestbed(); - - // Get a gNMI config from the switch to use for testing. - ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, testbed.Sut().CreateGnmiStub()); - ASSERT_OK_AND_ASSIGN(std::string sut_gnmi_config, - pins_test::GetGnmiConfig(*sut_gnmi_stub)); - // Push the gnmi configuration. - ASSERT_OK( - pins_test::PushGnmiConfig(GetMirrorTestbed().Sut(), sut_gnmi_config)); - ASSERT_OK(pins_test::PushGnmiConfig(GetMirrorTestbed().ControlSwitch(), - sut_gnmi_config)); - - // Initialize the connection and clear table entries. - ASSERT_OK_AND_ASSIGN(sut_p4rt_session_, - pdpi::P4RuntimeSession::CreateWithP4InfoAndClearTables( - GetMirrorTestbed().Sut(), p4info_)); - } - - void TearDown() override { - if (SutP4RuntimeSession() != nullptr) { - // Clear all table entries to leave the switch in a clean state. - EXPECT_OK(pdpi::ClearTableEntries(SutP4RuntimeSession())); - } - - MirrorTestbedFixture::TearDown(); - } - - pdpi::P4RuntimeSession* SutP4RuntimeSession() const { - return sut_p4rt_session_.get(); - } - - const pdpi::IrP4Info& IrP4Info() const { return ir_p4info_; } - const p4::config::v1::P4Info& P4Info() const { return p4info_; } - - private: - std::unique_ptr sut_p4rt_session_; - pdpi::IrP4Info ir_p4info_ = - sai::GetIrP4Info(sai::Instantiation::kMiddleblock); - p4::config::v1::P4Info p4info_ = - sai::GetP4Info(sai::Instantiation::kMiddleblock); -}; - -} // namespace pins - -#endif // PINS_TESTS_FORWARDING_P4_BLACKBOX_FIXTURE_H_ diff --git a/tests/forwarding/smoke_test.cc b/tests/forwarding/smoke_test.cc index 53576bfb..adf045b8 100644 --- a/tests/forwarding/smoke_test.cc +++ b/tests/forwarding/smoke_test.cc @@ -29,11 +29,15 @@ #include "sai_p4/instantiations/google/sai_pd.pb.h" #include "tests/forwarding/test_data.h" -namespace pins { +namespace pins_test { namespace { -// TODO: modify failing because of policer attributes. -TEST_P(SmokeTestFixture, DISABLED_ModifyWorks) { +TEST_P(SmokeTestFixture, SessionsAreNonNull) { + ASSERT_NE(&GetSutP4RuntimeSession(), nullptr); + ASSERT_NE(&GetControlP4RuntimeSession(), nullptr); +} + +TEST_P(SmokeTestFixture, AclTableAddDeleteOkButModifyFails) { const sai::WriteRequest pd_insert = gutil::ParseProtoOrDie( R"pb( updates { @@ -42,15 +46,17 @@ TEST_P(SmokeTestFixture, DISABLED_ModifyWorks) { acl_ingress_table_entry { match { is_ip { value: "0x1" } } priority: 10 - action { copy { qos_queue: "0x1" } } + action { acl_copy { qos_queue: "0x1" } } } } } )pb"); ASSERT_OK_AND_ASSIGN(p4::v1::WriteRequest pi_insert, - pdpi::PdWriteRequestToPi(IrP4Info(), pd_insert)); - ASSERT_OK( - pdpi::SetMetadataAndSendPiWriteRequest(SutP4RuntimeSession(), pi_insert)); + pdpi::PdWriteRequestToPi( + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), pd_insert)); + + ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), + pi_insert)); const sai::WriteRequest pd_modify = gutil::ParseProtoOrDie( R"pb( @@ -60,100 +66,19 @@ TEST_P(SmokeTestFixture, DISABLED_ModifyWorks) { acl_ingress_table_entry { match { is_ip { value: "0x1" } } priority: 10 - action { forward {} } + action { acl_forward {} } } } } )pb"); ASSERT_OK_AND_ASSIGN(p4::v1::WriteRequest pi_modify, - pdpi::PdWriteRequestToPi(IrP4Info(), pd_modify)); - ASSERT_OK( - pdpi::SetMetadataAndSendPiWriteRequest(SutP4RuntimeSession(), pi_modify)); - // This used to fail with a read error, see b/185508142. - ASSERT_OK(pdpi::ClearTableEntries(SutP4RuntimeSession())); -} - -// TODO: Enable once the bug is fixed. -TEST_P(SmokeTestFixture, DISABLED_Bug181149419) { - // Adding 8 mirror sessions should succeed. - for (int i = 0; i < 8; i++) { - sai::TableEntry pd_entry = gutil::ParseProtoOrDie( - R"pb( - mirror_session_table_entry { - match { mirror_session_id: "session" } - action { - mirror_as_ipv4_erspan { - port: "1" - src_ip: "10.206.196.0" - dst_ip: "172.20.0.202" - src_mac: "00:02:03:04:05:06" - dst_mac: "00:1a:11:17:5f:80" - ttl: "0x40" - tos: "0x00" - } - } - } - )pb"); - pd_entry.mutable_mirror_session_table_entry() - ->mutable_match() - ->set_mirror_session_id(absl::StrCat("session-", i)); - - ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(IrP4Info(), pd_entry)); - EXPECT_OK(pdpi::InstallPiTableEntry(SutP4RuntimeSession(), pi_entry)); - } - // Adding one entry above the limit will fail. - { - sai::TableEntry pd_entry = gutil::ParseProtoOrDie( - R"pb( - mirror_session_table_entry { - match { mirror_session_id: "session-9" } - action { - mirror_as_ipv4_erspan { - port: "1" - src_ip: "10.206.196.0" - dst_ip: "172.20.0.202" - src_mac: "00:02:03:04:05:06" - dst_mac: "00:1a:11:17:5f:80" - ttl: "0x40" - tos: "0x00" - } - } - } - )pb"); - - ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(IrP4Info(), pd_entry)); - EXPECT_FALSE( - pdpi::InstallPiTableEntry(SutP4RuntimeSession(), pi_entry).ok()); - } - // Adding ACL entries that use the 8 mirrors should all succeed. - for (int i = 0; i < 8; i++) { - sai::TableEntry pd_entry = gutil::ParseProtoOrDie( - R"pb( - acl_ingress_table_entry { - match { - is_ipv4 { value: "0x1" } - src_ip { value: "10.0.0.0" mask: "255.255.255.255" } - dscp { value: "0x1c" mask: "0x3c" } - } - action { mirror { mirror_session_id: "session-1" } } - priority: 2100 - } - )pb"); - pd_entry.mutable_acl_ingress_table_entry() - ->mutable_action() - ->mutable_acl_mirror() - ->set_mirror_session_id(absl::StrCat("session-", i)); - pd_entry.mutable_acl_ingress_table_entry() - ->mutable_match() - ->mutable_src_ip() - ->set_value(absl::StrCat("10.0.0.", i)); - - ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(IrP4Info(), pd_entry)); - ASSERT_OK(pdpi::InstallPiTableEntry(SutP4RuntimeSession(), pi_entry)); - } + pdpi::PdWriteRequestToPi( + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), pd_modify)); + ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), + pi_modify)); + + // This used to fail with a read error. + ASSERT_OK(pdpi::ClearTableEntries(&GetSutP4RuntimeSession())); } TEST_P(SmokeTestFixture, InsertTableEntry) { @@ -168,8 +93,9 @@ TEST_P(SmokeTestFixture, InsertTableEntry) { )pb"); ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(IrP4Info(), pd_entry)); - ASSERT_OK(pdpi::InstallPiTableEntry(SutP4RuntimeSession(), pi_entry)); + pdpi::PartialPdTableEntryToPiTableEntry( + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), pd_entry)); + ASSERT_OK(pdpi::InstallPiTableEntry(&GetSutP4RuntimeSession(), pi_entry)); } TEST_P(SmokeTestFixture, InsertTableEntryWithRandomCharacterId) { @@ -184,17 +110,18 @@ TEST_P(SmokeTestFixture, InsertTableEntryWithRandomCharacterId) { )pb"); ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(IrP4Info(), pd_entry)); - ASSERT_OK(pdpi::InstallPiTableEntry(SutP4RuntimeSession(), pi_entry)); + pdpi::PartialPdTableEntryToPiTableEntry( + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), pd_entry)); + ASSERT_OK(pdpi::InstallPiTableEntry(&GetSutP4RuntimeSession(), pi_entry)); ASSERT_OK_AND_ASSIGN(auto entries, - pdpi::ReadPiTableEntries(SutP4RuntimeSession())); + pdpi::ReadPiTableEntries(&GetSutP4RuntimeSession())); ASSERT_EQ(entries.size(), 1); ASSERT_THAT(entries[0], gutil::EqualsProto(pi_entry)); } TEST_P(SmokeTestFixture, InsertAndReadTableEntries) { - pdpi::P4RuntimeSession* session = SutP4RuntimeSession(); - const pdpi::IrP4Info& ir_p4info = IrP4Info(); + pdpi::P4RuntimeSession& session = GetSutP4RuntimeSession(); + const pdpi::IrP4Info& ir_p4info = sai::GetIrP4Info(sai::Instantiation::kMiddleblock); std::vector write_pd_entries = sai_pd::CreateUpTo255GenericTableEntries(3); @@ -213,13 +140,13 @@ TEST_P(SmokeTestFixture, InsertAndReadTableEntries) { write_pi_entries.push_back(std::move(pi_entry)); } - ASSERT_OK(pdpi::InstallPiTableEntries(session, ir_p4info, write_pi_entries)); + ASSERT_OK(pdpi::InstallPiTableEntries(&session, ir_p4info, write_pi_entries)); p4::v1::ReadRequest read_request; read_request.add_entities()->mutable_table_entry(); ASSERT_OK_AND_ASSIGN( p4::v1::ReadResponse read_response, - pdpi::SetMetadataAndSendPiReadRequest(session, read_request)); + pdpi::SetMetadataAndSendPiReadRequest(&session, read_request)); for (const auto& entity : read_response.entities()) { ASSERT_OK(test_environment.AppendToTestArtifact( @@ -246,7 +173,7 @@ TEST_P(SmokeTestFixture, EnsureClearTables) { // Sets up initial session. ASSERT_OK_AND_ASSIGN(auto session, pdpi::P4RuntimeSession::CreateWithP4InfoAndClearTables( - GetMirrorTestbed().Sut(), P4Info())); + GetMirrorTestbed().Sut(), p4_info())); // The table should be clear after setup. ASSERT_OK(pdpi::CheckNoTableEntries(session.get())); // Sets up an example table entry. @@ -260,15 +187,23 @@ TEST_P(SmokeTestFixture, EnsureClearTables) { } )pb"); ASSERT_OK_AND_ASSIGN(p4::v1::TableEntry pi_entry, - pdpi::PartialPdTableEntryToPiTableEntry(IrP4Info(), pd_entry)); - ASSERT_OK(pdpi::InstallPiTableEntries(session.get(), IrP4Info(), {pi_entry})); + pdpi::PartialPdTableEntryToPiTableEntry( + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), pd_entry)); + ASSERT_OK( + pdpi::InstallPiTableEntries(session.get(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), {pi_entry})); ASSERT_OK(pdpi::ClearTableEntries(session.get())); // The table should be clear after clearing. ASSERT_OK(pdpi::CheckNoTableEntries(session.get())); - ASSERT_OK(pdpi::InstallPiTableEntries(session.get(), IrP4Info(), {pi_entry})); + + ASSERT_OK( + pdpi::InstallPiTableEntries(session.get(), + sai::GetIrP4Info(sai::Instantiation::kMiddleblock), {pi_entry})); + ASSERT_OK_AND_ASSIGN(auto session2, pdpi::P4RuntimeSession::CreateWithP4InfoAndClearTables( - GetMirrorTestbed().Sut(), P4Info())); + GetMirrorTestbed().Sut(), p4_info())); + // The table should be clear for both sessions after setting up a new session. ASSERT_OK(pdpi::CheckNoTableEntries(session.get())); ASSERT_OK(pdpi::CheckNoTableEntries(session2.get())); diff --git a/tests/forwarding/smoke_test.h b/tests/forwarding/smoke_test.h index c2584fe1..3b85504c 100644 --- a/tests/forwarding/smoke_test.h +++ b/tests/forwarding/smoke_test.h @@ -15,13 +15,13 @@ #ifndef PINS_TESTS_FORWARDING_SMOKE_TEST_H_ #define PINS_TESTS_FORWARDING_SMOKE_TEST_H_ -#include "tests/forwarding/p4_blackbox_fixture.h" +#include "tests/forwarding/mirror_blackbox_test_fixture.h" #include "thinkit/mirror_testbed_fixture.h" -namespace pins { +namespace pins_test { -class SmokeTestFixture : public P4BlackboxFixture {}; +class SmokeTestFixture : public MirrorBlackboxTestFixture {}; -} // namespace pins +} // namespace pins_test #endif // PINS_TESTS_FORWARDING_SMOKE_TEST_H_ diff --git a/tests/lib/BUILD.bazel b/tests/lib/BUILD.bazel index 130c8c4f..11a9084d 100644 --- a/tests/lib/BUILD.bazel +++ b/tests/lib/BUILD.bazel @@ -35,6 +35,16 @@ cc_library( ], ) +cc_test( + name = "p4info_helper_test", + srcs = ["p4info_helper_test.cc"], + deps = [ + ":p4info_helper", + "//p4_pdpi:ir_cc_proto", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "p4rt_fixed_table_programming_helper", testonly = True, diff --git a/tests/lib/p4info_helper_test.cc b/tests/lib/p4info_helper_test.cc new file mode 100644 index 00000000..df6a7b7f --- /dev/null +++ b/tests/lib/p4info_helper_test.cc @@ -0,0 +1,39 @@ +// 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/lib/p4info_helper.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "p4_pdpi/ir.pb.h" + +namespace gpins { +namespace { + +TEST(P4InfoHelperTest, TableHasMatchField) { + // Add a 'good' match field to a table definition. + pdpi::IrTableDefinition table_def; + table_def.mutable_match_fields_by_name()->insert( + {"good_match", pdpi::IrMatchFieldDefinition{}}); + + // Then add that table definition to an IrP4Info. + pdpi::IrP4Info ir_p4info; + ir_p4info.mutable_tables_by_name()->insert({"good_table", table_def}); + + EXPECT_TRUE(pins::TableHasMatchField(ir_p4info, "good_table", "good_match")); + EXPECT_FALSE(pins::TableHasMatchField(ir_p4info, "bad_table", "good_match")); + EXPECT_FALSE(pins::TableHasMatchField(ir_p4info, "good_table", "bad_match")); +} + +} // namespace +} // namespace gpins