Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions sai_p4/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,29 @@ cc_proto_library(
name = "capabilities_cc_proto",
deps = [":capabilities_proto"],
)

cc_library(
name = "capabilities",
srcs = ["capabilities.cc"],
hdrs = ["capabilities.h"],
deps = [
":capabilities_cc_proto",
"//gutil/gutil:status",
"@com_github_p4lang_p4runtime//:p4runtime_cc_proto",
"@com_google_absl//absl/status",
"@com_google_protobuf//:protobuf",
],
)

cc_test(
name = "capabilities_test",
srcs = ["capabilities_test.cc"],
deps = [
":capabilities",
":capabilities_cc_proto",
"//gutil/gutil:proto_matchers",
"//gutil/gutil:status_matchers",
"@com_google_googletest//:gtest_main",
],
)

39 changes: 39 additions & 0 deletions sai_p4/capabilities.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "sai_p4/capabilities.h"

#include <utility>

#include "absl/status/status.h"
#include "google/protobuf/any.pb.h"
#include "p4/v1/p4runtime.pb.h"
#include "sai_p4/capabilities.pb.h"

namespace sai {

using ::p4::v1::CapabilitiesResponse;

absl::Status AddExperimentalResourceCapabilities(
ExperimentalResourceCapabilities capabilities,
CapabilitiesResponse& response) {
if (!response.mutable_experimental()->PackFrom(std::move(capabilities))) {
return absl::InvalidArgumentError(
"Failed to pack ExperimentalResourceCapabilities into "
"CapabilitiesResponse.");
}
return absl::OkStatus();
}

ExperimentalResourceCapabilities GetExperimentalResourceCapabilities(
const CapabilitiesResponse& response) {
ExperimentalResourceCapabilities capabilities;
response.experimental().UnpackTo(&capabilities);
return capabilities;
};

WcmpGroupLimitations GetWcmpGroupLimitations(
const CapabilitiesResponse& response) {
ExperimentalResourceCapabilities capabilities =
GetExperimentalResourceCapabilities(response);
return capabilities.wcmp_group_limitations();
}

} // namespace sai
28 changes: 28 additions & 0 deletions sai_p4/capabilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef PINS_SAI_P4_CAPABILITIES_H_
#define PINS_SAI_P4_CAPABILITIES_H_

#include "absl/status/status.h"
#include "p4/v1/p4runtime.pb.h"
#include "sai_p4/capabilities.pb.h"

namespace sai {

// Adds `experimental_resource_capabilities` to `response`. Returns an error if
// `experimental_resource_capabilities` cannot be added into `response`.
absl::Status AddExperimentalResourceCapabilities(
ExperimentalResourceCapabilities capabilities,
p4::v1::CapabilitiesResponse& response);

// If `response` contains a `ExperimentalResourceCapabilities`, returns it.
// Otherwise, returns an empty `ExperimentalResourceCapabilities`.
ExperimentalResourceCapabilities GetExperimentalResourceCapabilities(
const p4::v1::CapabilitiesResponse& response);

// If `response` contains a `WcmpGroupLimitations`, returns it.
// Otherwise, returns an empty `WcmpGroupLimitations`.
WcmpGroupLimitations GetWcmpGroupLimitations(
const p4::v1::CapabilitiesResponse& response);

} // namespace sai

#endif // PINS_SAI_P4_CAPABILITIES_H_
2 changes: 1 addition & 1 deletion sai_p4/capabilities.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

syntax = "proto3";

package p4.v1;
package sai;

// A switch can provide limitations of the WCMP groups that can be installed
// on the switch. A switch may report a ResourceExhausted error, if:
Expand Down
100 changes: 100 additions & 0 deletions sai_p4/capabilities_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include "sai_p4/capabilities.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "gutil/gutil/proto_matchers.h"
#include "gutil/gutil/status_matchers.h" // IWYU pragma: keep
#include "sai_p4/capabilities.pb.h"

