Skip to content

Commit

Permalink
PUBLIC: [p4-constraints] Add support for action restrictions in the i…
Browse files Browse the repository at this point in the history
…nterpreter.

PiperOrigin-RevId: 579986405
  • Loading branch information
PINS Team authored and Bara Kopi committed Nov 7, 2023
1 parent d38d215 commit 909dca9
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 116 deletions.
1 change: 1 addition & 0 deletions p4_constraints/backend/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cc_library(
"//gutils:ordered_map",
"//gutils:overload",
"//gutils:ret_check",
"//gutils:source_location",
"//gutils:status",
"//p4_constraints:ast",
"//p4_constraints:ast_cc_proto",
Expand Down
7 changes: 7 additions & 0 deletions p4_constraints/backend/constraint_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,13 @@ const TableInfo* GetTableInfoOrNull(const ConstraintInfo& constraint_info,
return &it->second;
}

const ActionInfo* GetActionInfoOrNull(const ConstraintInfo& constraint_info,
uint32_t action_id) {
auto it = constraint_info.action_info_by_id.find(action_id);
if (it == constraint_info.action_info_by_id.end()) return nullptr;
return &it->second;
}

absl::StatusOr<ConstraintInfo> P4ToConstraintInfo(
const p4::config::v1::P4Info& p4info) {
// Allocate output.
Expand Down
135 changes: 80 additions & 55 deletions p4_constraints/backend/interpreter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include "gutils/ordered_map.h"
#include "gutils/overload.h"
#include "gutils/ret_check.h"
#include "gutils/source_location.h"
#include "gutils/status_builder.h"
#include "gutils/status_macros.h"
#include "p4/v1/p4runtime.pb.h"
#include "p4_constraints/ast.h"
Expand Down Expand Up @@ -199,6 +201,7 @@ absl::StatusOr<std::pair<std::string, EvalResult>> ParseKey(
absl::StatusOr<TableEntry> ParseEntry(const p4::v1::TableEntry& entry,
const TableInfo& table_info) {
absl::flat_hash_map<std::string, EvalResult> keys;
absl::flat_hash_map<std::string, Integer> action_parameters;

// Parse all keys that are explicitly present.
for (const p4::v1::FieldMatch& field : entry.match()) {
Expand All @@ -212,7 +215,7 @@ absl::StatusOr<TableEntry> ParseEntry(const p4::v1::TableEntry& entry,
}

// Use default value for omitted keys.
// See Section 9.1.1. of the P4runtime specification.
// See Section 9.1.1. of the P4 Runtime specification.
for (const auto& [name, key_info] : table_info.keys_by_name) {
if (keys.contains(name)) continue;
switch (key_info.type.type_case()) {
Expand Down Expand Up @@ -246,10 +249,22 @@ absl::StatusOr<TableEntry> ParseEntry(const p4::v1::TableEntry& entry,
<< key_info.type.DebugString();
}

for (auto& param : entry.action().action().params()) {
std::string param_id = std::to_string(param.param_id());
absl::StatusOr<Integer> param_value = ParseP4RTInteger(param.value());
if (!param_value.ok()) {
return gutils::InternalErrorBuilder(GUTILS_LOC)
<< "Unable to parse " << param.value() << " into an integer";
} else {
action_parameters[param_id] = param_value.value();
}
}

return TableEntry{
.table_name = table_info.name,
.priority = entry.priority(),
.keys = keys,
.action_parameters = action_parameters,
};
}

Expand Down Expand Up @@ -628,17 +643,23 @@ absl::StatusOr<EvalResult> Eval_(const Expression& expr,
case Expression::kKey: {
auto it = context.entry.keys.find(expr.key());
if (it == context.entry.keys.end()) {
RuntimeTypeError(context.source, expr.start_location(),
expr.end_location())
<< "unknown key " << expr.key() << " in table "
<< context.entry.table_name;
return RuntimeTypeError(context.source, expr.start_location(),
expr.end_location())
<< "unknown key " << expr.key() << " in table "
<< context.entry.table_name;
}
return it->second;
}

case Expression::kActionParameter: {
return absl::UnimplementedError(
"TODO: b/293656077 - Support action constraints");
auto it = context.entry.action_parameters.find(expr.action_parameter());
if (it == context.entry.action_parameters.end()) {
return RuntimeTypeError(context.source, expr.start_location(),
expr.end_location())
<< "unknown action parameter " << expr.key() << " in table "
<< context.entry.table_name;
}
return it->second;
}

case Expression::kAttributeAccess: {
Expand Down Expand Up @@ -752,43 +773,6 @@ absl::StatusOr<EvalResult> Eval(const Expression& expr,

// -- Public interface ---------------------------------------------------------

absl::StatusOr<bool> EntryMeetsConstraint(const p4::v1::TableEntry& entry,
const ConstraintInfo& context) {
using ::p4_constraints::internal_interpreter::EvalToBool;
using ::p4_constraints::internal_interpreter::EvaluationContext;
using ::p4_constraints::internal_interpreter::P4IDToString;
using ::p4_constraints::internal_interpreter::ParseEntry;
using ::p4_constraints::internal_interpreter::TableEntry;

// Find table associated with entry.
auto* table_info = GetTableInfoOrNull(context, entry.table_id());
if (table_info == nullptr) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table entry with unknown table ID "
<< P4IDToString(entry.table_id());
}

// Check if entry satisfies table constraint (if present).
if (!table_info->constraint.has_value()) return "";
const Expression& constraint = table_info->constraint.value();
if (constraint.type().type_case() != Type::kBoolean) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table " << table_info->name
<< " has non-boolean constraint: " << constraint.DebugString();
}

// Parse entry and check constraint.
ASSIGN_OR_RETURN(TableEntry parsed_entry, ParseEntry(entry, *table_info),
_ << " while parsing P4RT table entry for table '"
<< table_info->name << "':");
EvaluationContext eval_context{
.entry = parsed_entry,
.source = table_info->constraint_source,
};
// No explanation is returned so no cache is provided.
return EvalToBool(constraint, eval_context, /*eval_cache=*/nullptr);
}

absl::StatusOr<std::string> ReasonEntryViolatesConstraint(
const p4::v1::TableEntry& entry, const ConstraintInfo& context) {
using ::p4_constraints::ast::SizeCache;
Expand All @@ -800,6 +784,33 @@ absl::StatusOr<std::string> ReasonEntryViolatesConstraint(
using ::p4_constraints::internal_interpreter::ParseEntry;
using ::p4_constraints::internal_interpreter::TableEntry;

// Find an action associated with entry if it exists.
Expression action_constraint;
Expression table_constraint;
bool action_constraint_exists = false;
bool table_constraint_exists = false;

if (entry.has_action()) {
const ::uint32_t action_id = entry.action().action().action_id();
auto* action_info = GetActionInfoOrNull(context, action_id);
if (action_info == nullptr) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "action entry with unknown action ID "
<< P4IDToString(action_id);
}
// Check if action has an action restriction.
if (action_info->constraint.has_value()) {
action_constraint = action_info->constraint.value();
if (action_constraint.type().type_case() != Type::kBoolean) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "action " << action_info->name
<< " has non-boolean constraint: "
<< action_constraint.DebugString();
}
action_constraint_exists = true;
}
}

// Find table associated with entry.
auto* table_info = GetTableInfoOrNull(context, entry.table_id());
if (table_info == nullptr) {
Expand All @@ -808,13 +819,16 @@ absl::StatusOr<std::string> ReasonEntryViolatesConstraint(
<< P4IDToString(entry.table_id());
}
// Check if entry satisfies table constraint (if present).
if (!table_info->constraint.has_value()) return "";
const Expression& constraint = table_info->constraint.value();
if (constraint.type().type_case() != Type::kBoolean) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table " << table_info->name
<< " has non-boolean constraint: " << constraint.DebugString();
if (table_info->constraint.has_value()) {
table_constraint = table_info->constraint.value();
if (table_constraint.type().type_case() != Type::kBoolean) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table " << table_info->name << " has non-boolean constraint: "
<< table_constraint.DebugString();
}
table_constraint_exists = true;
}
if (!table_constraint_exists && !action_constraint_exists) return "";

// Parse entry and check constraint.
ASSIGN_OR_RETURN(const TableEntry parsed_entry,
Expand All @@ -826,12 +840,23 @@ absl::StatusOr<std::string> ReasonEntryViolatesConstraint(
.source = table_info->constraint_source,
};
EvaluationCache eval_cache;
ASSIGN_OR_RETURN(bool entry_satisfies_constraint,
EvalToBool(constraint, eval_context, &eval_cache));
if (entry_satisfies_constraint) return "";
SizeCache size_cache;
return ExplainConstraintViolation(constraint, eval_context, eval_cache,
size_cache);

bool entry_satisfies_action_constraint = true;
bool entry_satisfies_table_constraint = true;
if (table_constraint_exists) {
ASSIGN_OR_RETURN(entry_satisfies_table_constraint,
EvalToBool(table_constraint, eval_context, &eval_cache));
}
if (action_constraint_exists) {
ASSIGN_OR_RETURN(entry_satisfies_action_constraint,
EvalToBool(action_constraint, eval_context, &eval_cache));
}
if (entry_satisfies_table_constraint && entry_satisfies_action_constraint)
return "";
return ExplainConstraintViolation(
entry_satisfies_table_constraint ? action_constraint : table_constraint,
eval_context, eval_cache, size_cache);
}

} // namespace p4_constraints
10 changes: 2 additions & 8 deletions p4_constraints/backend/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@ namespace p4_constraints {
absl::StatusOr<std::string> ReasonEntryViolatesConstraint(
const p4::v1::TableEntry& entry, const ConstraintInfo& context);

// Checks if a given table entry satisfies the entry constraint attached to its
// associated table. Returns true if this is the case or if no constraint
// exists. Returns an InvalidArgument Status if the entry belongs to a table not
// present in ConstraintInfo, or if it is inconsistent with the table definition
// in ConstraintInfo.
absl::StatusOr<bool> EntryMeetsConstraint(const p4::v1::TableEntry& entry,
const ConstraintInfo& context);

// -- END OF PUBLIC INTERFACE --------------------------------------------------

// Exposed for testing only.
Expand Down Expand Up @@ -150,6 +142,8 @@ struct TableEntry {
// be a total map from key names to values.
absl::flat_hash_map<std::string, EvalResult> keys;
// TODO(smolkaj): once we support actions, they will be added here.
// Map of action parameter IDs to values.
absl::flat_hash_map<std::string, Integer> action_parameters;
};

// Parses p4::v1::TableEntry
Expand Down
Loading

0 comments on commit 909dca9

Please sign in to comment.