On 30 Jul 2026 at 01:49:20 PM, Aaron Conole <[email protected]> wrote:
> 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); > +} > + a comment header for consistency? > +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 { same as above, maybe specifying its uniqueness is checked at registration time. > + 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
