On 9/16/2026 1:18 PM, Anatoly Burakov wrote:
Use the new flow graph API and the common parsing framework to implement
flow parser for ntuple.
The 5tuple filter tracking infrastructure is moved completely inside the
new engine and is removed from the rest of the driver.
Signed-off-by: Anatoly Burakov<[email protected]>
---
<snip>
+static const struct flow_graph ixgbe_ntuple_graph = {
+ .nodes = (struct flow_graph_node[]) {
+ [IXGBE_NTUPLE_NODE_START] = {
+ .name = "START",
+ },
+ [IXGBE_NTUPLE_NODE_ETH] = {
+ .name = "ETH",
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .constraints = FLOW_GRAPH_NODE_EXPECT_EMPTY,
legacy code accepts patterns with non null spec/mask containing all
zeros. Same for VLAN.
+ },
+ [IXGBE_NTUPLE_NODE_VLAN] = {
+ .name = "VLAN",
+ .type = RTE_FLOW_ITEM_TYPE_VLAN,
+ .constraints = FLOW_GRAPH_NODE_EXPECT_EMPTY,
+ },
+ [IXGBE_NTUPLE_NODE_IPV4] = {
+ .name = "IPV4",
+ .type = RTE_FLOW_ITEM_TYPE_IPV4,
+ .constraints = FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
mask was optional
+ .validate = ixgbe_validate_ntuple_ipv4,
+ .process = ixgbe_process_ntuple_ipv4,
+ },
+ [IXGBE_NTUPLE_NODE_TCP] = {
+ .name = "TCP",
+ .type = RTE_FLOW_ITEM_TYPE_TCP,
+ .constraints = FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+ .validate = ixgbe_validate_ntuple_tcp,
+ .process = ixgbe_process_ntuple_tcp,
+ },
+ [IXGBE_NTUPLE_NODE_UDP] = {
+ .name = "UDP",
+ .type = RTE_FLOW_ITEM_TYPE_UDP,
+ .constraints = FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
spec and mask was optional
+ .validate = ixgbe_validate_ntuple_udp,
+ .process = ixgbe_process_ntuple_udp,
+ },
+ [IXGBE_NTUPLE_NODE_SCTP] = {
+ .name = "SCTP",
+ .type = RTE_FLOW_ITEM_TYPE_SCTP,
+ .constraints = FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+ .validate = ixgbe_validate_ntuple_sctp,
+ .process = ixgbe_process_ntuple_sctp,
+ },
+ [IXGBE_NTUPLE_NODE_END] = {
+ .name = "END",
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ },
<snip>
+ priority = RTE_MIN(IXGBE_MAX_N_TUPLE_PRIO - 1,
(uint16_t)attr->priority);
+ priority = IXGBE_MAX_N_TUPLE_PRIO - 1 - priority;
+ priority += IXGBE_MIN_N_TUPLE_PRIO;
+ ntuple_ctx->ntuple.priority = priority;
priority = RTE_MIN(IXGBE_MAX_N_TUPLE_PRIO,
RTE_MAX(IXGBE_MIN_N_TUPLE_PRIO, attr->priority))
otherwise priority 0 maps to 7
+
+ /* fixed value for ixgbe */
+ ntuple_ctx->ntuple.flags = RTE_5TUPLE_FLAGS;
+
+ return 0;
+}
+
+static enum ixgbe_5tuple_protocol
+convert_protocol_type(uint8_t protocol_value)
+{
+ if (protocol_value == IPPROTO_TCP)
+ return IXGBE_FILTER_PROTOCOL_TCP;
+ else if (protocol_value == IPPROTO_UDP)
+ return IXGBE_FILTER_PROTOCOL_UDP;
+ else if (protocol_value == IPPROTO_SCTP)
+ return IXGBE_FILTER_PROTOCOL_SCTP;
+ else
+ return IXGBE_FILTER_PROTOCOL_NONE;
+}
+
+static int
+ixgbe_flow_ntuple_ctx_to_flow(const struct ci_flow_engine_ctx *ctx,
+ struct ci_flow *flow,
+ struct rte_flow_error *error __rte_unused)
+{
+ const struct ixgbe_ntuple_ctx *ntuple_ctx = (const struct
ixgbe_ntuple_ctx *)ctx;
+ struct ixgbe_ntuple_flow *ntuple_flow = (struct ixgbe_ntuple_flow
*)flow;
+ const struct rte_eth_ntuple_filter *ntuple = &ntuple_ctx->ntuple;
+ struct ixgbe_5tuple_filter_info *key = &ntuple_flow->key;
+
+ /* mask shape (0 or all-ones) is already guaranteed by the graph */
+ memset(key, 0, sizeof(*key));
+
+ key->dst_ip_mask = ntuple->dst_ip_mask == 0;
+ key->dst_ip = ntuple->dst_ip;
I'm not sure we need to copy from the flow if the corresponding mask is
set. For example, in ixgbe_process_ntuple_ipv4(), dst_ip is copied from
an item independently of the mask. Thus, a user may install two or more
semantically identical rules with different values in masked-out fields
and consume all available filters.
+ key->src_ip_mask = ntuple->src_ip_mask == 0;
+ key->src_ip = ntuple->src_ip;
+ key->dst_port_mask = ntuple->dst_port_mask == 0;
+ key->dst_port = ntuple->dst_port;
+ key->src_port_mask = ntuple->src_port_mask == 0;
+ key->src_port = ntuple->src_port;
+ key->proto_mask = ntuple->proto_mask == 0;
+ key->proto = convert_protocol_type(ntuple->proto);
+ key->priority = (uint8_t)ntuple->priority;
+
+ ntuple_flow->queue = ntuple->queue;
+
+ return 0;
+}
<snip>
--
Regards,
Vladimir