From patchwork Mon Apr 15 12:14:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Kalcok X-Patchwork-Id: 1923708 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" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=XRWge+BK; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::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 4VJ5hp6pfKz1yZf for ; Mon, 15 Apr 2024 22:15:06 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 5EE7E81759; Mon, 15 Apr 2024 12:15:04 +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 3BziDzdAHbpy; Mon, 15 Apr 2024 12:15:03 +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 1D6258176F Authentication-Results: smtp1.osuosl.org; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=XRWge+BK Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 1D6258176F; Mon, 15 Apr 2024 12:15:03 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E34BAC0077; Mon, 15 Apr 2024 12:15:02 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id BA3B2C0037 for ; Mon, 15 Apr 2024 12:15:01 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 9369A40516 for ; Mon, 15 Apr 2024 12:15:01 +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 r4388v4qlonq for ; Mon, 15 Apr 2024 12:15:00 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=185.125.188.121; helo=smtp-relay-canonical-1.canonical.com; envelope-from=martin.kalcok@canonical.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org 41014401D5 Authentication-Results: smtp2.osuosl.org; dmarc=pass (p=none dis=none) header.from=canonical.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 41014401D5 Authentication-Results: smtp2.osuosl.org; dkim=pass (2048-bit key, unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=XRWge+BK Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp2.osuosl.org (Postfix) with ESMTPS id 41014401D5 for ; Mon, 15 Apr 2024 12:14:59 +0000 (UTC) Received: from localhost.localdomain (2.general.kalcok.uk.vpn [10.172.196.41]) (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 smtp-relay-canonical-1.canonical.com (Postfix) with ESMTPSA id 439293F103; Mon, 15 Apr 2024 12:14:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1713183297; bh=RR2ZwVwlO6KNFcG3FfVC2J/O7rjAeFoX20yceL65Jz4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XRWge+BKyTNGcq5Yal5yHoYUxFRDUD3m9gunjNp3qnERCYd/YvqT6/pPkQN2CXCWO kBk+S2eK6U1BhL83pq0VmLrIb/fjBHGEZ6qaSUigoHnUSIc33fHRcKVAZr9qWa4B+P bDA0MdbRK70NQ6WiGBtO4WmSrvH88U0XqmuiU2d8QCQKGF22Oakvi4lzrX70UgTspJ 9Q/Ga1KHvTrWFD9RXZieCRSzMSfL7KyKcdQjZFKWyQaCIZKiTvQde9p9XwoaNZP9xE NQQDezBwV1OFHjZd65FjGf9Z0K9tmThRLPz7G5+Y8Zqcx7vwXcZrV3VHjXeB7f3iwB 7lZ7eMi3t73Zg== From: Martin Kalcok To: dceara@redhat.com, amusil@redhat.com, dev@openvswitch.org Date: Mon, 15 Apr 2024 14:14:13 +0200 Message-Id: <20240415121414.1024577-1-martin.kalcok@canonical.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: References: MIME-Version: 1.0 Subject: [ovs-dev] [Patch ovn v3 1/2] actions: New action ct_commit_to_zone. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" This change adds a new action 'ct_commit_to_zone' that enables users to commit the flow into a specific zone in the connection tracker. A new feature flag, OVN_FEATURE_CT_COMMIT_TO_ZONE, is also included to avoid issues during upgrade in case the northd is upgraded to a version that supports this action before the controller is upgraded. Note that this action is meaningful only in the context of Logical Router datapath. Logical Switch datapath does not use multiple zones and this action falls back to committing the connection into the default zone for the Logical Switch. Signed-off-by: Martin Kalcok --- controller/chassis.c | 8 ++++++ include/ovn/actions.h | 1 + include/ovn/features.h | 1 + lib/actions.c | 60 +++++++++++++++++++++++++++++++++++++++ northd/en-global-config.c | 11 +++++++ northd/en-global-config.h | 1 + ovn-sb.xml | 24 ++++++++++++++++ tests/ovn.at | 16 +++++++++++ utilities/ovn-trace.c | 1 + 9 files changed, 123 insertions(+) diff --git a/controller/chassis.c b/controller/chassis.c index ad75df288..9bb2eba95 100644 --- a/controller/chassis.c +++ b/controller/chassis.c @@ -371,6 +371,7 @@ chassis_build_other_config(const struct ovs_chassis_cfg *ovs_cfg, smap_replace(config, OVN_FEATURE_FDB_TIMESTAMP, "true"); smap_replace(config, OVN_FEATURE_LS_DPG_COLUMN, "true"); smap_replace(config, OVN_FEATURE_CT_COMMIT_NAT_V2, "true"); + smap_replace(config, OVN_FEATURE_CT_COMMIT_TO_ZONE, "true"); } /* @@ -516,6 +517,12 @@ chassis_other_config_changed(const struct ovs_chassis_cfg *ovs_cfg, return true; } + if (!smap_get_bool(&chassis_rec->other_config, + OVN_FEATURE_CT_COMMIT_TO_ZONE, + false)) { + return true; + } + return false; } @@ -648,6 +655,7 @@ update_supported_sset(struct sset *supported) sset_add(supported, OVN_FEATURE_FDB_TIMESTAMP); sset_add(supported, OVN_FEATURE_LS_DPG_COLUMN); sset_add(supported, OVN_FEATURE_CT_COMMIT_NAT_V2); + sset_add(supported, OVN_FEATURE_CT_COMMIT_TO_ZONE); } static void diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 8e794450c..4bcd1f58c 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -68,6 +68,7 @@ struct collector_set_ids; OVNACT(CT_NEXT, ovnact_ct_next) \ /* CT_COMMIT_V1 is not supported anymore. */ \ OVNACT(CT_COMMIT_V2, ovnact_nest) \ + OVNACT(CT_COMMIT_TO_ZONE, ovnact_ct_commit_nat) \ OVNACT(CT_DNAT, ovnact_ct_nat) \ OVNACT(CT_SNAT, ovnact_ct_nat) \ OVNACT(CT_DNAT_IN_CZONE, ovnact_ct_nat) \ diff --git a/include/ovn/features.h b/include/ovn/features.h index 08f1d8288..35a5d8ba0 100644 --- a/include/ovn/features.h +++ b/include/ovn/features.h @@ -28,6 +28,7 @@ #define OVN_FEATURE_FDB_TIMESTAMP "fdb-timestamp" #define OVN_FEATURE_LS_DPG_COLUMN "ls-dpg-column" #define OVN_FEATURE_CT_COMMIT_NAT_V2 "ct-commit-nat-v2" +#define OVN_FEATURE_CT_COMMIT_TO_ZONE "ct-commit-to-zone" /* OVS datapath supported features. Based on availability OVN might generate * different types of openflows. diff --git a/lib/actions.c b/lib/actions.c index 39bb5bc8a..f26817018 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -791,6 +791,64 @@ encode_CT_COMMIT_V2(const struct ovnact_nest *on, ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset); ct = ofpacts->header; ofpact_finish(ofpacts, &ct->ofpact); +} + +static void +parse_CT_COMMIT_TO_ZONE(struct action_context *ctx) +{ + add_prerequisite(ctx, "ip"); + + struct ovnact_ct_commit_nat *ct_commit = + ovnact_put_CT_COMMIT_TO_ZONE(ctx->ovnacts); + + lexer_force_match(ctx->lexer, LEX_T_LPAREN); + + if (lexer_match_id(ctx->lexer, "dnat")) { + ct_commit->dnat_zone = true; + } else if (lexer_match_id(ctx->lexer, "snat")) { + ct_commit->dnat_zone = false; + } else { + lexer_syntax_error(ctx->lexer, "expecting parameter 'dnat' or 'snat'"); + } + + lexer_force_match(ctx->lexer, LEX_T_RPAREN); +} + +static void +format_CT_COMMIT_TO_ZONE(const struct ovnact_ct_commit_nat *ct_commit, + struct ds *s) +{ + ds_put_format(s, "ct_commit_to_zone(%s);", + ct_commit->dnat_zone ? "dnat" : "snat"); + +} + +static void +encode_CT_COMMIT_TO_ZONE(const struct ovnact_ct_commit_nat *ct_commit, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); + ct->flags = NX_CT_F_COMMIT; + ct->recirc_table = NX_CT_RECIRC_NONE; + ct->zone_src.ofs = 0; + ct->zone_src.n_bits = 16; + + if (ep->is_switch) { + ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE); + } else { + ct->zone_src.field = mf_from_id(ct_commit->dnat_zone + ? MFF_LOG_DNAT_ZONE + : MFF_LOG_SNAT_ZONE); + } + + size_t set_field_offset = ofpacts->size; + ofpbuf_pull(ofpacts, set_field_offset); + ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset); + ct = ofpacts->header; + ofpact_finish(ofpacts, &ct->ofpact); + + } static void @@ -5306,6 +5364,8 @@ parse_action(struct action_context *ctx) parse_CT_NEXT(ctx); } else if (lexer_match_id(ctx->lexer, "ct_commit")) { parse_CT_COMMIT(ctx); + } else if (lexer_match_id(ctx->lexer, "ct_commit_to_zone")) { + parse_CT_COMMIT_TO_ZONE(ctx); } else if (lexer_match_id(ctx->lexer, "ct_dnat")) { parse_CT_DNAT(ctx); } else if (lexer_match_id(ctx->lexer, "ct_snat")) { diff --git a/northd/en-global-config.c b/northd/en-global-config.c index 34e393b33..842ac5b64 100644 --- a/northd/en-global-config.c +++ b/northd/en-global-config.c @@ -370,6 +370,7 @@ northd_enable_all_features(struct ed_type_global_config *data) .fdb_timestamp = true, .ls_dpg_column = true, .ct_commit_nat_v2 = true, + .ct_commit_to_zone = true, }; } @@ -439,6 +440,16 @@ build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table, chassis_features->ct_commit_nat_v2) { chassis_features->ct_commit_nat_v2 = false; } + + bool ct_commit_to_zone = + smap_get_bool(&chassis->other_config, + OVN_FEATURE_CT_COMMIT_TO_ZONE, + false); + if (!ct_commit_to_zone && + chassis_features->ct_commit_to_zone) { + chassis_features->ct_commit_to_zone = false; + } + } } diff --git a/northd/en-global-config.h b/northd/en-global-config.h index 38d732808..842bcee70 100644 --- a/northd/en-global-config.h +++ b/northd/en-global-config.h @@ -20,6 +20,7 @@ struct chassis_features { bool fdb_timestamp; bool ls_dpg_column; bool ct_commit_nat_v2; + bool ct_commit_to_zone; }; struct global_config_tracked_data { diff --git a/ovn-sb.xml b/ovn-sb.xml index 4c26c6714..0d1f640dd 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -1432,6 +1432,30 @@

