Skip to content

Commit

Permalink
[pdpi][multicast] Add PRE multicast support to IR. (sonic-net#226)
Browse files Browse the repository at this point in the history

* [pdpi][multicast] Add PRE multicast support to IR.

---------

Co-authored-by: smolkaj <[email protected]>
Co-authored-by: Srikishen Pondicherry Shanmugam <[email protected]>
  • Loading branch information
3 people authored Jun 24, 2024
1 parent 42c107e commit 85dd958
Show file tree
Hide file tree
Showing 8 changed files with 506 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .bazelversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.2.0
5.1.0
1 change: 1 addition & 0 deletions p4_pdpi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ cc_library(
"@com_github_p4lang_p4runtime//:p4runtime_cc_proto",
"@com_github_p4lang_p4runtime//:p4types_cc_proto",
"@com_google_absl//absl/algorithm:container",
"@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",
Expand Down
161 changes: 159 additions & 2 deletions p4_pdpi/ir.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <vector>

#include "absl/algorithm/container.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
Expand Down Expand Up @@ -1642,6 +1643,81 @@ StatusOr<IrTableEntry> PiTableEntryToIr(const IrP4Info &info,
return ir;
}

StatusOr<IrReplica> PiReplicaToIr(const IrP4Info &info,
const p4::v1::Replica &pi) {
IrReplica ir;
if (pi.port_kind_case() != p4::v1::Replica::kPort) {
return gutil::InvalidArgumentErrorBuilder()
<< "expected `port` field to be set in Replica, but found < "
<< gutil::PrintShortTextProto(pi) << " >";
}
ir.set_port(pi.port());
ir.set_instance(pi.instance());
return ir;
}

StatusOr<IrMulticastGroupEntry> PiMulticastGroupEntryToIr(
const IrP4Info &info, const p4::v1::MulticastGroupEntry &pi,
TranslationOptions options) {
IrMulticastGroupEntry ir;
ir.set_multicast_group_id(pi.multicast_group_id());

if (options.key_only) {
return ir;
}

absl::flat_hash_map<std::string, absl::flat_hash_set<uint32_t>>
instances_by_port;
std::vector<std::string> invalid_reasons;
for (const auto &replica : pi.replicas()) {
absl::StatusOr<IrReplica> ir_replica = PiReplicaToIr(info, replica);
if (!ir_replica.ok()) {
invalid_reasons.push_back(
absl::StrCat(kNewBullet, ir_replica.status().message()));
continue;
}
// Check that {port, instance} pair is unique.
bool replica_is_unique = instances_by_port[ir_replica->port()]
.insert(ir_replica->instance())
.second;
if (!replica_is_unique) {
invalid_reasons.push_back(absl::StrCat(
kNewBullet,
"Each replica must have a unique (port, instance)-pair, but found "
"multiple replicas with pair ('",
ir_replica->port(), "', ", ir_replica->instance(), ")."));
}
*ir.add_replicas() = std::move(*ir_replica);
}

if (!invalid_reasons.empty()) {
return gutil::InvalidArgumentErrorBuilder() << GenerateFormattedError(
absl::StrCat("MulticastGroupEntry with group id '",
pi.multicast_group_id(), "'"),
absl::StrJoin(invalid_reasons, "\n"));
}
return ir;
}

StatusOr<IrPacketReplicationEngineEntry> PiPacketReplicationEngineEntryToIr(
const IrP4Info &info, const p4::v1::PacketReplicationEngineEntry &pi,
TranslationOptions options) {
IrPacketReplicationEngineEntry ir;
switch (pi.type_case()) {
case p4::v1::PacketReplicationEngineEntry::kMulticastGroupEntry: {
ASSIGN_OR_RETURN(
*ir.mutable_multicast_group_entry(),
PiMulticastGroupEntryToIr(info, pi.multicast_group_entry(), options));
break;
}
default: {
return gutil::UnimplementedErrorBuilder()
<< "Only PRE entries of type multicast group entry are supported.";
}
}
return ir;
}

StatusOr<IrPacketIn> PiPacketInToIr(const IrP4Info &info,
const p4::v1::PacketIn &packet) {
return PiPacketIoToIr<p4::v1::PacketIn, IrPacketIn>(info, "packet-in",
Expand Down Expand Up @@ -1713,7 +1789,13 @@ StatusOr<IrEntity> PiEntityToIr(const IrP4Info &info, const p4::v1::Entity &pi,
PiTableEntryToIr(info, pi.table_entry(), options));
break;
}
// TODO: Add PacketReplicationEngine support to IR.
case p4::v1::Entity::kPacketReplicationEngineEntry: {
ASSIGN_OR_RETURN(
*ir_entity.mutable_packet_replication_engine_entry(),
PiPacketReplicationEngineEntryToIr(
info, pi.packet_replication_engine_entry(), options));
break;
}
default: {
auto entity_name = gutil::GetOneOfFieldName(pi, "entity");
if (!entity_name.ok()) {
Expand Down Expand Up @@ -2123,6 +2205,75 @@ StatusOr<p4::v1::TableEntry> IrTableEntryToPi(const IrP4Info &info,
return pi;
}

StatusOr<p4::v1::Replica> IrReplicaToPi(const IrP4Info &info,
const IrReplica &ir) {
p4::v1::Replica pi;
pi.set_port(ir.port());
pi.set_instance(ir.instance());
return pi;
}

StatusOr<p4::v1::MulticastGroupEntry> IrMulticastGroupEntryToPi(
const IrP4Info &info, const IrMulticastGroupEntry &ir,
TranslationOptions options) {
p4::v1::MulticastGroupEntry pi;
pi.set_multicast_group_id(ir.multicast_group_id());

if (options.key_only) {
return pi;
}

absl::flat_hash_map<std::string, absl::flat_hash_set<uint32_t>>
instances_by_port;
std::vector<std::string> invalid_reasons;
for (const auto &replica : ir.replicas()) {
absl::StatusOr<p4::v1::Replica> pi_replica = IrReplicaToPi(info, replica);
if (!pi_replica.ok()) {
invalid_reasons.push_back(
absl::StrCat(kNewBullet, pi_replica.status().message()));
continue;
}
// Check that {port, instance} pair is unique.
bool replica_is_unique = instances_by_port[pi_replica->port()]
.insert(pi_replica->instance())
.second;
if (!replica_is_unique) {
invalid_reasons.push_back(absl::StrCat(
kNewBullet,
"Each replica must have a unique (port, instance)-pair, but found "
"multiple replicas with pair ('",
pi_replica->port(), "', ", pi_replica->instance(), ")."));
}
*pi.add_replicas() = std::move(*pi_replica);
}
if (!invalid_reasons.empty()) {
return gutil::InvalidArgumentErrorBuilder() << GenerateFormattedError(
absl::StrCat("MulticastGroupEntry with group id '",
ir.multicast_group_id(), "'"),
absl::StrJoin(invalid_reasons, "\n"));
}
return pi;
}

StatusOr<p4::v1::PacketReplicationEngineEntry>
IrPacketReplicationEngineEntryToPi(const IrP4Info &info,
const IrPacketReplicationEngineEntry &ir,
TranslationOptions options) {
p4::v1::PacketReplicationEngineEntry pi;
switch (ir.type_case()) {
case IrPacketReplicationEngineEntry::kMulticastGroupEntry: {
ASSIGN_OR_RETURN(
*pi.mutable_multicast_group_entry(),
IrMulticastGroupEntryToPi(info, ir.multicast_group_entry(), options));
break;
}
default:
return gutil::UnimplementedErrorBuilder()
<< "Only PRE entries of type multicast group entry are supported.";
}
return pi;
}

StatusOr<p4::v1::PacketIn> IrPacketInToPi(const IrP4Info &info,
const IrPacketIn &packet) {
return IrPacketIoToPi<p4::v1::PacketIn, IrPacketIn>(info, "packet-in",
Expand Down Expand Up @@ -2160,7 +2311,13 @@ StatusOr<p4::v1::Entity> IrEntityToPi(const IrP4Info &info, const IrEntity &ir,
IrTableEntryToPi(info, ir.table_entry(), options));
break;
}
// TODO: Add PacketReplicationEngine support to IR.
case IrEntity::kPacketReplicationEngineEntry: {
ASSIGN_OR_RETURN(
*pi_entity.mutable_packet_replication_engine_entry(),
IrPacketReplicationEngineEntryToPi(
info, ir.packet_replication_engine_entry(), options));
break;
}
default: {
auto entity_name = gutil::GetOneOfFieldName(ir, "entity");
if (!entity_name.ok()) {
Expand Down
8 changes: 8 additions & 0 deletions p4_pdpi/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ absl::StatusOr<IrTableEntries> PiTableEntriesToIr(
const IrP4Info& info, absl::Span<const p4::v1::TableEntry> pi,
TranslationOptions options = {});

absl::StatusOr<IrMulticastGroupEntry> PiMulticastGroupEntryToIr(
const IrP4Info& info, const p4::v1::MulticastGroupEntry& pi,
TranslationOptions options = {});

absl::StatusOr<IrPacketIn> PiPacketInToIr(const IrP4Info& info,
const p4::v1::PacketIn& packet);

Expand Down Expand Up @@ -93,6 +97,10 @@ absl::StatusOr<std::vector<p4::v1::TableEntry>> IrTableEntriesToPi(
const IrP4Info& info, absl::Span<const IrTableEntry> ir,
TranslationOptions options = {});

absl::StatusOr<p4::v1::MulticastGroupEntry> IrMulticastGroupEntryToPi(
const IrP4Info& info, const IrMulticastGroupEntry& ir,
TranslationOptions options = {});

absl::StatusOr<p4::v1::PacketIn> IrPacketInToPi(const IrP4Info& info,
const IrPacketIn& packet);

Expand Down
21 changes: 21 additions & 0 deletions p4_pdpi/ir.proto
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,26 @@ message IrTableEntries {
repeated IrTableEntry entries = 1;
}

//-- Packet Replication Engine -------------------------------------------------

// Describes a PacketReplicationEngine (PRE) entry. Currently, the only
// supported PRE entry is multicast group entry.
message IrPacketReplicationEngineEntry {
oneof type {
IrMulticastGroupEntry multicast_group_entry = 1;
}
}

message IrReplica {
string port = 1;
uint32 instance = 2;
}

message IrMulticastGroupEntry {
uint32 multicast_group_id = 1;
repeated IrReplica replicas = 2;
}

// -- Packet IO ----------------------------------------------------------------

// Describes a packet in.
Expand All @@ -328,6 +348,7 @@ message IrPacketMetadata {
message IrEntity {
oneof entity {
IrTableEntry table_entry = 1;
IrPacketReplicationEngineEntry packet_replication_engine_entry = 2;
}
}

Expand Down
95 changes: 95 additions & 0 deletions p4_pdpi/testing/table_entry_test_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,51 @@ static void RunPdTableEntryTest(const pdpi::IrP4Info& info,
});
}

static void RunPiMulticastTest(const pdpi::IrP4Info& info) {
RunPiEntityTest(info, "multicast group entry with deprecated egress_port set",
gutil::ParseProtoOrDie<p4::v1::Entity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { egress_port: 3 instance: 1 }
}
}
)pb"));
RunPiEntityTest(info, "multicast group entry with duplicate replica",
gutil::ParseProtoOrDie<p4::v1::Entity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { port: "some_port" instance: 1 }
replicas { port: "some_port" instance: 1 }
}
}
)pb"));
RunPiEntityTest(info, "valid multicast group entry",
gutil::ParseProtoOrDie<p4::v1::Entity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { port: "some_port" instance: 1 }
replicas { port: "some_port" instance: 2 }
replicas { port: "some_other_port" instance: 1 }
}
}
)pb"),
/*validity=*/INPUT_IS_VALID);
RunPiEntityTest(info, "valid multicast group entry without explicit instance",
gutil::ParseProtoOrDie<p4::v1::Entity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { port: "some_port" }
replicas { port: "some_other_port" }
}
}
)pb"),
/*validity=*/INPUT_IS_VALID);
}

