-
Notifications
You must be signed in to change notification settings - Fork 7
/
p4check.cc
152 lines (134 loc) · 5.46 KB
/
p4check.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2020 The P4-Constraints Authors
//
// 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
//
// http://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.
// Usage: p4check --p4info=<file> [<table_entry_file> ...]
//
// Parses the table constraints in the given P4 program (in p4info.proto text
// format) and checks if the given table entries (in p4runtime.proto text
// format) satisfy the constraints imposed on their respective tables.
//
// This CLI is not intended for use in production; it is intended for testing
// and showcasing the p4_constraints library.
#include <stdint.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/flags/usage.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"
#include "p4/config/v1/p4info.pb.h"
#include "p4/v1/p4runtime.pb.h"
#include "p4_constraints/backend/constraint_info.h"
#include "p4_constraints/backend/interpreter.h"
using ::p4_constraints::ConstraintInfo;
using ::p4_constraints::P4ToConstraintInfo;
using ::p4_constraints::ReasonEntryViolatesConstraint;
ABSL_FLAG(std::string, p4info, "", "p4info file (required)");
constexpr char kUsage[] =
"--p4info=<file> [<table entry file in P4RT protobuf format> ...]";
// The 8 most significant bits of any P4Runtime table ID must equal
// p4::config::v1::P4Ids::TABLE. To ease writing table entries by hand in
// testing, we simply coerce all table IDs into the correct format.
// See P4Runtime specification, "6.3 ID Allocation for P4Info Objects".
uint32_t CoerceToTableId(uint32_t table_id) {
return (table_id & 0x00FFFFFF) | (p4::config::v1::P4Ids::TABLE << 24);
}
std::string ToString(const absl::Status& status) {
return absl::StrCat(absl::StatusCodeToString(status.code()), ": ",
status.message());
}
int main(int argc, char** argv) {
const absl::string_view usage[] = {"usage:", argv[0], kUsage};
absl::SetProgramUsageMessage(absl::StrJoin(usage, " "));
std::vector<char*> positional_args = absl::ParseCommandLine(argc, argv);
// Read p4info flag.
const std::string p4info_filename = absl::GetFlag(FLAGS_p4info);
if (p4info_filename.empty()) {
std::cerr << "Missing argument: --p4info=<file>\n";
return 1;
}
// Open p4info file.
std::ifstream p4info_file(p4info_filename);
if (!p4info_file.is_open()) {
std::cerr << "Unable to open p4info file: " << p4info_filename << "\n";
return 1;
}
// Parse p4info file.
p4::config::v1::P4Info p4info;
{
google::protobuf::io::IstreamInputStream stream(&p4info_file);
if (!google::protobuf::TextFormat::Parse(&stream, &p4info)) {
std::cerr << "Unable to parse p4info file: " << p4info_filename << "\n";
return 1;
}
}
// p4c 2019 and earlier does not set the 8 most significant bits of table IDs
// correctly, but p4c 2020 (since PR p4lang/p4c#2243) does. To make p4check
// compatible with both, we coerce all table IDs into the right format here.
for (auto& table : *p4info.mutable_tables()) {
table.mutable_preamble()->set_id(CoerceToTableId(table.preamble().id()));
}
// Parse constraints and report potential errors.
absl::StatusOr<ConstraintInfo> constraint_info = P4ToConstraintInfo(p4info);
if (!constraint_info.ok()) {
std::cerr << constraint_info.status().message();
return 1;
}
// Check table entries, if any where given.
for (const char* entry_filename :
absl::MakeSpan(positional_args).subspan(1)) {
std::cout << "### P4Constraints Table Entry Test #######################\n";
std::cout << "=== Input Table Entry File ===\n";
std::cout << entry_filename << "\n";
std::cout << "=== Output ===\n";
// Open entry file.
std::ifstream entry_file(entry_filename);
if (!entry_file.is_open()) {
std::cout << "not found\n\n";
continue;
}
// Parse entry file.
p4::v1::TableEntry entry;
{
google::protobuf::io::IstreamInputStream entry_stream(&entry_file);
if (!google::protobuf::TextFormat::Parse(&entry_stream, &entry)) {
std::cout << "unable to parse\n\n";
continue;
}
}
// For testing, it is convenient to write table entries whose table ID
// matches the @id annotation of the corresponding table in the .p4 program.
// However, p4c sets the 8 most significant bits of IDs from @id annotations
// to p4::config::v1::P4Ids::TABLE, so we must do the same here.
entry.set_table_id(CoerceToTableId(entry.table_id()));
// Check entry.
absl::StatusOr<std::string> result =
ReasonEntryViolatesConstraint(entry, *constraint_info);
if (!result.ok()) {
std::cout << "Error: " << ToString(result.status()) << "\n\n";
continue;
}
std::cout << (result->empty() ? "Constraint satisfied\n" : result.value())
<< "\n";
}
return 0;
}