Use the new flow graph API and common flow engine infrastructure to
implement flow parser for security filter. As a result, flow item checks
have become more stringent:

- Mask is now explicitly validated to not have unsupported items in it,
  when previously they were ignored
- Mask is also validated to mask src/dst addresses, as otherwise it is
  inconsistent with rte_flow API

Previously, security parser was a special case, now it is a first class
citizen.

Signed-off-by: Anatoly Burakov <[email protected]>
---
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |   2 -
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 112 +------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   2 +
 drivers/net/intel/ixgbe/ixgbe_flow_security.c | 297 ++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build           |   1 +
 5 files changed, 301 insertions(+), 113 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_security.c

diff --git a/drivers/net/intel/ixgbe/ixgbe_ethdev.h 
b/drivers/net/intel/ixgbe/ixgbe_ethdev.h
index eaeeb35dea..ccfe23c233 100644
--- a/drivers/net/intel/ixgbe/ixgbe_ethdev.h
+++ b/drivers/net/intel/ixgbe/ixgbe_ethdev.h
@@ -348,8 +348,6 @@ struct ixgbe_l2_tn_info {
 struct rte_flow {
        struct ci_flow flow;
        enum rte_filter_type filter_type;
-       /* security flows are not rte_filter_type */
-       bool is_security;
        void *rule;
 };
 
diff --git a/drivers/net/intel/ixgbe/ixgbe_flow.c 
b/drivers/net/intel/ixgbe/ixgbe_flow.c
index f509b47733..74ddc699fa 100644
--- a/drivers/net/intel/ixgbe/ixgbe_flow.c
+++ b/drivers/net/intel/ixgbe/ixgbe_flow.c
@@ -80,6 +80,7 @@ const struct ci_flow_engine_list ixgbe_flow_engine_list = {
                &ixgbe_syn_flow_engine,
                &ixgbe_l2_tunnel_flow_engine,
                &ixgbe_ntuple_flow_engine,
+               &ixgbe_security_flow_engine,
        },
 };
 
