On Wed, Feb 2, 2022 at 8:27 PM Mark Michelson <[email protected]> wrote:
>
> It can be desirable for replies to stateful ACLs to be logged. And in
> some cases, it can actually be a bit confusing why they aren't logged.
> Consider a situation where a port group called "port_group" exists and
> logical switch ports swp1 and swp2 belong to it. We create the following
> ACL, where logging is enabled:
>
> from-lport 100 'inport == @port_group' allow-stateless
>
> swp1 sends traffic to swp2 and swp2 responds within the same connection.
> In this case, the logs will show both the packets from swp1 to swp2, as
> well as the response packets from swp2 to swp1.
>
> Now change the ACL:
>
> from-lport 100 'inport == @port_group' allow-related
>
> Now with the same traffic pattern, the packets from swp1 to swp2 are
> logged, but the packets from swp2 to swp1 are not. Why is that?
>
> The reason is that as a shortcut, when stateful ACLs are used, a single
> priority 65532 flow is programmed to allow reply traffic to pass. When
> no stateful ACL is present, the reply traffic is at the mercy of the
> stateless ACL on the reply. Therefore, with the stateful ACL, the reply
> traffic is not actually hitting an ACL but is let through by default,
> but with the stateless ACL, the reply traffic does hit the ACL
> evaluation, so the reply traffic is logged.
>
> This change adds a feature that allows for reply traffic to be
> optionally logged for stateful ACLs, therefore allowing for the behavior
> to be similar for both ACL types. Since logging reply traffic requires
> adding more flows, it is not enabled by default. In order to have reply
> traffic logged, the ACL must have logging enabled, be stateful, have a
> label, and have the new log_related column set to true,
>
> Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2031150
>
> Signed-off-by: Mark Michelson <[email protected]>
Hi Mark,
The patch LGTM.
Can you please update the documentation in ovn-northd.8.xml for the
new flows added.
Instead of adding a new column in the ACL table for the log direction,
how about adding options column ?
I'd suggest a generic options column.
Thanks
Numan
> ---
> v2 -> v3:
> * Seeing Dumitru's ACL direction patch, I realized check_acl_log
> shouldn't be hard-coded to think "severity" is the final ACL field.
> Tweaked script to be more resilient in the face of change.
> * Added a few more (possibly) common acl_log packet fields to check.
> * Fixed errors in system-ovn.at where I was checking the icmp_type
> instead of the icmp_code
> * Updated commit message to mention that a label is required on the ACL.
> ---
> northd/northd.c | 41 +++++
> ovn-nb.ovsschema | 5 +-
> ovn-nb.xml | 10 ++
> tests/automake.mk | 3 +-
> tests/check_acl_log.py | 107 ++++++++++++
> tests/ovn-northd.at | 253 +++++++++++++++++++++++++++++
> tests/system-ovn.at | 359 +++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 775 insertions(+), 3 deletions(-)
> create mode 100644 tests/check_acl_log.py
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 22e783ff6..c1a8f71f5 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -6242,6 +6242,47 @@ consider_acl(struct hmap *lflows, struct ovn_datapath
> *od,
> acl->priority + OVN_ACL_PRI_OFFSET,
> ds_cstr(match), ds_cstr(actions),
> &acl->header_);
> +
> + /* Related and reply traffic are universally allowed by priority
> + * 65532 flows created in build_acls(). If logging is enabled on
> + * the ACL, then we need to ensure that the related and reply
> + * traffic is logged, so we install a slightly higher-priority
> + * flow that matches the ACL, allows the traffic, and logs it.
> + */
> + if (acl->log && acl->label && acl->log_related) {
> + /* Related/reply flows need to be set on the opposite
> pipeline
> + * from where the ACL itself is set.
> + */
> + enum ovn_stage log_related_stage = ingress ?
> + S_SWITCH_OUT_ACL :
> + S_SWITCH_IN_ACL;
> + ds_clear(match);
> + ds_clear(actions);
> +
> + ds_put_format(match, "ct.est && !ct.rel && !ct.new%s && "
> + "ct.rpl && ct_label.blocked == 0 && "
> + "ct_label.label == %" PRId64,
> + use_ct_inv_match ? " && !ct.inv" : "",
> + acl->label);
> + build_acl_log(actions, acl, meter_groups);
> + ds_put_cstr(actions, "next;");
> + ovn_lflow_add_with_hint(lflows, od, log_related_stage,
> + UINT16_MAX - 2,
> + ds_cstr(match), ds_cstr(actions),
> + &acl->header_);
> +
> + ds_clear(match);
> + ds_put_format(match, "!ct.est && ct.rel && !ct.new%s && "
> + "ct_label.blocked == 0 && "
> + "ct_label.label == %" PRId64,
> + use_ct_inv_match ? " && !ct.inv" : "",
> + acl->label);
> + ovn_lflow_add_with_hint(lflows, od, log_related_stage,
> + UINT16_MAX - 2,
> + ds_cstr(match), ds_cstr(actions),
> + &acl->header_);
> + }
> +
> }
> } else if (!strcmp(acl->action, "drop")
> || !strcmp(acl->action, "reject")) {
> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> index 55977339a..b72a5bf63 100644
> --- a/ovn-nb.ovsschema
> +++ b/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
> {
> "name": "OVN_Northbound",
> - "version": "5.34.1",
> - "cksum": "2177334725 30782",
> + "version": "5.35.0",
> + "cksum": "2223177829 30834",
> "tables": {
> "NB_Global": {
> "columns": {
> @@ -258,6 +258,7 @@
> "notice", "info",
> "debug"]]},
> "min": 0, "max": 1}},
> + "log_related": {"type": "boolean"},
> "meter": {"type": {"key": "string", "min": 0, "max": 1}},
> "label": {"type": {"key": {"type": "integer",
> "minInteger": 0,
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 6a6972856..154bd79e8 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -2061,6 +2061,16 @@
> separately, set the <ref column="fair" table="meter"/> column.
> </p>
> </column>
> +
> + <column name="log_related">
> + <p>
> + If set to <code>true</code>, then log when reply or related
> + traffic is admitted from a stateful ACL. In order for this
> + option to function, the <ref column="log"/> option must be
> + set to <code>true</code> and a <ref column="label"/> must
> + be set.
> + </p>
> + </column>
> </group>
>
> <group title="Common Columns">
> diff --git a/tests/automake.mk b/tests/automake.mk
> index a08dfa632..a5934d2b9 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -270,7 +270,8 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \
> CHECK_PYFILES = \
> tests/test-l7.py \
> tests/uuidfilt.py \
> - tests/test-tcp-rst.py
> + tests/test-tcp-rst.py \
> + tests/check_acl_log.py
>
> EXTRA_DIST += $(CHECK_PYFILES)
> PYCOV_CLEAN_FILES += $(CHECK_PYFILES:.py=.py,cover) .coverage
> diff --git a/tests/check_acl_log.py b/tests/check_acl_log.py
> new file mode 100644
> index 000000000..1dd9630c0
> --- /dev/null
> +++ b/tests/check_acl_log.py
> @@ -0,0 +1,107 @@
> +#!/usr/bin/env python3
> +import argparse
> +import string
> +
> +
> +def strip(val):
> + """Strip whitespace and quotation marks from val"""
> + return val.strip(f"{string.whitespace}\"'")
> +
> +
> +def parse_acl_log(line):
> + """Convert an ACL log string into a dict"""
> + # First cut off the logging preamble.
> + # We're assuming the default log format.
> + acl_log = {}
> + _, _, details = line.rpartition("|")
> +
> + # acl_details are things like the acl name, direction,
> + # verdict, and severity. packet_details are things like
> + # the protocol, addresses, and ports of the packet being
> + # logged.
> + acl_details, _, packet_details = details.partition(":")
> + for datum in acl_details.split(","):
> + name, _, value = datum.rpartition("=")
> + acl_log[strip(name)] = strip(value)
> +
> + for datum in packet_details.split(","):
> + name, _, value = datum.rpartition("=")
> + if not name:
> + # The protocol is not preceded by "protocol="
> + # so we need to add it manually.
> + name = "protocol"
> + acl_log[strip(name)] = strip(value)
> +
> + return acl_log
> +
> +
> +def get_acl_log(entry_num=1):
> + with open("ovn-controller.log", "r") as controller_log:
> + acl_logs = [line for line in controller_log if "acl_log" in line]
> + try:
> + return acl_logs[entry_num - 1]
> + except IndexError:
> + print(
> + f"There were not {entry_num} acl_log entries, \
> + only {len(acl_logs)}"
> + )
> + exit(1)
> +
> +
> +def add_parser_args(parser):
> + parser.add_argument("--entry-num", type=int, default=1)
> +
> + # There are other possible things that can be in an ACL log,
> + # and if we need those in the future, we can add them later.
> + parser.add_argument("--name")
> + parser.add_argument("--verdict")
> + parser.add_argument("--severity")
> + parser.add_argument("--protocol")
> + parser.add_argument("--vlan_tci")
> + parser.add_argument("--dl_src")
> + parser.add_argument("--dl_dst")
> + parser.add_argument("--nw_src")
> + parser.add_argument("--nw_dst")
> + parser.add_argument("--nw_tos")
> + parser.add_argument("--nw_ecn")
> + parser.add_argument("--nw_ttl")
> + parser.add_argument("--icmp_type")
> + parser.add_argument("--icmp_code")
> + parser.add_argument("--tp_src")
> + parser.add_argument("--tp_dst")
> + parser.add_argument("--tcp_flags")
> + parser.add_argument("--ipv6_src")
> + parser.add_argument("--ipv6_dst")
> +
> +
> +def main():
> + parser = argparse.ArgumentParser()
> + add_parser_args(parser)
> + args = parser.parse_args()
> +
> + acl_log = get_acl_log(args.entry_num)
> + parsed_log = parse_acl_log(acl_log)
> +
> + # Express command line arguments as a dict, omitting any arguments that
> + # were not provided by the user.
> + expected = {k: v for k, v in vars(args).items() if v is not None}
> + del expected["entry_num"]
> +
> + for key, val in expected.items():
> + try:
> + if parsed_log[key] != val:
> + print(
> + f"Expected log {key}={val} but got
> {key}={parsed_log[key]} \
> + in:\n\t'{acl_log}"
> + )
> + exit(1)
> + except KeyError:
> + print(
> + f"Expected log {key}={val} but {key} does not exist \
> + in:\n\t'{acl_log}'"
> + )
> + exit(1)
> +
> +
> +if __name__ == "__main__":
> + main()
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 652903761..060f2af0a 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -5888,5 +5888,258 @@ AT_CHECK([grep -e "(lr_in_ip_routing ).*outport"
> lr0flows | sed 's/table=../ta
> table=??(lr_in_ip_routing ), priority=97 , match=(reg7 == 2 && ip4.dst
> == 1.1.1.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.20;
> reg1 = 192.168.0.1; eth.src = 00:00:00:00:00:01; outport = "lrp0";
> flags.loopback = 1; next;)
> ])
>
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([ACL log replies -- flows])
> +
> +set_acl_options() {
> + local acl_name=$1
> + local label=$2
> + local log_related=$3
> +
> + local acl_uuid=$(fetch_column nb:ACL _uuid name=$acl_name)
> + check ovn-nbctl set ACL $acl_uuid label=$label log_related=$log_related
> +}
> +
> +record_log_flows() {
> + ovn-sbctl lflow-list sw0 | grep -E 'ls_(out|in)_acl.*, priority=65533' |
> sed 's/table=../table=??/' | sort > log_flows
> +}
> +
> +check_log_flows_count() {
> + local expected=$1
> + local table=$2
> + local count=
> +
> + echo $table
> + if test -f log_flows; then
> + count=$(grep -c -E ls_${table}_acl log_flows)
> + else
> + count=$(ovn-sbctl lflow-list sw0 | grep -c -E "ls_$table_acl.*,
> priority=65533")
> + fi
> +
> + check test "$count" -eq "$expected"
> +}
> +
> +ovn_start
> +
> +check ovn-nbctl ls-add sw0
> +check ovn-nbctl lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1
> "00:00:00:00:00:01 10.0.0.1"
> +check ovn-nbctl lsp-add sw0 sw0-p2 -- lsp-set-addresses sw0-p2
> "00:00:00:00:00:02 10.0.0.2"
> +
> +check ovn-nbctl pg-add pg1 sw0-p1 sw0-p2
> +check ovn-nbctl pg-add pg2 sw0-p1 sw0-p2
> +check ovn-nbctl pg-add pg3 sw0-p1 sw0-p2
> +
> +check ovn-nbctl --log --name=allow_acl acl-add pg1 from-lport 100
> 'inport=@pg1 && ip4' allow
> +set_acl_options allow_acl 1 true
> +
> +check ovn-nbctl --wait=sb sync
> +
> +# An allow ACL should *not* result in a priority 65533 log flow being
> installed
> +# since there are no stateful ACLs on the system.
> +check_log_flows_count 0 in
> +check_log_flows_count 0 out
> +
> +# Now add an allow-related ACL. This should result in both the allow-related
> +# ACL and the allow ACL having priority 65533 log flows added.
> +check ovn-nbctl --log --name=allow_related_acl acl-add pg2 from-lport 100
> 'inport=@pg2 && ip4' allow-related
> +set_acl_options allow_related_acl 2 true
> +check ovn-nbctl --wait=sb sync
> +
> +record_log_flows
> +
> +# The count will be 4 since we have
> +# 2 flows for reply traffic for each ACL
> +# 2 flows for related traffic for each ACL
> +check_log_flows_count 4 out
> +# Since the ACLs are ingress, the ingress table
> +# should have no log flows
> +check_log_flows_count 0 in
> +
> +# Now ensure the flows are what we expect them to be for the ACLs we created
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_out_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 2),
> action=(log(name="allow_related_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 2), action=(log(name="allow_related_acl", severity=info, verdict=allow);
> next;)
> +])
> +
> +rm log_flows
> +
> +# Now add a stateless-allow ACL.
> +check ovn-nbctl --log --name=allow_stateless_acl acl-add pg3 from-lport 100
> 'inport=@pg3 && ip4' allow-stateless
> +set_acl_options allow_stateless_acl 3 true
> +check ovn-nbctl --wait=sb sync
> +
> +record_log_flows
> +
> +# The count will still be 4 since the stateless ACL should not have special
> log flows created
> +check_log_flows_count 4 out
> +check_log_flows_count 0 in
> +
> +# And the log flows will remain the same since the stateless ACL will not be
> represented.
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_out_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 2),
> action=(log(name="allow_related_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 2), action=(log(name="allow_related_acl", severity=info, verdict=allow);
> next;)
> +])
> +
> +rm log_flows
> +
> +# Now remove the label from the allow-related ACL.
> +set_acl_options allow_related_acl 0 true
> +ovn-nbctl --wait=sb sync
> +
> +record_log_flows
> +
> +# The count should now be 2 since the allow_related ACL will not have special
> +# log flows created. But since there there is an allow-related ACL present,
> the
> +# allow ACL will be stateful and have special log flows created.
> +check_log_flows_count 2 out
> +check_log_flows_count 0 in
> +
> +# And make sure only the allow ACL has the log flows installed
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_out_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> +])
> +
> +rm log_flows
> +
> +# And now add the label back, but disable log_related on the allow-related
> ACL.
> +set_acl_options allow_related_acl 2 false
> +
> +record_log_flows
> +
> +# The count will again be 2 because only the allow ACL will have log flows
> installed.
> +check_log_flows_count 2 out
> +check_log_flows_count 0 in
> +
> +# And make sure only the allow ACL has the log flows installed
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_out_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_out_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> +])
> +
> +rm log_flows
> +
> +# And just for sanity's sake, let's remove the allow-related ACL and make
> sure
> +# all the special log messages are gone.
> +check ovn-nbctl acl-del pg2
> +check ovn-nbctl --wait=sb sync
> +
> +check_log_flows_count 0 out
> +check_log_flows_count 0 in
> +
> +# Now let's clear out all the ACLs, and re-do everything but with egress
> ACLs.
> +check ovn-nbctl acl-del pg1
> +check ovn-nbctl acl-del pg3
> +check_row_count nb:ACL 0
> +
> +# Start again with an allow_acl only
> +check ovn-nbctl --log --name=allow_acl acl-add pg1 to-lport 100 'inport=@pg1
> && ip4' allow
> +set_acl_options allow_acl 1 true
> +
> +check ovn-nbctl --wait=sb sync
> +
> +# Again, the allow ACL is stateless, so no related log flows.
> +check_log_flows_count 0 in
> +check_log_flows_count 0 out
> +
> +# Adding a new allow-related ACL...
> +check ovn-nbctl --log --name=allow_related_acl acl-add pg2 to-lport 100
> 'inport=@pg2 && ip4' allow-related
> +set_acl_options allow_related_acl 2 true
> +check ovn-nbctl --wait=sb sync
> +
> +record_log_flows
> +
> +# The count will be 4 since we have
> +# 2 flows for reply traffic for each ACL
> +# 2 flows for related traffic for each ACL
> +check_log_flows_count 4 in
> +# And this time, we should have no egress flows
> +check_log_flows_count 0 out
> +
> +# Now ensure the flows are what we expect them to be for the ACLs we created
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_in_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 2),
> action=(log(name="allow_related_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 2), action=(log(name="allow_related_acl", severity=info, verdict=allow);
> next;)
> +])
> +
> +rm log_flows
> +
> +# Now add a stateless-allow ACL.
> +check ovn-nbctl --log --name=allow_stateless_acl acl-add pg3 from-lport 100
> 'inport=@pg3 && ip4' allow-stateless
> +set_acl_options allow_stateless_acl 3 true
> +check ovn-nbctl --wait=sb sync
> +
> +record_log_flows
> +
> +# The count will still be 4 since the stateless ACL should not have special
> log flows created
> +check_log_flows_count 4 in
> +check_log_flows_count 0 out
> +
> +# And the log flows will remain the same since the stateless ACL will not be
> represented.
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_in_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 2),
> action=(log(name="allow_related_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 2), action=(log(name="allow_related_acl", severity=info, verdict=allow);
> next;)
> +])
> +
> +rm log_flows
> +
> +# Now remove the label from the allow-related ACL.
> +set_acl_options allow_related_acl 0 true
> +ovn-nbctl --wait=sb sync
> +
> +record_log_flows
> +
> +# The count should now be 2 since the allow_related ACL will not have special
> +# log flows created. But since there there is an allow-related ACL present,
> the
> +# allow ACL will be stateful and have special log flows created.
> +check_log_flows_count 2 in
> +check_log_flows_count 0 out
> +
> +# And make sure only the allow ACL has the log flows installed
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_in_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> +])
> +
> +rm log_flows
> +
> +# And now add the label back, but disable log_related on the allow-related
> ACL.
> +set_acl_options allow_related_acl 2 false
> +
> +record_log_flows
> +
> +# The count will again be 2 because only the allow ACL will have log flows
> installed.
> +check_log_flows_count 2 in
> +check_log_flows_count 0 out
> +
> +# And make sure only the allow ACL has the log flows installed
> +AT_CHECK([cat log_flows], [0], [dnl
> + table=??(ls_in_acl ), priority=65533, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv && ct_label.blocked == 0 && ct_label.label == 1),
> action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> + table=??(ls_in_acl ), priority=65533, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0 && ct_label.label ==
> 1), action=(log(name="allow_acl", severity=info, verdict=allow); next;)
> +])
> +
> +rm log_flows
> +
> +# And just for sanity's sake, let's remove the allow-related ACL and make
> sure
> +# all the special log messages are gone.
> +check ovn-nbctl acl-del pg2
> +check ovn-nbctl --wait=sb sync
> +
> +check_log_flows_count 0 out
> +check_log_flows_count 0 in
> +
> +
> +
> AT_CLEANUP
> ])
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 3ae812296..9acd97ea5 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -7002,3 +7002,362 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port
> patch-.*/d
>
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([ACL log_related])
> +
> +CHECK_CONNTRACK()
> +ovn_start
> +
> +OVS_TRAFFIC_VSWITCHD_START()
> +ADD_BR([br-int])
> +
> +set_acl_options() {
> + local acl_name=$1; shift
> +
> + local acl_uuid=$(fetch_column nb:ACL _uuid name=$acl_name)
> + check ovn-nbctl set ACL $acl_uuid "$@"
> +}
> +
> +clear_log() {
> + ovn-appctl -t ovn-controller vlog/close
> + rm ovn-controller.log
> + ovn-appctl -t ovn-controller vlog/reopen
> +}
> +
> +test_ping() {
> + NS_CHECK_EXEC([sw0-p1], [ping -q -c 1 -i 0.3 -w 2 10.0.0.2 |
> FORMAT_PING], \
> +[0], [dnl
> +1 packets transmitted, 1 received, 0% packet loss, time 0ms
> +])
> +}
> +
> +check_acl_log_count() {
> + local expected_count=$1
> +
> + AT_CHECK_UNQUOTED([grep -c acl_log ovn-controller.log], [0], [dnl
> +$expected_count
> +])
> +}
> +
> +# Set external-ids in br-int needed for ovn-controller
> +ovs-vsctl \
> + -- set Open_vSwitch . external-ids:system-id=hv1 \
> + -- set Open_vSwitch .
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
> + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
> + -- set bridge br-int fail-mode=secure
> other-config:disable-in-band=true
> +
> +# Start ovn-controller
> +start_daemon ovn-controller
> +
> +check ovn-nbctl ls-add sw0
> +check ovn-nbctl lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1
> "00:00:00:00:00:01 10.0.0.1"
> +check ovn-nbctl lsp-add sw0 sw0-p2 -- lsp-set-addresses sw0-p2
> "00:00:00:00:00:02 10.0.0.2"
> +
> +check ovn-nbctl pg-add pg1 sw0-p1 sw0-p2
> +
> +ADD_NAMESPACES(sw0-p1)
> +ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.1/24", "00:00:00:00:00:01")
> +ADD_NAMESPACES(sw0-p2)
> +ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.2/24", "00:00:00:00:00:02")
> +
> +wait_for_ports_up
> +
> +check ovn-nbctl --log --name=allow_acl acl-add pg1 from-lport 100 'inport ==
> @pg1 && ip4' allow
> +
> +check ovn-nbctl --wait=hv sync
> +
> +test_ping
> +
> +# The allow ACL should match on the request and reply traffic, resulting in
> 2 logs.
> +check_acl_log_count 2
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=2 \
> + --name=allow_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:02 \
> + --dl_dst=00:00:00:00:00:01 \
> + --nw_src=10.0.0.2 \
> + --nw_dst=10.0.0.1 \
> + --icmp_type=0 \
> + --icmp_code=0
> +
> +# Now add a higher-priority stateful ACL that matches on the same
> +# parameters. Don't enable reply logging.
> +check ovn-nbctl --log --name=allow_related_acl acl-add pg1 from-lport 200
> 'inport == @pg1 && ip4' allow-related
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# Since reply logging is not enabled, the allow-related ACL should match on
> the
> +# request, but the reply will not be logged.
> +check_acl_log_count 1
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +# As a control, set a label on the allow-related ACL, but still don't enable
> +# reply traffic logging.
> +set_acl_options allow_related_acl label=1 log_related=false
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# This should have the same result as the previous ping
> +check_acl_log_count 1
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +# As another control, remove the label but enable reply logging.
> +set_acl_options allow_related_acl label=0 log_related=true
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# This should have the same result as the previous ping
> +check_acl_log_count 1
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +# This time, add a label and enable reply logging on the allow_related ACL.
> +set_acl_options allow_related_acl label=1 log_related=true
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# Now we should have the request and reply logged.
> +check_acl_log_count 2
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=2 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:02 \
> + --dl_dst=00:00:00:00:00:01 \
> + --nw_src=10.0.0.2 \
> + --nw_dst=10.0.0.1 \
> + --icmp_type=0 \
> + --icmp_code=0
> +
> +
> +# And now, let's start from scratch but make sure everything works when
> +# using egress ACLs.
> +check ovn-nbctl acl-del pg1
> +check_row_count nb:ACL 0
> +
> +check ovn-nbctl --log --name=allow_acl acl-add pg1 to-lport 100 'outport ==
> @pg1 && ip4' allow
> +
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# The allow ACL should match on the request and reply traffic, resulting in
> 2 logs.
> +check_acl_log_count 2
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=2 \
> + --name=allow_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:02 \
> + --dl_dst=00:00:00:00:00:01 \
> + --nw_src=10.0.0.2 \
> + --nw_dst=10.0.0.1 \
> + --icmp_type=0 \
> + --icmp_code=0
> +
> +# Now add a higher-priority stateful ACL that matches on the same
> +# parameters. Don't enable reply logging.
> +check ovn-nbctl --log --name=allow_related_acl acl-add pg1 to-lport 200
> 'outport == @pg1 && ip4' allow-related
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# Since reply logging is not enabled, the allow-related ACL should match on
> the
> +# request, but the reply will not be logged.
> +check_acl_log_count 1
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +# As a control, set a label on the allow-related ACL, but still don't enable
> +# reply traffic logging.
> +set_acl_options allow_related_acl label=1 log_related=false
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# This should have the same result as the previous ping
> +check_acl_log_count 1
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +# As another control, remove the label but enable reply logging.
> +set_acl_options allow_related_acl label=0 log_related=true
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# This should have the same result as the previous ping
> +check_acl_log_count 1
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +# This time, add a label and enable reply logging on the allow_related ACL.
> +set_acl_options allow_related_acl label=1 log_related=true
> +check ovn-nbctl --wait=hv sync
> +
> +clear_log
> +test_ping
> +
> +# Now we should have the request and reply logged.
> +check_acl_log_count 2
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=1 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:01 \
> + --dl_dst=00:00:00:00:00:02 \
> + --nw_src=10.0.0.1 \
> + --nw_dst=10.0.0.2 \
> + --icmp_type=8 \
> + --icmp_code=0
> +
> +check $PYTHON $srcdir/check_acl_log.py \
> + --entry-num=2 \
> + --name=allow_related_acl \
> + --verdict=allow \
> + --protocol=icmp \
> + --dl_src=00:00:00:00:00:02 \
> + --dl_dst=00:00:00:00:00:01 \
> + --nw_src=10.0.0.2 \
> + --nw_dst=10.0.0.1 \
> + --icmp_type=0 \
> + --icmp_code=0
> +
> +
> +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> +/connection dropped.*/d"])
> +
> +AT_CLEANUP
> +])
> --
> 2.31.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev