diff --git a/app/test/test_reorder.c b/app/test/test_reorder.c
index 7b5e590bac..a37dc28f65 100644
--- a/app/test/test_reorder.c
+++ b/app/test/test_reorder.c
@@ -337,6 +337,96 @@ test_reorder_drain(void)
 	return ret;
 }
 
+static void
+buffer_to_reorder_move(struct rte_mbuf **mbuf, struct rte_reorder_buffer *b)
+{
+	rte_reorder_insert(b, *mbuf);
+	*mbuf = NULL;
+}
+
+static int
+test_reorder_drain_up_to_seqn(void)
+{
+	struct rte_mempool *p = test_params->p;
+	struct rte_reorder_buffer *b = NULL;
+	const unsigned int num_bufs = 10;
+	const unsigned int size = 4;
+	unsigned int i, cnt;
+	int ret = 0;
+
+	struct rte_mbuf *bufs[num_bufs];
+	struct rte_mbuf *robufs[num_bufs];
+
+	/* initialize all robufs to NULL */
+	memset(robufs, 0, sizeof(robufs));
+
+	/* This would create a reorder buffer instance consisting of:
+	 * reorder_seq = 0
+	 * ready_buf: RB[size] = {NULL, NULL, NULL, NULL}
+	 * order_buf: OB[size] = {NULL, NULL, NULL, NULL}
+	 */
+	b = rte_reorder_create("test_drain_up_to_seqn", rte_socket_id(), size);
+	TEST_ASSERT_NOT_NULL(b, "Failed to create reorder buffer");
+
+	for (i = 0; i < num_bufs; i++) {
+		bufs[i] = rte_pktmbuf_alloc(p);
+		TEST_ASSERT_NOT_NULL(bufs[i], "Packet allocation failed\n");
+		*rte_reorder_seqn(bufs[i]) = i;
+	}
+
+	/* Insert packet with seqn 1 and 3:
+	 * RB[] = {NULL, NULL, NULL, NULL}
+	 * OB[] = {1, 2, 3, NULL}
+	 */
+	buffer_to_reorder_move(&bufs[1], b);
+	buffer_to_reorder_move(&bufs[2], b);
+	buffer_to_reorder_move(&bufs[3], b);
+	/* Draining 1, 2 */
+	cnt = rte_reorder_drain_up_to_seqn(b, robufs, num_bufs, 3);
+	if (cnt != 2) {
+		printf("%s:%d:%d: number of expected packets not drained\n",
+				__func__, __LINE__, cnt);
+		ret = -1;
+		goto exit;
+	}
+	for (i = 0; i < 2; i++)
+		rte_pktmbuf_free(robufs[i]);
+	memset(robufs, 0, sizeof(robufs));
+
+	/* Insert more packets
+	 * RB[] = {NULL, NULL, NULL, NULL}
+	 * OB[] = {3, 4, NULL, 6}
+	 */
+	buffer_to_reorder_move(&bufs[4], b);
+	buffer_to_reorder_move(&bufs[6], b);
+	/* Insert more packets to utilize Ready buffer
+	 * RB[] = {3, NULL, 5, 6}
+	 * OB[] = {NULL, NULL, 8, NULL}
+	 */
+	buffer_to_reorder_move(&bufs[8], b);
+
+	/* Drain 3 and 5 */
+	cnt = rte_reorder_drain_up_to_seqn(b, robufs, num_bufs, 6);
+	if (cnt != 2) {
+		printf("%s:%d:%d: number of expected packets not drained\n",
+				__func__, __LINE__, cnt);
+		ret = -1;
+		goto exit;
+	}
+	for (i = 0; i < 2; i++)
+		rte_pktmbuf_free(robufs[i]);
+	memset(robufs, 0, sizeof(robufs));
+
+	ret = 0;
+exit:
+	rte_reorder_free(b);
+	for (i = 0; i < num_bufs; i++) {
+		rte_pktmbuf_free(bufs[i]);
+		rte_pktmbuf_free(robufs[i]);
+	}
+	return ret;
+}
+
 static int
 test_setup(void)
 {
@@ -387,6 +477,7 @@ static struct unit_test_suite reorder_test_suite  = {
 		TEST_CASE(test_reorder_free),
 		TEST_CASE(test_reorder_insert),
 		TEST_CASE(test_reorder_drain),
+		TEST_CASE(test_reorder_drain_up_to_seqn),
 		TEST_CASES_END()
 	}
 };
diff --git a/lib/reorder/rte_reorder.c b/lib/reorder/rte_reorder.c
index b38e71f460..bd0e1f8793 100644
--- a/lib/reorder/rte_reorder.c
+++ b/lib/reorder/rte_reorder.c
@@ -407,3 +407,81 @@ rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs,
 
 	return drain_cnt;
 }
