This adds the basic primitives, initialization, and operations that
conntrack offload providers will need to implement in order to
offer a path to offloading.

Assisted-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Aaron Conole <[email protected]>
---
 lib/automake.mk         |   2 +
 lib/conntrack-private.h |  27 -----
 lib/conntrack.h         |  29 +++++
 lib/ct-offload.c        | 255 ++++++++++++++++++++++++++++++++++++++++
 lib/ct-offload.h        |  81 +++++++++++++
 5 files changed, 367 insertions(+), 27 deletions(-)
 create mode 100644 lib/ct-offload.c
 create mode 100644 lib/ct-offload.h

diff --git a/lib/automake.mk b/lib/automake.mk
index 8051362354..dc165f4153 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -57,6 +57,8 @@ lib_libopenvswitch_la_SOURCES = \
        lib/conntrack-other.c \
        lib/conntrack.c \
        lib/conntrack.h \
+       lib/ct-offload.c \
+       lib/ct-offload.h \
        lib/cooperative-multitasking.c \
        lib/cooperative-multitasking.h \
        lib/cooperative-multitasking-private.h \
diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h
index 6dd6c0ab1c..d011de3557 100644
--- a/lib/conntrack-private.h
+++ b/lib/conntrack-private.h
@@ -33,39 +33,12 @@
 #include "unaligned.h"
 #include "dp-packet.h"
 
-struct ct_endpoint {
-    union ct_addr addr;
-    union {
-        ovs_be16 port;
-        struct {
-            ovs_be16 icmp_id;
-            uint8_t icmp_type;
-            uint8_t icmp_code;
-        };
-    };
-};
-
-/* Verify that there is no padding in struct ct_endpoint, to facilitate
- * hashing in ct_endpoint_hash_add(). */
-BUILD_ASSERT_DECL(sizeof(struct ct_endpoint) == sizeof(union ct_addr) + 4);
-
 enum key_dir {
     CT_DIR_FWD = 0,
     CT_DIR_REV,
     CT_DIRS,
 };
 
-/* Changes to this structure need to be reflected in conn_key_hash()
- * and conn_key_cmp(). */
-struct conn_key {
-    struct ct_endpoint src;
-    struct ct_endpoint dst;
-
-    ovs_be16 dl_type;
-    uint16_t zone;
-    uint8_t nw_proto;
-};
-
 /* Verify that nw_proto stays uint8_t as it's used to index into l4_protos[] */
 BUILD_ASSERT_DECL(MEMBER_SIZEOF(struct conn_key, nw_proto) == sizeof(uint8_t));
 
diff --git a/lib/conntrack.h b/lib/conntrack.h
index 0f791e75b4..f09a0c3774 100644
--- a/lib/conntrack.h
+++ b/lib/conntrack.h
@@ -70,6 +70,35 @@ union ct_addr {
     struct in6_addr ipv6;
 };
 