+
ct_commit_to_zone(dnat);
+
ct_commit_to_zone(snat);
+
+

+ Commit the flow to the specific zone in the connection tracker. + Similar to the ct_commit action, this action requires + previous call to ct_next to initialize connection + tracking state. +

+ +

+ If you want processing to continue in the next table, you must + execute the next action after + ct_commit_to_zone. +

+ +

+ Note that this action is meaningful only in the Logical Router + Datapath as the Logical Switch Datapath does not use separate + connection tracking zones. Using this action in Logical Switch + Datapath falls back to committing the flow into the switch's + default zone. +

+
ct_dnat;
ct_dnat(IP);
diff --git a/tests/ovn.at b/tests/ovn.at index c8cc1d37f..cbb303459 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1316,6 +1316,22 @@ ct_commit { ct_label=0x181716151413121110090807060504030201; }; ct_commit { ip4.dst = 192.168.0.1; }; Field ip4.dst is not modifiable. +# ct_commit_to_zone +ct_commit_to_zone(dnat); + encodes as ct(commit,zone=NXM_NX_REG13[[0..15]]) + has prereqs ip +ct_commit_to_zone(dnat); + encodes as ct(commit,zone=NXM_NX_REG13[[0..15]]) + has prereqs ip +ct_commit_to_zone; + Syntax error at `;' expecting `('. +ct_commit_to_zone(); + Syntax error at `)' expecting parameter 'dnat' or 'snat'. +ct_commit_to_zone(foo); + Syntax error at `foo' expecting parameter 'dnat' or 'snat'. +ct_commit_to_zone(dnat; + Syntax error at `;' expecting `)'. + # Legact ct_commit_v1 action. ct_commit(); Syntax error at `(' expecting `;'. diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index 5e55fbbcc..554682cd0 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -3107,6 +3107,7 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, execute_ct_next(ovnact_get_CT_NEXT(a), dp, uflow, pipeline, super); break; + case OVNACT_CT_COMMIT_TO_ZONE: case OVNACT_CT_COMMIT_V2: /* Nothing to do. */ break; From patchwork Mon Apr 15 12:14:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Kalcok X-Patchwork-Id: 1923709 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" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=ZXjkueu+; 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 4VJ5hv1B1tz1yXv for ; Mon, 15 Apr 2024 22:15:11 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 7A3DF81C20; Mon, 15 Apr 2024 12:15:09 +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 cANng8fRU4J4; Mon, 15 Apr 2024 12:15:06 +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 smtp1.osuosl.org 2A1A18179B Authentication-Results: smtp1.osuosl.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=ZXjkueu+ Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id 2A1A18179B; Mon, 15 Apr 2024 12:15:06 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0DAAFC0077; Mon, 15 Apr 2024 12:15:06 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1E056C0DD4 for ; Mon, 15 Apr 2024 12:15:03 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 042C1405D4 for ; Mon, 15 Apr 2024 12:15:03 +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 9D1XhnOdgRTc for ; Mon, 15 Apr 2024 12:15:00 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=185.125.188.121; helo=smtp-relay-canonical-1.canonical.com; envelope-from=martin.kalcok@canonical.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp4.osuosl.org 4136F40598 Authentication-Results: smtp4.osuosl.org; dmarc=pass (p=none dis=none) header.from=canonical.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 4136F40598 Authentication-Results: smtp4.osuosl.org; dkim=pass (2048-bit key, unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=ZXjkueu+ Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp4.osuosl.org (Postfix) with ESMTPS id 4136F40598 for ; Mon, 15 Apr 2024 12:14:59 +0000 (UTC) Received: from localhost.localdomain (2.general.kalcok.uk.vpn [10.172.196.41]) (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 smtp-relay-canonical-1.canonical.com (Postfix) with ESMTPSA id 9A9C440B6F; Mon, 15 Apr 2024 12:14:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1713183297; bh=ZkH2mQlQilbx0MFjxixul2X15uksSoSJ+raTpsWdwM0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZXjkueu+WDw47pdtgqB411Jm/7dSncUkCaRGXNsYOQIv33PjIrjW6QO0Bpn0Qpb40 1w9snEHihqhLNQm+4ww4gxjYL2/7ZS7qLo7iBhx6e3MetBAY6nHCDQ5ZTY0S29D81Y l+3XcYMFnfLTLm6Gforvhik5/bjr2IfEs7NdWDPrNKJnEUkwYVqq3X4eygUslbMeqV ZVa6LkfuqY4fNjoPDvf9yhXvEuTe9BcJBPkL5omIZctyzfTUS/+FnamlL39As8Cq0o gMH9ZGW0Y4PAfB16cK03pRGV/Ham9N/HIHIBBLIEtQ4grkvpugWGpTfOwjMHdK+hpe 2YJRxrsAMek7A== From: Martin Kalcok To: dceara@redhat.com, amusil@redhat.com, dev@openvswitch.org Date: Mon, 15 Apr 2024 14:14:14 +0200 Message-Id: <20240415121414.1024577-2-martin.kalcok@canonical.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20240415121414.1024577-1-martin.kalcok@canonical.com> References: <20240415121414.1024577-1-martin.kalcok@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [Patch ovn v3 2/2] northd: Fix direct access to SNAT network. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" This change fixes bug that breaks ability of machines from external networks, to communicate with machines in SNATed networks (specifically when using a Distributed router). Currently when a machine (S1) on an external network tries to talk over TCP with a machine (A1) in a network that has enabled SNAT, the connection is established successfully. However after the three-way handshake, any packets that come from the A1 machine will have their source address translated by the Distributed router, breaking the communication. Existing rule in `build_lrouter_out_snat_flow` that decides which packets should be SNATed already tries to avoid SNATing packets in reply direction with `(!ct.trk || !ct.rpl)`. However, previous stages in the distributed LR egress pipeline do not initiate the CT state. Additionally we need to commit new connections that originate from external networks into CT, so that the packets in the reply direction (back to the external network) can be properly identified. Rationale: In my original RFC [0], there were questions about the motivation for fixing this issue. I'll try to summarize why I think this is a bug that should be fixed. 1. Current implementation for Distributed router already tries to avoid SNATing packets in the reply direction, it's just missing initialized CT states to make proper decisions. 2. This same scenario works with Gateway Router. I tested with following setup: foo -- R1 -- join - R3 -- alice | bar ----------R2 R1 is a Distributed router with SNAT for foo. R2 is a Gateway router with SNAT for bar. R3 is a Gateway router with no SNAT. Using 'alice1' as a client I was able to talk over TCP with 'bar1' but connection with 'foo1' failed. 3. Regarding security and "leaking" of internal IPs. Reading through RFC 4787 [1], 5382 [2] and their update in 7857 [3], the specifications do not seem to mandate that SNAT implementations must filter incoming traffic destined directly to the internal network. Sections like "5. Filtering Behavior" in 4787 and "4.3 Externally Initiated Connections" in 5382 describe only behavior for traffic destined to external IP/Port associated with NAT on the device that performs NAT. Besides, with the current implementation, it's already possible to scan the internal network with pings and TCP syn scanning. 4. We do have customers/clouds that depend on this functionality. This is a scenario that used to work in Openstack with ML2/OVS and migrating those clouds to ML2/OVN would break it. [0]https://mail.openvswitch.org/pipermail/ovs-dev/2024-February/411670.html [1]https://datatracker.ietf.org/doc/html/rfc4787 [2]https://datatracker.ietf.org/doc/html/rfc5382 [3]https://datatracker.ietf.org/doc/html/rfc7857 Signed-off-by: Martin Kalcok --- northd/northd.c | 66 ++++++++++++++++++++++++++++------- northd/ovn-northd.8.xml | 29 ++++++++++++++++ tests/ovn-northd.at | 76 +++++++++++++++++++++++++++++++++++++---- tests/system-ovn.at | 68 ++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 19 deletions(-) diff --git a/northd/northd.c b/northd/northd.c index 02cf5b234..0726c8429 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -14413,20 +14413,27 @@ build_lrouter_out_is_dnat_local(struct lflow_table *lflows, static void build_lrouter_out_snat_match(struct lflow_table *lflows, - const struct ovn_datapath *od, - const struct nbrec_nat *nat, struct ds *match, - bool distributed_nat, int cidr_bits, bool is_v6, - struct ovn_port *l3dgw_port, - struct lflow_ref *lflow_ref) + const struct ovn_datapath *od, + const struct nbrec_nat *nat, + struct ds *match, + bool distributed_nat, int cidr_bits, + bool is_v6, + struct ovn_port *l3dgw_port, + struct lflow_ref *lflow_ref, + bool is_reverse) { ds_clear(match); - ds_put_format(match, "ip && ip%c.src == %s", is_v6 ? '6' : '4', + ds_put_format(match, "ip && ip%c.%s == %s", + is_v6 ? '6' : '4', + is_reverse ? "dst" : "src", nat->logical_ip); if (!od->is_gw_router) { /* Distributed router. */ - ds_put_format(match, " && outport == %s", l3dgw_port->json_key); + ds_put_format(match, " && %s == %s", + is_reverse ? "inport" : "outport", + l3dgw_port->json_key); if (od->n_l3dgw_ports) { ds_put_format(match, " && is_chassis_resident(\"%s\")", distributed_nat @@ -14437,7 +14444,7 @@ build_lrouter_out_snat_match(struct lflow_table *lflows, if (nat->allowed_ext_ips || nat->exempted_ext_ips) { lrouter_nat_add_ext_ip_match(od, lflows, match, nat, - is_v6, false, cidr_bits, + is_v6, is_reverse, cidr_bits, lflow_ref); } } @@ -14464,7 +14471,8 @@ build_lrouter_out_snat_stateless_flow(struct lflow_table *lflows, uint16_t priority = cidr_bits + 1; build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat, - cidr_bits, is_v6, l3dgw_port, lflow_ref); + cidr_bits, is_v6, l3dgw_port, lflow_ref, + false); if (!od->is_gw_router) { /* Distributed router. */ @@ -14511,7 +14519,7 @@ build_lrouter_out_snat_in_czone_flow(struct lflow_table *lflows, build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat, cidr_bits, is_v6, l3dgw_port, - lflow_ref); + lflow_ref, false); if (od->n_l3dgw_ports) { priority += 128; @@ -14560,7 +14568,8 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows, struct ds *actions, bool distributed_nat, struct eth_addr mac, int cidr_bits, bool is_v6, struct ovn_port *l3dgw_port, - struct lflow_ref *lflow_ref) + struct lflow_ref *lflow_ref, + const struct chassis_features *features) { if (strcmp(nat->type, "snat") && strcmp(nat->type, "dnat_and_snat")) { return; @@ -14574,7 +14583,9 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows, uint16_t priority = cidr_bits + 1; build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat, - cidr_bits, is_v6, l3dgw_port, lflow_ref); + cidr_bits, is_v6, l3dgw_port, lflow_ref, + false); + size_t original_match_len = match->length; if (!od->is_gw_router) { /* Distributed router. */ @@ -14599,6 +14610,35 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows, priority, ds_cstr(match), ds_cstr(actions), &nat->header_, lflow_ref); + + /* For the SNAT networks, we need to make sure that connections are + * properly tracked so we can decide whether to perform SNAT on traffic + * exiting the network. */ + if (features->ct_commit_to_zone && !strcmp(nat->type, "snat") && + !od->is_gw_router) { + /* For traffic that comes from SNAT network, initiate CT state before + * entering S_ROUTER_OUT_SNAT to allow matching on various CT states. + */ + ds_truncate(match, original_match_len); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70, + ds_cstr(match), "ct_snat; ", + lflow_ref); + + build_lrouter_out_snat_match(lflows, od, nat, match, + distributed_nat, cidr_bits, is_v6, + l3dgw_port, lflow_ref, true); + + /* New traffic that goes into SNAT network is committed to CT to avoid + * SNAT-ing replies.*/ + ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority, + ds_cstr(match), "ct_snat;", + lflow_ref); + + ds_put_cstr(match, " && ct.new"); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority, + ds_cstr(match), "ct_commit_to_zone(snat); next; ", + lflow_ref); + } } static void @@ -15137,7 +15177,7 @@ build_lrouter_nat_defrag_and_lb( } else { build_lrouter_out_snat_flow(lflows, od, nat, match, actions, distributed_nat, mac, cidr_bits, is_v6, - l3dgw_port, lflow_ref); + l3dgw_port, lflow_ref, features); } /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */ diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index 90ce0de3f..93a0b781a 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -4876,6 +4876,13 @@ nd_ns {

    +
  • + A priority-70 logical flow is added that initiates CT state for + traffic that is configured to be SNATed on Distributed routers. + This allows the next table, lr_out_snat, to + effectively match on various CT states. +
  • +
  • A priority-50 logical flow is added that commits any untracked flows from the previous table lr_out_undnat for Gateway @@ -5068,6 +5075,18 @@ nd_ns {
  • +
  • + An additional flow is added for traffic that goes in opposite + direction (i.e. it enters a network with configured SNAT). Where the + flow above matched on ip4.src == A && outport + == GW, this flow matches on ip4.dst == + A && inport == GW. A CT state is + initiated for this traffic so that the following table, + lr_out_post_snat, can identify whether the traffic flow was + initiated from the internal or external network. + +
  • +
  • A priority-0 logical flow with match 1 has actions next;. @@ -5079,6 +5098,16 @@ nd_ns {

    Packets reaching this table are processed according to the flows below:

      +
    • + Traffic that goes directly into a network configured with SNAT on + Distributed routers, and was initiated from an external network (i.e. + it matches ct.new), is committed to the SNAT CT zone. + This ensures that replies returning from the SNATed network do not + have their source address translated. For details about match rules + and priority see section "Egress Table 3: SNAT on Distributed + Routers". +
    • +
    • A priority-0 logical flow that matches all packets not already handled (match 1) and action next;. diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index be006fb32..3c9d9309b 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -1107,7 +1107,8 @@ ovn_start # # DR is connected to S1 and CR is connected to S2 -check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \ + -- set chassis gw1 other_config:ct-commit-to-zone="true" check ovn-nbctl lr-add DR check ovn-nbctl lrp-add DR DR-S1 02:ac:10:01:00:01 172.16.1.1/24 @@ -1155,15 +1156,24 @@ AT_CAPTURE_FILE([crflows]) AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ip4.src == $allowed_range), action=(ct_snat;) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);) ]) +AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ip4.src == $allowed_range && ct.new), action=(ct_commit_to_zone(snat); next; ) +]) + AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) table=??(lr_out_snat ), priority=33 , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);) ]) +AT_CHECK([grep -e "lr_out_post_snat" crflows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) +]) # SNAT with DISALLOWED_IPs check ovn-nbctl lr-nat-del DR snat 50.0.0.11 @@ -1185,10 +1195,16 @@ AT_CAPTURE_FILE([crflows2]) AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_snat;) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);) table=??(lr_out_snat ), priority=163 , match=(ip && ip4.src == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ip4.dst == $disallowed_range), action=(next;) ]) +AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ct.new), action=(ct_commit_to_zone(snat); next; ) +]) + AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) @@ -1196,6 +1212,10 @@ AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=35 , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;) ]) +AT_CHECK([grep -e "lr_out_post_snat" crflows2 | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) +]) + # Stateful FIP with ALLOWED_IPs check ovn-nbctl lr-nat-del DR snat 50.0.0.11 check ovn-nbctl lr-nat-del CR snat 50.0.0.11 @@ -1217,12 +1237,20 @@ AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);) ]) +AT_CHECK([grep -e "lr_out_post_snat" drflows3 | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) +]) + AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) table=??(lr_out_snat ), priority=33 , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);) ]) +AT_CHECK([grep -e "lr_out_post_snat" crflows3 | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) +]) + # Stateful FIP with DISALLOWED_IPs ovn-nbctl lr-nat-del DR dnat_and_snat 172.16.1.2 ovn-nbctl lr-nat-del CR dnat_and_snat 172.16.1.2 @@ -1279,7 +1307,7 @@ AT_CHECK([grep -e "lr_out_snat" crflows5 | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=33 , match=(ip && ip4.src == 50.0.0.11 && ip4.dst == $allowed_range), action=(ip4.src=172.16.1.2; next;) ]) -# Stateful FIP with DISALLOWED_IPs +# Stateless FIP with DISALLOWED_IPs ovn-nbctl lr-nat-del DR dnat_and_snat 172.16.1.2 ovn-nbctl lr-nat-del CR dnat_and_snat 172.16.1.2 @@ -5521,7 +5549,8 @@ AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \ -- set chassis gw1 other_config:ct-no-masked-label="true" \ - -- set chassis gw1 other_config:ovn-ct-lb-related="true" + -- set chassis gw1 other_config:ovn-ct-lb-related="true" \ + -- set chassis gw1 other_config:ct-commit-to-zone="true" # Create a distributed gw port on lr0 check ovn-nbctl ls-add public @@ -5622,16 +5651,26 @@ AT_CHECK([grep "lr_out_undnat" lr0flows | ovn_strip_lflows], [0], [dnl AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_undnat ), priority=70 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat; ) + table=??(lr_out_post_undnat ), priority=70 , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat; ) ]) AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.dst == 10.0.0.0/24 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);) ]) +AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_snat ), priority=153 , match=(ip && ip4.dst == 10.0.0.0/24 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public") && ct.new), action=(ct_commit_to_zone(snat); next; ) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public") && ct.new), action=(ct_commit_to_zone(snat); next; ) +]) + # Associate load balancer to lr0 check ovn-nbctl lb-add lb0 172.168.0.100:8082 "10.0.0.50:82,10.0.0.60:82" @@ -5770,16 +5809,26 @@ AT_CHECK([grep "lr_out_undnat" lr0flows | ovn_strip_lflows], [0], [dnl AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_undnat ), priority=70 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat; ) + table=??(lr_out_post_undnat ), priority=70 , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat; ) ]) AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.dst == 10.0.0.0/24 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);) ]) +AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_snat ), priority=153 , match=(ip && ip4.dst == 10.0.0.0/24 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public") && ct.new), action=(ct_commit_to_zone(snat); next; ) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public") && ct.new), action=(ct_commit_to_zone(snat); next; ) +]) + # Make the logical router as Gateway router check ovn-nbctl clear logical_router_port lr0-public gateway_chassis check ovn-nbctl set logical_router lr0 options:chassis=gw1 @@ -7606,9 +7655,14 @@ ovn_start # Validate SNAT, DNAT and DNAT_AND_SNAT behavior with multiple # distributed gateway LRPs. -check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 -check ovn-sbctl chassis-add gw2 geneve 128.0.0.1 -check ovn-sbctl chassis-add gw3 geneve 129.0.0.1 +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \ + -- set chassis gw1 other_config:ct-commit-to-zone="true" + +check ovn-sbctl chassis-add gw2 geneve 128.0.0.1 \ + -- set chassis gw2 other_config:ct-commit-to-zone="true" + +check ovn-sbctl chassis-add gw3 geneve 129.0.0.1 \ + -- set chassis gw3 other_config:ct-commit-to-zone="true" check ovn-nbctl lr-add DR check ovn-nbctl lrp-add DR DR-S1 02:ac:10:01:00:01 172.16.1.1/24 @@ -7673,11 +7727,21 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | ovn_strip_lflows], [0], [dn ]) AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")), action=(ct_snat;) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")), action=(ct_snat;) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.dst == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")), action=(ct_snat;) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.10);) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport == "DR-S2" && is_chassis_resident("cr-DR-S2") && (!ct.trk || !ct.rpl)), action=(ct_snat(10.0.0.10);) table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.10 && outport == "DR-S3" && is_chassis_resident("cr-DR-S3") && (!ct.trk || !ct.rpl)), action=(ct_snat(192.168.0.10);) ]) +AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ct.new), action=(ct_commit_to_zone(snat); next; ) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2") && ct.new), action=(ct_commit_to_zone(snat); next; ) + table=??(lr_out_post_snat ), priority=161 , match=(ip && ip4.dst == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3") && ct.new), action=(ct_commit_to_zone(snat); next; ) +]) + check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10 AT_CHECK([ovn-sbctl dump-flows DR | grep -e lr_in_unsnat -e lr_out_snat | grep ct_snat | wc -l], [0], [0 ]) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index c699f7960..68adfa4e6 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -3574,6 +3574,39 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.1 | FORMAT_PING], \ 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) +# test_connectivity_from_ext takes parameters 'vm' and 'ip'. It tests +# icmp, udp and tcp connectivity from external network to the 'vm' on +# the specified 'ip'. +test_connectivity_from_ext() { + local vm=$1; shift + local ip=$1; shift + + # Start listening daemons for UDP and TCP connections + NETNS_DAEMONIZE($vm, [nc -l -u 1234], [nc-$vm-$ip-udp.pid]) + NETNS_DAEMONIZE($vm, [nc -l -k 1235], [nc-$vm-$ip-tcp.pid]) + + # Ensure that vm can be pinged on the specified IP + NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 $ip | FORMAT_PING], \ + [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + # Perform two consecutive UDP connections to the specified IP + NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z]) + NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z]) + + # Send data over TCP connection to the specified IP + NS_CHECK_EXEC([alice1], [echo "TCP test" | nc --send-only $ip 1235]) +} + +# Test access from external network to the internal IP of a VM that +# has also configured DNAT +test_connectivity_from_ext foo1 192.168.1.2 + +# Test access from external network to the internal IP of a VM that +# does not have DNAT +test_connectivity_from_ext bar1 192.168.2.2 + OVS_WAIT_UNTIL([ total_pkts=$(cat ext-net.tcpdump | wc -l) test "${total_pkts}" = "3" @@ -3740,6 +3773,39 @@ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmpv6,orig=(src=fd12::2,dst=fd20::2,id=,type=128,code=0),reply=(src=fd20::2,dst=fd20::1,id=,type=129,code=0),zone= ]) +# test_connectivity_from_ext takes parameters 'vm' and 'ip'. It tests +# icmp, udp and tcp connectivity from external network to the 'vm' on +# the specified 'ip'. +test_connectivity_from_ext() { + local vm=$1; shift + local ip=$1; shift + + # Start listening daemons for UDP and TCP connections + NETNS_DAEMONIZE($vm, [nc -l -u 1234], [nc-$vm-$ip-udp.pid]) + NETNS_DAEMONIZE($vm, [nc -l -k 1235], [nc-$vm-$ip-tcp.pid]) + + # Ensure that vm can be pinged on the specified IP + NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 $ip | FORMAT_PING], \ + [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + # Perform two consecutive UDP connections to the specified IP + NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z]) + NS_CHECK_EXEC([alice1], [nc -u $ip 1234 -p 2000 -z]) + + # Send data over TCP connection to the specified IP + NS_CHECK_EXEC([alice1], [echo "TCP test" | nc --send-only $ip 1235]) +} + +# Test access from external network to the internal IP of a VM that +# has also configured DNAT +test_connectivity_from_ext foo1 fd11::2 + +# Test access from external network to the internal IP of a VM that +# does not have DNAT +test_connectivity_from_ext bar1 fd12::2 + OVS_APP_EXIT_AND_WAIT([ovn-controller]) as ovn-sb @@ -3920,6 +3986,7 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp | FORMAT_CT(172.16.1.1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=,type=0,code=0),zone= +icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=,type=0,code=0),zone= icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=,type=0,code=0),zone= ]) @@ -4088,6 +4155,7 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], \ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl icmpv6,orig=(src=fd11::3,dst=fd20::4,id=,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=,type=129,code=0),zone= +icmpv6,orig=(src=fd20::1,dst=fd12::2,id=,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=,type=129,code=0),zone= icmpv6,orig=(src=fd20::1,dst=fd20::4,id=,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=,type=129,code=0),zone= ])