static void RunPiTests(const pdpi::IrP4Info info) {
RunPiEntityTest(info, "empty PI", gutil::ParseProtoOrDie<p4::v1::Entity>(R"pb(
)pb"));
Expand Down Expand Up @@ -776,6 +821,7 @@ static void RunPiTests(const pdpi::IrP4Info info) {
}
)pb"),
/*validity=*/INPUT_IS_VALID);
RunPiMulticastTest(info);
} // NOLINT(readability/fn_size)

static void RunIrNoActionTableTests(const pdpi::IrP4Info& info) {
Expand Down Expand Up @@ -844,6 +890,54 @@ static void RunIrTernaryTableTests(const pdpi::IrP4Info info) {
)pb"));
}

static void RunIrMulticastTest(const pdpi::IrP4Info& info) {
RunIrEntityTest(info, "multicast group entry with duplicate replica",
gutil::ParseProtoOrDie<pdpi::IrEntity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { port: "some_port" instance: 1 }
replicas { port: "some_port" instance: 1 }
}
}
)pb"),
IrTestConfig{
// TODO: Add PRE support to PD.
.test_ir_to_pd = false,
});
RunIrEntityTest(info, "valid multicast group entry",
gutil::ParseProtoOrDie<pdpi::IrEntity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { port: "some_port" instance: 1 }
replicas { port: "some_port" instance: 2 }
replicas { port: "some_other_port" instance: 1 }
}
}
)pb"),
IrTestConfig{
.validity = INPUT_IS_VALID,
// TODO: Add PRE support to PD.
.test_ir_to_pd = false,
});
RunIrEntityTest(info, "valid multicast group entry without explicit instance",
gutil::ParseProtoOrDie<pdpi::IrEntity>(R"pb(
packet_replication_engine_entry {
multicast_group_entry {
multicast_group_id: 7
replicas { port: "some_port" }
replicas { port: "some_other_port" }
}
}
)pb"),
IrTestConfig{
.validity = INPUT_IS_VALID,
// TODO: Add PRE support to PD.
.test_ir_to_pd = false,
});
}

static void RunIrMeterCounterTableEntryTests(const pdpi::IrP4Info& info) {
RunIrEntityTest(info, "meter counter data in a table with no counters",
gutil::ParseProtoOrDie<pdpi::IrEntity>(R"pb(
Expand Down Expand Up @@ -1769,6 +1863,7 @@ static void RunIrTests(const pdpi::IrP4Info info) {
IrTestConfig{
.validity = INPUT_IS_VALID,
});
RunIrMulticastTest(info);
} // NOLINT(readability/fn_size)

static void RunPdMeterCounterTableEntryTests(const pdpi::IrP4Info& info) {
Expand Down
Loading

0 comments on commit 85dd958

Please sign in to comment.