+/* One endpoint (address + L4 port or ICMP id/type/code) of a connection. */
+struct ct_endpoint {
+    union ct_addr addr;
+    union {
+        ovs_be16 port;
+        struct {
+            ovs_be16 icmp_id;
+            uint8_t icmp_type;
+            uint8_t icmp_code;
+        };
+    };
+};
+
+/* Verify that there is no padding in struct ct_endpoint, to facilitate
+ * hashing. */
+BUILD_ASSERT_DECL(sizeof(struct ct_endpoint) == sizeof(union ct_addr) + 4);
+
+/* Connection 5-tuple: forward-direction match key.
+ * Changes to this structure must be reflected in conn_key_hash()
+ * and conn_key_cmp(). */
+struct conn_key {
+    struct ct_endpoint src;
+    struct ct_endpoint dst;
+
+    ovs_be16 dl_type;
+    uint16_t zone;
+    uint8_t nw_proto;
+};
+
 enum nat_action_e {
     NAT_ACTION_SRC = 1 << 0,
     NAT_ACTION_SRC_PORT = 1 << 1,
diff --git a/lib/ct-offload.c b/lib/ct-offload.c
new file mode 100644
index 0000000000..e92d8f9508
--- /dev/null
+++ b/lib/ct-offload.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2026 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 <config.h>
+
+#include "ct-offload.h"
+
+#include <errno.h>
+
+#include "ovs-thread.h"
+#include "util.h"
+
+#include "openvswitch/list.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ct_offload);
+
+/* Node in the registered-provider list. */
+struct ct_offload_class_node {
+    const struct ct_offload_class *class;
+    struct ovs_list               list_node;
+};
+
+/* Global list of registered CT offload classes and a rwlock to protect it.
+ * Write lock is held only during register/unregister; fast-path operations
+ * hold the read lock so multiple PMD threads can iterate concurrently. */
+static struct ovs_rwlock ct_offload_rwlock = OVS_RWLOCK_INITIALIZER;
+static struct ovs_list   ct_offload_classes
+    OVS_GUARDED_BY(ct_offload_rwlock)
+    = OVS_LIST_INITIALIZER(&ct_offload_classes);
+
+
+/* ct_offload_register() - register a CT offload provider class.
+ *
+ * Calls class->init() if provided.  Returns 0 on success or a positive
+ * errno value on failure.  Attempting to register the same class twice
+ * returns EEXIST. */
+int
+ct_offload_register(const struct ct_offload_class *class)
+{
+    struct ct_offload_class_node *node;
+    int error = 0;
+
+    ovs_assert(class);
+    ovs_assert(class->name);
+    ovs_assert(class->conn_add);
+    ovs_assert(class->conn_del);
+    ovs_assert(class->can_offload);
+
+    ovs_rwlock_wrlock(&ct_offload_rwlock);
+
+    /* Detect duplicate registrations. */
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        if (!strcmp(node->class->name, class->name)) {
+            VLOG_WARN("attempted to register duplicate ct offload class: %s",
+                      class->name);
+            error = EEXIST;
+            goto out;
+        }
+    }
+
+    error = class->init ? class->init() : 0;
+    if (error) {
+        VLOG_WARN("failed to initialize ct offload class %s: %s",
+                  class->name, ovs_strerror(error));
+        goto out;
+    }
+
+    node = xmalloc(sizeof *node);
+    node->class = class;
+    ovs_list_push_back(&ct_offload_classes, &node->list_node);
+    VLOG_DBG("registered ct offload class: %s", class->name);
+
+out:
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+    return error;
+}
+
+/* ct_offload_unregister() - unregister a previously registered class.
+ *
+ * Safe to call even if the class was never registered (no-op in that
+ * case). */
+void
+ct_offload_unregister(const struct ct_offload_class *class)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_assert(class);
+
+    ovs_rwlock_wrlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        if (node->class == class) {
+            ovs_list_remove(&node->list_node);
+            free(node);
+            VLOG_DBG("unregistered ct offload class: %s", class->name);
+            goto out;
+        }
+    }
+    VLOG_WARN("attempted to unregister unknown ct offload class: %s",
+              class->name);
+
+out:
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+}
+
+/* ct_offload_module_init() - register built-in CT offload providers.
+ *
+ * Must be called once before any connections are created. */
+void
+ct_offload_module_init(void)
+{
+    /* No built-in providers yet; third parties call ct_offload_register()
+     * directly from their own module-init routines. */
+}
+
+/* ct_offload_conn_add() - notify all eligible providers of a new connection.
+ *
+ * Iterates over registered providers and calls conn_add() on each one that
+ * reports can_offload() == true for this context.  Returns the first non-zero
+ * error encountered, but continues notifying remaining providers.  This allows
+ * the underlying hardware conntrack details across providers function. */
+int
+ct_offload_conn_add(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+    int ret = 0;
+
+    ovs_rwlock_rdlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (!class->can_offload(ctx)) {
+            continue;
+        }
+
+        int error = class->conn_add(ctx);
+
+        if (error && !ret) {
+            ret = error;
+        }
+    }
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+
+    return ret;
+}
+
+/* ct_offload_conn_del() - notify all providers that a connection was removed.
+ *
+ * Called unconditionally on all providers so that each can clean up any
+ * state it may have installed. */
+void
+ct_offload_conn_del(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_rwlock_rdlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        node->class->conn_del(ctx);
+    }
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+}
+
+void
+ct_offload_conn_established(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_rwlock_rdlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->conn_established) {
+            class->conn_established(ctx);
+        }
+    }
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+}
+
+/* ct_offload_conn_update() - query the hardware last-used timestamp.
+ *
+ * Iterates over providers and returns the first non-zero timestamp returned
+ * by a provider's conn_update() callback.  Returns 0 if no provider
+ * supplies a timestamp. */
+long long
+ct_offload_conn_update(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+    long long last_used = 0;
+
+    ovs_rwlock_rdlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->conn_update) {
+            long long ts = class->conn_update(ctx);
+
+            if (ts) {
+                last_used = ts;
+                break;
+            }
+        }
+    }
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+
+    return last_used;
+}
+
+/* ct_offload_can_offload() - returns true if any provider can offload ctx. */
+bool
+ct_offload_can_offload(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+    bool result = false;
+
+    ovs_rwlock_rdlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        if (node->class->can_offload(ctx)) {
+            result = true;
+            break;
+        }
+    }
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+
+    return result;
+}
+
+/* ct_offload_flush() - flush all offloaded connections from every provider. */
+void
+ct_offload_flush(void)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_rwlock_rdlock(&ct_offload_rwlock);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->flush) {
+            class->flush();
+        }
+    }
+    ovs_rwlock_unlock(&ct_offload_rwlock);
+}
diff --git a/lib/ct-offload.h b/lib/ct-offload.h
new file mode 100644
index 0000000000..f44bfee217
--- /dev/null
+++ b/lib/ct-offload.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2026 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 CT_OFFLOAD_H
+#define CT_OFFLOAD_H
+
+#include "conntrack.h"
+#include "conntrack-private.h"
+#include "openvswitch/types.h"
+
+struct netdev;
+
+/* Context for offload as part of the callbacks that all connection
+ * offload APIs receive.
+ */
+struct ct_offload_ctx {
+    struct conn *conn;              /* Connection object being offloaded. */
+    struct netdev *netdev_in;       /* Input netdev (may be NULL). */
+    odp_port_t input_port_id;       /* ODP port number. */
+    const struct conn_key *key;     /* Forward-direction 5-tuple. */
+};
+
+/* CT offload class describes a conntrack offload provider implementation. */
+struct ct_offload_class {
+    const char *name;
+
+    /* Optional initialization routine for the provider. */
+    int (*init)(void);
+
+    /* Per-connection operation callbacks get called for individual operations
+     * on the fast path or when batching is not in use.
+     * conn_add, conn_del, and can_offload are mandatory (non-NULL). */
+    int  (*conn_add)(const struct ct_offload_ctx *);
+    void (*conn_del)(const struct ct_offload_ctx *);
+
+    /* Populate the last-used timestamp for the connection.  Returns the
+     * last-used time in milliseconds since epoch, or 0 if the connection
+     * is not offloaded or the timestamp is not available.  The caller only
+     * updates the connection expiration if the returned value is newer than
+     * the current expiration. */
+    long long (*conn_update)(const struct ct_offload_ctx *);
+    /* Called exactly once when the first reply-direction packet is seen
+     * for an offloaded connection. */
+    void (*conn_established)(const struct ct_offload_ctx *);
+    /* Check whether this provider can offload a connection. */
+    bool (*can_offload)(const struct ct_offload_ctx *);
+    /* Flush all offloaded connections. */
+    void (*flush)(void);
+};
+
+/* Register/unregister a provider.  Must be called at module init, before
+ * any connections are created.  conn_add, conn_del, and can_offload must
+ * be non-NULL. */
+int  ct_offload_register(const struct ct_offload_class *);
+void ct_offload_unregister(const struct ct_offload_class *);
+
+/* Module initialization (register built-in providers). */
+void ct_offload_module_init(void);
+
+/* Per-connection offload API that dispatches to all registered providers. */
+int       ct_offload_conn_add(const struct ct_offload_ctx *);
+void      ct_offload_conn_del(const struct ct_offload_ctx *);
+long long ct_offload_conn_update(const struct ct_offload_ctx *);
+void      ct_offload_conn_established(const struct ct_offload_ctx *);
+bool      ct_offload_can_offload(const struct ct_offload_ctx *);
+void      ct_offload_flush(void);
+
+#endif /* CT_OFFLOAD_H */
-- 
2.51.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to