Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2221014/?format=api
{ "id": 2221014, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2221014/?format=api", "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/20260408170613.587902-12-aconole@redhat.com/", "project": { "id": 47, "url": "http://patchwork.ozlabs.org/api/1.1/projects/47/?format=api", "name": "Open vSwitch", "link_name": "openvswitch", "list_id": "ovs-dev.openvswitch.org", "list_email": "ovs-dev@openvswitch.org", "web_url": "http://openvswitch.org/", "scm_url": "git@github.com:openvswitch/ovs.git", "webscm_url": "https://github.com/openvswitch/ovs" }, "msgid": "<20260408170613.587902-12-aconole@redhat.com>", "date": "2026-04-08T17:06:07", "name": "[ovs-dev,RFC,11/12] ct-offload-dummy: Introduce dummy ct offload.", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "6bed7e6977f782c61893d2187e7eee13b6845887", "submitter": { "id": 67184, "url": "http://patchwork.ozlabs.org/api/1.1/people/67184/?format=api", "name": "Aaron Conole", "email": "aconole@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/20260408170613.587902-12-aconole@redhat.com/mbox/", "series": [ { "id": 499163, "url": "http://patchwork.ozlabs.org/api/1.1/series/499163/?format=api", "web_url": "http://patchwork.ozlabs.org/project/openvswitch/list/?series=499163", "date": "2026-04-08T17:05:56", "name": "ct-offload: Introduce a conntrack offload infrastructure.", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/499163/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2221014/comments/", "check": "success", "checks": "http://patchwork.ozlabs.org/api/patches/2221014/checks/", "tags": {}, "headers": { "Return-Path": "<ovs-dev-bounces@openvswitch.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "dev@openvswitch.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "ovs-dev@lists.linuxfoundation.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=QxQ2m/xL;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=140.211.166.138; helo=smtp1.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)", "smtp1.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=QxQ2m/xL", "smtp3.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com", "smtp3.osuosl.org;\n dkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=QxQ2m/xL" ], "Received": [ "from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4frTyV3rVVz1xv0\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 09 Apr 2026 03:07:30 +1000 (AEST)", "from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id 683FC824DF;\n\tWed, 8 Apr 2026 17:07:28 +0000 (UTC)", "from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id 8dGeEhqDOKzP; Wed, 8 Apr 2026 17:07:23 +0000 (UTC)", "from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp1.osuosl.org (Postfix) with ESMTPS id 0FF7F82861;\n\tWed, 8 Apr 2026 17:07:20 +0000 (UTC)", "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 05F1BC054A;\n\tWed, 8 Apr 2026 17:07:20 +0000 (UTC)", "from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 04850C0902\n for <dev@openvswitch.org>; Wed, 8 Apr 2026 17:07:19 +0000 (UTC)", "from localhost (localhost [127.0.0.1])\n by smtp3.osuosl.org (Postfix) with ESMTP id ADD8F60F9D\n for <dev@openvswitch.org>; Wed, 8 Apr 2026 17:06:47 +0000 (UTC)", "from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id XOzoG9zeYnL9 for <dev@openvswitch.org>;\n Wed, 8 Apr 2026 17:06:46 +0000 (UTC)", "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by smtp3.osuosl.org (Postfix) with ESMTPS id D0C1060FA7\n for <dev@openvswitch.org>; Wed, 8 Apr 2026 17:06:45 +0000 (UTC)", "from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-561-HaIZrK0LPtGnRsKFo3ZWKg-1; Wed,\n 08 Apr 2026 13:06:41 -0400", "from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 69DA7195608A; Wed, 8 Apr 2026 17:06:40 +0000 (UTC)", "from RHTRH0061144.redhat.com (unknown [10.22.89.172])\n by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 9DB94300019F; Wed, 8 Apr 2026 17:06:38 +0000 (UTC)" ], "X-Virus-Scanned": [ "amavis at osuosl.org", "amavis at osuosl.org" ], "X-Comment": "SPF check N/A for local connections - client-ip=140.211.9.56;\n helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ", "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 smtp1.osuosl.org 0FF7F82861", "OpenDKIM Filter v2.11.0 smtp3.osuosl.org D0C1060FA7" ], "Received-SPF": "Pass (mailfrom) identity=mailfrom; client-ip=170.10.129.124;\n helo=us-smtp-delivery-124.mimecast.com; envelope-from=aconole@redhat.com;\n receiver=<UNKNOWN>", "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp3.osuosl.org D0C1060FA7", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1775668004;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=967KTdKt3UxhOnjgK316UNFyip0Iv+PUu9GdP6AK710=;\n b=QxQ2m/xL6jdoHiFLA+6tmhJcpxy4PgNXKL9V8d6cZmBBgLsdOuWAWybWuQ/eGZDPHocvVN\n syygfhNth5KpLtqdVZIFjLKftfXSBqhNwTb3VxM7dRoJFMEoFnFLoZdP4hDwMtCGmFoAwy\n Mnw6D1yCo6TRYVQ2ksuGdhIiJOcCHA0=", "X-MC-Unique": "HaIZrK0LPtGnRsKFo3ZWKg-1", "X-Mimecast-MFC-AGG-ID": "HaIZrK0LPtGnRsKFo3ZWKg_1775668000", "To": "dev@openvswitch.org", "Date": "Wed, 8 Apr 2026 13:06:07 -0400", "Message-ID": "<20260408170613.587902-12-aconole@redhat.com>", "In-Reply-To": "<20260408170613.587902-1-aconole@redhat.com>", "References": "<20260408170613.587902-1-aconole@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": "_gnBeOEOZqn-xnQvSG32QhfsIyNCOanT4muWwHG0ZEM_1775668000", "X-Mimecast-Originator": "redhat.com", "Subject": "[ovs-dev] [RFC 11/12] ct-offload-dummy: Introduce dummy ct offload.", "X-BeenThere": "ovs-dev@openvswitch.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "<ovs-dev.openvswitch.org>", "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>", "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>", "List-Post": "<mailto:ovs-dev@openvswitch.org>", "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>", "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>", "From": "Aaron Conole via dev <ovs-dev@openvswitch.org>", "Reply-To": "Aaron Conole <aconole@redhat.com>", "Cc": "Eli Britstein <elibr@nvidia.com>, Florian Westphal <fwestpha@redhat.com>,\n Flavio Leitner <fbl@redhat.com>", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "base64", "Errors-To": "ovs-dev-bounces@openvswitch.org", "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>" }, "content": "This includes a test netdev offload an a suite of unit tests to\nensure functionality. To facilitate the testing, some special\noffload APIs are added that force offload to true. It is expected\nthat these are not called unless within a testing environment.\n\nSigned-off-by: Aaron Conole <aconole@redhat.com>\n---\n lib/automake.mk | 2 +\n lib/ct-offload-dummy.c | 253 +++++++++++++++++++++++++++++++++\n lib/ct-offload-dummy.h | 64 +++++++++\n lib/ct-offload.c | 12 +-\n lib/ct-offload.h | 10 ++\n tests/dpif-netdev.at | 72 ++++++++++\n tests/library.at | 36 +++++\n tests/test-conntrack.c | 314 +++++++++++++++++++++++++++++++++++++++++\n 8 files changed, 762 insertions(+), 1 deletion(-)\n create mode 100644 lib/ct-offload-dummy.c\n create mode 100644 lib/ct-offload-dummy.h", "diff": "diff --git a/lib/automake.mk b/lib/automake.mk\nindex f11e3de27c..b9dc5118fa 100644\n--- a/lib/automake.mk\n+++ b/lib/automake.mk\n@@ -99,6 +99,8 @@ lib_libopenvswitch_la_SOURCES = \\\n \tlib/conntrack.h \\\n \tlib/ct-offload.c \\\n \tlib/ct-offload.h \\\n+\tlib/ct-offload-dummy.c \\\n+\tlib/ct-offload-dummy.h \\\n \tlib/cooperative-multitasking.c \\\n \tlib/cooperative-multitasking.h \\\n \tlib/cooperative-multitasking-private.h \\\ndiff --git a/lib/ct-offload-dummy.c b/lib/ct-offload-dummy.c\nnew file mode 100644\nindex 0000000000..c85f478e6c\n--- /dev/null\n+++ b/lib/ct-offload-dummy.c\n@@ -0,0 +1,253 @@\n+/*\n+ * Copyright (c) 2026 Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+\n+#include \"ct-offload-dummy.h\"\n+#include \"ct-offload.h\"\n+#include \"hash.h\"\n+#include \"openvswitch/list.h\"\n+#include \"openvswitch/vlog.h\"\n+#include \"ovs-thread.h\"\n+#include \"timeval.h\"\n+#include \"util.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(ct_offload_dummy);\n+\n+/* -----------------------------------------------------------------------\n+ * Per-connection tracking\n+ * ----------------------------------------------------------------------- */\n+\n+struct ct_dummy_entry {\n+ struct ovs_list list_node;\n+ const struct conn *conn;\n+ struct netdev *netdev_fwd_in;\n+ struct netdev *netdev_rev_in;\n+};\n+\n+/* ct-offload infrastructure guarantees that we get called under the offload\n+ * mutex, but the counters that we have are simple ints that can be erased\n+ * at any time from any thread, so we have this extra mutex for consistency.\n+ */\n+static struct ovs_mutex dummy_mutex = OVS_MUTEX_INITIALIZER;\n+\n+/* Since this is a testing interface, we can use the above mutex when checking\n+ * the fake list of offloaded connections for other properties (like the\n+ * bidireactionality, etc). A proper hardware offload implementation shouldn't\n+ * generally need this amount of critical sections.\n+ */\n+static struct ovs_list dummy_conns OVS_GUARDED_BY(dummy_mutex)\n+ = OVS_LIST_INITIALIZER(&dummy_conns);\n+\n+static unsigned int n_added = 0;\n+static unsigned int n_deleted = 0;\n+static unsigned int n_updated = 0;\n+static unsigned int n_established = 0;\n+\n+/* Lookup must be called with dummy_mutex held. */\n+static struct ct_dummy_entry *\n+dummy_find__(const struct conn *conn)\n+ OVS_REQUIRES(dummy_mutex)\n+{\n+ struct ct_dummy_entry *e;\n+\n+ LIST_FOR_EACH (e, list_node, &dummy_conns) {\n+ if (e->conn == conn) {\n+ return e;\n+ }\n+ }\n+ return NULL;\n+}\n+\n+static bool\n+dummy_can_offload(const struct ct_offload_ctx *ctx OVS_UNUSED)\n+{\n+ /* Always accept that we can offload in the dummy provider */\n+ return true;\n+}\n+\n+static int\n+dummy_conn_add(const struct ct_offload_ctx *ctx)\n+{\n+ struct ct_dummy_entry *e = xmalloc(sizeof *e);\n+\n+ e->conn = ctx->conn;\n+ e->netdev_fwd_in = ctx->netdev_in;\n+ e->netdev_rev_in = NULL;\n+\n+ ovs_mutex_lock(&dummy_mutex);\n+ ovs_list_push_back(&dummy_conns, &e->list_node);\n+ n_added++;\n+ ovs_mutex_unlock(&dummy_mutex);\n+\n+ VLOG_DBG(\"ct_offload_dummy: conn add: conn=%p, netdev_fwd_in=%p\",\n+ ctx->conn, ctx->netdev_in);\n+ return 0;\n+}\n+\n+static void\n+dummy_conn_del(const struct ct_offload_ctx *ctx)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ struct ct_dummy_entry *e = dummy_find__(ctx->conn);\n+\n+ if (e) {\n+ ovs_list_remove(&e->list_node);\n+ n_deleted++;\n+ free(e);\n+ }\n+ ovs_mutex_unlock(&dummy_mutex);\n+\n+ VLOG_DBG(\"ct_offload_dummy: conn del: conn=%p\", ctx->conn);\n+}\n+\n+static void\n+dummy_conn_established(const struct ct_offload_ctx *ctx)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ struct ct_dummy_entry *e = dummy_find__(ctx->conn);\n+\n+ if (e && !e->netdev_rev_in) {\n+ e->netdev_rev_in = ctx->netdev_in;\n+ n_established++;\n+ VLOG_DBG(\"ct_offload_dummy: conn established: conn=%p \"\n+ \"netdev_fwd_in=%p netdev_rev_in=%p\",\n+ ctx->conn, e->netdev_fwd_in, e->netdev_rev_in);\n+ }\n+ ovs_mutex_unlock(&dummy_mutex);\n+}\n+\n+static long long\n+dummy_conn_update(const struct ct_offload_ctx *ctx)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ struct ct_dummy_entry *e = dummy_find__(ctx->conn);\n+\n+ if (!e) {\n+ ovs_mutex_unlock(&dummy_mutex);\n+ return 0;\n+ }\n+\n+ n_updated++;\n+ ovs_mutex_unlock(&dummy_mutex);\n+\n+ VLOG_DBG(\"ct_offload_dummy: conn update: conn=%p\", ctx->conn);\n+ return time_msec();\n+}\n+\n+static void\n+dummy_flush(void)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ struct ct_dummy_entry *e;\n+ LIST_FOR_EACH_POP (e, list_node, &dummy_conns) {\n+ n_deleted++;\n+ free(e);\n+ }\n+ ovs_mutex_unlock(&dummy_mutex);\n+}\n+\n+/* -----------------------------------------------------------------------\n+ * Provider class\n+ * ----------------------------------------------------------------------- */\n+\n+const struct ct_offload_class ct_offload_dummy_class = {\n+ .name = \"dummy\",\n+ .init = NULL,\n+ .batch_submit = NULL,\n+ .conn_add = dummy_conn_add,\n+ .conn_del = dummy_conn_del,\n+ .conn_update = dummy_conn_update,\n+ .conn_established = dummy_conn_established,\n+ .can_offload = dummy_can_offload,\n+ .flush = dummy_flush,\n+};\n+\n+/* -----------------------------------------------------------------------\n+ * Public API\n+ * ----------------------------------------------------------------------- */\n+\n+void\n+ct_offload_dummy_register(void)\n+{\n+ ct_offload_dummy_reset_counters();\n+ ct_offload_register(&ct_offload_dummy_class);\n+}\n+\n+void\n+ct_offload_dummy_unregister(void)\n+{\n+ /* Flush any leftover entries before unregistering so we do not leak. */\n+ dummy_flush();\n+ ct_offload_unregister(&ct_offload_dummy_class);\n+}\n+\n+unsigned int\n+ct_offload_dummy_n_added(void)\n+{\n+ return n_added;\n+}\n+\n+unsigned int\n+ct_offload_dummy_n_deleted(void)\n+{\n+ return n_deleted;\n+}\n+\n+unsigned int\n+ct_offload_dummy_n_updated(void)\n+{\n+ return n_updated;\n+}\n+\n+unsigned int\n+ct_offload_dummy_n_established(void)\n+{\n+ return n_established;\n+}\n+\n+void\n+ct_offload_dummy_reset_counters(void)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ n_added = 0;\n+ n_deleted = 0;\n+ n_updated = 0;\n+ n_established = 0;\n+ ovs_mutex_unlock(&dummy_mutex);\n+}\n+\n+bool\n+ct_offload_dummy_contains(const struct conn *conn)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ bool found = dummy_find__(conn) != NULL;\n+ ovs_mutex_unlock(&dummy_mutex);\n+ return found;\n+}\n+\n+/* Returns true if the dummy provider has seen both the forward-direction\n+ * input netdev (recorded at conn_add) and the reply-direction input netdev\n+ * (recorded at conn_established) for 'conn'. */\n+bool\n+ct_offload_dummy_is_bidirectional(const struct conn *conn)\n+{\n+ ovs_mutex_lock(&dummy_mutex);\n+ struct ct_dummy_entry *e = dummy_find__(conn);\n+ bool bidi = e && e->netdev_fwd_in && e->netdev_rev_in;\n+ ovs_mutex_unlock(&dummy_mutex);\n+ return bidi;\n+}\ndiff --git a/lib/ct-offload-dummy.h b/lib/ct-offload-dummy.h\nnew file mode 100644\nindex 0000000000..1e7ecfdb04\n--- /dev/null\n+++ b/lib/ct-offload-dummy.h\n@@ -0,0 +1,64 @@\n+/*\n+ * Copyright (c) 2026 Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#ifndef CT_OFFLOAD_DUMMY_H\n+#define CT_OFFLOAD_DUMMY_H 1\n+\n+/* Dummy CT offload provider\n+ * =========================\n+ *\n+ * A software-only implementation of the ct_offload_class interface used for\n+ * unit testing. It records every conn_add/conn_del/conn_update call and\n+ * exposes inspection helpers so tests can verify that the correct hooks are\n+ * reached without requiring any hardware.\n+ *\n+ * Typical usage:\n+ *\n+ * ct_offload_dummy_register(); // activate the provider\n+ * conntrack_execute(...); // exercises conn_add\n+ * ovs_assert(ct_offload_dummy_n_added() == 1);\n+ * conntrack_flush(...); // exercises conn_del\n+ * ovs_assert(ct_offload_dummy_n_deleted() == 1);\n+ * ct_offload_dummy_unregister(); // tear down after test\n+ */\n+\n+#include <stdbool.h>\n+\n+struct conn;\n+\n+/* Register (or unregister) the dummy provider.\n+ *\n+ * ct_offload_dummy_register() also marks CT offload as \"enabled\" within the\n+ * dummy so that the guards in conntrack.c fire even without hardware offload\n+ * being configured globally. Call ct_offload_dummy_unregister() to undo. */\n+void ct_offload_dummy_register(void);\n+void ct_offload_dummy_unregister(void);\n+\n+/* Counters. Initialized to zero and can be reset. */\n+unsigned int ct_offload_dummy_n_added(void);\n+unsigned int ct_offload_dummy_n_deleted(void);\n+unsigned int ct_offload_dummy_n_updated(void);\n+unsigned int ct_offload_dummy_n_established(void);\n+\n+/* Reset all counters without changing registered state. */\n+void ct_offload_dummy_reset_counters(void);\n+\n+/* Returns true if 'conn' is currently tracked by the dummy (was added but\n+ * not yet deleted or flushed). */\n+bool ct_offload_dummy_contains(const struct conn *conn);\n+bool ct_offload_dummy_is_bidirectional(const struct conn *conn);\n+\n+#endif /* CT_OFFLOAD_DUMMY_H */\ndiff --git a/lib/ct-offload.c b/lib/ct-offload.c\nindex b777801ab9..707e71c03f 100644\n--- a/lib/ct-offload.c\n+++ b/lib/ct-offload.c\n@@ -57,6 +57,10 @@ static struct ovs_list ct_offload_classes\n * registered dpif offload class will be activated by ct_offload_module_init().\n */\n static const struct ct_offload_class *base_ct_offload_classes[] = {\n+ /* Dummy provider: activated whenever the \"dummy\" dpif offload class is\n+ * registered (hw-offload=true with a dummy datapath). Also used directly\n+ * by unit tests via ct_offload_dummy_register(). */\n+ &ct_offload_dummy_class,\n };\n \n \n@@ -166,6 +170,12 @@ ct_offload_module_init(void)\n }\n }\n \n+static bool ct_offload_forced = false;\n+void ct_offload_force_enable(bool value)\n+{\n+ ct_offload_forced = value;\n+}\n+\n /* ct_offload_enabled() - returns true when hardware offload is active.\n *\n * Delegates to dpif_offload_enabled() so CT offload shares the same global\n@@ -173,7 +183,7 @@ ct_offload_module_init(void)\n bool\n ct_offload_enabled(void)\n {\n- return dpif_offload_enabled();\n+ return dpif_offload_enabled() || ct_offload_forced;\n }\n \n /* ct_offload_set_global_cfg() - configure CT offload from OVSDB.\ndiff --git a/lib/ct-offload.h b/lib/ct-offload.h\nindex fe4ecd33b8..3836852703 100644\n--- a/lib/ct-offload.h\n+++ b/lib/ct-offload.h\n@@ -83,6 +83,12 @@ struct ct_offload_class {\n void (*flush)(void);\n };\n \n+/* Dummy (software-only) CT offload provider, always compiled in.\n+ * Registered automatically when the \"dummy\" dpif offload class is active\n+ * (e.g. hw-offload=true with a dummy datapath), and available directly for\n+ * unit tests via ct_offload_dummy_register() in ct-offload-dummy.h. */\n+extern const struct ct_offload_class ct_offload_dummy_class;\n+\n /* Register/unregister a provider. Must be called at module init, before\n * any connections are created. */\n int ct_offload_register(const struct ct_offload_class *);\n@@ -100,6 +106,10 @@ void ct_offload_set_global_cfg(const struct ovsrec_open_vswitch *);\n */\n bool ct_offload_enabled(void);\n \n+/* Used for testing. Forces an additional parameter for the offload enable\n+ * check. Set to 'true' to always enable the offloads. */\n+void ct_offload_force_enable(bool);\n+\n /* Per-connection offload API that dispatches to all registered providers. */\n int ct_offload_conn_add(const struct ct_offload_ctx *);\n void ct_offload_conn_del(const struct ct_offload_ctx *);\ndiff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at\nindex 2311979709..ae890f72fb 100644\n--- a/tests/dpif-netdev.at\n+++ b/tests/dpif-netdev.at\n@@ -50,6 +50,14 @@ filter_hw_packet_netdev_dummy () {\n | sort | uniq\n }\n \n+filter_ct_offload_dummy_conn_add () {\n+ grep 'ct_offload_dummy.*conn add:' | sed 's/.*|DBG|//' | sort | uniq\n+}\n+\n+filter_ct_offload_dummy_conn_del () {\n+ grep 'ct_offload_dummy.*conn del:' | sed 's/.*|DBG|//' | sort | uniq\n+}\n+\n filter_flow_dump () {\n grep 'flow_dump ' | sed '\n s/.*flow_dump //\n@@ -3709,3 +3717,67 @@ AT_CHECK_UNQUOTED([tail -n 1 p1.pcap.txt], [0], [${good_expected_v6}\n \n OVS_VSWITCHD_STOP\n AT_CLEANUP\n+\n+dnl Test that the CT offload dummy provider receives conn_add and conn_del\n+dnl callbacks when packets are processed through a conntrack commit flow on a\n+dnl dummy datapath with hw-offload enabled.\n+AT_SETUP([dpif-netdev - conntrack offload dummy])\n+AT_KEYWORDS([conntrack offload])\n+OVS_VSWITCHD_START(\n+ [add-port br0 p1 -- \\\n+ set interface p1 type=dummy ofport_request=1 \\\n+ options:pstream=punix:$OVS_RUNDIR/p1.sock \\\n+ options:ifindex=1100 -- \\\n+ add-port br0 p2 -- \\\n+ set interface p2 type=dummy ofport_request=2 \\\n+ options:pstream=punix:$OVS_RUNDIR/p2.sock \\\n+ options:ifindex=1101 -- \\\n+ set bridge br0 datapath-type=dummy \\\n+ other-config:datapath-id=1234 fail-mode=secure], [], [], [])\n+\n+dnl Enable debug logging for the dpif offload and CT offload dummy modules so\n+dnl the test can detect hook calls via log grep.\n+AT_CHECK([ovs-appctl vlog/set dpif_offload_dummy:file:dbg ct_offload_dummy:file:dbg])\n+\n+dnl Enable hardware offload — this registers the \"dummy\" dpif offload class\n+dnl and automatically activates the CT offload dummy provider.\n+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])\n+OVS_WAIT_UNTIL([grep \"Flow HW offload is enabled\" ovs-vswitchd.log])\n+\n+dnl Add a two-table conntrack flow:\n+dnl table 0: untracked packets → ct(commit) recirculate to table 1\n+dnl table 1: tracked packets → output on p2\n+AT_CHECK([ovs-ofctl add-flow br0 \\\n+ 'table=0,priority=100,in_port=p1,ip,ct_state=-trk,actions=ct(commit,table=1)'])\n+AT_CHECK([ovs-ofctl add-flow br0 \\\n+ 'table=1,priority=100,in_port=p1,ip,ct_state=+trk,actions=output:p2'])\n+\n+dnl Compose and inject a UDP packet on p1. The first packet misses the\n+dnl datapath, causes an upcall, executes ct(commit) to create a conntrack\n+dnl entry, and triggers the ct_offload_dummy conn_add callback.\n+flow_s=\"eth_src=50:54:00:00:00:01,eth_dst=50:54:00:00:00:02,udp,ip_src=10.0.0.1,ip_dst=10.0.0.2,ip_frag=no,udp_src=1000,udp_dst=2000\"\n+pkt=$(ovs-ofctl compose-packet --bare \"${flow_s}\")\n+AT_CHECK([ovs-appctl netdev-dummy/receive p1 \"${pkt}\"])\n+\n+dnl Wait for the CT offload dummy conn_add hook to fire.\n+OVS_WAIT_UNTIL([grep 'ct_offload_dummy.*conn add:' ovs-vswitchd.log])\n+\n+dnl Verify exactly one connection was added.\n+AT_CHECK([filter_ct_offload_dummy_conn_add < ovs-vswitchd.log | wc -l | tr -d ' '],\n+ [0], [1\n+])\n+\n+dnl Flush all conntrack entries — conn_clean is called for every tracked\n+dnl connection, which invokes ct_offload_conn_del on each registered provider.\n+AT_CHECK([ovs-appctl dpctl/flush-conntrack])\n+\n+dnl Wait for the CT offload dummy conn_del hook to fire.\n+OVS_WAIT_UNTIL([grep 'ct_offload_dummy.*conn del:' ovs-vswitchd.log])\n+\n+dnl Verify exactly one connection was deleted.\n+AT_CHECK([filter_ct_offload_dummy_conn_del < ovs-vswitchd.log | wc -l | tr -d ' '],\n+ [0], [1\n+])\n+\n+OVS_VSWITCHD_STOP\n+AT_CLEANUP\ndiff --git a/tests/library.at b/tests/library.at\nindex 6c5b55f045..2d5b02f75b 100644\n--- a/tests/library.at\n+++ b/tests/library.at\n@@ -325,3 +325,39 @@ AT_KEYWORDS([conntrack])\n AT_CHECK([ovstest test-conntrack private-destructor], [0], [.\n ])\n AT_CLEANUP\n+\n+AT_SETUP([conntrack offload dummy - conn add hook])\n+AT_KEYWORDS([conntrack offload])\n+AT_CHECK([ovstest test-conntrack offload-conn-add], [0], [.\n+])\n+AT_CLEANUP\n+\n+AT_SETUP([conntrack offload dummy - conn del hook])\n+AT_KEYWORDS([conntrack offload])\n+AT_CHECK([ovstest test-conntrack offload-conn-del], [0], [.\n+])\n+AT_CLEANUP\n+\n+AT_SETUP([conntrack offload dummy - conn update hook])\n+AT_KEYWORDS([conntrack offload])\n+AT_CHECK([ovstest test-conntrack offload-conn-update], [0], [.\n+])\n+AT_CLEANUP\n+\n+AT_SETUP([conntrack offload dummy - multiple connections])\n+AT_KEYWORDS([conntrack offload])\n+AT_CHECK([ovstest test-conntrack offload-multi-conn], [0], [.\n+])\n+AT_CLEANUP\n+\n+AT_SETUP([conntrack offload dummy - conn established hook (end-to-end)])\n+AT_KEYWORDS([conntrack offload])\n+AT_CHECK([ovstest test-conntrack offload-conn-established], [0], [.\n+])\n+AT_CLEANUP\n+\n+AT_SETUP([conntrack offload dummy - conn established fires exactly once (API)])\n+AT_KEYWORDS([conntrack offload])\n+AT_CHECK([ovstest test-conntrack offload-conn-established-api], [0], [.\n+])\n+AT_CLEANUP\ndiff --git a/tests/test-conntrack.c b/tests/test-conntrack.c\nindex 3c409b373b..86f1f36d3f 100644\n--- a/tests/test-conntrack.c\n+++ b/tests/test-conntrack.c\n@@ -17,6 +17,8 @@\n #include <config.h>\n #include \"conntrack.h\"\n #include \"conntrack-private.h\"\n+#include \"ct-offload.h\"\n+#include \"ct-offload-dummy.h\"\n \n #include \"dp-packet.h\"\n #include \"fatal-signal.h\"\n@@ -691,6 +693,304 @@ test_private_destructor(struct ovs_cmdl_context *ctx OVS_UNUSED)\n printf(\".\\n\");\n }\n \n+\f\n+/* ===========================================================================\n+ * CT offload dummy provider tests\n+ *\n+ * These tests exercise the ct_offload provider API directly without going\n+ * through conntrack_execute. The offload global-enable flag is deliberately\n+ * not set here: the unit tests own the provider list and call the API\n+ * functions directly. End-to-end enablement (hw-offload=true via DB config)\n+ * is covered by the dpif-netdev integration test.\n+ *\n+ * Each test must be run as a separate ovstest invocation so that the\n+ * process-global provider list starts empty.\n+ * ===========================================================================\n+ */\n+\n+/* The dummy only compares pointer addresses and never dereferences them, so a\n+ * small integer cast is sufficient. */\n+#define FAKE_CONN(n) ((struct conn *)(uintptr_t)(n))\n+#define FAKE_NETDEV(n) ((struct netdev *)(uintptr_t)(n))\n+\n+/* Test: offload-conn-add\n+ * ----------------------\n+ * Register the dummy provider, call ct_offload_conn_add() directly, and\n+ * verify that the conn_add hook was invoked and the connection is tracked.\n+ */\n+static void\n+test_offload_conn_add(struct ovs_cmdl_context *ctx OVS_UNUSED)\n+{\n+ ct_offload_force_enable(true);\n+ ct_offload_dummy_register();\n+\n+ struct conn *fake = FAKE_CONN(1);\n+ struct ct_offload_ctx offload_ctx = {\n+ .conn = fake, .netdev_in = NULL,\n+ };\n+ ct_offload_conn_add(&offload_ctx);\n+\n+ ovs_assert(ct_offload_dummy_n_added() == 1);\n+ ovs_assert(ct_offload_dummy_contains(fake));\n+\n+ ct_offload_dummy_unregister();\n+ ct_offload_force_enable(false);\n+ printf(\".\\n\");\n+}\n+\n+/* Test: offload-conn-del\n+ * ----------------------\n+ * Register the dummy, add then delete a connection via the API, and verify\n+ * that conn_del was called and the connection is no longer tracked.\n+ */\n+static void\n+test_offload_conn_del(struct ovs_cmdl_context *ctx OVS_UNUSED)\n+{\n+ ct_offload_force_enable(true);\n+ ct_offload_dummy_register();\n+\n+ struct conn *fake = FAKE_CONN(1);\n+ struct ct_offload_ctx offload_ctx = {\n+ .conn = fake, .netdev_in = NULL,\n+ };\n+\n+ ct_offload_conn_add(&offload_ctx);\n+ ovs_assert(ct_offload_dummy_n_added() == 1);\n+\n+ ct_offload_conn_del(&offload_ctx);\n+ ovs_assert(ct_offload_dummy_n_deleted() == 1);\n+ ovs_assert(!ct_offload_dummy_contains(fake));\n+\n+ ct_offload_dummy_unregister();\n+ ct_offload_force_enable(false);\n+ printf(\".\\n\");\n+}\n+\n+/* Test: offload-conn-update\n+ * -------------------------\n+ * Register the dummy, add a connection, call ct_offload_conn_update()\n+ * directly, and verify that a non-zero last-used timestamp is returned.\n+ */\n+static void\n+test_offload_conn_update(struct ovs_cmdl_context *ctx OVS_UNUSED)\n+{\n+ ct_offload_force_enable(true);\n+ ct_offload_dummy_register();\n+\n+ struct conn *fake = FAKE_CONN(1);\n+ struct ct_offload_ctx offload_ctx = {\n+ .conn = fake, .netdev_in = NULL,\n+ };\n+\n+ ct_offload_conn_add(&offload_ctx);\n+\n+ long long ts = ct_offload_conn_update(&offload_ctx);\n+ ovs_assert(ts != 0);\n+ ovs_assert(ct_offload_dummy_n_updated() == 1);\n+\n+ ct_offload_dummy_unregister();\n+ ct_offload_force_enable(false);\n+ printf(\".\\n\");\n+}\n+\n+/* Test: offload-multi-conn\n+ * ------------------------\n+ * Register the dummy, add N connections via the API, and verify that each\n+ * is tracked independently.\n+ */\n+#define OFFLOAD_MULTI_N 4\n+\n+static void\n+test_offload_multi_conn(struct ovs_cmdl_context *ctx OVS_UNUSED)\n+{\n+ ct_offload_force_enable(true);\n+ ct_offload_dummy_register();\n+\n+ for (unsigned i = 1; i <= OFFLOAD_MULTI_N; i++) {\n+ struct ct_offload_ctx offload_ctx = {\n+ .conn = FAKE_CONN(i), .netdev_in = NULL,\n+ };\n+ ct_offload_conn_add(&offload_ctx);\n+ }\n+\n+ ovs_assert(ct_offload_dummy_n_added() == OFFLOAD_MULTI_N);\n+ for (unsigned i = 1; i <= OFFLOAD_MULTI_N; i++) {\n+ ovs_assert(ct_offload_dummy_contains(FAKE_CONN(i)));\n+ }\n+\n+ ct_offload_dummy_unregister();\n+ ct_offload_force_enable(false);\n+ printf(\".\\n\");\n+}\n+\n+/* Test: offload-conn-established\n+ * --------------------------------\n+ * Drive a TCP three-way handshake through conntrack_execute() with the dummy\n+ * offload provider registered. Verifies three properties:\n+ *\n+ * (a) conn_add fires on the SYN (new connection created, forward netdev\n+ * recorded); conn_established does NOT fire yet.\n+ * (b) conn_established fires exactly once on the first ESTABLISHED reply\n+ * (SYN-ACK), recording the reply-direction netdev so that the dummy\n+ * entry is fully bidirectional.\n+ * (c) A subsequent reply packet (ACK) does NOT cause a second\n+ * conn_established call the \"exactly once\" guarantee holds.\n+ *\n+ * ct_offload_dummy_register() calls ct_offload_force_enable(true), which\n+ * makes ct_offload_enabled() return true so the guards in conntrack.c fire\n+ * without a real hardware offload backend.\n+ */\n+static void\n+test_offload_conn_established(struct ovs_cmdl_context *ctx OVS_UNUSED)\n+{\n+ /* Allocate the per-connection private slot before registering so that the\n+ * ADD/ESTABLISHED state transitions are tracked in conn->private[].\n+ * The simple FAKE_CONN tests skip this step because they do not exercise\n+ * the private-slot code path. */\n+ ct_offload_alloc_private_slot();\n+ ct_offload_force_enable(true);\n+ ct_offload_dummy_register();\n+\n+ struct conntrack *lct = conntrack_init();\n+ /* Disable TCP sequence-number checking so test packets with seq=0 are\n+ * accepted by the state machine. */\n+ conntrack_set_tcp_seq_chk(lct, false);\n+\n+ long long now = time_msec();\n+\n+ struct eth_addr eth_a = ETH_ADDR_C(00, 00, 00, 00, 00, 01);\n+ struct eth_addr eth_b = ETH_ADDR_C(00, 00, 00, 00, 00, 02);\n+ ovs_be32 ip_a = inet_addr(\"10.0.0.1\");\n+ ovs_be32 ip_b = inet_addr(\"10.0.0.2\");\n+ uint16_t sport = 1234;\n+ uint16_t dport = 80;\n+\n+ /* --- (a) SYN: forward direction, creates the connection entry. --- */\n+ struct dp_packet *syn = build_eth_ip_packet(NULL, eth_a, eth_b,\n+ ip_a, ip_b,\n+ IPPROTO_TCP, 0);\n+ build_tcp_packet(syn, sport, dport, TCP_SYN, NULL, 0);\n+\n+ struct dp_packet_batch syn_batch;\n+ dp_packet_batch_init_packet(&syn_batch, syn);\n+ conntrack_execute(lct, &syn_batch, htons(ETH_TYPE_IP), false, true, 0,\n+ NULL, NULL, NULL, NULL, now, 0, FAKE_NETDEV(1));\n+\n+ /* conn_add must have fired; conn_established must not have. */\n+ ovs_assert(ct_offload_dummy_n_added() == 1);\n+ ovs_assert(ct_offload_dummy_n_established() == 0);\n+\n+ /* The packet carries the conn pointer after commit. */\n+ struct conn *conn = syn->md.conn;\n+ ovs_assert(conn != NULL);\n+ ovs_assert(ct_offload_conn_is_offloaded(conn));\n+ ovs_assert(!ct_offload_conn_is_established(conn));\n+\n+ dp_packet_delete_batch(&syn_batch, true);\n+\n+ /* --- (b) SYN-ACK: reply direction, transitions to ESTABLISHED. --- */\n+ struct dp_packet *synack = build_eth_ip_packet(NULL, eth_b, eth_a,\n+ ip_b, ip_a,\n+ IPPROTO_TCP, 0);\n+ build_tcp_packet(synack, dport, sport, TCP_SYN | TCP_ACK, NULL, 0);\n+\n+ struct dp_packet_batch synack_batch;\n+ dp_packet_batch_init_packet(&synack_batch, synack);\n+ conntrack_execute(lct, &synack_batch, htons(ETH_TYPE_IP), false, true, 0,\n+ NULL, NULL, NULL, NULL, now, 0, FAKE_NETDEV(2));\n+\n+ /* conn_established fires exactly once on the first ESTABLISHED reply. */\n+ ovs_assert(ct_offload_dummy_n_established() == 1);\n+ ovs_assert(ct_offload_conn_is_established(conn));\n+ /* Both netdev pointers are now known: the entry is fully bidirectional. */\n+ ovs_assert(ct_offload_dummy_is_bidirectional(conn));\n+\n+ dp_packet_delete_batch(&synack_batch, true);\n+\n+ /* --- (c) ACK: another reply packet must NOT trigger conn_established\n+ * again. The private-slot guard enforces this. --- */\n+ struct dp_packet *ack = build_eth_ip_packet(NULL, eth_b, eth_a,\n+ ip_b, ip_a,\n+ IPPROTO_TCP, 0);\n+ build_tcp_packet(ack, dport, sport, TCP_ACK, NULL, 0);\n+\n+ struct dp_packet_batch ack_batch;\n+ dp_packet_batch_init_packet(&ack_batch, ack);\n+ conntrack_execute(lct, &ack_batch, htons(ETH_TYPE_IP), false, true, 0,\n+ NULL, NULL, NULL, NULL, now, 0, FAKE_NETDEV(2));\n+\n+ /* Counter must still be 1 - conn_established must not have fired again. */\n+ ovs_assert(ct_offload_dummy_n_established() == 1);\n+\n+ dp_packet_delete_batch(&ack_batch, true);\n+\n+ conntrack_destroy(lct);\n+ ct_offload_dummy_unregister();\n+ ct_offload_force_enable(false);\n+ printf(\".\\n\");\n+}\n+\n+/* Test: offload-conn-established-api\n+ * ------------------------------------\n+ * Exercise ct_offload_conn_established() directly (not through\n+ * conntrack_execute) to verify that the \"exactly once\" guarantee in the\n+ * dispatch layer holds independently of the conntrack state machine.\n+ *\n+ * Sequence:\n+ * 1. conn_add() - transitions the private slot to CT_OFFLOAD_STATE_ADDED.\n+ * 2. conn_established() - should dispatch to the provider exactly once and\n+ * advance the slot to CT_OFFLOAD_STATE_EST.\n+ * 3. A second conn_established() call with the same conn must be a no-op\n+ * (provider not called again, counter unchanged).\n+ */\n+static void\n+test_offload_conn_established_api(struct ovs_cmdl_context *ctx OVS_UNUSED)\n+{\n+ ct_offload_alloc_private_slot();\n+ ct_offload_force_enable(true);\n+ ct_offload_dummy_register();\n+\n+ /* We need a real conn with a live private-data slot, so spin up a minimal\n+ * conntrack instance and commit one UDP packet to get a conn. */\n+ struct conntrack *lct = conntrack_init();\n+ long long now = time_msec();\n+\n+ ovs_be16 dl_type;\n+ struct dp_packet *pkt = build_packet(1, 2, &dl_type);\n+ struct dp_packet_batch batch;\n+ dp_packet_batch_init_packet(&batch, pkt);\n+ conntrack_execute(lct, &batch, dl_type, false, true, 0,\n+ NULL, NULL, NULL, NULL, now, 0, FAKE_NETDEV(1));\n+ struct conn *conn = pkt->md.conn;\n+ ovs_assert(conn != NULL);\n+ dp_packet_delete_batch(&batch, true);\n+\n+ /* conn_add should have fired (via conntrack_execute). */\n+ ovs_assert(ct_offload_dummy_n_added() == 1);\n+ ovs_assert(ct_offload_dummy_n_established() == 0);\n+ ovs_assert(ct_offload_conn_is_offloaded(conn));\n+ ovs_assert(!ct_offload_conn_is_established(conn));\n+\n+ /* First call: must dispatch to the provider. */\n+ struct ct_offload_ctx ctx1 = {\n+ .conn = conn, .netdev_in = FAKE_NETDEV(2),\n+ };\n+ ct_offload_conn_established(&ctx1);\n+ ovs_assert(ct_offload_dummy_n_established() == 1);\n+ ovs_assert(ct_offload_conn_is_established(conn));\n+ ovs_assert(ct_offload_dummy_is_bidirectional(conn));\n+\n+ /* Second call with the same conn: must be a no-op. */\n+ ct_offload_conn_established(&ctx1);\n+\n+ ovs_assert(ct_offload_dummy_n_established() == 1); /* unchanged */\n+\n+ conntrack_destroy(lct);\n+ ct_offload_dummy_unregister();\n+ ct_offload_force_enable(false);\n+ printf(\".\\n\");\n+}\n+\n \f\n static const struct ovs_cmdl_command commands[] = {\n /* Connection tracker tests. */\n@@ -725,6 +1025,20 @@ static const struct ovs_cmdl_command commands[] = {\n test_private_id_exhaustion, OVS_RO},\n {\"private-destructor\", \"\", 0, 0,\n test_private_destructor, OVS_RO},\n+ /* CT offload dummy provider tests.\n+ * Each must be run as a separate ovstest invocation. */\n+ {\"offload-conn-add\", \"\", 0, 0,\n+ test_offload_conn_add, OVS_RO},\n+ {\"offload-conn-del\", \"\", 0, 0,\n+ test_offload_conn_del, OVS_RO},\n+ {\"offload-conn-update\", \"\", 0, 0,\n+ test_offload_conn_update, OVS_RO},\n+ {\"offload-multi-conn\", \"\", 0, 0,\n+ test_offload_multi_conn, OVS_RO},\n+ {\"offload-conn-established\", \"\", 0, 0,\n+ test_offload_conn_established, OVS_RO},\n+ {\"offload-conn-established-api\", \"\", 0, 0,\n+ test_offload_conn_established_api, OVS_RO},\n \n {NULL, NULL, 0, 0, NULL, OVS_RO},\n };\n", "prefixes": [ "ovs-dev", "RFC", "11/12" ] }