@@ -27,6 +27,23 @@ output relation Warning[string]
index Logical_Flow_Index() on sb::Out_Logical_Flow()
+/* Single-row relation that contains the set of meters marked as fair. */
+relation AclFairLogMeters[Set<string>]
+AclFairLogMeters[meters] :- AclFairLogMeters0[meters].
+AclFairLogMeters[set_empty()] :-
+ Unit(),
+ not AclFairLogMeters0[_].
+
+relation AclFairLogMeters0[Set<string>]
+AclFairLogMeters0[meters] :-
+ meter in nb::Meter(.fair = Some{fair}),
+ fair,
+ var meters = {
+ var meters = set_empty();
+ set_insert(meters, meter.name);
+ meters
+ }.
+
/* Meter_Band table */
for (mb in nb::Meter_Band) {
sb::Out_Meter_Band(._uuid = mb._uuid,
@@ -42,6 +59,14 @@ for (meter in nb::Meter) {
.unit = meter.unit,
.bands = meter.bands)
}
+sb::Out_Meter(._uuid = hash128(meter_uuid ++ acl_uuid),
+ .name = "${name}__${uuid2str(acl_uuid)}",
+ .unit = unit,
+ .bands = bands) :-
+ AclFairLogMeters[names],
+ var name = FlatMap(names),
+ nb::Meter(._uuid = meter_uuid, .name = name, .unit = unit, .bands = bands),
+ nb::ACL(._uuid = acl_uuid, .meter = Some{name}).
/* Proxy table for Out_Datapath_Binding: contains all Datapath_Binding fields,
* except tunnel id, which is allocated separately (see TunKeyAllocation). */
@@ -2017,7 +2042,7 @@ for (&Switch(.ls = ls)) {
.external_ids = map_empty())
}
-function build_acl_log(acl: nb::ACL): string =
+function build_acl_log(acl: nb::ACL, fair_meters: Set<string>): string =
{
if (not acl.log) {
""
@@ -2049,7 +2074,14 @@ function build_acl_log(acl: nb::ACL): string =
};
match (acl.meter) {
None -> (),
- Some{meter} -> vec_push(strs, "meter=${json_string_escape(meter)}")
+ Some{meter} -> {
+ var s = if (fair_meters.contains(meter)) {
+ "${meter}__${uuid2str(acl._uuid)}"
+ } else {
+ meter
+ };
+ vec_push(strs, "meter=${json_string_escape(s)}")
+ }
};
"log(${string_join(strs, \", \")}); "
}
@@ -2067,31 +2099,33 @@ function oVN_ACL_PRI_OFFSET(): integer = 1000
relation Reject(lsuuid: uuid, pipeline: string, stage: Stage, acl: nb::ACL, extra_match: string, extra_actions: string)
/* build_reject_acl_rules() */
-for (Reject(lsuuid, pipeline, stage, acl, extra_match_, extra_actions_)) {
- var extra_match = match (extra_match_) {
- "" -> "",
- s -> "(${s}) && "
- } in
- var extra_actions = match (extra_actions_) {
- "" -> "",
- s -> "${s} "
- } in
- var next = match (pipeline == "ingress") {
- true -> "next(pipeline=egress,table=${stage_id(switch_stage(OUT, QOS_MARK)).0})",
- false -> "next(pipeline=ingress,table=${stage_id(switch_stage(IN, L2_LKUP)).0})"
- } in
- var acl_log = build_acl_log(acl) in {
- var __match = extra_match ++ acl.__match in
- var actions = acl_log ++ extra_actions ++ "reg0 = 0; "
- "reject { "
- "/* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ "
- "outport <-> inport; ${next}; };" in
- Flow(.logical_datapath = lsuuid,
- .stage = stage,
- .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = __match,
- .actions = actions,
- .external_ids = stage_hint(acl._uuid))
+for (AclFairLogMeters[fair_meters]) {
+ for (Reject(lsuuid, pipeline, stage, acl, extra_match_, extra_actions_)) {
+ var extra_match = match (extra_match_) {
+ "" -> "",
+ s -> "(${s}) && "
+ } in
+ var extra_actions = match (extra_actions_) {
+ "" -> "",
+ s -> "${s} "
+ } in
+ var next = match (pipeline == "ingress") {
+ true -> "next(pipeline=egress,table=${stage_id(switch_stage(OUT, QOS_MARK)).0})",
+ false -> "next(pipeline=ingress,table=${stage_id(switch_stage(IN, L2_LKUP)).0})"
+ } in
+ var acl_log = build_acl_log(acl, fair_meters) in {
+ var __match = extra_match ++ acl.__match in
+ var actions = acl_log ++ extra_actions ++ "reg0 = 0; "
+ "reject { "
+ "/* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ "
+ "outport <-> inport; ${next}; };" in
+ Flow(.logical_datapath = lsuuid,
+ .stage = stage,
+ .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
+ .__match = __match,
+ .actions = actions,
+ .external_ids = stage_hint(acl._uuid))
+ }
}
}
@@ -2347,114 +2381,117 @@ for (&Switch(.ls = ls)) {
}
/* Ingress or Egress ACL Table (Various priorities). */
-for (&SwitchACL(.sw = &Switch{.ls = ls, .has_stateful_acl = has_stateful}, .acl = &acl)) {
- /* consider_acl */
- var ingress = acl.direction == "from-lport" in
- var stage = if (ingress) { switch_stage(IN, ACL) } else { switch_stage(OUT, ACL) } in
- var pipeline = if ingress "ingress" else "egress" in
- var stage_hint = stage_hint(acl._uuid) in
- if (acl.action == "allow" or acl.action == "allow-related") {
- /* If there are any stateful flows, we must even commit "allow"
- * actions. This is because, while the initiater's
- * direction may not have any stateful rules, the server's
- * may and then its return traffic would not have an
- * associated conntrack entry and would return "+invalid". */
- if (not has_stateful) {
- Flow(.logical_datapath = ls._uuid,
- .stage = stage,
- .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = acl.__match,
- .actions = "${build_acl_log(acl)}next;",
- .external_ids = stage_hint)
- } else {
- /* Commit the connection tracking entry if it's a new
- * connection that matches this ACL. After this commit,
- * the reply traffic is allowed by a flow we create at
- * priority 65535, defined earlier.
- *
- * It's also possible that a known connection was marked for
- * deletion after a policy was deleted, but the policy was
- * re-added while that connection is still known. We catch
- * that case here and un-set ct_label.blocked (which will be done
- * by ct_commit in the "stateful" stage) to indicate that the
- * connection should be allowed to resume.
- */
- Flow(.logical_datapath = ls._uuid,
- .stage = stage,
- .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = "${rEGBIT_ACL_HINT_ALLOW_NEW()} == 1 && (${acl.__match})",
- .actions = "${rEGBIT_CONNTRACK_COMMIT()} = 1; ${build_acl_log(acl)}next;",
- .external_ids = stage_hint);
-
- /* Match on traffic in the request direction for an established
- * connection tracking entry that has not been marked for
- * deletion. There is no need to commit here, so we can just
- * proceed to the next table. We use this to ensure that this
- * connection is still allowed by the currently defined
- * policy. Match untracked packets too. */
- Flow(.logical_datapath = ls._uuid,
- .stage = stage,
- .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = "${rEGBIT_ACL_HINT_ALLOW()} == 1 && (${acl.__match})",
- .actions = "${build_acl_log(acl)}next;",
- .external_ids = stage_hint)
- }
- } else if (acl.action == "drop" or acl.action == "reject") {
- /* The implementation of "drop" differs if stateful ACLs are in
- * use for this datapath. In that case, the actions differ
- * depending on whether the connection was previously committed
- * to the connection tracker with ct_commit. */
- if (has_stateful) {
- /* If the packet is not tracked or not part of an established
- * connection, then we can simply reject/drop it. */
- var __match = "${rEGBIT_ACL_HINT_DROP()} == 1" in
- if (acl.action == "reject") {
- Reject(ls._uuid, pipeline, stage, acl, __match, "")
- } else {
+for (AclFairLogMeters[fair_meters]) {
+ for (&SwitchACL(.sw = &Switch{.ls = ls, .has_stateful_acl = has_stateful}, .acl = &acl)) {
+ /* consider_acl */
+ var ingress = acl.direction == "from-lport" in
+ var stage = if (ingress) { switch_stage(IN, ACL) } else { switch_stage(OUT, ACL) } in
+ var pipeline = if ingress "ingress" else "egress" in
+ var stage_hint = stage_hint(acl._uuid) in
+ var log_acl = build_acl_log(acl, fair_meters) in
+ if (acl.action == "allow" or acl.action == "allow-related") {
+ /* If there are any stateful flows, we must even commit "allow"
+ * actions. This is because, while the initiater's
+ * direction may not have any stateful rules, the server's
+ * may and then its return traffic would not have an
+ * associated conntrack entry and would return "+invalid". */
+ if (not has_stateful) {
Flow(.logical_datapath = ls._uuid,
.stage = stage,
.priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = __match ++ " && (${acl.__match})",
- .actions = "${build_acl_log(acl)}/* drop */",
+ .__match = acl.__match,
+ .actions = "${log_acl}next;",
.external_ids = stage_hint)
- };
- /* For an existing connection without ct_label set, we've
- * encountered a policy change. ACLs previously allowed
- * this connection and we committed the connection tracking
- * entry. Current policy says that we should drop this
- * connection. First, we set bit 0 of ct_label to indicate
- * that this connection is set for deletion. By not
- * specifying "next;", we implicitly drop the packet after
- * updating conntrack state. We would normally defer
- * ct_commit() to the "stateful" stage, but since we're
- * rejecting/dropping the packet, we go ahead and do it here.
- */
- var __match = "${rEGBIT_ACL_HINT_BLOCK()} == 1" in
- var actions = "ct_commit { ct_label.blocked = 1; }; " in
- if (acl.action == "reject") {
- Reject(ls._uuid, pipeline, stage, acl, __match, actions)
} else {
+ /* Commit the connection tracking entry if it's a new
+ * connection that matches this ACL. After this commit,
+ * the reply traffic is allowed by a flow we create at
+ * priority 65535, defined earlier.
+ *
+ * It's also possible that a known connection was marked for
+ * deletion after a policy was deleted, but the policy was
+ * re-added while that connection is still known. We catch
+ * that case here and un-set ct_label.blocked (which will be done
+ * by ct_commit in the "stateful" stage) to indicate that the
+ * connection should be allowed to resume.
+ */
Flow(.logical_datapath = ls._uuid,
.stage = stage,
.priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = __match ++ " && (${acl.__match})",
- .actions = "${actions}${build_acl_log(acl)}/* drop */",
- .external_ids = stage_hint)
- }
- } else {
- /* There are no stateful ACLs in use on this datapath,
- * so a "reject/drop" ACL is simply the "reject/drop"
- * logical flow action in all cases. */
- if (acl.action == "reject") {
- Reject(ls._uuid, pipeline, stage, acl, "", "")
- } else {
+ .__match = "${rEGBIT_ACL_HINT_ALLOW_NEW()} == 1 && (${acl.__match})",
+ .actions = "${rEGBIT_CONNTRACK_COMMIT()} = 1; ${log_acl}next;",
+ .external_ids = stage_hint);
+
+ /* Match on traffic in the request direction for an established
+ * connection tracking entry that has not been marked for
+ * deletion. There is no need to commit here, so we can just
+ * proceed to the next table. We use this to ensure that this
+ * connection is still allowed by the currently defined
+ * policy. Match untracked packets too. */
Flow(.logical_datapath = ls._uuid,
.stage = stage,
.priority = acl.priority + oVN_ACL_PRI_OFFSET(),
- .__match = acl.__match,
- .actions = "${build_acl_log(acl)}/* drop */",
+ .__match = "${rEGBIT_ACL_HINT_ALLOW()} == 1 && (${acl.__match})",
+ .actions = "${log_acl}next;",
.external_ids = stage_hint)
}
+ } else if (acl.action == "drop" or acl.action == "reject") {
+ /* The implementation of "drop" differs if stateful ACLs are in
+ * use for this datapath. In that case, the actions differ
+ * depending on whether the connection was previously committed
+ * to the connection tracker with ct_commit. */
+ if (has_stateful) {
+ /* If the packet is not tracked or not part of an established
+ * connection, then we can simply reject/drop it. */
+ var __match = "${rEGBIT_ACL_HINT_DROP()} == 1" in
+ if (acl.action == "reject") {
+ Reject(ls._uuid, pipeline, stage, acl, __match, "")
+ } else {
+ Flow(.logical_datapath = ls._uuid,
+ .stage = stage,
+ .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
+ .__match = __match ++ " && (${acl.__match})",
+ .actions = "${log_acl}/* drop */",
+ .external_ids = stage_hint)
+ };
+ /* For an existing connection without ct_label set, we've
+ * encountered a policy change. ACLs previously allowed
+ * this connection and we committed the connection tracking
+ * entry. Current policy says that we should drop this
+ * connection. First, we set bit 0 of ct_label to indicate
+ * that this connection is set for deletion. By not
+ * specifying "next;", we implicitly drop the packet after
+ * updating conntrack state. We would normally defer
+ * ct_commit() to the "stateful" stage, but since we're
+ * rejecting/dropping the packet, we go ahead and do it here.
+ */
+ var __match = "${rEGBIT_ACL_HINT_BLOCK()} == 1" in
+ var actions = "ct_commit { ct_label.blocked = 1; }; " in
+ if (acl.action == "reject") {
+ Reject(ls._uuid, pipeline, stage, acl, __match, actions)
+ } else {
+ Flow(.logical_datapath = ls._uuid,
+ .stage = stage,
+ .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
+ .__match = __match ++ " && (${acl.__match})",
+ .actions = "${actions}${log_acl}/* drop */",
+ .external_ids = stage_hint)
+ }
+ } else {
+ /* There are no stateful ACLs in use on this datapath,
+ * so a "reject/drop" ACL is simply the "reject/drop"
+ * logical flow action in all cases. */
+ if (acl.action == "reject") {
+ Reject(ls._uuid, pipeline, stage, acl, "", "")
+ } else {
+ Flow(.logical_datapath = ls._uuid,
+ .stage = stage,
+ .priority = acl.priority + oVN_ACL_PRI_OFFSET(),
+ .__match = acl.__match,
+ .actions = "${log_acl}/* drop */",
+ .external_ids = stage_hint)
+ }
+ }
}
}
}
@@ -1920,6 +1920,7 @@ sw1flows3: table=5 (ls_out_acl ), priority=2003 , match=((reg0[[9]] ==
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD([
AT_SETUP([ovn-northd -- ACL fair Meters])
AT_KEYWORDS([acl log meter fair])
ovn_start
@@ -2016,6 +2017,7 @@ check_meter_by_name meter_me
check_meter_by_name NOT meter_me__${acl1} meter_me__${acl2}
AT_CLEANUP
+])
OVN_FOR_EACH_NORTHD([
AT_SETUP([datapath requested-tnl-key])
ddlog for ACL log meters. Implement fair meters for acl logs in ovn_northd.dl. Enable fair Meter test to also exercise ovn-northd-ddlog. This is a small variation of blp's implementation of ddlog for ACL log meters, now using the northbound schema changes [1]. The changes address overall design issues mentioned in that link. [1]: https://patchwork.ozlabs.org/project/ovn/patch/20201107213913.GC2753733@ovn.org/ Co-authored-by: Ben Pfaff <blp@ovn.org> Signed-off-by: Flavio Fernandes <flavio@flaviof.com> --- northd/ovn_northd.dl | 281 ++++++++++++++++++++++++------------------- tests/ovn-northd.at | 2 + 2 files changed, 161 insertions(+), 122 deletions(-)