@@ -157,94 +158,6 @@ ixgbe_flow_actions_check(const struct ci_flow_actions 
*actions,
  * normally the packets should use network order.
  */
 
-static int
-ixgbe_parse_security_filter(struct rte_eth_dev *dev, const struct 
rte_flow_attr *attr,
-               const struct rte_flow_item pattern[], const struct 
rte_flow_action actions[],
-               struct rte_flow_error *error)
-{
-       struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-       const struct rte_flow_action_security *security;
-       struct rte_security_session *session;
-       const struct rte_flow_item *item;
-       struct ci_flow_actions parsed_actions;
-       struct ci_flow_actions_check_param ap_param = {
-               .allowed_types = (const enum rte_flow_action_type[]){
-                       /* only security is allowed here */
-                       RTE_FLOW_ACTION_TYPE_SECURITY,
-                       RTE_FLOW_ACTION_TYPE_END
-               },
-               .max_actions = 1,
-       };
-       const struct rte_flow_action *action;
-       struct ip_spec spec;
-       int ret;
-
-       if (hw->mac.type != ixgbe_mac_82599EB &&
-                       hw->mac.type != ixgbe_mac_X540 &&
-                       hw->mac.type != ixgbe_mac_X550 &&
-                       hw->mac.type != ixgbe_mac_X550EM_x &&
-                       hw->mac.type != ixgbe_mac_X550EM_a &&
-                       hw->mac.type != ixgbe_mac_E610)
-               return -ENOTSUP;
-
-       /* validate attributes */
-       ret = ci_flow_check_attr(attr, NULL, error);
-       if (ret)
-               return ret;
-
-       /* parse requested actions */
-       ret = ci_flow_check_actions(actions, &ap_param, &parsed_actions, error);
-       if (ret)
-               return ret;
-
-       action = parsed_actions.actions[0];
-       security = action->conf;
-
-       /* get the IP pattern*/
-       item = next_no_void_pattern(pattern, NULL);
-       while (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
-                       item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
-               if (item->last || item->type == RTE_FLOW_ITEM_TYPE_END) {
-                       rte_flow_error_set(error, EINVAL,
-                               RTE_FLOW_ERROR_TYPE_ITEM,
-                               item, "IP pattern missing.");
-                       return -rte_errno;
-               }
-               item = next_no_void_pattern(pattern, item);
-       }
-       if (item->spec == NULL) {
-               rte_flow_error_set(error, EINVAL,
-                               RTE_FLOW_ERROR_TYPE_ITEM_SPEC, item,
-                               "NULL IP pattern.");
-               return -rte_errno;
-       }
-       spec.is_ipv6 = item->type == RTE_FLOW_ITEM_TYPE_IPV6;
-       if (spec.is_ipv6) {
-               const struct rte_flow_item_ipv6 *ipv6 = item->spec;
-               spec.spec.ipv6 = *ipv6;
-       } else {
-               const struct rte_flow_item_ipv4 *ipv4 = item->spec;
-               spec.spec.ipv4 = *ipv4;
-       }
-
-       /*
-        * we get pointer to security session from security action, which is
-        * const. however, we do need to act on the session, so either we do
-        * some kind of pointer based lookup to get session pointer internally
-        * (which quickly gets unwieldy for lots of flows case), or we simply
-        * cast away constness. the latter path was chosen.
-        */
-       session = RTE_CAST_PTR(struct rte_security_session *, 
security->security_session);
-       ret = ixgbe_crypto_add_ingress_sa_from_flow(session, &spec);
-       if (ret) {
-               rte_flow_error_set(error, -ret,
-                               RTE_FLOW_ERROR_TYPE_ACTION, action,
-                               "Failed to add security session.");
-               return -rte_errno;
-       }
-       return 0;
-}
-
 /* search next no void pattern and skip fuzzy */
 static inline
 const struct rte_flow_item *next_no_fuzzy_pattern(
@@ -1837,15 +1750,6 @@ ixgbe_flow_create(struct rte_eth_dev *dev,
        TAILQ_INSERT_TAIL(&ixgbe_flow_list,
                                ixgbe_flow_mem_ptr, entries);
 
-       /**
-        *  Special case for flow action type RTE_FLOW_ACTION_TYPE_SECURITY
-        */
-       ret = ixgbe_parse_security_filter(dev, attr, pattern, actions, error);
-       if (!ret) {
-               flow->is_security = true;
-               return flow;
-       }
-
        memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule));
        ret = ixgbe_parse_fdir_filter(dev, attr, pattern,
                                actions, &fdir_rule, error);
@@ -1979,13 +1883,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev,
 
        /* fall back to legacy engines */
 
-       /**
-        *  Special case for flow action type RTE_FLOW_ACTION_TYPE_SECURITY
-        */
-       ret = ixgbe_parse_security_filter(dev, attr, pattern, actions, error);
-       if (!ret)
-               return 0;
-
        memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule));
        ret = ixgbe_parse_fdir_filter(dev, attr, pattern,
                                actions, &fdir_rule, error);
@@ -2025,12 +1922,6 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev,
 
        /* fall back to legacy engines */
 
-       /* Special case for SECURITY flows */
-       if (flow->is_security) {
-               ret = 0;
-               goto free;
-       }
-
        switch (filter_type) {
        case RTE_ETH_FILTER_FDIR:
                fdir_rule_ptr = (struct ixgbe_fdir_rule_ele *)pmd_flow->rule;
@@ -2071,7 +1962,6 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev,
                return ret;
        }
 
