From patchwork Wed Dec 4 21:52:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 2018499 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XH46T6v0; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Y3WVC4yh6z1yRQ for ; Thu, 5 Dec 2024 08:53:07 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id EF4F760B59; Wed, 4 Dec 2024 21:53:04 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 2Ry5KTgqsX0v; Wed, 4 Dec 2024 21:53:01 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 355F660E49 Authentication-Results: smtp3.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XH46T6v0 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id 355F660E49; Wed, 4 Dec 2024 21:53:00 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E5A00C07E9; Wed, 4 Dec 2024 21:52:59 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 23E63C07ED for ; Wed, 4 Dec 2024 21:52:58 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id E4B6280AD5 for ; Wed, 4 Dec 2024 21:52:54 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Ce87393_R66B for ; Wed, 4 Dec 2024 21:52:53 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.129.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=mmichels@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org 8AD9482675 Authentication-Results: smtp1.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 8AD9482675 Authentication-Results: smtp1.osuosl.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XH46T6v0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id 8AD9482675 for ; Wed, 4 Dec 2024 21:52:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1733349171; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=t/kt9EV0b6f48Gd0fyiogNCiDg7o7Rqr/bGdhMJaRu8=; b=XH46T6v0/xaLIw/KGQzQor7S+5i3i5jACRzIl5WOG6+o7TngUaIGpDucZLJh16S1eys69A eu+Lc53U6+jUO9fxOV8wdAEwGj22jFbaZ0MdCrchz0LzyfMa73Jj5lQAYVNVr1F4TatCH7 WCwXhEoyfbnR0jb72y/+T6oIIc0f1cU= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-145-U1Q-t2LpOF-h3WzkJkP1fA-1; Wed, 04 Dec 2024 16:52:48 -0500 X-MC-Unique: U1Q-t2LpOF-h3WzkJkP1fA-1 X-Mimecast-MFC-AGG-ID: U1Q-t2LpOF-h3WzkJkP1fA Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B196F1954221 for ; Wed, 4 Dec 2024 21:52:47 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.22.76.14]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 44FC93000197 for ; Wed, 4 Dec 2024 21:52:47 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 4 Dec 2024 16:52:39 -0500 Message-ID: <20241204215246.642636-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: lxc6uqbZjNdfMlRJAXHmaRamXEB_-63aXh_t0ePa4LI_1733349167 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn 1/3] northd: Add "allow-established" ACL action. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" "allow-established" ACLs are identical to allow-related ACLs, with one exception. On allow-related ACLs, if the ACL changes and no longer matches an established session, then traffic will no longer automatically be allowed. Instead, traffic on the established session will be subject to ACL matching, and therefore the traffic may be dropped. For allow-established ACLs, this behavior is altered. We set a specific bit in the ct_mark to indicate this is an allow-established ACL. If this bit is set, then established session traffic will always be allowed, even if the ACL is altered to no longer match the traffic. Upcoming commits will put in place methods so that deleting the ACL or changing the action type on the ACL will remove the conntrack entry that allows the established traffic. Signed-off-by: Mark Michelson Reviewed-by: Ales Musil --- include/ovn/logical-fields.h | 1 + lib/logical-fields.c | 5 ++ northd/en-ls-stateful.c | 3 +- northd/northd.c | 36 +++++++- ovn-nb.ovsschema | 7 +- ovn-nb.xml | 11 +++ tests/automake.mk | 4 +- tests/client.py | 38 ++++++++ tests/ovn-northd.at | 134 +++++++++++++++++++++++++++++ tests/server.py | 30 +++++++ tests/system-ovn.at | 162 +++++++++++++++++++++++++++++++++++ 11 files changed, 424 insertions(+), 7 deletions(-) create mode 100755 tests/client.py create mode 100755 tests/server.py diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h index d563e044c..c7e4baa51 100644 --- a/include/ovn/logical-fields.h +++ b/include/ovn/logical-fields.h @@ -203,6 +203,7 @@ const struct ovn_field *ovn_field_from_name(const char *name); #define OVN_CT_LB_FORCE_SNAT_BIT 3 #define OVN_CT_OBS_STAGE_1ST_BIT 4 #define OVN_CT_OBS_STAGE_END_BIT 5 +#define OVN_CT_ALLOW_ESTABLISHED_BIT 6 #define OVN_CT_BLOCKED 1 #define OVN_CT_NATTED 2 diff --git a/lib/logical-fields.c b/lib/logical-fields.c index 5a8b53f2b..5b578b5c2 100644 --- a/lib/logical-fields.c +++ b/lib/logical-fields.c @@ -171,6 +171,11 @@ ovn_init_symtab(struct shash *symtab) OVN_CT_STR(OVN_CT_OBS_STAGE_END_BIT) "]", WR_CT_COMMIT); + expr_symtab_add_subfield_scoped(symtab, "ct_mark.allow_established", NULL, + "ct_mark[" + OVN_CT_STR(OVN_CT_ALLOW_ESTABLISHED_BIT) + "]", + WR_CT_COMMIT); expr_symtab_add_subfield_scoped(symtab, "ct_mark.obs_collector_id", NULL, "ct_mark[16..23]", WR_CT_COMMIT); diff --git a/northd/en-ls-stateful.c b/northd/en-ls-stateful.c index 44f74ea08..1dc8ce01f 100644 --- a/northd/en-ls-stateful.c +++ b/northd/en-ls-stateful.c @@ -412,7 +412,8 @@ ls_stateful_record_set_acl_flags_(struct ls_stateful_record *ls_stateful_rec, ls_stateful_rec->max_acl_tier = acl->tier; } if (!ls_stateful_rec->has_stateful_acl - && !strcmp(acl->action, "allow-related")) { + && (!strcmp(acl->action, "allow-related") + || !strcmp(acl->action, "allow-established"))) { ls_stateful_rec->has_stateful_acl = true; } if (ls_stateful_rec->has_stateful_acl && diff --git a/northd/northd.c b/northd/northd.c index 3a488ff3d..0aae627a2 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -124,6 +124,7 @@ static bool vxlan_mode; #define REGBIT_ACL_HINT_ALLOW_REL "reg0[17]" #define REGBIT_FROM_ROUTER_PORT "reg0[18]" #define REGBIT_IP_FRAG "reg0[19]" +#define REGBIT_ACL_PERSIST_ID "reg0[20]" #define REG_ORIG_DIP_IPV4 "reg1" #define REG_ORIG_DIP_IPV6 "xxreg1" @@ -7121,7 +7122,8 @@ consider_acl(struct lflow_table *lflows, const struct ovn_datapath *od, } if (!strcmp(acl->action, "allow") - || !strcmp(acl->action, "allow-related")) { + || !strcmp(acl->action, "allow-related") + || !strcmp(acl->action, "allow-established")) { /* 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 @@ -7147,6 +7149,11 @@ consider_acl(struct lflow_table *lflows, const struct ovn_datapath *od, ds_truncate(actions, log_verdict_len); ds_put_cstr(actions, REGBIT_CONNTRACK_COMMIT" = 1; "); + if (!strcmp(acl->action, "allow-established")) { + ds_put_format(actions, + REGBIT_ACL_PERSIST_ID " = 1; "); + } + /* For stateful ACLs sample "new" and "established" packets. */ build_acl_sample_label_action(actions, acl, acl->sample_new, acl->sample_est, obs_stage); @@ -7626,6 +7633,26 @@ build_acls(const struct ls_stateful_record *ls_stateful_rec, REGBIT_ACL_HINT_ALLOW_REL" == 1", REGBIT_ACL_VERDICT_ALLOW " = 1; next;", lflow_ref); + + /* Ingress and egress ACL Table (Priority 65535). + * + * Allow traffic that is established if the ACL has a persistent + * conntrack ID configured. + */ + ds_clear(&match); + ds_put_format(&match, "ct.est && ct_mark.allow_established == 1"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL_EVAL, UINT16_MAX, + ds_cstr(&match), + REGBIT_ACL_VERDICT_ALLOW " = 1; next;", + lflow_ref); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL_AFTER_LB_EVAL, UINT16_MAX, + ds_cstr(&match), + REGBIT_ACL_VERDICT_ALLOW " = 1; next;", + lflow_ref); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL_EVAL, UINT16_MAX, + ds_cstr(&match), + REGBIT_ACL_VERDICT_ALLOW " = 1; next;", + lflow_ref); } /* Ingress and Egress ACL Table (Priority 65532). @@ -8362,6 +8389,7 @@ build_stateful(struct ovn_datapath *od, struct lflow_table *lflows, ds_put_cstr(&actions, "ct_commit { " "ct_mark.blocked = 0; " + "ct_mark.allow_established = " REGBIT_ACL_PERSIST_ID "; " "ct_mark.obs_stage = " REGBIT_ACL_OBS_STAGE "; " "ct_mark.obs_collector_id = " REG_OBS_COLLECTOR_ID_EST "; " "ct_label.obs_point_id = " REG_OBS_POINT_ID_EST "; " @@ -8382,7 +8410,11 @@ build_stateful(struct ovn_datapath *od, struct lflow_table *lflows, * any packet that makes it this far is part of a connection we * want to allow to continue. */ ds_clear(&actions); - ds_put_cstr(&actions, "ct_commit { ct_mark.blocked = 0; }; next;"); + ds_put_cstr(&actions, + "ct_commit { " + "ct_mark.blocked = 0; " + "ct_mark.allow_established = " REGBIT_ACL_PERSIST_ID "; " + "}; next;"); ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100, REGBIT_CONNTRACK_COMMIT" == 1 && " REGBIT_ACL_LABEL" == 0", diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index c4a48183d..a02ea67b0 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "7.7.0", - "cksum": "116357561 38626", + "version": "7.8.0", + "cksum": "640528173 38695", "tables": { "NB_Global": { "columns": { @@ -295,7 +295,8 @@ "enum": ["set", ["allow", "allow-related", "allow-stateless", "drop", - "reject", "pass"]]}}}, + "reject", "pass", + "allow-established"]]}}}, "log": {"type": "boolean"}, "severity": {"type": {"key": {"type": "string", "enum": ["set", diff --git a/ovn-nb.xml b/ovn-nb.xml index 5114bbc2e..8f1ca302d 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -2489,6 +2489,17 @@ or (e.g. inbound replies to an outbound connection). +
  • + allow-established: This is similar to + allow-related. The difference is that if the ACL + match is changed, then established traffic can + still pass, even if the traffic does not match the ACL any longer. + When the ACL is deleted, or if the action changes to something + other than allow-established, then the established + traffic is no longer automatically allowed. At that point, the + traffic is subject to other configured ACLs. +
  • +
  • drop: Silently drop the packet.
  • diff --git a/tests/automake.mk b/tests/automake.mk index 3899c9e80..940f5b923 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -313,7 +313,9 @@ CHECK_PYFILES = \ tests/uuidfilt.py \ tests/test-tcp-rst.py \ tests/check_acl_log.py \ - tests/scapy-server.py + tests/scapy-server.py \ + tests/client.py \ + tests/server.py EXTRA_DIST += $(CHECK_PYFILES) PYCOV_CLEAN_FILES += $(CHECK_PYFILES:.py=.py,cover) .coverage diff --git a/tests/client.py b/tests/client.py new file mode 100755 index 000000000..7662347ff --- /dev/null +++ b/tests/client.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import socket +import os +import time +import argparse + + +def send_data_from_fifo_to_server( + fifo_path='/tmp/myfifo', host='127.0.0.1', port=10000 +): + # Open the FIFO for reading (blocking mode) + with open(fifo_path, 'r') as fifo_file: + with socket.socket( + socket.AF_INET, socket.SOCK_STREAM + ) as client_socket: + client_socket.connect((host, port)) + # Continuously read from the FIFO and send to the server + while True: + data = fifo_file.readline().strip() + if data: + client_socket.sendall(data.encode()) + else: + # If no data is read (blocking mode), add a small delay or handle as needed + time.sleep(0.1) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + group = parser.add_argument_group() + group.add_argument("-f", "--fifo_path") + group.add_argument("-i", "--server-host") + group.add_argument("-p", "--server-port", type=int) + args = parser.parse_args() + + send_data_from_fifo_to_server( + args.fifo_path, args.server_host, args.server_port + ) diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 4eae1c67c..1d90622e1 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -14253,3 +14253,137 @@ AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep "30.0.0.1"], [0], AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([ACL persistent ID - Logical Flows]) +ovn_start + +dnl For this test, we want to ensure that the logical flows for ACLs are +dnl what we expect. +dnl +dnl First, we'll ensure that an ACL that is not "allow-established" sets the +dnl relevant CT values to 0. +dnl +dnl Then we'll change the ACL to be "allow-established" and ensure the logical +dnl flows do set the appropriate values. +dnl +dnl Then finally, we'll set the ACL back away from "allow-established" and +dnl ensure that the logical flows set the relevant CT values to 0. + +check ovn-nbctl ls-add sw + +check ovn-nbctl acl-add sw from-lport 1001 "tcp" allow-related +check ovn-nbctl acl-add sw to-lport 1002 "ip" allow-related +check ovn-nbctl --apply-after-lb acl-add sw from-lport 1003 "udp" allow-related + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[7]] == 1 && (tcp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; next;) + table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[8]] == 1 && (tcp)), action=(reg8[[16]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[7]] == 1 && (udp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; next;) + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[8]] == 1 && (udp)), action=(reg8[[16]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[7]] == 1 && (ip)), action=(reg8[[16]] = 1; reg0[[1]] = 1; next;) + table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[8]] == 1 && (ip)), action=(reg8[[16]] = 1; next;) +]) + +ingress_uuid=$(fetch_column nb:ACL _uuid priority=1001) +egress_uuid=$(fetch_column nb:ACL _uuid priority=1002) +after_lb_uuid=$(fetch_column nb:ACL _uuid priority=1003) + +check ovn-nbctl set acl $ingress_uuid action=allow-established +check ovn-nbctl set acl $egress_uuid action=allow-established +check ovn-nbctl set acl $after_lb_uuid action=allow-established + +dnl Now we should see the registers being set to the appropriate values. +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[7]] == 1 && (tcp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg0[[20]] = 1; next;) + table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[8]] == 1 && (tcp)), action=(reg8[[16]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[7]] == 1 && (udp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg0[[20]] = 1; next;) + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[8]] == 1 && (udp)), action=(reg8[[16]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[7]] == 1 && (ip)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg0[[20]] = 1; next;) + table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[8]] == 1 && (ip)), action=(reg8[[16]] = 1; next;) +]) + +dnl Now try the other ACL verdicts and ensure that they do not +dnl try to set the values. +for verdict in allow allow-stateless +do + echo "verdict is $verdict" + check ovn-nbctl set acl $ingress_uuid action=$verdict + check ovn-nbctl set acl $egress_uuid action=$verdict + check ovn-nbctl set acl $after_lb_uuid action=$verdict + + AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=((tcp)), action=(reg8[[16]] = 1; next;) +]) + + AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=((udp)), action=(reg8[[16]] = 1; next;) +]) + + AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=((ip)), action=(reg8[[16]] = 1; next;) +]) +done + +check ovn-nbctl set acl $ingress_uuid action=drop +check ovn-nbctl set acl $egress_uuid action=drop +check ovn-nbctl set acl $after_lb_uuid action=drop + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=((tcp)), action=(reg8[[17]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=((udp)), action=(reg8[[17]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=((ip)), action=(reg8[[17]] = 1; next;) +]) + +check ovn-nbctl set acl $ingress_uuid action=reject +check ovn-nbctl set acl $egress_uuid action=reject +check ovn-nbctl set acl $after_lb_uuid action=reject + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=((tcp)), action=(reg8[[18]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=((udp)), action=(reg8[[18]] = 1; next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=((ip)), action=(reg8[[18]] = 1; next;) +]) + +check ovn-nbctl set acl $ingress_uuid action=pass +check ovn-nbctl set acl $egress_uuid action=pass +check ovn-nbctl set acl $after_lb_uuid action=pass + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=((tcp)), action=(next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=((udp)), action=(next;) +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=((ip)), action=(next;) +]) + +AT_CLEANUP +]) diff --git a/tests/server.py b/tests/server.py new file mode 100755 index 000000000..1eb378fd2 --- /dev/null +++ b/tests/server.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import socket +import argparse + + +def start_server(host='127.0.0.1', port=10000): + # Create a TCP/IP socket + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: + server_socket.bind((host, port)) + server_socket.listen() + while True: + client_socket, client_address = server_socket.accept() + with client_socket: + # Receive the data from the client in chunks and write to a file + data = client_socket.recv(1024) + while data: + with open("output.txt", "a") as f: + f.write(data.decode() + "\n") + data = client_socket.recv(1024) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + group = parser.add_argument_group() + group.add_argument("-i", "--bind-host") + group.add_argument("-p", "--bind-port", type=int) + args = parser.parse_args() + + start_server(args.bind_host, args.bind_port) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 4452d5676..a6ddda0f7 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -14117,5 +14117,167 @@ as OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d /failed to query port patch-.*/d /.*terminating with signal 15.*/d"]) + +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ACLs - persistent sessions]) + +CHECK_CONNTRACK() +CHECK_CONNTRACK_NAT() +ovn_start +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +ovs-vsctl set-fail-mode br-ext standalone +# 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_daemon ovn-controller + +# For this test, we want to ensure that established traffic +# is allowed on ACLs with allow-established. +# +# To start, we will set up allow-related ACLs. +# We will send traffic and ensure it is allowed. Then we will adjust +# the ACL so it no longer matches, and we will ensure that the traffic +# is no longer allowed. +# +# Next, we will reset the ACL to its initial state, but we will also +# change the ACL to be allow-established. We will flush conntrack, +# and rerun the test exactly as before. The difference this time is +# that after we adjust the ACL so it no longer matches, the traffic +# should still be allowed. + +check ovn-nbctl ls-add sw +check ovn-nbctl lsp-add sw swp1 -- lsp-set-addresses swp1 "00:00:00:00:00:01 192.168.1.1" +check ovn-nbctl lsp-add sw swp2 -- lsp-set-addresses swp2 "00:00:00:00:00:02 192.168.1.2" + +ADD_NAMESPACES(swp1) +ADD_VETH(swp1, swp1, br-int, "192.168.1.1/24", "00:00:00:00:00:01") + +ADD_NAMESPACES(swp2) +ADD_VETH(swp2, swp2, br-int, "192.168.1.2/24", "00:00:00:00:00:02") + +# Start a TCP server on swp2. +NETNS_DAEMONIZE(swp2, [server.py -i 192.168.1.2 -p 10000], [server.pid]) + +# Make a FIFO and send its output to a client +# from swp1 +mkfifo /tmp/myfifo +on_exit 'rm -rf /tmp/myfifo' + +NETNS_DAEMONIZE(swp1, [client.py -f "/tmp/myfifo" -i 192.168.1.2 -p 10000], [client.pid]) + +# First, ensure that we have basic connectivity before we even start setting +# up ACLs. +AT_CHECK([printf "test\n" > /tmp/myfifo], [0], [dnl +]) + +OVS_WAIT_FOR_OUTPUT([cat output.txt], [0], [dnl +test +]) + +: > output.txt + +check ovn-nbctl acl-add sw from-lport 1000 'ip4.dst == 192.168.1.2' allow-related +check ovn-nbctl acl-add sw from-lport 0 '1' drop + +# Do another basic connectivity check to ensure the ACL is allowing traffic as expected. +AT_CHECK([printf "test\n" > /tmp/myfifo], [0], [dnl +]) + +OVS_WAIT_FOR_OUTPUT([cat output.txt], [0], [dnl +test +]) + +: > output.txt + +# At this point, I need to adjust the ACL so it no longer matches. We then need +# to ensure that the traffic does not pass. How we test this is...interesting. I'm +# not sure how to test for a negative condition accurately. + +acl_uuid=$(fetch_column nb:ACL _uuid priority=1000) + +# Update the ACL so that it no longer matches our client-server traffic +check ovn-nbctl set ACL $acl_uuid match="\"ip4.dst == 192.168.1.3\"" + +# Send another packet from the client to the server. +AT_CHECK([printf "test\n" > /tmp/myfifo], [0], [dnl +]) + +# The traffic should be blocked. We'll check the "drop" ACL to see if it has +# been hit. We can't predict the number of packets that will be seen, but we know +# it will be non-zero. +oftable=$(ovn-debug lflow-stage-to-oftable ls_in_acl_eval) +OVS_WAIT_FOR_OUTPUT([ovs-ofctl dump-flows br-int | grep table="$oftable" | grep "priority=1000" | grep -v commit | grep -c "n_packets=[[1-9]]"], [0], [dnl +1 +]) + +# And just to be safe, let's make sure the output file is still empty +AT_CHECK([cat output.txt], [0], [dnl +]) + +# Reset the client and server processes so that we create a new connection +client_pid=$(cat client.pid) +server_pid=$(cat server.pid) +kill $server_pid +kill $client_pid +OVS_WAIT_WHILE([kill -0 $server_pid 2>/dev/null]) +OVS_WAIT_WHILE([kill -0 $client_pid 2>/dev/null]) + +NETNS_DAEMONIZE(swp2, [server.py -i 192.168.1.2 -p 20000], [server.pid]) +NETNS_DAEMONIZE(swp1, [client.py -f "/tmp/myfifo" -i 192.168.1.2 -p 20000], [client.pid]) + +# Now we'll re-set the ACL to allow the traffic. +check ovn-nbctl set ACL $acl_uuid match="\"ip4.dst == 192.168.1.2\"" + +# We'll also change to allow-established +check ovn-nbctl set ACL $acl_uuid action=allow-established + +# Make sure traffic passes +AT_CHECK([printf "test\n" > /tmp/myfifo], [0], [dnl +]) + +OVS_WAIT_FOR_OUTPUT([cat output.txt], [0], [dnl +test +]) + +: > output.txt + +# Adjust the ACL so that it no longer matches +check ovn-nbctl set ACL $acl_uuid match="\"ip4.dst == 192.168.1.3\"" + +# Traffic should still pass +AT_CHECK([printf "test\n" > /tmp/myfifo], [0], [dnl +]) + +OVS_WAIT_FOR_OUTPUT([cat output.txt], [0], [dnl +test +]) + +: > output.txt + +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([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d +/connection dropped.*/d"]) + AT_CLEANUP ]) From patchwork Wed Dec 4 21:52:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 2018498 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XA4IZwuN; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Y3WVC18H8z1yQl for ; Thu, 5 Dec 2024 08:53:07 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id A80AB84161; Wed, 4 Dec 2024 21:53:05 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id lnXKacote-_K; Wed, 4 Dec 2024 21:53:00 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 5F2D9840D8 Authentication-Results: smtp1.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XA4IZwuN Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 5F2D9840D8; Wed, 4 Dec 2024 21:52:59 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 74128C07EE; Wed, 4 Dec 2024 21:52:58 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id CD916C07E6 for ; Wed, 4 Dec 2024 21:52:55 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id D186540BE3 for ; Wed, 4 Dec 2024 21:52:53 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id SWooqCeTA_ip for ; Wed, 4 Dec 2024 21:52:51 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=mmichels@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org 93C4240B9C Authentication-Results: smtp2.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 93C4240B9C Authentication-Results: smtp2.osuosl.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XA4IZwuN Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp2.osuosl.org (Postfix) with ESMTPS id 93C4240B9C for ; Wed, 4 Dec 2024 21:52:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1733349170; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8KSbb8bpevXzWQgHMsrtnhsUVMqbWLSYrJvjtN0pr4E=; b=XA4IZwuNvdGRpgHQ2Lp7joj6ylz5X2W8+63zPfo5SIccICfwxil0tyLQD+sey7cqqZsQ4/ rcfd2LJVmPRJslMDIxMDZk+Vb5gNBAxx881+L/ifS1YX2CRXYiC861MZkeMjDFFirAqmsJ 0jB3NrlriOSN1Z5GGillqGWYX8D3ZDw= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-596-GLZOzuv6P46rDwvqwnSEdA-1; Wed, 04 Dec 2024 16:52:49 -0500 X-MC-Unique: GLZOzuv6P46rDwvqwnSEdA-1 X-Mimecast-MFC-AGG-ID: GLZOzuv6P46rDwvqwnSEdA Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5DAEF1955DAB for ; Wed, 4 Dec 2024 21:52:48 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.22.76.14]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id F3D8F3000197 for ; Wed, 4 Dec 2024 21:52:47 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 4 Dec 2024 16:52:40 -0500 Message-ID: <20241204215246.642636-2-mmichels@redhat.com> In-Reply-To: <20241204215246.642636-1-mmichels@redhat.com> References: <20241204215246.642636-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 6lhKs0k7GMWTtpIl0XaAk5wkbIP0kpIBdi-cn4YFu30_1733349168 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn 2/3] northd: Copy ACL IDs to the southbound DB. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" This introduces a new southbound table called ACL_ID for storing persistent ACL conntrack IDs. These IDs are generated by ovn-northd for any ACL that is of type "allow-established". Signed-off-by: Mark Michelson --- northd/automake.mk | 2 + northd/en-acl-ids.c | 206 +++++++++++++++++++++++++++++++++++++++ northd/en-acl-ids.h | 17 ++++ northd/en-northd.c | 6 ++ northd/inc-proc-northd.c | 15 ++- northd/northd.c | 3 + northd/northd.h | 4 + northd/ovn-northd.c | 4 + ovn-sb.ovsschema | 14 ++- ovn-sb.xml | 13 +++ tests/ovn.at | 39 ++++++++ 11 files changed, 319 insertions(+), 4 deletions(-) create mode 100644 northd/en-acl-ids.c create mode 100644 northd/en-acl-ids.h diff --git a/northd/automake.mk b/northd/automake.mk index 6566ad299..d46dfd763 100644 --- a/northd/automake.mk +++ b/northd/automake.mk @@ -34,6 +34,8 @@ northd_ovn_northd_SOURCES = \ northd/en-ls-stateful.h \ northd/en-sampling-app.c \ northd/en-sampling-app.h \ + northd/en-acl-ids.c \ + northd/en-acl-ids.h \ northd/inc-proc-northd.c \ northd/inc-proc-northd.h \ northd/ipam.c \ diff --git a/northd/en-acl-ids.c b/northd/en-acl-ids.c new file mode 100644 index 000000000..897cc2da1 --- /dev/null +++ b/northd/en-acl-ids.c @@ -0,0 +1,206 @@ +/* + * 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. + */ + +#include + +#include "en-acl-ids.h" +#include "lib/uuidset.h" +#include "lib/ovn-sb-idl.h" +#include "lib/ovn-nb-idl.h" +#include "lib/bitmap.h" + +#include "openvswitch/vlog.h" + +VLOG_DEFINE_THIS_MODULE(northd_acl_ids); + +enum id_state { + /* The ID represents a new northbound ACL that has + * not yet been synced to the southbound DB + */ + ID_NEW, + /* The ID represents an ACL ID that has been synced + * with the southbound DB already + */ + ID_SYNCED, + /* The ID represents a deleted NB ACL that also needs + * to be removed from the southbound DB + */ + ID_INACTIVE, +}; + +struct acl_id { + struct hmap_node node; + int64_t id; + struct uuid nb_acl_uuid; + enum id_state state; +}; + +#define MAX_ACL_ID 65535 + +struct acl_id_data { + struct hmap ids; + unsigned long *id_bitmap; +}; + +static void +acl_id_data_init(struct acl_id_data *id_data) +{ + hmap_init(&id_data->ids); + id_data->id_bitmap = bitmap_allocate(MAX_ACL_ID); +} + +static struct acl_id_data * +acl_id_data_alloc(void) +{ + struct acl_id_data *id_data = xzalloc(sizeof *id_data); + acl_id_data_init(id_data); + + return id_data; +} + +static void +acl_id_data_destroy(struct acl_id_data *id_data) +{ + struct acl_id *acl_id; + HMAP_FOR_EACH_POP (acl_id, node, &id_data->ids) { + free(acl_id); + } + hmap_destroy(&id_data->ids); + bitmap_free(id_data->id_bitmap); +} + +void * +en_acl_id_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + struct acl_id_data *id_data = acl_id_data_alloc(); + return id_data; +} + +static void +add_acl_id(struct hmap *id_map, int64_t id, enum id_state state, + const struct uuid *acl_uuid) +{ + struct acl_id *acl_id = xzalloc(sizeof *acl_id); + acl_id->id = id; + acl_id->state = state; + acl_id->nb_acl_uuid = *acl_uuid; + hmap_insert(id_map, &acl_id->node, uuid_hash(acl_uuid)); +} + +void +en_acl_id_run(struct engine_node *node, void *data) +{ + const struct engine_context *eng_ctx = engine_get_context(); + if (!eng_ctx->ovnnb_idl_txn || !eng_ctx->ovnsb_idl_txn) { + return; + } + + const struct nbrec_acl_table *nb_acl_table = + EN_OVSDB_GET(engine_get_input("NB_acl", node)); + const struct sbrec_acl_id_table *sb_acl_id_table = + EN_OVSDB_GET(engine_get_input("SB_acl_id", node)); + struct uuidset visited = UUIDSET_INITIALIZER(&visited); + struct acl_id_data *id_data = data; + + acl_id_data_destroy(id_data); + acl_id_data_init(id_data); + + const struct nbrec_acl *nb_acl; + const struct sbrec_acl_id *sb_id; + SBREC_ACL_ID_TABLE_FOR_EACH (sb_id, sb_acl_id_table) { + nb_acl = nbrec_acl_table_get_for_uuid(nb_acl_table, &sb_id->nb_acl); + if (nb_acl && !strcmp(nb_acl->action, "allow-established")) { + bitmap_set1(id_data->id_bitmap, sb_id->id); + uuidset_insert(&visited, &sb_id->nb_acl); + add_acl_id(&id_data->ids, sb_id->id, ID_SYNCED, &sb_id->nb_acl); + } else { + /* NB ACL is deleted or has changed action type. This + * ID is no longer active. + */ + add_acl_id(&id_data->ids, sb_id->id, ID_INACTIVE, &sb_id->nb_acl); + } + } + + size_t scan_start = 1; + size_t scan_end = MAX_ACL_ID; + NBREC_ACL_TABLE_FOR_EACH (nb_acl, nb_acl_table) { + if (uuidset_find_and_delete(&visited, &nb_acl->header_.uuid)) { + continue; + } + if (strcmp(nb_acl->action, "allow-established")) { + continue; + } + int64_t new_id = bitmap_scan(id_data->id_bitmap, 0, + scan_start, scan_end + 1); + if (new_id == scan_end + 1) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "Exhausted all ACL IDs"); + break; + } + add_acl_id(&id_data->ids, new_id, ID_NEW, &nb_acl->header_.uuid); + bitmap_set1(id_data->id_bitmap, new_id); + scan_start = new_id + 1; + } + + engine_set_node_state(node, EN_UPDATED); + uuidset_destroy(&visited); +} + +void +en_acl_id_cleanup(void *data) +{ + acl_id_data_destroy(data); +} + +static const struct sbrec_acl_id * +acl_id_lookup_by_id(struct ovsdb_idl_index *sbrec_acl_id_by_id, + int64_t id) +{ + struct sbrec_acl_id *target = sbrec_acl_id_index_init_row( + sbrec_acl_id_by_id); + sbrec_acl_id_index_set_id(target, id); + + struct sbrec_acl_id *retval = sbrec_acl_id_index_find( + sbrec_acl_id_by_id, target); + + sbrec_acl_id_index_destroy_row(target); + + return retval; +} + +void sync_acl_ids(const struct acl_id_data *id_data, + struct ovsdb_idl_txn *ovnsb_txn, + struct ovsdb_idl_index *sbrec_acl_id_by_id) +{ + struct acl_id *acl_id; + const struct sbrec_acl_id *sb_id; + HMAP_FOR_EACH (acl_id, node, &id_data->ids) { + switch (acl_id->state) { + case ID_NEW: + sb_id = sbrec_acl_id_insert(ovnsb_txn); + sbrec_acl_id_set_id(sb_id, acl_id->id); + sbrec_acl_id_set_nb_acl(sb_id, acl_id->nb_acl_uuid); + break; + case ID_INACTIVE: + sb_id = acl_id_lookup_by_id(sbrec_acl_id_by_id, acl_id->id); + if (sb_id) { + sbrec_acl_id_delete(sb_id); + } + break; + case ID_SYNCED: + break; + } + } +} diff --git a/northd/en-acl-ids.h b/northd/en-acl-ids.h new file mode 100644 index 000000000..8b60b3c7c --- /dev/null +++ b/northd/en-acl-ids.h @@ -0,0 +1,17 @@ +#ifndef EN_ACL_IDS_H +#define EN_ACL_IDS_H + +#include +#include + +#include "lib/inc-proc-eng.h" + +bool northd_acl_id_handler(struct engine_node *node, void *data); +void *en_acl_id_init(struct engine_node *, struct engine_arg *); +void en_acl_id_run(struct engine_node *, void *data); +void en_acl_id_cleanup(void *data); + +struct acl_id_data; +void sync_acl_ids(const struct acl_id_data *, struct ovsdb_idl_txn *, + struct ovsdb_idl_index *sbrec_acl_id_by_id); +#endif diff --git a/northd/en-northd.c b/northd/en-northd.c index c7d1ebcb3..5545fe7a6 100644 --- a/northd/en-northd.c +++ b/northd/en-northd.c @@ -61,6 +61,10 @@ northd_get_input_data(struct engine_node *node, engine_ovsdb_node_get_index( engine_get_input("SB_fdb", node), "sbrec_fdb_by_dp_and_port"); + input_data->sbrec_acl_id_by_id = + engine_ovsdb_node_get_index( + engine_get_input("SB_acl_id", node), + "sbrec_acl_id_by_id"); input_data->nbrec_logical_switch_table = EN_OVSDB_GET(engine_get_input("NB_logical_switch", node)); @@ -110,6 +114,8 @@ northd_get_input_data(struct engine_node *node, input_data->svc_monitor_mac = global_config->svc_monitor_mac; input_data->svc_monitor_mac_ea = global_config->svc_monitor_mac_ea; input_data->features = &global_config->features; + + input_data->acl_id_data = engine_get_input_data("acl_id", node); } void diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c index 6e0aa04c4..43abf042d 100644 --- a/northd/inc-proc-northd.c +++ b/northd/inc-proc-northd.c @@ -41,6 +41,7 @@ #include "en-sampling-app.h" #include "en-sync-sb.h" #include "en-sync-from-sb.h" +#include "en-acl-ids.h" #include "unixctl.h" #include "util.h" @@ -102,7 +103,8 @@ static unixctl_cb_func chassis_features_list; SB_NODE(fdb, "fdb") \ SB_NODE(static_mac_binding, "static_mac_binding") \ SB_NODE(chassis_template_var, "chassis_template_var") \ - SB_NODE(logical_dp_group, "logical_dp_group") + SB_NODE(logical_dp_group, "logical_dp_group") \ + SB_NODE(acl_id, "acl_id") enum sb_engine_node { #define SB_NODE(NAME, NAME_STR) SB_##NAME, @@ -161,6 +163,7 @@ static ENGINE_NODE(route_policies, "route_policies"); static ENGINE_NODE(routes, "routes"); static ENGINE_NODE(bfd, "bfd"); static ENGINE_NODE(bfd_sync, "bfd_sync"); +static ENGINE_NODE(acl_id, "acl_id"); void inc_proc_northd_init(struct ovsdb_idl_loop *nb, struct ovsdb_idl_loop *sb) @@ -186,6 +189,9 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, global_config_sb_chassis_handler); engine_add_input(&en_global_config, &en_sampling_app, NULL); + engine_add_input(&en_acl_id, &en_nb_acl, NULL); + engine_add_input(&en_acl_id, &en_sb_acl_id, NULL); + engine_add_input(&en_northd, &en_nb_mirror, NULL); engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL); engine_add_input(&en_northd, &en_nb_chassis_template_var, NULL); @@ -201,8 +207,10 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_northd, &en_sb_static_mac_binding, NULL); engine_add_input(&en_northd, &en_sb_chassis_template_var, NULL); engine_add_input(&en_northd, &en_sb_fdb, northd_sb_fdb_change_handler); + engine_add_input(&en_northd, &en_sb_acl_id, NULL); engine_add_input(&en_northd, &en_global_config, northd_global_config_handler); + engine_add_input(&en_northd, &en_acl_id, NULL); /* northd engine node uses the sb mac binding table to * cleanup mac binding entries for deleted logical ports @@ -364,6 +372,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, = mac_binding_by_datapath_index_create(sb->idl); struct ovsdb_idl_index *fdb_by_dp_key = ovsdb_idl_index_create1(sb->idl, &sbrec_fdb_col_dp_key); + struct ovsdb_idl_index *sbrec_acl_id_by_id = + ovsdb_idl_index_create1(sb->idl, &sbrec_acl_id_col_id); engine_init(&en_northd_output, &engine_arg); @@ -388,6 +398,9 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_ovsdb_node_add_index(&en_sb_fdb, "fdb_by_dp_key", fdb_by_dp_key); + engine_ovsdb_node_add_index(&en_sb_acl_id, + "sbrec_acl_id_by_id", + sbrec_acl_id_by_id); struct ovsdb_idl_index *sbrec_address_set_by_name = ovsdb_idl_index_create1(sb->idl, &sbrec_address_set_col_name); diff --git a/northd/northd.c b/northd/northd.c index 0aae627a2..8dab88f62 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -50,6 +50,7 @@ #include "en-lr-stateful.h" #include "en-ls-stateful.h" #include "en-sampling-app.h" +#include "en-acl-ids.h" #include "lib/ovn-parallel-hmap.h" #include "ovn/actions.h" #include "ovn/features.h" @@ -19120,6 +19121,8 @@ ovnnb_db_run(struct northd_input *input_data, &data->ls_datapaths.datapaths); sync_template_vars(ovnsb_txn, input_data->nbrec_chassis_template_var_table, input_data->sbrec_chassis_template_var_table); + sync_acl_ids(input_data->acl_id_data, ovnsb_txn, + input_data->sbrec_acl_id_by_id); cleanup_stale_fdb_entries(input_data->sbrec_fdb_table, &data->ls_datapaths.datapaths); diff --git a/northd/northd.h b/northd/northd.h index d60c944df..bccb1c5d8 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -63,12 +63,16 @@ struct northd_input { struct eth_addr svc_monitor_mac_ea; const struct chassis_features *features; + /* ACL ID inputs. */ + const struct acl_id_data *acl_id_data; + /* Indexes */ struct ovsdb_idl_index *sbrec_chassis_by_name; struct ovsdb_idl_index *sbrec_chassis_by_hostname; struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name; struct ovsdb_idl_index *sbrec_ip_mcast_by_dp; struct ovsdb_idl_index *sbrec_fdb_by_dp_and_port; + struct ovsdb_idl_index *sbrec_acl_id_by_id; }; /* A collection of datapaths. E.g. all logical switch datapaths, or all diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index fb29c3c21..bbb321248 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -896,6 +896,10 @@ main(int argc, char *argv[]) ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_logical_dp_group_columns[i]); } + for (size_t i = 0; i < SBREC_ACL_ID_N_COLUMNS; i++) { + ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, + &sbrec_acl_id_columns[i]); + } unixctl_command_register("sb-connection-status", "", 0, 0, ovn_conn_show, ovnsb_idl_loop.idl); diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index 73abf2c8d..837aee620 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.37.0", - "cksum": "1950136776 31493", + "version": "20.38.0", + "cksum": "4077385391 31851", "tables": { "SB_Global": { "columns": { @@ -617,6 +617,14 @@ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["chassis"]], - "isRoot": true} + "isRoot": true}, + "ACL_ID": { + "columns": { + "id": {"type": {"key": {"type": "integer", + "minInteger": 0, + "maxInteger": 32767}}}, + "nb_acl" : {"type": {"key": {"type": "uuid"}}}}, + "indexes": [["id"]], + "isRoot": true} } } diff --git a/ovn-sb.xml b/ovn-sb.xml index ea4adc1c3..a3d71e100 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -5217,4 +5217,17 @@ tcp.flags = RST; The set of variable values for a given chassis. + + +

    + Each record represents an identifier that ovn-northd needs + to synchronize with instances of ovn-controller. +

    + + The actual identifier being synchronized. + + + The Northbound ACL UUID for which this ID corresponds. + +
    diff --git a/tests/ovn.at b/tests/ovn.at index 2fdf1a88c..53e6712cc 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -39761,3 +39761,42 @@ OVN_CLEANUP([hv1]) AT_CLEANUP ]) + +AT_SETUP([ACL Conntrack ID propagation]) +ovn_start + +dnl In this test, we want to ensure that southbound ACL_ID +dnl entries are created for northbound ACLs of type "allow-established". +dnl +dnl If an ACL is of a different type, or if an ACL is deleted, +dnl then there should be no soutbhound ACL_ID. + +check ovn-nbctl ls-add sw +check ovn-nbctl --wait=sb acl-add sw from-lport 1000 1 allow-related +acl_uuid=$(fetch_column nb:ACL _uuid priority=1000) + +dnl The ACL is not allow-established, so SBDB should have no rows. +wait_row_count ACL_ID 0 + +dnl When we change to allow-established, the SBDB should pick it up. +check ovn-nbctl --wait=sb set ACL $acl_uuid action=allow-established + +wait_row_count ACL_ID 1 + +dnl Setting to a new action should remove the row from the SBDB. +check ovn-nbctl --wait=sb set ACL $acl_uuid action=drop + +wait_row_count ACL_ID 0 + +dnl Set back to allow-established and make sure it shows up in the SBDB. +check ovn-nbctl --wait=sb set ACL $acl_uuid action=allow-established + +wait_row_count ACL_ID 1 + +dnl Delete the ACL and ensure the SBDB entry is deleted. +check ovn-nbctl --wait=sb acl-del sw from-lport 1000 1 + +wait_row_count ACL_ID 0 + +AT_CLEANUP +]) From patchwork Wed Dec 4 21:52:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 2018497 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=B0Q6zz3n; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Y3WV435DGz1yQl for ; Thu, 5 Dec 2024 08:53:00 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 1A8414072F; Wed, 4 Dec 2024 21:52:59 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 5C29Vm8OFbnN; Wed, 4 Dec 2024 21:52:57 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org D7DDA40659 Authentication-Results: smtp4.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=B0Q6zz3n Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id D7DDA40659; Wed, 4 Dec 2024 21:52:56 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id AA596C07E7; Wed, 4 Dec 2024 21:52:56 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 42E11C07E6 for ; Wed, 4 Dec 2024 21:52:55 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 2EA53406C5 for ; Wed, 4 Dec 2024 21:52:55 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id eV3riX0oq-al for ; Wed, 4 Dec 2024 21:52:53 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=mmichels@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp4.osuosl.org BFC4440659 Authentication-Results: smtp4.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org BFC4440659 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp4.osuosl.org (Postfix) with ESMTPS id BFC4440659 for ; Wed, 4 Dec 2024 21:52:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1733349171; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PtBF6CBoDOFBAz+I/WOZNqymKpuTnMznGNs2T6sOUTE=; b=B0Q6zz3n8dAdGIr6M6z+5mzv8vOTn3n+XmN9Kcil/HhP00J4mMdk6l59b95j9KkQmsb/+J tIcJME2NR9vQCoaBiEv9gb0eomFVb0oZaB6Tbo4GH+jSMAAGLNkPg4AoLms2rCUtahCwBd Aem+KMBF0q4FTc8gJ5jm/eEXYyKXYg8= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-168-cUrpFudiNBCByhzQLTRtPg-1; Wed, 04 Dec 2024 16:52:50 -0500 X-MC-Unique: cUrpFudiNBCByhzQLTRtPg-1 X-Mimecast-MFC-AGG-ID: cUrpFudiNBCByhzQLTRtPg Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2EEA619560A2 for ; Wed, 4 Dec 2024 21:52:49 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.22.76.14]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A55F73000197 for ; Wed, 4 Dec 2024 21:52:48 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 4 Dec 2024 16:52:41 -0500 Message-ID: <20241204215246.642636-3-mmichels@redhat.com> In-Reply-To: <20241204215246.642636-1-mmichels@redhat.com> References: <20241204215246.642636-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 1ktyK9g0lhup4YlgPW_1DsPUgw6Pgyl37oSlbWp2ySk_1733349169 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn 3/3] ovn-controller: Manage lifetime of allow-established ACLs. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Using the ACL IDs created in the previous commit, northd adds this ID to the conntrack entry for allow-established ACLs. ovn-controller keeps track of the southbound ACL IDs. If an ACL ID is removed from the southbound database, then ovn-controller flushes the conntrack entry whose label contains the deleted ACL ID. With this change, it means that deleting an allow-established ACL, or changing its action type to something else, will result in the conntrack entry being flushed. This will cause the traffic to no longer automatically be allowed. Reported-at: https://issues.redhat.com/browse/FDP-815 Signed-off-by: Mark Michelson --- controller/acl_ids.c | 279 ++++++++++++++++++++++++++++++++++++ controller/acl_ids.h | 30 ++++ controller/automake.mk | 2 + controller/ovn-controller.c | 12 +- lib/logical-fields.c | 2 + northd/en-acl-ids.c | 13 ++ northd/en-acl-ids.h | 3 + northd/en-lflow.c | 1 + northd/inc-proc-northd.c | 1 + northd/northd.c | 54 +++++-- northd/northd.h | 1 + tests/ovn-northd.at | 20 ++- tests/system-ovn.at | 20 +++ 13 files changed, 417 insertions(+), 21 deletions(-) create mode 100644 controller/acl_ids.c create mode 100644 controller/acl_ids.h diff --git a/controller/acl_ids.c b/controller/acl_ids.c new file mode 100644 index 000000000..6267f3399 --- /dev/null +++ b/controller/acl_ids.c @@ -0,0 +1,279 @@ +/* Copyright (c) 2024, Red Hat, Inc. + * + * 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. + */ + +#include + +#include "openvswitch/hmap.h" +#include "openvswitch/rconn.h" +#include "openvswitch/ofp-ct.h" +#include "openvswitch/ofp-util.h" +#include "openvswitch/ofp-msgs.h" +#include "openvswitch/vlog.h" +#include "lib/socket-util.h" + +#include "lib/ovn-sb-idl.h" +#include "acl_ids.h" + +VLOG_DEFINE_THIS_MODULE(acl_ids); + +enum acl_id_state { + /* The ID exists in the SB DB. */ + ACTIVE, + /* The ID has been removed from the DB and needs to have its conntrack + * entries flushed. + */ + SB_DELETED, + /* We have sent the conntrack flush request to OVS for this ACL ID. */ + FLUSHING, + /* We have either successfully flushed the ID, or we have failed enough + * times that we have given up. + */ + TO_DELETE, +}; + +struct acl_id { + int64_t id; + enum acl_id_state state; + struct hmap_node hmap_node; + ovs_be32 xid; + int flush_count; +}; + +struct tracked_acl_ids { + struct hmap ids; +}; + +static struct acl_id * +find_tracked_acl_id(struct tracked_acl_ids *tracked_ids, int64_t id) +{ + uint32_t hash = hash_uint64(id); + struct acl_id *acl_id; + HMAP_FOR_EACH_WITH_HASH (acl_id, hmap_node, hash, &tracked_ids->ids) { + if (acl_id->id == id) { + return acl_id; + } + } + return NULL; +} + +static void +acl_id_destroy(struct acl_id *acl_id) +{ + free(acl_id); +} + +void * +en_acl_id_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + struct tracked_acl_ids *ids = xzalloc(sizeof *ids); + hmap_init(&ids->ids); + return ids; +} + +void +en_acl_id_run(struct engine_node *node, void *data) +{ + const struct sbrec_acl_id_table *sb_acl_id_table = + EN_OVSDB_GET(engine_get_input("SB_acl_id", node)); + const struct sbrec_acl_id *sb_id; + + struct tracked_acl_ids *ids = data; + struct acl_id *id; + + /* Pre-mark each active ID as SB_DELETED. */ + HMAP_FOR_EACH (id, hmap_node, &ids->ids) { + if (id->state == ACTIVE) { + id->state = SB_DELETED; + } + } + + SBREC_ACL_ID_TABLE_FOR_EACH (sb_id, sb_acl_id_table) { + id = find_tracked_acl_id(ids, sb_id->id); + if (!id) { + id = xzalloc(sizeof *id); + id->id = sb_id->id; + hmap_insert(&ids->ids, &id->hmap_node, hash_uint64(sb_id->id)); + } + id->state = ACTIVE; + } + + engine_set_node_state(node, EN_UPDATED); +} + +void +en_acl_id_cleanup(void *data) +{ + struct tracked_acl_ids *tracked_ids = data; + struct acl_id *id; + HMAP_FOR_EACH_POP (id, hmap_node, &tracked_ids->ids) { + acl_id_destroy(id); + } + hmap_destroy(&tracked_ids->ids); +} + +static struct rconn *swconn; +static ovs_be32 barrier_xid; + +void +acl_ids_update_swconn(const char *target, int probe_interval) +{ + if (!swconn) { + swconn = rconn_create(0, 0, DSCP_DEFAULT, 1 << OFP15_VERSION); + } + ovn_update_swconn_at(swconn, target, probe_interval, "acl_ids"); +} + +#define MAX_FLUSHES 3 + +static void +acl_ids_handle_rconn_msg(struct ofpbuf *msg, struct tracked_acl_ids *acl_ids) +{ + const struct ofp_header *oh = msg->data; + + enum ofptype type; + ofptype_decode(&type, oh); + + if (type == OFPTYPE_ECHO_REQUEST) { + rconn_send(swconn, ofputil_encode_echo_reply(oh), NULL); + return; + } + + struct acl_id *acl_id; + if (oh->xid != barrier_xid) { + if (type != OFPTYPE_ERROR) { + return; + } + /* Uh oh! It looks like one of the flushes failed :( + * Let's find this particular one and move its state + * back to SB_DELETED so we can retry the flush. Of + * course, if this is a naughty little ID and has + * been flushed unsuccessfully too many times, we'll + * set it to TO_DELETE so it doesn't cause any more + * trouble. + */ + HMAP_FOR_EACH (acl_id, hmap_node, &acl_ids->ids) { + if (acl_id->xid != oh->xid) { + continue; + } + + acl_id->xid = 0; + acl_id->flush_count++; + if (acl_id->flush_count >= MAX_FLUSHES) { + acl_id->state = TO_DELETE; + } else { + acl_id->state = SB_DELETED; + } + + break; + } + } else { + HMAP_FOR_EACH (acl_id, hmap_node, &acl_ids->ids) { + if (acl_id->state != FLUSHING) { + continue; + } + acl_id->state = TO_DELETE; + } + barrier_xid = 0; + } +} + +static void +flush_expired_ids(struct tracked_acl_ids *acl_ids) +{ + if (barrier_xid != 0) { + /* We haven't received the previous barrier's reply, so + * hold off on sending new flushes until we get the + * reply. + */ + return; + } + + ovs_u128 mask = { + /* ct_labels.label BITS[80-95] */ + .u64.hi = 0xffff0000, + }; + struct acl_id *acl_id; + bool send_barrier = false; + HMAP_FOR_EACH (acl_id, hmap_node, &acl_ids->ids) { + if (acl_id->state != SB_DELETED) { + continue; + } + ovs_u128 ct_id = { + .u64.hi = acl_id->id << 16, + }; + VLOG_DBG("Flushing conntrack entry for ACL id %"PRId64, acl_id->id); + struct ofp_ct_match match = { + .labels = ct_id, + .labels_mask = mask, + }; + struct ofpbuf *msg = ofp_ct_match_encode(&match, NULL, + rconn_get_version(swconn)); + const struct ofp_header *oh = msg->data; + acl_id->xid = oh->xid; + acl_id->state = FLUSHING; + rconn_send(swconn, msg, NULL); + send_barrier = true; + } + + if (!send_barrier) { + return; + } + + struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP15_VERSION); + const struct ofp_header *oh = barrier->data; + barrier_xid = oh->xid; + rconn_send(swconn, barrier, NULL); +} + +static void +clear_flushed_ids(struct tracked_acl_ids *acl_ids) +{ + struct acl_id *acl_id; + HMAP_FOR_EACH_SAFE (acl_id, hmap_node, &acl_ids->ids) { + if (acl_id->state != TO_DELETE) { + continue; + } + hmap_remove(&acl_ids->ids, &acl_id->hmap_node); + acl_id_destroy(acl_id); + } +} + +#define MAX_RECV_MSGS 50 + +void +acl_ids_run(struct tracked_acl_ids *acl_ids) +{ + rconn_run(swconn); + if (!rconn_is_connected(swconn)) { + rconn_run_wait(swconn); + rconn_recv_wait(swconn); + return; + } + + for (int i = 0; i < MAX_RECV_MSGS; i++) { + struct ofpbuf *msg = rconn_recv(swconn); + if (!msg) { + break; + } + acl_ids_handle_rconn_msg(msg, acl_ids); + ofpbuf_delete(msg); + } + flush_expired_ids(acl_ids); + clear_flushed_ids(acl_ids); + + rconn_run_wait(swconn); + rconn_recv_wait(swconn); +} diff --git a/controller/acl_ids.h b/controller/acl_ids.h new file mode 100644 index 000000000..e3556c37e --- /dev/null +++ b/controller/acl_ids.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2024 Red Hat, INc. + * + * 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. + */ + +#ifndef OVN_ACL_IDS_H +#define OVN_ACL_IDS_H + +#include +#include "lib/inc-proc-eng.h" + +void *en_acl_id_init(struct engine_node *, struct engine_arg *); +void en_acl_id_run(struct engine_node *, void *); +void en_acl_id_cleanup(void *); + +struct tracked_acl_ids; +void acl_ids_update_swconn(const char *target, int probe_interval); +void acl_ids_run(struct tracked_acl_ids *); + +#endif /* OVN_ACL_IDS_H */ diff --git a/controller/automake.mk b/controller/automake.mk index bb0bf2d33..c19ae27be 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -1,5 +1,7 @@ bin_PROGRAMS += controller/ovn-controller controller_ovn_controller_SOURCES = \ + controller/acl_ids.c \ + controller/acl_ids.h \ controller/bfd.c \ controller/bfd.h \ controller/binding.c \ diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 157def2a3..f19c291d6 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -88,6 +88,7 @@ #include "lib/dns-resolve.h" #include "ct-zone.h" #include "ovn-dns.h" +#include "acl_ids.h" VLOG_DEFINE_THIS_MODULE(main); @@ -864,7 +865,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) SB_NODE(fdb, "fdb") \ SB_NODE(meter, "meter") \ SB_NODE(static_mac_binding, "static_mac_binding") \ - SB_NODE(chassis_template_var, "chassis_template_var") + SB_NODE(chassis_template_var, "chassis_template_var") \ + SB_NODE(acl_id, "acl_id") enum sb_engine_node { #define SB_NODE(NAME, NAME_STR) SB_##NAME, @@ -5095,6 +5097,7 @@ main(int argc, char *argv[]) ENGINE_NODE(mac_cache, "mac_cache"); ENGINE_NODE(bfd_chassis, "bfd_chassis"); ENGINE_NODE(dns_cache, "dns_cache"); + ENGINE_NODE(acl_id, "acl_id"); #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR); SB_NODES @@ -5303,6 +5306,9 @@ main(int argc, char *argv[]) engine_add_input(&en_controller_output, &en_bfd_chassis, controller_output_bfd_chassis_handler); + engine_add_input(&en_acl_id, &en_sb_acl_id, NULL); + engine_add_input(&en_controller_output, &en_acl_id, NULL); + struct engine_arg engine_arg = { .sb_idl = ovnsb_idl_loop.idl, .ovs_idl = ovs_idl_loop.idl, @@ -5538,6 +5544,8 @@ main(int argc, char *argv[]) br_int_remote.probe_interval); pinctrl_update_swconn(br_int_remote.target, br_int_remote.probe_interval); + acl_ids_update_swconn(br_int_remote.target, + br_int_remote.probe_interval); /* Enable ACL matching for double tagged traffic. */ if (ovs_idl_txn && cfg) { @@ -5842,6 +5850,8 @@ main(int argc, char *argv[]) !ovnsb_idl_txn, !ovs_idl_txn); stopwatch_stop(IF_STATUS_MGR_RUN_STOPWATCH_NAME, time_msec()); + + acl_ids_run(engine_get_data(&en_acl_id)); } } diff --git a/lib/logical-fields.c b/lib/logical-fields.c index 5b578b5c2..89431b939 100644 --- a/lib/logical-fields.c +++ b/lib/logical-fields.c @@ -192,6 +192,8 @@ ovn_init_symtab(struct shash *symtab) "ct_label[96..127]", WR_CT_COMMIT); expr_symtab_add_subfield_scoped(symtab, "ct_label.obs_unused", NULL, "ct_label[0..95]", WR_CT_COMMIT); + expr_symtab_add_subfield_scoped(symtab, "ct_label.acl_id", NULL, + "ct_label[80..95]", WR_CT_COMMIT); expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false); diff --git a/northd/en-acl-ids.c b/northd/en-acl-ids.c index 897cc2da1..cb220dd80 100644 --- a/northd/en-acl-ids.c +++ b/northd/en-acl-ids.c @@ -204,3 +204,16 @@ void sync_acl_ids(const struct acl_id_data *id_data, } } } + +int64_t +get_acl_id(const struct acl_id_data *acl_id_data, const struct nbrec_acl *acl) +{ + struct acl_id *acl_id; + uint32_t hash = uuid_hash(&acl->header_.uuid); + HMAP_FOR_EACH_WITH_HASH (acl_id, node, hash, &acl_id_data->ids) { + if (uuid_equals(&acl_id->nb_acl_uuid, &acl->header_.uuid)) { + return acl_id->id; + } + } + return 0; +} diff --git a/northd/en-acl-ids.h b/northd/en-acl-ids.h index 8b60b3c7c..8bea2e078 100644 --- a/northd/en-acl-ids.h +++ b/northd/en-acl-ids.h @@ -14,4 +14,7 @@ void en_acl_id_cleanup(void *data); struct acl_id_data; void sync_acl_ids(const struct acl_id_data *, struct ovsdb_idl_txn *, struct ovsdb_idl_index *sbrec_acl_id_by_id); + +struct nbrec_acl; +int64_t get_acl_id(const struct acl_id_data *, const struct nbrec_acl *); #endif diff --git a/northd/en-lflow.c b/northd/en-lflow.c index fa1f0236d..3d57a469b 100644 --- a/northd/en-lflow.c +++ b/northd/en-lflow.c @@ -96,6 +96,7 @@ lflow_get_input_data(struct engine_node *node, struct ed_type_sampling_app_data *sampling_app_data = engine_get_input_data("sampling_app", node); lflow_input->sampling_apps = &sampling_app_data->apps; + lflow_input->acl_id_data = engine_get_input_data("acl_id", node); } void en_lflow_run(struct engine_node *node, void *data) diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c index 43abf042d..04c208c97 100644 --- a/northd/inc-proc-northd.c +++ b/northd/inc-proc-northd.c @@ -293,6 +293,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_lflow, &en_port_group, lflow_port_group_handler); engine_add_input(&en_lflow, &en_lr_stateful, lflow_lr_stateful_handler); engine_add_input(&en_lflow, &en_ls_stateful, lflow_ls_stateful_handler); + engine_add_input(&en_lflow, &en_acl_id, NULL); engine_add_input(&en_sync_to_sb_addr_set, &en_northd, NULL); engine_add_input(&en_sync_to_sb_addr_set, &en_lr_stateful, NULL); diff --git a/northd/northd.c b/northd/northd.c index 8dab88f62..081305b09 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -206,6 +206,9 @@ BUILD_ASSERT_DECL(ACL_OBS_STAGE_MAX < (1 << 2)); #define REG_OBS_COLLECTOR_ID_NEW "reg8[0..7]" #define REG_OBS_COLLECTOR_ID_EST "reg8[8..15]" +/* Register used for storing persistent ACL IDs */ +#define REG_ACL_ID "reg7[0..15]" + /* Register used for temporarily store ECMP eth.src to avoid masked ct_label * access. It doesn't really occupy registers because the content of the * register is saved to stack and then restored in the same flow. @@ -7063,7 +7066,8 @@ consider_acl(struct lflow_table *lflows, const struct ovn_datapath *od, const struct nbrec_acl *acl, bool has_stateful, const struct shash *meter_groups, uint64_t max_acl_tier, struct ds *match, struct ds *actions, - struct lflow_ref *lflow_ref) + struct lflow_ref *lflow_ref, + const struct acl_id_data *acl_id_data) { bool ingress = !strcmp(acl->direction, "from-lport") ? true :false; enum ovn_stage stage; @@ -7151,8 +7155,17 @@ consider_acl(struct lflow_table *lflows, const struct ovn_datapath *od, ds_put_cstr(actions, REGBIT_CONNTRACK_COMMIT" = 1; "); if (!strcmp(acl->action, "allow-established")) { - ds_put_format(actions, - REGBIT_ACL_PERSIST_ID " = 1; "); + int64_t id = get_acl_id(acl_id_data, acl); + if (id) { + ds_put_format(actions, + REG_ACL_ID " = %"PRId64 "; " + REGBIT_ACL_PERSIST_ID " = 1; ", + id); + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "No ID found for ACL "UUID_FMT" (%s)", + UUID_ARGS(&acl->header_.uuid), acl->match); + } } /* For stateful ACLs sample "new" and "established" packets. */ @@ -7476,7 +7489,8 @@ build_acls(const struct ls_stateful_record *ls_stateful_rec, const struct shash *meter_groups, const struct sampling_app_table *sampling_apps, const struct chassis_features *features, - struct lflow_ref *lflow_ref) + struct lflow_ref *lflow_ref, + const struct acl_id_data *acl_id_data) { const char *default_acl_action = default_acl_drop ? debug_implicit_drop_action() @@ -7686,7 +7700,8 @@ build_acls(const struct ls_stateful_record *ls_stateful_rec, lflow_ref); consider_acl(lflows, od, acl, has_stateful, meter_groups, ls_stateful_rec->max_acl_tier, - &match, &actions, lflow_ref); + &match, &actions, lflow_ref, + acl_id_data); build_acl_sample_flows(ls_stateful_rec, od, lflows, acl, &match, &actions, sampling_apps, features, lflow_ref); @@ -7705,7 +7720,8 @@ build_acls(const struct ls_stateful_record *ls_stateful_rec, lflow_ref); consider_acl(lflows, od, acl, has_stateful, meter_groups, ls_stateful_rec->max_acl_tier, - &match, &actions, lflow_ref); + &match, &actions, lflow_ref, + acl_id_data); build_acl_sample_flows(ls_stateful_rec, od, lflows, acl, &match, &actions, sampling_apps, features, lflow_ref); @@ -8394,6 +8410,7 @@ build_stateful(struct ovn_datapath *od, struct lflow_table *lflows, "ct_mark.obs_stage = " REGBIT_ACL_OBS_STAGE "; " "ct_mark.obs_collector_id = " REG_OBS_COLLECTOR_ID_EST "; " "ct_label.obs_point_id = " REG_OBS_POINT_ID_EST "; " + "ct_label.acl_id = " REG_ACL_ID "; " "}; next;"); ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100, REGBIT_CONNTRACK_COMMIT" == 1 && " @@ -8415,6 +8432,7 @@ build_stateful(struct ovn_datapath *od, struct lflow_table *lflows, "ct_commit { " "ct_mark.blocked = 0; " "ct_mark.allow_established = " REGBIT_ACL_PERSIST_ID "; " + "ct_label.acl_id = " REG_ACL_ID "; " "}; next;"); ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100, REGBIT_CONNTRACK_COMMIT" == 1 && " @@ -17216,7 +17234,8 @@ build_ls_stateful_flows(const struct ls_stateful_record *ls_stateful_rec, const struct shash *meter_groups, const struct sampling_app_table *sampling_apps, const struct chassis_features *features, - struct lflow_table *lflows) + struct lflow_table *lflows, + const struct acl_id_data *acl_id_data) { build_ls_stateful_rec_pre_acls(ls_stateful_rec, od, ls_pgs, lflows, ls_stateful_rec->lflow_ref); @@ -17225,7 +17244,8 @@ build_ls_stateful_flows(const struct ls_stateful_record *ls_stateful_rec, build_acl_hints(ls_stateful_rec, od, lflows, ls_stateful_rec->lflow_ref); build_acls(ls_stateful_rec, od, lflows, ls_pgs, meter_groups, - sampling_apps, features, ls_stateful_rec->lflow_ref); + sampling_apps, features, ls_stateful_rec->lflow_ref, + acl_id_data); build_lb_hairpin(ls_stateful_rec, od, lflows, ls_stateful_rec->lflow_ref); } @@ -17253,6 +17273,7 @@ struct lswitch_flow_build_info { struct hmap *parsed_routes; struct hmap *route_policies; struct simap *route_tables; + const struct acl_id_data *acl_id_data; }; /* Helper function to combine all lflow generation which is iterated by @@ -17552,7 +17573,8 @@ build_lflows_thread(void *arg) lsi->meter_groups, lsi->sampling_apps, lsi->features, - lsi->lflows); + lsi->lflows, + lsi->acl_id_data); } } @@ -17629,7 +17651,8 @@ build_lswitch_and_lrouter_flows( const struct sampling_app_table *sampling_apps, struct hmap *parsed_routes, struct hmap *route_policies, - struct simap *route_tables) + struct simap *route_tables, + const struct acl_id_data *acl_id_data) { char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac); @@ -17667,6 +17690,7 @@ build_lswitch_and_lrouter_flows( lsiv[index].parsed_routes = parsed_routes; lsiv[index].route_tables = route_tables; lsiv[index].route_policies = route_policies; + lsiv[index].acl_id_data = acl_id_data; ds_init(&lsiv[index].match); ds_init(&lsiv[index].actions); @@ -17713,6 +17737,7 @@ build_lswitch_and_lrouter_flows( .route_policies = route_policies, .match = DS_EMPTY_INITIALIZER, .actions = DS_EMPTY_INITIALIZER, + .acl_id_data = acl_id_data, }; /* Combined build - all lflow generation from lswitch and lrouter @@ -17786,7 +17811,8 @@ build_lswitch_and_lrouter_flows( lsi.meter_groups, lsi.sampling_apps, lsi.features, - lsi.lflows); + lsi.lflows, + lsi.acl_id_data); } stopwatch_stop(LFLOWS_LS_STATEFUL_STOPWATCH_NAME, time_msec()); stopwatch_start(LFLOWS_IGMP_STOPWATCH_NAME, time_msec()); @@ -17879,7 +17905,8 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn, input_data->sampling_apps, input_data->parsed_routes, input_data->route_policies, - input_data->route_tables); + input_data->route_tables, + input_data->acl_id_data); if (parallelization_state == STATE_INIT_HASH_SIZES) { parallelization_state = STATE_USE_PARALLELIZATION; @@ -18306,7 +18333,8 @@ lflow_handle_ls_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn, lflow_input->meter_groups, lflow_input->sampling_apps, lflow_input->features, - lflows); + lflows, + lflow_input->acl_id_data); /* Sync the new flows to SB. */ bool handled = lflow_ref_sync_lflows( diff --git a/northd/northd.h b/northd/northd.h index bccb1c5d8..175c318af 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -232,6 +232,7 @@ struct lflow_input { struct hmap *parsed_routes; struct hmap *route_policies; struct simap *route_tables; + const struct acl_id_data *acl_id_data; }; extern int parallelization_state; diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 1d90622e1..b5b94330b 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -14297,21 +14297,27 @@ after_lb_uuid=$(fetch_column nb:ACL _uuid priority=1003) check ovn-nbctl set acl $ingress_uuid action=allow-established check ovn-nbctl set acl $egress_uuid action=allow-established -check ovn-nbctl set acl $after_lb_uuid action=allow-established +check ovn-nbctl --wait=sb set acl $after_lb_uuid action=allow-established + +dnl Retrieve the IDs for the ACLs so we can check them properly. + +ingress_id=$(fetch_column ACL_ID id nb_acl=$ingress_uuid) +egress_id=$(fetch_column ACL_ID id nb_acl=$egress_uuid) +after_lb_id=$(fetch_column ACL_ID id nb_acl=$after_lb_uuid) dnl Now we should see the registers being set to the appropriate values. -AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl - table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[7]] == 1 && (tcp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg0[[20]] = 1; next;) +AT_CHECK_UNQUOTED([ovn-sbctl lflow-list sw | grep ls_in_acl_eval | grep priority=2001 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[7]] == 1 && (tcp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg7[[0..15]] = $ingress_id; reg0[[20]] = 1; next;) table=??(ls_in_acl_eval ), priority=2001 , match=(reg0[[8]] == 1 && (tcp)), action=(reg8[[16]] = 1; next;) ]) -AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl - table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[7]] == 1 && (udp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg0[[20]] = 1; next;) +AT_CHECK_UNQUOTED([ovn-sbctl lflow-list sw | grep ls_in_acl_after_lb_eval | grep priority=2003 | ovn_strip_lflows], [0], [dnl + table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[7]] == 1 && (udp)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg7[[0..15]] = $after_lb_id; reg0[[20]] = 1; next;) table=??(ls_in_acl_after_lb_eval), priority=2003 , match=(reg0[[8]] == 1 && (udp)), action=(reg8[[16]] = 1; next;) ]) -AT_CHECK([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl - table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[7]] == 1 && (ip)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg0[[20]] = 1; next;) +AT_CHECK_UNQUOTED([ovn-sbctl lflow-list sw | grep ls_out_acl_eval | grep priority=2002 | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[7]] == 1 && (ip)), action=(reg8[[16]] = 1; reg0[[1]] = 1; reg7[[0..15]] = $egress_id; reg0[[20]] = 1; next;) table=??(ls_out_acl_eval ), priority=2002 , match=(reg0[[8]] == 1 && (ip)), action=(reg8[[16]] = 1; next;) ]) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index a6ddda0f7..27e112a26 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -14251,6 +14251,18 @@ test : > output.txt +# Get the ID for this ACL +acl_id=$(fetch_column ACL_ID id nb_acl=$acl_uuid) +acl_id=$(printf %x $acl_id) + +OVS_WAIT_FOR_OUTPUT([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \ +grep "labels=0x"$acl_id"00000000000000000000" | \ +sed -e 's/zone=[[0-9]]*/zone=/' | \ +sed -e 's/mark=[[0-9]]*/mark=/' | \ +sed -e 's/labels=0x[[0-9a-f]]*/labels=/'], [0], [dnl +tcp,orig=(src=192.168.1.1,dst=192.168.1.2,sport=,dport=),reply=(src=192.168.1.2,dst=192.168.1.1,sport=,dport=),zone=,mark=,labels=,protoinfo=(state=) +]) + # Adjust the ACL so that it no longer matches check ovn-nbctl set ACL $acl_uuid match="\"ip4.dst == 192.168.1.3\"" @@ -14264,6 +14276,14 @@ test : > output.txt +# Now remove the ACL. This should remove the conntrack entry as well. +check ovn-nbctl --wait=hv acl-del sw from-lport 1000 'ip4.dst == 192.168.1.3' + +OVS_WAIT_FOR_OUTPUT([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \ +grep "labels=0x"$acl_id"00000000000000000000" | \ +sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl +]) + OVS_APP_EXIT_AND_WAIT([ovn-controller]) as ovn-sb