Skip to content

Commit 00103e7

Browse files
matthewtlamcopybara-github
authored andcommitted
[NetKAT] Support difference operation.
PiperOrigin-RevId: 781676296
1 parent 75ccc95 commit 00103e7

14 files changed

+269
-4
lines changed

netkat/evaluator.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ absl::flat_hash_set<Packet> Evaluate(const PolicyProto& policy,
9898
} while (last_size != result.size());
9999
return result;
100100
}
101+
case PolicyProto::kDifferenceOp: {
102+
absl::flat_hash_set<Packet> result =
103+
Evaluate(policy.difference_op().left(), packet);
104+
for (const Packet& packet :
105+
Evaluate(policy.difference_op().right(), packet)) {
106+
if (result.contains(packet)) result.erase(packet);
107+
}
108+
return result;
109+
}
101110
case PolicyProto::POLICY_NOT_SET:
102111
// Unset policy is treated as Deny.
103112
return {};

netkat/evaluator_test.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,19 @@ void UnionCombines(Packet packet, PolicyProto left, PolicyProto right) {
260260
}
261261
FUZZ_TEST(EvaluatePolicyProtoTest, UnionCombines);
262262

263+
void DifferenceRemoves(Packet packet, PolicyProto left, PolicyProto right) {
264+
absl::flat_hash_set<Packet> expected_packets = Evaluate(left, packet);
265+
for (const Packet& packet : Evaluate(right, packet)) {
266+
if (expected_packets.contains(packet)) {
267+
expected_packets.erase(packet);
268+
}
269+
}
270+
271+
EXPECT_THAT(Evaluate(DifferenceProto(left, right), packet),
272+
ContainerEq(expected_packets));
273+
}
274+
FUZZ_TEST(EvaluatePolicyProtoTest, DifferenceRemoves);
275+
263276
void SequenceSequences(Packet packet, PolicyProto left, PolicyProto right) {
264277
absl::flat_hash_set<Packet> expected_packets =
265278
Evaluate(right, Evaluate(left, packet));

netkat/frontend.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ absl::Status RecursivelyCheckIsValid(const PolicyProto& policy_proto) {
126126
RecursivelyCheckIsValid(policy_proto.iterate_op().iterable()))
127127
<< "PolicyProto::Iterate::policy is invalid: ";
128128
return absl::OkStatus();
129+
case PolicyProto::kDifferenceOp:
130+
RETURN_IF_ERROR(
131+
RecursivelyCheckIsValid(policy_proto.difference_op().left()))
132+
.SetPrepend()
133+
<< "PolicyProto::DifferenceOp::left is invalid: ";
134+
RETURN_IF_ERROR(
135+
RecursivelyCheckIsValid(policy_proto.difference_op().right()))
136+
.SetPrepend()
137+
<< "PolicyProto::DifferenceOp::right is invalid: ";
138+
return absl::OkStatus();
129139
case PolicyProto::POLICY_NOT_SET:
130140
return absl::InvalidArgumentError("Unset Policy case is invalid");
131141
}
@@ -179,6 +189,11 @@ Policy Filter(Predicate predicate) {
179189
return Policy(FilterProto(std::move(predicate).ToProto()));
180190
}
181191

192+
Policy Difference(Policy left, Policy right) {
193+
return Policy(
194+
DifferenceProto(std::move(left).ToProto(), std::move(right).ToProto()));
195+
}
196+
182197
Policy Policy::Accept() { return Filter(Predicate::True()); }
183198

184199
Policy Policy::Deny() { return Filter(Predicate::False()); }

netkat/frontend.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class Policy {
201201
friend Policy Sequence(std::vector<Policy>);
202202
friend Policy Union(std::vector<Policy>);
203203
friend Policy Iterate(Policy);
204+
friend Policy Difference(Policy, Policy);
204205
friend Policy Record();
205206

206207
// Policies that conceptually represent a program that should accept or
@@ -254,7 +255,7 @@ Policy Modify(absl::string_view field, int new_value);
254255
Policy Sequence(std::vector<Policy> policies);
255256

256257
// Allows callers to Sequence policies without wrapping them in a list. Prefer
257-
// this overload when reasonble. For example, instead of
258+
// this overload when reasonable. For example, instead of
258259
//
259260
// Sequence({p0, p1, p2, p3})
260261
//

netkat/frontend_test.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ void ExpectFromProtoToFailWithInvalidPolicyProto(PolicyProto policy_proto) {
170170
case PolicyProto::kIterateOp:
171171
policy_proto.mutable_iterate_op()->clear_iterable();
172172
break;
173+
case PolicyProto::kDifferenceOp:
174+
policy_proto.mutable_difference_op()->clear_left();
175+
break;
173176
// Unset policy is invalid.
174177
case PolicyProto::POLICY_NOT_SET:
175178
break;
@@ -209,6 +212,14 @@ void IterateToProtoIsCorrect(Policy policy) {
209212
FUZZ_TEST(FrontEndTest, IterateToProtoIsCorrect)
210213
.WithDomains(/*policy=*/AtomicDupFreePolicyDomain());
211214

215+
void DifferenceToProtoIsCorrect(Policy left, Policy right) {
216+
EXPECT_THAT(Difference(left, right).ToProto(),
217+
EqualsProto(DifferenceProto(left.ToProto(), right.ToProto())));
218+
}
219+
FUZZ_TEST(FrontEndTest, DifferenceToProtoIsCorrect)
220+
.WithDomains(/*policy=*/AtomicDupFreePolicyDomain(),
221+
/*policy=*/AtomicDupFreePolicyDomain());
222+
212223
TEST(FrontEndTest, SequenceWithNoElementsIsAccept) {
213224
EXPECT_THAT(Sequence().ToProto(), EqualsProto(AcceptProto()));
214225
}

netkat/netkat.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ message PolicyProto {
116116
Sequence sequence_op = 4;
117117
Union union_op = 5;
118118
Iterate iterate_op = 6;
119+
Difference difference_op = 7;
119120
}
120121

121122
// Sets the field to the given value.
@@ -148,6 +149,12 @@ message PolicyProto {
148149
PolicyProto iterable = 1;
149150
}
150151

152+
// Represents the difference of two policies, i.e. a - b.
153+
message Difference {
154+
PolicyProto left = 1;
155+
PolicyProto right = 2;
156+
}
157+
151158
// Records the packet, at the given point, into the history. Referred to as
152159
// "dup" in the literature.
153160
message Record {}

netkat/netkat_proto_constructors.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ PolicyProto IterateProto(PolicyProto iterable) {
113113
return policy;
114114
}
115115

116+
PolicyProto DifferenceProto(PolicyProto left, PolicyProto right) {
117+
PolicyProto policy;
118+
*policy.mutable_difference_op()->mutable_left() = std::move(left);
119+
*policy.mutable_difference_op()->mutable_right() = std::move(right);
120+
return policy;
121+
}
122+
116123
// -- Derived Policy constructors ----------------------------------------------
117124

118125
PolicyProto DenyProto() { return FilterProto(FalseProto()); }
@@ -165,6 +172,10 @@ std::string AsShorthandString(PolicyProto policy) {
165172
case PolicyProto::kIterateOp:
166173
return absl::StrFormat("(%s)*",
167174
AsShorthandString(policy.iterate_op().iterable()));
175+
case PolicyProto::kDifferenceOp:
176+
return absl::StrFormat("(%s - %s)",
177+
AsShorthandString(policy.difference_op().left()),
178+
AsShorthandString(policy.difference_op().right()));
168179
case PolicyProto::POLICY_NOT_SET:
169180
return "deny";
170181
}

netkat/netkat_proto_constructors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ PolicyProto RecordProto();
4747
PolicyProto SequenceProto(PolicyProto left, PolicyProto right);
4848
PolicyProto UnionProto(PolicyProto left, PolicyProto right);
4949
PolicyProto IterateProto(PolicyProto iterable);
50+
PolicyProto DifferenceProto(PolicyProto left, PolicyProto right);
5051

5152
// -- Derived Policy constructors ----------------------------------------------
5253

@@ -63,6 +64,7 @@ PolicyProto AcceptProto();
6364
// Policy Sequence -> ';'
6465
// Policy Or -> '+'
6566
// Iterate -> '*'
67+
// Difference -> '-'
6668
// Record -> 'record'
6769
// Match -> '@field==value'
6870
// Modify -> '@field:=value'

netkat/netkat_proto_constructors_test.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ void IterateProtoReturnsIterate(PolicyProto iterable) {
127127
}
128128
FUZZ_TEST(PolicyProtoTest, IterateProtoReturnsIterate);
129129

130+
void DifferenceProtoReturnsDifference(PolicyProto left, PolicyProto right) {
131+
PolicyProto expected_policy;
132+
*expected_policy.mutable_difference_op()->mutable_left() = left;
133+
*expected_policy.mutable_difference_op()->mutable_right() = right;
134+
135+
EXPECT_THAT(DifferenceProto(left, right), EqualsProto(expected_policy));
136+
}
137+
FUZZ_TEST(PolicyProtoTest, DifferenceProtoReturnsDifference);
138+
130139
// -- Derived Policy tests -----------------------------------------------------
131140

132141
TEST(PolicyProtoTest, DenyProtoFiltersOnFalse) {

netkat/netkat_test.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ namespace netkat {
2222
namespace {
2323

2424
// Sanity fuzz test to show that the FuzzTest library works.
25-
void DummmyFuzzTest(PredicateProto pred, PolicyProto pol) {
25+
void DummyFuzzTest(PredicateProto pred, PolicyProto pol) {
2626
LOG_EVERY_N_SEC(INFO, 1) << "pred = " << pred;
2727
LOG_EVERY_N_SEC(INFO, 1) << "pol = " << pol;
2828
}
29-
FUZZ_TEST(NetkatProtoTest, DummmyFuzzTest);
29+
FUZZ_TEST(NetkatProtoTest, DummyFuzzTest);
3030

3131
// Ensures that the protobuf C++ compiler does not add underscores to the
3232
// generated code for sub messages and oneof fields of `PredicateProto`.
@@ -104,6 +104,11 @@ TEST(NetkatProtoTest, PolicyOneOfFieldNamesDontRequireUnderscores) {
104104
LOG(INFO) << "iterate: " << iter;
105105
break;
106106
}
107+
case PolicyProto::kDifferenceOp: {
108+
const PolicyProto::Difference& difference_op = policy.difference_op();
109+
LOG(INFO) << "difference: " << difference_op;
110+
break;
111+
}
107112
case PolicyProto::POLICY_NOT_SET:
108113
break;
109114
}

0 commit comments

Comments
 (0)