-free:
        TAILQ_FOREACH(ixgbe_flow_mem_ptr, &ixgbe_flow_list, entries) {
                if (ixgbe_flow_mem_ptr->flow == pmd_flow) {
                        TAILQ_REMOVE(&ixgbe_flow_list,
diff --git a/drivers/net/intel/ixgbe/ixgbe_flow.h 
b/drivers/net/intel/ixgbe/ixgbe_flow.h
index c1df74c0e7..daff23e227 100644
--- a/drivers/net/intel/ixgbe/ixgbe_flow.h
+++ b/drivers/net/intel/ixgbe/ixgbe_flow.h
@@ -13,6 +13,7 @@ enum ixgbe_flow_engine_type {
        IXGBE_FLOW_ENGINE_TYPE_SYN,
        IXGBE_FLOW_ENGINE_TYPE_L2_TUNNEL,
        IXGBE_FLOW_ENGINE_TYPE_NTUPLE,
+       IXGBE_FLOW_ENGINE_TYPE_SECURITY,
 };
 
 int
@@ -26,5 +27,6 @@ extern const struct ci_flow_engine 
ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
+extern const struct ci_flow_engine ixgbe_security_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
diff --git a/drivers/net/intel/ixgbe/ixgbe_flow_security.c 
b/drivers/net/intel/ixgbe/ixgbe_flow_security.c
new file mode 100644
index 0000000000..c22cc394e3
--- /dev/null
+++ b/drivers/net/intel/ixgbe/ixgbe_flow_security.c
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#include <rte_common.h>
+#include <rte_flow.h>
+#include <rte_flow_graph.h>
+#include <rte_ether.h>
+#include <rte_security_driver.h>
+
+#include "ixgbe_ethdev.h"
+#include "ixgbe_flow.h"
+#include "../common/flow_check.h"
+#include "../common/flow_util.h"
+#include "../common/flow_engine.h"
+
+struct ixgbe_security_filter {
+       struct ip_spec spec;
+       struct rte_security_session *session;
+};
+
+struct ixgbe_security_flow {
+       struct rte_flow flow;
+       struct ixgbe_security_filter security;
+};
+
+struct ixgbe_security_ctx {
+       struct ci_flow_engine_ctx base;
+       struct ixgbe_security_filter security;
+};
+
+/**
+ * Ntuple security filter graph implementation
+ * Pattern: START -> IPV4 | IPV6 -> END
+ */
+
+enum ixgbe_security_node_id {
+       IXGBE_SECURITY_NODE_START = RTE_FLOW_NODE_FIRST,
+       IXGBE_SECURITY_NODE_IPV4,
+       IXGBE_SECURITY_NODE_IPV6,
+       IXGBE_SECURITY_NODE_END,
+       IXGBE_SECURITY_NODE_MAX,
+};
+
+static int
+ixgbe_validate_security_ipv4(const void *ctx __rte_unused,
+               const struct rte_flow_item *item,
+               struct rte_flow_error *error)
+{
+       const struct rte_flow_item_ipv4 *ipv4_mask = item->mask;
+
+       /* only src/dst addresses are supported */
+       if (ipv4_mask->hdr.version_ihl ||
+           ipv4_mask->hdr.type_of_service ||
+           ipv4_mask->hdr.total_length ||
+           ipv4_mask->hdr.packet_id ||
+           ipv4_mask->hdr.fragment_offset ||
+           ipv4_mask->hdr.next_proto_id ||
+           ipv4_mask->hdr.time_to_live ||
+           ipv4_mask->hdr.hdr_checksum) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPv4 mask");
+       }
+
+       /* both src/dst addresses must be fully masked */
+       if (!CI_FIELD_IS_MASKED(&ipv4_mask->hdr.src_addr) ||
+           !CI_FIELD_IS_MASKED(&ipv4_mask->hdr.dst_addr)) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPv4 mask");
+       }
+
+       return 0;
+}
+
+static int
+ixgbe_process_security_ipv4(void *ctx, const struct rte_flow_item *item,
+               struct rte_flow_error *error __rte_unused)
+{
+       struct ixgbe_security_ctx *sec_ctx = (struct ixgbe_security_ctx *)ctx;
+       const struct rte_flow_item_ipv4 *ipv4_spec = item->spec;
+
+       /* copy entire spec */
+       sec_ctx->security.spec.spec.ipv4 = *ipv4_spec;
+       sec_ctx->security.spec.is_ipv6 = false;
+
+       return 0;
+}
+
+static int
+ixgbe_validate_security_ipv6(const void *ctx __rte_unused,
+                         const struct rte_flow_item *item,
+                         struct rte_flow_error *error)
+{
+       const struct rte_flow_item_ipv6 *ipv6_mask = item->mask;
+
+       /* only src/dst addresses are supported */
+       if (ipv6_mask->hdr.vtc_flow ||
+           ipv6_mask->hdr.payload_len ||
+           ipv6_mask->hdr.proto ||
+           ipv6_mask->hdr.hop_limits) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPv6 mask");
+       }
+       /* both src/dst addresses must be fully masked */
+       if (!CI_FIELD_IS_MASKED(&ipv6_mask->hdr.src_addr) ||
+           !CI_FIELD_IS_MASKED(&ipv6_mask->hdr.dst_addr)) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPv6 mask");
+       }
+
+       return 0;
+}
+
+static int
+ixgbe_process_security_ipv6(void *ctx, const struct rte_flow_item *item,
+               struct rte_flow_error *error __rte_unused)
+{
+       struct ixgbe_security_ctx *sec_ctx = (struct ixgbe_security_ctx *)ctx;
+       const struct rte_flow_item_ipv6 *ipv6_spec = item->spec;
+
+       /* copy entire spec */
+       sec_ctx->security.spec.spec.ipv6 = *ipv6_spec;
+       sec_ctx->security.spec.is_ipv6 = true;
+
+       return 0;
+}
+
+const struct rte_flow_graph ixgbe_security_graph = {
+       .nodes = (struct rte_flow_graph_node[]) {
+               [IXGBE_SECURITY_NODE_START] = {
+                       .name = "START",
+               },
+               [IXGBE_SECURITY_NODE_IPV4] = {
+                       .name = "IPV4",
+                       .type = RTE_FLOW_ITEM_TYPE_IPV4,
+                       .constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+                       .validate = ixgbe_validate_security_ipv4,
+                       .process = ixgbe_process_security_ipv4,
+               },
+               [IXGBE_SECURITY_NODE_IPV6] = {
+                       .name = "IPV6",
+                       .type = RTE_FLOW_ITEM_TYPE_IPV6,
+                       .constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+                       .validate = ixgbe_validate_security_ipv6,
+                       .process = ixgbe_process_security_ipv6,
+               },
+               [IXGBE_SECURITY_NODE_END] = {
+                       .name = "END",
+                       .type = RTE_FLOW_ITEM_TYPE_END,
+               },
+       },
+       .edges = (struct rte_flow_graph_edge[]) {
+               [IXGBE_SECURITY_NODE_START] = {
+                       .next = (const size_t[]) {
+                               IXGBE_SECURITY_NODE_IPV4,
+                               IXGBE_SECURITY_NODE_IPV6,
+                               RTE_FLOW_NODE_EDGE_END
+                       }
+               },
+               [IXGBE_SECURITY_NODE_IPV4] = {
+                       .next = (const size_t[]) {
+                               IXGBE_SECURITY_NODE_END,
+                               RTE_FLOW_NODE_EDGE_END
+                       }
+               },
+               [IXGBE_SECURITY_NODE_IPV6] = {
+                       .next = (const size_t[]) {
+                               IXGBE_SECURITY_NODE_END,
+                               RTE_FLOW_NODE_EDGE_END
+                       }
+               },
+       },
+};
+
+static int
+ixgbe_flow_security_ctx_parse(const struct rte_flow_action *actions,
+               const struct rte_flow_attr *attr,
+               struct ci_flow_engine_ctx *ctx,
+               struct rte_flow_error *error)
+{
+       struct ixgbe_security_ctx *sec_ctx = (struct ixgbe_security_ctx *)ctx;
+       struct ci_flow_actions parsed_actions;
+       struct ci_flow_actions_check_param ap_param = {
+               .allowed_types = (const enum rte_flow_action_type[]){
+                       /* only security is allowed here */
+                       RTE_FLOW_ACTION_TYPE_SECURITY,
+                       RTE_FLOW_ACTION_TYPE_END
+               },
+               .max_actions = 1,
+       };
+       const struct rte_flow_action_security *security;
+       struct rte_security_session *session;
+       const struct ixgbe_crypto_session *ic_session;
+       int ret;
+
+       /* validate attributes */
+       ret = ci_flow_check_attr(attr, NULL, error);
+       if (ret)
+               return ret;
+
+       /* parse requested actions */
+       ret = ci_flow_check_actions(actions, &ap_param, &parsed_actions, error);
+       if (ret)
+               return ret;
+
+       security = (const struct rte_flow_action_security 
*)parsed_actions.actions[0]->conf;
+
+       if (security->security_session == NULL) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ACTION, 
&parsed_actions.actions[0],
+                               "NULL security session");
+       }
+
+       /* cast away constness since we need to store the session pointer in 
the context */
+       session = RTE_CAST_PTR(struct rte_security_session *, 
security->security_session);
+
+       /* verify that the session is of a correct type */
+       ic_session = SECURITY_GET_SESS_PRIV(session);
+       if (ic_session->dev != ctx->dev) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ACTION, 
&parsed_actions.actions[0],
+                               "Security session was created for a different 
device");
+       }
+       if (ic_session->op != IXGBE_OP_AUTHENTICATED_DECRYPTION) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ACTION, 
&parsed_actions.actions[0],
+                               "Only authenticated decryption is supported");
+       }
+       sec_ctx->security.session = session;
+
+       return 0;
+}
+
+static int
+ixgbe_flow_security_ctx_to_flow(const struct ci_flow_engine_ctx *ctx,
+               struct ci_flow *flow,
+               struct rte_flow_error *error __rte_unused)
+{
+       const struct ixgbe_security_ctx *security_ctx = (const struct 
ixgbe_security_ctx *)ctx;
+       struct ixgbe_security_flow *security_flow = (struct ixgbe_security_flow 
*)flow;
+
+       security_flow->security = security_ctx->security;
+
+       return 0;
+}
+
+static int
+ixgbe_flow_security_flow_install(struct ci_flow *flow,
+               struct rte_flow_error *error)
+{
+       struct ixgbe_security_flow *security_flow = (struct ixgbe_security_flow 
*)flow;
+       struct ixgbe_security_filter *filter = &security_flow->security;
+       int ret;
+
+       ret =  ixgbe_crypto_add_ingress_sa_from_flow(filter->session, 
&filter->spec);
+       if (ret) {
+               return rte_flow_error_set(error, ret,
+                               RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+                               "Failed to add ingress SA from flow");;
+       }
+       return 0;
+}
+
+static bool
+ixgbe_flow_security_is_available(const struct ci_flow_engine *engine 
__rte_unused,
+               const struct rte_eth_dev *dev)
+{
+       struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+       return hw->mac.type == ixgbe_mac_82599EB ||
+                       hw->mac.type == ixgbe_mac_X540 ||
+                       hw->mac.type == ixgbe_mac_X550 ||
+                       hw->mac.type == ixgbe_mac_X550EM_x ||
+                       hw->mac.type == ixgbe_mac_X550EM_a ||
+                       hw->mac.type == ixgbe_mac_E610;
+}
+
+const struct ci_flow_engine_ops ixgbe_security_ops = {
+       .is_available = ixgbe_flow_security_is_available,
+       .ctx_parse = ixgbe_flow_security_ctx_parse,
+       .ctx_to_flow = ixgbe_flow_security_ctx_to_flow,
+       .flow_install = ixgbe_flow_security_flow_install,
+       /* uninstall is not handled in rte_flow */
+};
+
+const struct ci_flow_engine ixgbe_security_flow_engine = {
+       .name = "ixgbe_security",
+       .ctx_size = sizeof(struct ixgbe_security_ctx),
+       .flow_size = sizeof(struct ixgbe_security_flow),
+       .type = IXGBE_FLOW_ENGINE_TYPE_SECURITY,
+       .ops = &ixgbe_security_ops,
+       .graph = &ixgbe_security_graph,
+};
diff --git a/drivers/net/intel/ixgbe/meson.build 
b/drivers/net/intel/ixgbe/meson.build
index f3052daf4f..65ffe19939 100644
--- a/drivers/net/intel/ixgbe/meson.build
+++ b/drivers/net/intel/ixgbe/meson.build
@@ -15,6 +15,7 @@ sources += files(
         'ixgbe_flow_syn.c',
         'ixgbe_flow_l2tun.c',
         'ixgbe_flow_ntuple.c',
+        'ixgbe_flow_security.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
-- 
2.47.3

Reply via email to