namespace sai {
namespace {

using ::gutil::EqualsProto;
using ::p4::v1::CapabilitiesResponse;

TEST(CapabilitiesTest, AddExperimentalResourceCapabilitiesWorks) {
CapabilitiesResponse response;
ExperimentalResourceCapabilities experimental_resource_capabilities;
experimental_resource_capabilities.mutable_wcmp_group_limitations()
->set_max_distinct_weights_per_group(8);
experimental_resource_capabilities.mutable_wcmp_group_limitations()
->set_max_total_weight_per_group(2000);

EXPECT_OK(AddExperimentalResourceCapabilities(
experimental_resource_capabilities, response));

ExperimentalResourceCapabilities unpacked_experimental_resource_capabilities;
response.mutable_experimental()->UnpackTo(
&unpacked_experimental_resource_capabilities);
EXPECT_THAT(unpacked_experimental_resource_capabilities,
EqualsProto(experimental_resource_capabilities));
}

TEST(CapabilitiesTest, GetExperimentalResourceCapabilitiesWorks) {
CapabilitiesResponse response;
ExperimentalResourceCapabilities experimental_resource_capabilities;
experimental_resource_capabilities.mutable_wcmp_group_limitations()
->set_max_distinct_weights_per_group(8);
experimental_resource_capabilities.mutable_wcmp_group_limitations()
->set_max_total_weight_per_group(2000);

ASSERT_OK(AddExperimentalResourceCapabilities(
experimental_resource_capabilities, response));
EXPECT_THAT(GetExperimentalResourceCapabilities(response),
EqualsProto(experimental_resource_capabilities));
}

TEST(CapabilitiesTest,
GetExperimentalResourceCapabilitiesReturnsEmptyWhenMissing) {
EXPECT_THAT(GetExperimentalResourceCapabilities(CapabilitiesResponse()),
EqualsProto(ExperimentalResourceCapabilities()));
}

TEST(CapabilitiesTest,
GetExperimentalResourceCapabilitiesReturnsEmptyWhenWrongType) {
CapabilitiesResponse response;
p4::v1::TableEntry entry;
ASSERT_TRUE(response.mutable_experimental()->PackFrom(entry));
EXPECT_THAT(GetExperimentalResourceCapabilities(response),
EqualsProto(ExperimentalResourceCapabilities()));
}

TEST(CapabilitiesTest, GetWcmpGroupLimitationsWorks) {
CapabilitiesResponse response;
ExperimentalResourceCapabilities experimental_resource_capabilities;
experimental_resource_capabilities.mutable_wcmp_group_limitations()
->set_max_distinct_weights_per_group(8);
experimental_resource_capabilities.mutable_wcmp_group_limitations()
->set_max_total_weight_per_group(2000);

ASSERT_OK(AddExperimentalResourceCapabilities(
experimental_resource_capabilities, response));
EXPECT_THAT(
GetWcmpGroupLimitations(response),
EqualsProto(experimental_resource_capabilities.wcmp_group_limitations()));
}

TEST(CapabilitiesTest, GetWcmpGroupLimitationsWorksWhenMissing) {
CapabilitiesResponse response;
ASSERT_OK(AddExperimentalResourceCapabilities(
ExperimentalResourceCapabilities(), response));
EXPECT_THAT(GetWcmpGroupLimitations(response),
EqualsProto(WcmpGroupLimitations()));
}

TEST(CapabilitiesTest,
GetWcmpGroupLimitationsReturnsEmptyWhenParentWhenMissing) {
EXPECT_THAT(GetWcmpGroupLimitations(CapabilitiesResponse()),
EqualsProto(WcmpGroupLimitations()));
}

TEST(CapabilitiesTest, GetWcmpGroupLimitationsReturnsEmptyWhenWrongType) {
CapabilitiesResponse response;
p4::v1::TableEntry entry;
ASSERT_TRUE(response.mutable_experimental()->PackFrom(entry));
EXPECT_THAT(GetWcmpGroupLimitations(response),
EqualsProto(WcmpGroupLimitations()));
}

} // namespace
} // namespace sai
2 changes: 2 additions & 0 deletions sai_p4/fixed/l3_admit.p4
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ control l3_admit(in headers_t headers,
key = {
headers.ethernet.dst_addr : ternary @name("dst_mac") @id(1)
@format(MAC_ADDRESS);
#if defined(L3_ADMIT_SUPPORTS_IN_PORT)
local_metadata.ingress_port : optional @name("in_port") @id(2);
#endif
}
actions = {
@proto_id(1) admit_to_l3;
Expand Down
4 changes: 3 additions & 1 deletion sai_p4/fixed/routing.p4
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,11 @@ control routing_resolution(in headers_t headers,
router_interface_id_value : exact @id(1)
@name("router_interface_id");
}
// TODO: Remove once no longer in use on our switches.
actions = {
#if defined(RIF_PROGRAMMING_MY_MAC_SUPPORTED)
// TODO: Remove once no longer in use on our switches.
@proto_id(1) set_port_and_src_mac;
#endif
@proto_id(2) unicast_set_port_and_src_mac_and_vlan_id;
@proto_id(3) unicast_set_port_and_src_mac;
@defaultonly NoAction;
Expand Down
10 changes: 10 additions & 0 deletions sai_p4/instantiations/google/acl_ingress.p4
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ control acl_ingress(in headers_t headers,
@sai_acl(INGRESS)
@sai_acl_priority(5)
@nonessential_for_upgrade
#if defined(SAI_INSTANTIATION_TOR)
@reinstall_during_upgrade
#endif
@entry_restriction("
// Forbid using ether_type for IP packets (by convention, use is_ip* instead).
ether_type != 0x0800 && ether_type != 0x86dd;
Expand Down Expand Up @@ -422,6 +425,9 @@ control acl_ingress(in headers_t headers,
@sai_acl_priority(10)
@p4runtime_role(P4RUNTIME_ROLE_SDN_CONTROLLER)
@nonessential_for_upgrade
#if defined(SAI_INSTANTIATION_TOR)
@reinstall_during_upgrade
#endif
@entry_restriction("
// Forbid using ether_type for IP packets (by convention, use is_ip* instead).
ether_type != 0x0800 && ether_type != 0x86dd;
Expand Down Expand Up @@ -652,6 +658,10 @@ control acl_ingress(in headers_t headers,
@id(ACL_INGRESS_MIRROR_AND_REDIRECT_TABLE_ID)
@sai_acl(INGRESS)
@sai_acl_priority(15)
@nonessential_for_upgrade
#if defined(SAI_INSTANTIATION_TOR)
@reinstall_during_upgrade
#endif
@p4runtime_role(P4RUNTIME_ROLE_SDN_CONTROLLER)
@entry_restriction("
// Only allow IP field matches for IP packets.
Expand Down
4 changes: 3 additions & 1 deletion sai_p4/instantiations/google/fabric_border_router.p4
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
#define ACL_REDIRECT_TO_NEXTHOP_CAPABLE
#define ACL_REDIRECT_TO_PORT_CAPABLE
#define DSCP_REWRITE_CAPABLE
#define NEXTHOP_DISABLE_REWRITES_CAPABLE
#define IP_MULTICAST_CAPABLE
#define L3_ADMIT_SUPPORTS_IN_PORT
#define NEXTHOP_DISABLE_REWRITES_CAPABLE
#define MIRROR_CAPABLE
#define RIF_PROGRAMMING_MY_MAC_SUPPORTED
#define TIMESTAMP_CAPABLE
#define TUNNEL_ENCAP_CAPABLE

Expand Down
4 changes: 3 additions & 1 deletion sai_p4/instantiations/google/middleblock.p4
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
#define ACL_REDIRECT_TO_NEXTHOP_CAPABLE
#define ACL_REDIRECT_TO_PORT_CAPABLE
#define DSCP_REWRITE_CAPABLE
#define NEXTHOP_DISABLE_REWRITES_CAPABLE
#define IP_MULTICAST_CAPABLE
#define L3_ADMIT_SUPPORTS_IN_PORT
#define NEXTHOP_DISABLE_REWRITES_CAPABLE
#define MIRROR_CAPABLE
#define RIF_PROGRAMMING_MY_MAC_SUPPORTED
#define TIMESTAMP_CAPABLE
#define TUNNEL_ENCAP_CAPABLE

Expand Down
1 change: 1 addition & 0 deletions sai_p4/instantiations/google/middleblock.p4info.pb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ tables {
alias: "acl_ingress_mirror_and_redirect_table"
annotations: "@sai_acl(INGRESS)"
annotations: "@sai_acl_priority(15)"
annotations: "@nonessential_for_upgrade"
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 \")"
}
Expand Down
13 changes: 12 additions & 1 deletion sai_p4/instantiations/google/sai_pd.proto
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,18 @@ message AclIngressSecurityTableEntry {
// is_ipv6::mask != 0 -> (is_ip::mask == 0 && is_ipv4::mask == 0);
// ## Forbid unsupported combinations of IP_TYPE fields.
// is_ipv4::mask != 0 -> (is_ipv4 == 1);
// is_ipv6::mask != 0 -> (is_ipv6 == 1);
// is_ipv6::mask != 0 -> (is_ipv6 == 1);" "route_hit::mask != 0 -> (
// ## Multicast IPv4.
// ## I.e. 224.X.X.X - 239.X.X.X
// (dst_ip::mask >= 0xf0000000
// && dst_ip::value >= 0xe0000000
// && dst_ip::value < 0xf0000000)
// ||
// ## Multicast IPv6.
// ## I.e. FFXX:XXXX:...
// (dst_ipv6::mask >= 0xff00000000000000
// && dst_ipv6::value >= 0xff00000000000000)
// );
message AclIngressMirrorAndRedirectTableEntry {
message Match {
Optional in_port = 1; // optional match / Format::STRING
Expand Down
4 changes: 3 additions & 1 deletion sai_p4/instantiations/google/tor.p4
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
#define ACL_REDIRECT_TO_NEXTHOP_CAPABLE
#define ACL_REDIRECT_TO_PORT_CAPABLE
#define DSCP_REWRITE_CAPABLE
#define NEXTHOP_DISABLE_REWRITES_CAPABLE
#define IP_MULTICAST_CAPABLE
#define L3_ADMIT_SUPPORTS_IN_PORT
#define NEXTHOP_DISABLE_REWRITES_CAPABLE
#define MIRROR_CAPABLE
#define RIF_PROGRAMMING_MY_MAC_SUPPORTED
#define TIMESTAMP_CAPABLE
#define TUNNEL_ENCAP_CAPABLE

Expand Down
4 changes: 4 additions & 0 deletions sai_p4/instantiations/google/tor.p4info.pb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ tables {
annotations: "@sai_acl(INGRESS)"
annotations: "@sai_acl_priority(5)"
annotations: "@nonessential_for_upgrade"
annotations: "@reinstall_during_upgrade"
annotations: "@entry_restriction(\"\n // Forbid using ether_type for IP packets (by convention, use is_ip* instead).\n ether_type != 0x0800 && ether_type != 0x86dd;\n // Forbid match on ethertype if is_ip* is set.\n (is_ip::mask != 0 || is_ipv4::mask != 0 || is_ipv6::mask != 0) -> ether_type::mask == 0;\n // Only allow IP field matches for IP packets.\n dst_ip::mask != 0 -> is_ipv4 == 1;\n\n\n\n dst_ipv6::mask != 0 -> is_ipv6 == 1;\n src_ipv6::mask != 0 -> is_ipv6 == 1;\n ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n\n\n\n\n ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n // Only allow l4_dst_port and l4_src_port matches for TCP/UDP packets.\n l4_src_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\n\n // Only allow arp_tpa matches for ARP packets.\n arp_tpa::mask != 0 -> ether_type == 0x0806;\n\n\n // Only allow icmp_type matches for ICMP packets\n icmp_type::mask != 0 -> ip_protocol == 1;\n\n icmpv6_type::mask != 0 -> ip_protocol == 58;\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 {
Expand Down Expand Up @@ -744,6 +745,7 @@ tables {
annotations: "@sai_acl_priority(10)"
annotations: "@p4runtime_role(\"sdn_controller\")"
annotations: "@nonessential_for_upgrade"
annotations: "@reinstall_during_upgrade"
annotations: "@entry_restriction(\"\n // Forbid using ether_type for IP packets (by convention, use is_ip* instead).\n ether_type != 0x0800 && ether_type != 0x86dd;\n // Forbid match on ethertype if is_ip* is set.\n (is_ip::mask != 0 || is_ipv4::mask != 0 || is_ipv6::mask != 0) -> ether_type::mask == 0;\n // Only allow IP field matches for IP packets.\n ttl::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n ip_protocol::mask != 0 -> (is_ip == 1 || is_ipv4 == 1 || is_ipv6 == 1);\n // Only allow l4_dst_port matches for TCP/UDP packets.\n l4_dst_port::mask != 0 -> (ip_protocol == 6 || ip_protocol == 17);\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 // Only allow icmp_type matches for ICMP packets\n icmpv6_type::mask != 0 -> ip_protocol == 58;\n\n\n\n\n\n\n\n // Only allow arp_tpa matches for ARP packets.\n arp_tpa::mask != 0 -> ether_type == 0x0806;\n\n \")"
}
match_fields {
Expand Down Expand Up @@ -897,6 +899,8 @@ tables {
alias: "acl_ingress_mirror_and_redirect_table"
annotations: "@sai_acl(INGRESS)"
annotations: "@sai_acl_priority(15)"
annotations: "@nonessential_for_upgrade"
annotations: "@reinstall_during_upgrade"
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 \")"
}
Expand Down
Loading
Loading