+
+/* Binary search seqn in ready buffer */
+static inline uint32_t
+ready_buffer_seqn_find(const struct cir_buffer *ready_buf, const uint32_t seqn)
+{
+	uint32_t mid, value, position, high;
+	uint32_t low = 0;
+
+	if (ready_buf->tail > ready_buf->head)
+		high = ready_buf->tail - ready_buf->head;
+	else
+		high = ready_buf->head - ready_buf->tail;
+
+	while (low <= high) {
+		mid = low + (high - low) / 2;
+		position = (ready_buf->tail + mid) & ready_buf->mask;
+		value = *rte_reorder_seqn(ready_buf->entries[position]);
+		if (seqn == value)
+			return mid;
+		else if (seqn > value)
+			low = mid + 1;
+		else
+			high = mid - 1;
+	}
+
+	return low;
+}
+
+unsigned int
+rte_reorder_drain_up_to_seqn(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs,
+		const unsigned int max_mbufs, const rte_reorder_seqn_t seqn)
+{
+	uint32_t i, position, offset;
+	unsigned int drain_cnt = 0;
+
+	struct cir_buffer *order_buf = &b->order_buf,
+			*ready_buf = &b->ready_buf;
+
+	/* Seqn in Ready buffer */
+	if (seqn < b->min_seqn) {
+		/* All sequence numbers are higher then given */
+		if ((ready_buf->tail == ready_buf->head) ||
+		    (*rte_reorder_seqn(ready_buf->entries[ready_buf->tail]) > seqn))
+			return 0;
+
+		offset = ready_buffer_seqn_find(ready_buf, seqn);
+
+		for (i = 0; (i < offset) && (drain_cnt < max_mbufs); i++) {
+			position = (ready_buf->tail + i) & ready_buf->mask;
+			mbufs[drain_cnt++] = ready_buf->entries[position];
+			ready_buf->entries[position] = NULL;
+		}
+		ready_buf->tail = (ready_buf->tail + i) & ready_buf->mask;
+
+		return drain_cnt;
+	}
+
+	/* Seqn in Order buffer, add all buffers from Ready buffer */
+	while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) {
+		mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail];
+		ready_buf->entries[ready_buf->tail] = NULL;
+		ready_buf->tail = (ready_buf->tail + 1) & ready_buf->mask;
+	}
+
+	/* Fetch buffers from Order buffer up to given sequence number (exclusive) */
+	offset = RTE_MIN(seqn - b->min_seqn, b->order_buf.size);
+	for (i = 0; (i < offset) && (drain_cnt < max_mbufs); i++) {
+		position = (order_buf->head + i) & order_buf->mask;
+		if (order_buf->entries[position] == NULL)
+			continue;
+		mbufs[drain_cnt++] = order_buf->entries[position];
+		order_buf->entries[position] = NULL;
+	}
+	b->min_seqn += i;
+	order_buf->head = (order_buf->head + i) & order_buf->mask;
+
+	return drain_cnt;
+}
diff --git a/lib/reorder/rte_reorder.h b/lib/reorder/rte_reorder.h
index 5abdb258e2..db740495be 100644
--- a/lib/reorder/rte_reorder.h
+++ b/lib/reorder/rte_reorder.h
@@ -167,6 +167,32 @@ unsigned int
 rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs,
 		unsigned max_mbufs);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Fetch set of reordered packets up to specified sequence number (exclusive)
+ *
+ * Returns a set of in-order packets from the reorder buffer structure.
+ * Gaps may be present since reorder buffer will try to fetch all possible packets up to given
+ * sequence number.
+ *
+ * @param b
+ *   Reorder buffer instance from which packets are to be drained.
+ * @param mbufs
+ *   Array of mbufs where reordered packets will be inserted from reorder buffer.
+ * @param max_mbufs
+ *   The number of elements in the mbufs array.
+ * @param seqn
+ *   Sequence number up to which buffer will be drained.
+ * @return
+ *   Number of mbuf pointers written to mbufs. 0 <= N < max_mbufs.
+ */
+__rte_experimental
+unsigned int
+rte_reorder_drain_up_to_seqn(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs,
+		unsigned int max_mbufs, rte_reorder_seqn_t seqn);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/reorder/version.map b/lib/reorder/version.map
index e114d17730..d322da03bd 100644
--- a/lib/reorder/version.map
+++ b/lib/reorder/version.map
@@ -16,4 +16,6 @@ EXPERIMENTAL {
 	global:
 
 	rte_reorder_seqn_dynfield_offset;
+	# added in 23.03
+	rte_reorder_drain_up_to_seqn;
 };
