diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c
index 2e87e59f3..96af18e16 100644
--- a/lib/librte_ethdev/rte_flow.c
+++ b/lib/librte_ethdev/rte_flow.c
@@ -526,3 +526,108 @@ rte_flow_copy(struct rte_flow_desc *desc, size_t len,
 	}
 	return 0;
 }
+
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const struct rte_flow_expand_node nodes[],
+		    const int node_entry_index)
+{
+	const int elt_n = 8;
+	const struct rte_flow_item *item;
+	const struct rte_flow_expand_node *node = &nodes[node_entry_index];
+	const int *next_node;
+	const int *stack[elt_n];
+	int stack_n = 0;
+	struct rte_flow_item flow_items[elt_n];
+	unsigned int i;
+	size_t lsize;
+	size_t user_pattern_size = 0;
+	void *addr = NULL;
+
+	lsize = sizeof(*buf) +
+		/* Size for the list of patterns. */
+		sizeof(*buf->patterns) +
+		RTE_ALIGN_CEIL(elt_n * sizeof(*item), sizeof(void *)) +
+		/* Size for priorities. */
+		sizeof(*buf->priority) +
+		RTE_ALIGN_CEIL(elt_n * sizeof(uint32_t), sizeof(void *));
+	if (lsize <= size) {
+		buf->priority = (void *)(buf + 1);
+		buf->priority[0] = 0;
+		buf->patterns = (void *)&buf->priority[elt_n];
+		buf->patterns[0] = (void *)&buf->patterns[elt_n];
+		buf->entries = 0;
+		addr = buf->patterns[0];
+	}
+	for (item = pat; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		const struct rte_flow_expand_node *next = NULL;
+
+		for (i = 0; node->next && node->next[i]; ++i) {
+			next = &nodes[node->next[i]];
+			if (next->type == item->type)
+				break;
+		}
+		if (next)
+			node = next;
+		user_pattern_size += sizeof(*item);
+	}
+	user_pattern_size += sizeof(*item); /**< Handle END item. */
+	lsize += user_pattern_size;
+	/* Copy the user pattern in the first entry of the buffer. */
+	if (lsize <= size) {
+		rte_memcpy(addr, pat, user_pattern_size);
+		addr = (void *)(((uintptr_t)addr) + user_pattern_size);
+		buf->priority[buf->entries] = 0;
+		buf->entries = 1;
+	}
+	/* Start expanding. */
+	memset(flow_items, 0, sizeof(flow_items));
+	user_pattern_size -= sizeof(*item);
+	next_node = node->next;
+	stack[stack_n] = next_node;
+	node = next_node ? &nodes[*next_node] : NULL;
+	while (node) {
+		flow_items[stack_n].type = node->type;
+		if ((node->rss_types & types) == node->rss_types) {
+			/*
+			 * compute the number of items to copy from the
+			 * expansion and copy it.
+			 * When the stack_n is 0, there are 1 element in it,
+			 * plus the addition END item.
+			 */
+			int elt = stack_n + 2;
+
+			flow_items[stack_n + 1].type = RTE_FLOW_ITEM_TYPE_END;
+			lsize += elt * sizeof(*item) + user_pattern_size;
+			if (lsize <= size) {
+				size_t n = elt * sizeof(*item);
+
+				buf->priority[buf->entries] = stack_n + 1;
+				buf->patterns[buf->entries++] = addr;
+				rte_memcpy(addr, buf->patterns[0],
+					   user_pattern_size);
+				addr = (void *)(((uintptr_t)addr) +
+						user_pattern_size);
+				rte_memcpy(addr, flow_items, n);
+				addr = (void *) (((uintptr_t)addr) + n);
+			}
+		}
+		/* Go deeper. */
+		if (node->next) {
+			next_node = node->next;
+			stack[++stack_n] = next_node;
+		} else if (*(next_node + 1)) {
+			/* Follow up with the next possibility. */
+			++next_node;
+		} else {
+			/* Move to the next path. */
+			if (stack_n)
+				next_node = stack[--stack_n];
+			next_node++;
+			stack[stack_n] = next_node;
+		}
+		node = *next_node ? &nodes[*next_node] : NULL;
+	};
+	return lsize;
+}
diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h
index 1c90c600d..538b46e54 100644
--- a/lib/librte_ethdev/rte_flow_driver.h
+++ b/lib/librte_ethdev/rte_flow_driver.h
@@ -114,6 +114,54 @@ struct rte_flow_ops {
 const struct rte_flow_ops *
 rte_flow_ops_get(uint16_t port_id, struct rte_flow_error *error);
 
+/** Macro to create the expansion graph. */
+#define RTE_FLOW_EXPAND_ITEMS(...) \
+	(const int []){ \
+		__VA_ARGS__, 0, \
+	}
+
+/** structure to generate the expansion graph. */
+struct rte_flow_expand_node {
+	const int *const next; /**< list of items finalised by 0. */
+	const enum rte_flow_item_type type; /**< Item type to add. */
+	uint64_t rss_types; /**< RSS bit-field value. */
+};
+
+/**
+ * Expansion structure for RSS flows.
+ */
+struct rte_flow_expand_rss {
+	uint32_t entries; /**< Number of entries in the following arrays. */
+	struct rte_flow_item **patterns; /**< Expanded pattern array. */
+	uint32_t *priority; /**< Priority offset for each expansion. */
+};
+
+/**
+ * Expand RSS flows into several possible flows according to the RSS hash
+ * fields requested and the driver capabilities.
+ *
+ * @param[in,out] buf
+ *   Buffer to store the result expansion.
+ * @param[in] size
+ *   Size in octets of the buffer.
+ * @param[in] pat
+ *   User flow pattern.
+ * @param[in] types
+ *   RSS types expected (see ETH_RSS_*).
+ * @param[in] nodes.
+ *   Expansion graph of possibilities for the RSS.
+ * @param[in] node_entry_index
+ *   The index in the \p nodes array as start point.
+ *
+ * @return
+ *   The size in octets used to expand.
+ */
+int
+rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
+		    const struct rte_flow_item *pat, uint64_t types,
+		    const struct rte_flow_expand_node nodes[],
+		    const int node_entry_index);
+
 #ifdef __cplusplus
 }
 #endif
