[RFC PATCH 1/4] fib: add multi-VRF support

Vladimir Medvedkin vladimir.medvedkin at intel.com
Sun Mar 22 16:42:12 CET 2026


Add VRF (Virtual Routing and Forwarding) support to the IPv4
FIB library, allowing multiple independent routing tables
within a single FIB instance.

Introduce max_vrfs and vrf_default_nh fields in rte_fib_conf
to configure the number of VRFs and per-VRF default nexthops.

Add four new experimental APIs:
- rte_fib_vrf_add() and rte_fib_vrf_delete() to manage routes
  per VRF
- rte_fib_vrf_lookup_bulk() for multi-VRF bulk lookups
- rte_fib_vrf_get_rib() to retrieve a per-VRF RIB handle

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin at intel.com>
---
 lib/fib/dir24_8.c        | 241 ++++++++++++++++------
 lib/fib/dir24_8.h        | 255 ++++++++++++++++--------
 lib/fib/dir24_8_avx512.c | 420 +++++++++++++++++++++++++++++++--------
 lib/fib/dir24_8_avx512.h |  80 +++++++-
 lib/fib/rte_fib.c        | 158 ++++++++++++---
 lib/fib/rte_fib.h        |  94 ++++++++-
 6 files changed, 988 insertions(+), 260 deletions(-)

diff --git a/lib/fib/dir24_8.c b/lib/fib/dir24_8.c
index 489d2ef427..ad295c5f16 100644
--- a/lib/fib/dir24_8.c
+++ b/lib/fib/dir24_8.c
@@ -32,41 +32,80 @@
 #define ROUNDUP(x, y)	 RTE_ALIGN_CEIL(x, (1 << (32 - y)))
 
 static inline rte_fib_lookup_fn_t
-get_scalar_fn(enum rte_fib_dir24_8_nh_sz nh_sz, bool be_addr)
+get_scalar_fn(const struct dir24_8_tbl *dp, enum rte_fib_dir24_8_nh_sz nh_sz,
+	bool be_addr)
 {
+	bool single_vrf = dp->num_vrfs <= 1;
+
 	switch (nh_sz) {
 	case RTE_FIB_DIR24_8_1B:
-		return be_addr ? dir24_8_lookup_bulk_1b_be : dir24_8_lookup_bulk_1b;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_1b_be :
+				dir24_8_lookup_bulk_1b;
+		return be_addr ? dir24_8_lookup_bulk_vrf_1b_be :
+			dir24_8_lookup_bulk_vrf_1b;
 	case RTE_FIB_DIR24_8_2B:
-		return be_addr ? dir24_8_lookup_bulk_2b_be : dir24_8_lookup_bulk_2b;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_2b_be :
+				dir24_8_lookup_bulk_2b;
+		return be_addr ? dir24_8_lookup_bulk_vrf_2b_be :
+			dir24_8_lookup_bulk_vrf_2b;
 	case RTE_FIB_DIR24_8_4B:
-		return be_addr ? dir24_8_lookup_bulk_4b_be : dir24_8_lookup_bulk_4b;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_4b_be :
+				dir24_8_lookup_bulk_4b;
+		return be_addr ? dir24_8_lookup_bulk_vrf_4b_be :
+			dir24_8_lookup_bulk_vrf_4b;
 	case RTE_FIB_DIR24_8_8B:
-		return be_addr ? dir24_8_lookup_bulk_8b_be : dir24_8_lookup_bulk_8b;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_8b_be :
+				dir24_8_lookup_bulk_8b;
+		return be_addr ? dir24_8_lookup_bulk_vrf_8b_be :
+			dir24_8_lookup_bulk_vrf_8b;
 	default:
 		return NULL;
 	}
 }
 
 static inline rte_fib_lookup_fn_t
-get_scalar_fn_inlined(enum rte_fib_dir24_8_nh_sz nh_sz, bool be_addr)
+get_scalar_fn_inlined(const struct dir24_8_tbl *dp,
+	enum rte_fib_dir24_8_nh_sz nh_sz, bool be_addr)
 {
+	bool single_vrf = dp->num_vrfs <= 1;
+
 	switch (nh_sz) {
 	case RTE_FIB_DIR24_8_1B:
-		return be_addr ? dir24_8_lookup_bulk_0_be : dir24_8_lookup_bulk_0;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_0_be :
+				dir24_8_lookup_bulk_0;
+		return be_addr ? dir24_8_lookup_bulk_vrf_0_be :
+			dir24_8_lookup_bulk_vrf_0;
 	case RTE_FIB_DIR24_8_2B:
-		return be_addr ? dir24_8_lookup_bulk_1_be : dir24_8_lookup_bulk_1;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_1_be :
+				dir24_8_lookup_bulk_1;
+		return be_addr ? dir24_8_lookup_bulk_vrf_1_be :
+			dir24_8_lookup_bulk_vrf_1;
 	case RTE_FIB_DIR24_8_4B:
-		return be_addr ? dir24_8_lookup_bulk_2_be : dir24_8_lookup_bulk_2;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_2_be :
+				dir24_8_lookup_bulk_2;
+		return be_addr ? dir24_8_lookup_bulk_vrf_2_be :
+			dir24_8_lookup_bulk_vrf_2;
 	case RTE_FIB_DIR24_8_8B:
-		return be_addr ? dir24_8_lookup_bulk_3_be : dir24_8_lookup_bulk_3;
+		if (single_vrf)
+			return be_addr ? dir24_8_lookup_bulk_3_be :
+				dir24_8_lookup_bulk_3;
+		return be_addr ? dir24_8_lookup_bulk_vrf_3_be :
+			dir24_8_lookup_bulk_vrf_3;
 	default:
 		return NULL;
 	}
 }
 
 static inline rte_fib_lookup_fn_t
-get_vector_fn(enum rte_fib_dir24_8_nh_sz nh_sz, bool be_addr)
+get_vector_fn(const struct dir24_8_tbl *dp, enum rte_fib_dir24_8_nh_sz nh_sz,
+	bool be_addr)
 {
 #ifdef CC_AVX512_SUPPORT
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F) <= 0 ||
@@ -77,24 +116,63 @@ get_vector_fn(enum rte_fib_dir24_8_nh_sz nh_sz, bool be_addr)
 	if (be_addr && rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512BW) <= 0)
 		return NULL;
 
+	if (dp->num_vrfs <= 1) {
+		switch (nh_sz) {
+		case RTE_FIB_DIR24_8_1B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_1b_be :
+				rte_dir24_8_vec_lookup_bulk_1b;
+		case RTE_FIB_DIR24_8_2B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_2b_be :
+				rte_dir24_8_vec_lookup_bulk_2b;
+		case RTE_FIB_DIR24_8_4B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_4b_be :
+				rte_dir24_8_vec_lookup_bulk_4b;
+		case RTE_FIB_DIR24_8_8B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_8b_be :
+				rte_dir24_8_vec_lookup_bulk_8b;
+		default:
+			return NULL;
+		}
+	}
+
+	if (dp->num_vrfs >= 256) {
+		switch (nh_sz) {
+		case RTE_FIB_DIR24_8_1B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_1b_be_large :
+				rte_dir24_8_vec_lookup_bulk_vrf_1b_large;
+		case RTE_FIB_DIR24_8_2B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_2b_be_large :
+				rte_dir24_8_vec_lookup_bulk_vrf_2b_large;
+		case RTE_FIB_DIR24_8_4B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_4b_be_large :
+				rte_dir24_8_vec_lookup_bulk_vrf_4b_large;
+		case RTE_FIB_DIR24_8_8B:
+			return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_8b_be_large :
+				rte_dir24_8_vec_lookup_bulk_vrf_8b_large;
+		default:
+			return NULL;
+		}
+	}
+
 	switch (nh_sz) {
 	case RTE_FIB_DIR24_8_1B:
-		return be_addr ? rte_dir24_8_vec_lookup_bulk_1b_be :
-			rte_dir24_8_vec_lookup_bulk_1b;
+		return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_1b_be :
+			rte_dir24_8_vec_lookup_bulk_vrf_1b;
 	case RTE_FIB_DIR24_8_2B:
-		return be_addr ? rte_dir24_8_vec_lookup_bulk_2b_be :
-			rte_dir24_8_vec_lookup_bulk_2b;
+		return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_2b_be :
+			rte_dir24_8_vec_lookup_bulk_vrf_2b;
 	case RTE_FIB_DIR24_8_4B:
-		return be_addr ? rte_dir24_8_vec_lookup_bulk_4b_be :
-			rte_dir24_8_vec_lookup_bulk_4b;
+		return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_4b_be :
+			rte_dir24_8_vec_lookup_bulk_vrf_4b;
 	case RTE_FIB_DIR24_8_8B:
-		return be_addr ? rte_dir24_8_vec_lookup_bulk_8b_be :
-			rte_dir24_8_vec_lookup_bulk_8b;
+		return be_addr ? rte_dir24_8_vec_lookup_bulk_vrf_8b_be :
+			rte_dir24_8_vec_lookup_bulk_vrf_8b;
 	default:
 		return NULL;
 	}
 #elif defined(RTE_RISCV_FEATURE_V)
 	RTE_SET_USED(be_addr);
+	RTE_SET_USED(dp);
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_RISCV_ISA_V) <= 0)
 		return NULL;
 	switch (nh_sz) {
@@ -130,16 +208,17 @@ dir24_8_get_lookup_fn(void *p, enum rte_fib_lookup_type type, bool be_addr)
 
 	switch (type) {
 	case RTE_FIB_LOOKUP_DIR24_8_SCALAR_MACRO:
-		return get_scalar_fn(nh_sz, be_addr);
+		return get_scalar_fn(dp, nh_sz, be_addr);
 	case RTE_FIB_LOOKUP_DIR24_8_SCALAR_INLINE:
-		return get_scalar_fn_inlined(nh_sz, be_addr);
+		return get_scalar_fn_inlined(dp, nh_sz, be_addr);
 	case RTE_FIB_LOOKUP_DIR24_8_SCALAR_UNI:
-		return be_addr ? dir24_8_lookup_bulk_uni_be : dir24_8_lookup_bulk_uni;
+		return be_addr ? dir24_8_lookup_bulk_uni_be :
+			dir24_8_lookup_bulk_uni;
 	case RTE_FIB_LOOKUP_DIR24_8_VECTOR_AVX512:
-		return get_vector_fn(nh_sz, be_addr);
+		return get_vector_fn(dp, nh_sz, be_addr);
 	case RTE_FIB_LOOKUP_DEFAULT:
-		ret_fn = get_vector_fn(nh_sz, be_addr);
-		return ret_fn != NULL ? ret_fn : get_scalar_fn(nh_sz, be_addr);
+		ret_fn = get_vector_fn(dp, nh_sz, be_addr);
+		return ret_fn != NULL ? ret_fn : get_scalar_fn(dp, nh_sz, be_addr);
 	default:
 		return NULL;
 	}
@@ -246,15 +325,18 @@ __rcu_qsbr_free_resource(void *p, void *data, unsigned int n __rte_unused)
 }
 
 static void
-tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
+tbl8_recycle(struct dir24_8_tbl *dp, uint16_t vrf_id, uint32_t ip, uint64_t tbl8_idx)
 {
 	uint32_t i;
 	uint64_t nh;
+	uint64_t tbl24_idx;
 	uint8_t *ptr8;
 	uint16_t *ptr16;
 	uint32_t *ptr32;
 	uint64_t *ptr64;
 
+	tbl24_idx = get_tbl24_idx(vrf_id, ip);
+
 	switch (dp->nh_sz) {
 	case RTE_FIB_DIR24_8_1B:
 		ptr8 = &((uint8_t *)dp->tbl8)[tbl8_idx *
@@ -264,7 +346,7 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
 			if (nh != ptr8[i])
 				return;
 		}
-		((uint8_t *)dp->tbl24)[ip >> 8] =
+		((uint8_t *)dp->tbl24)[tbl24_idx] =
 			nh & ~DIR24_8_EXT_ENT;
 		break;
 	case RTE_FIB_DIR24_8_2B:
@@ -275,7 +357,7 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
 			if (nh != ptr16[i])
 				return;
 		}
-		((uint16_t *)dp->tbl24)[ip >> 8] =
+		((uint16_t *)dp->tbl24)[tbl24_idx] =
 			nh & ~DIR24_8_EXT_ENT;
 		break;
 	case RTE_FIB_DIR24_8_4B:
@@ -286,7 +368,7 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
 			if (nh != ptr32[i])
 				return;
 		}
-		((uint32_t *)dp->tbl24)[ip >> 8] =
+		((uint32_t *)dp->tbl24)[tbl24_idx] =
 			nh & ~DIR24_8_EXT_ENT;
 		break;
 	case RTE_FIB_DIR24_8_8B:
@@ -297,7 +379,7 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
 			if (nh != ptr64[i])
 				return;
 		}
-		((uint64_t *)dp->tbl24)[ip >> 8] =
+		((uint64_t *)dp->tbl24)[tbl24_idx] =
 			nh & ~DIR24_8_EXT_ENT;
 		break;
 	}
@@ -314,7 +396,7 @@ tbl8_recycle(struct dir24_8_tbl *dp, uint32_t ip, uint64_t tbl8_idx)
 }
 
 static int
-install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
+install_to_fib(struct dir24_8_tbl *dp, uint16_t vrf_id, uint32_t ledge, uint32_t redge,
 	uint64_t next_hop)
 {
 	uint64_t	tbl24_tmp;
@@ -328,7 +410,7 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
 
 	if (((ledge >> 8) != (redge >> 8)) || (len == 1 << 24)) {
 		if ((ROUNDUP(ledge, 24) - ledge) != 0) {
-			tbl24_tmp = get_tbl24(dp, ledge, dp->nh_sz);
+			tbl24_tmp = get_tbl24(dp, vrf_id, ledge, dp->nh_sz);
 			if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
 					DIR24_8_EXT_ENT) {
 				/**
@@ -346,7 +428,7 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
 				}
 				tbl8_free_idx(dp, tmp_tbl8_idx);
 				/*update dir24 entry with tbl8 index*/
-				write_to_fib(get_tbl24_p(dp, ledge,
+				write_to_fib(get_tbl24_p(dp, vrf_id, ledge,
 					dp->nh_sz), (tbl8_idx << 1)|
 					DIR24_8_EXT_ENT,
 					dp->nh_sz, 1);
@@ -360,19 +442,19 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
 			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
 				DIR24_8_EXT_ENT,
 				dp->nh_sz, ROUNDUP(ledge, 24) - ledge);
-			tbl8_recycle(dp, ledge, tbl8_idx);
+			tbl8_recycle(dp, vrf_id, ledge, tbl8_idx);
 		}
-		write_to_fib(get_tbl24_p(dp, ROUNDUP(ledge, 24), dp->nh_sz),
+		write_to_fib(get_tbl24_p(dp, vrf_id, ROUNDUP(ledge, 24), dp->nh_sz),
 			next_hop << 1, dp->nh_sz, len);
 		if (redge & ~DIR24_8_TBL24_MASK) {
-			tbl24_tmp = get_tbl24(dp, redge, dp->nh_sz);
+			tbl24_tmp = get_tbl24(dp, vrf_id, redge, dp->nh_sz);
 			if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
 					DIR24_8_EXT_ENT) {
 				tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
 				if (tbl8_idx < 0)
 					return -ENOSPC;
 				/*update dir24 entry with tbl8 index*/
-				write_to_fib(get_tbl24_p(dp, redge,
+				write_to_fib(get_tbl24_p(dp, vrf_id, redge,
 					dp->nh_sz), (tbl8_idx << 1)|
 					DIR24_8_EXT_ENT,
 					dp->nh_sz, 1);
@@ -385,17 +467,17 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
 			write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
 				DIR24_8_EXT_ENT,
 				dp->nh_sz, redge & ~DIR24_8_TBL24_MASK);
-			tbl8_recycle(dp, redge, tbl8_idx);
+			tbl8_recycle(dp, vrf_id, redge, tbl8_idx);
 		}
 	} else if ((redge - ledge) != 0) {
-		tbl24_tmp = get_tbl24(dp, ledge, dp->nh_sz);
+		tbl24_tmp = get_tbl24(dp, vrf_id, ledge, dp->nh_sz);
 		if ((tbl24_tmp & DIR24_8_EXT_ENT) !=
 				DIR24_8_EXT_ENT) {
 			tbl8_idx = tbl8_alloc(dp, tbl24_tmp);
 			if (tbl8_idx < 0)
 				return -ENOSPC;
 			/*update dir24 entry with tbl8 index*/
-			write_to_fib(get_tbl24_p(dp, ledge, dp->nh_sz),
+			write_to_fib(get_tbl24_p(dp, vrf_id, ledge, dp->nh_sz),
 				(tbl8_idx << 1)|
 				DIR24_8_EXT_ENT,
 				dp->nh_sz, 1);
@@ -409,13 +491,13 @@ install_to_fib(struct dir24_8_tbl *dp, uint32_t ledge, uint32_t redge,
 		write_to_fib((void *)tbl8_ptr, (next_hop << 1)|
 			DIR24_8_EXT_ENT,
 			dp->nh_sz, redge - ledge);
-		tbl8_recycle(dp, ledge, tbl8_idx);
+		tbl8_recycle(dp, vrf_id, ledge, tbl8_idx);
 	}
 	return 0;
 }
 
 static int
-modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint32_t ip,
+modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint16_t vrf_id, uint32_t ip,
 	uint8_t depth, uint64_t next_hop)
 {
 	struct rte_rib_node *tmp = NULL;
@@ -438,7 +520,7 @@ modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint32_t ip,
 					(uint32_t)(1ULL << (32 - tmp_depth));
 				continue;
 			}
-			ret = install_to_fib(dp, ledge, redge,
+			ret = install_to_fib(dp, vrf_id, ledge, redge,
 				next_hop);
 			if (ret != 0)
 				return ret;
@@ -454,7 +536,7 @@ modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint32_t ip,
 			redge = ip + (uint32_t)(1ULL << (32 - depth));
 			if (ledge == redge && ledge != 0)
 				break;
-			ret = install_to_fib(dp, ledge, redge,
+			ret = install_to_fib(dp, vrf_id, ledge, redge,
 				next_hop);
 			if (ret != 0)
 				return ret;
@@ -465,7 +547,7 @@ modify_fib(struct dir24_8_tbl *dp, struct rte_rib *rib, uint32_t ip,
 }
 
 int
-dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+dir24_8_modify(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip, uint8_t depth,
 	uint64_t next_hop, int op)
 {
 	struct dir24_8_tbl *dp;
@@ -480,8 +562,13 @@ dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
 		return -EINVAL;
 
 	dp = rte_fib_get_dp(fib);
-	rib = rte_fib_get_rib(fib);
-	RTE_ASSERT((dp != NULL) && (rib != NULL));
+	RTE_ASSERT(dp != NULL);
+
+	if (vrf_id >= dp->num_vrfs)
+		return -EINVAL;
+
+	rib = rte_fib_vrf_get_rib(fib, vrf_id);
+	RTE_ASSERT(rib != NULL);
 
 	if (next_hop > get_max_nh(dp->nh_sz))
 		return -EINVAL;
@@ -495,7 +582,7 @@ dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
 			rte_rib_get_nh(node, &node_nh);
 			if (node_nh == next_hop)
 				return 0;
-			ret = modify_fib(dp, rib, ip, depth, next_hop);
+			ret = modify_fib(dp, rib, vrf_id, ip, depth, next_hop);
 			if (ret == 0)
 				rte_rib_set_nh(node, next_hop);
 			return 0;
@@ -518,7 +605,7 @@ dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
 			if (par_nh == next_hop)
 				goto successfully_added;
 		}
-		ret = modify_fib(dp, rib, ip, depth, next_hop);
+		ret = modify_fib(dp, rib, vrf_id, ip, depth, next_hop);
 		if (ret != 0) {
 			rte_rib_remove(rib, ip, depth);
 			return ret;
@@ -536,9 +623,9 @@ dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
 			rte_rib_get_nh(parent, &par_nh);
 			rte_rib_get_nh(node, &node_nh);
 			if (par_nh != node_nh)
-				ret = modify_fib(dp, rib, ip, depth, par_nh);
+				ret = modify_fib(dp, rib, vrf_id, ip, depth, par_nh);
 		} else
-			ret = modify_fib(dp, rib, ip, depth, dp->def_nh);
+			ret = modify_fib(dp, rib, vrf_id, ip, depth, dp->def_nh[vrf_id]);
 		if (ret == 0) {
 			rte_rib_remove(rib, ip, depth);
 			if (depth > 24) {
@@ -562,7 +649,10 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf)
 	struct dir24_8_tbl *dp;
 	uint64_t	def_nh;
 	uint32_t	num_tbl8;
+	uint16_t	num_vrfs;
 	enum rte_fib_dir24_8_nh_sz	nh_sz;
+	uint64_t	tbl24_sz;
+	uint16_t	vrf;
 
 	if ((name == NULL) || (fib_conf == NULL) ||
 			(fib_conf->dir24_8.nh_sz < RTE_FIB_DIR24_8_1B) ||
@@ -580,19 +670,56 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf)
 	nh_sz = fib_conf->dir24_8.nh_sz;
 	num_tbl8 = RTE_ALIGN_CEIL(fib_conf->dir24_8.num_tbl8,
 			BITMAP_SLAB_BIT_SIZE);
+	num_vrfs = (fib_conf->max_vrfs == 0) ? 1 : fib_conf->max_vrfs;
+
+	/* Validate per-VRF default nexthops if provided */
+	if (fib_conf->vrf_default_nh != NULL) {
+		for (vrf = 0; vrf < num_vrfs; vrf++) {
+			if (fib_conf->vrf_default_nh[vrf] > get_max_nh(nh_sz)) {
+				rte_errno = EINVAL;
+				return NULL;
+			}
+		}
+	}
+
+	tbl24_sz = (uint64_t)num_vrfs * DIR24_8_TBL24_NUM_ENT * (1 << nh_sz);
 
 	snprintf(mem_name, sizeof(mem_name), "DP_%s", name);
 	dp = rte_zmalloc_socket(name, sizeof(struct dir24_8_tbl) +
-		DIR24_8_TBL24_NUM_ENT * (1 << nh_sz) + sizeof(uint32_t),
+		tbl24_sz + sizeof(uint32_t),
 		RTE_CACHE_LINE_SIZE, socket_id);
 	if (dp == NULL) {
 		rte_errno = ENOMEM;
 		return NULL;
 	}
 
-	/* Init table with default value */
-	write_to_fib(dp->tbl24, (def_nh << 1), nh_sz, 1 << 24);
+	dp->num_vrfs = num_vrfs;
+	dp->nh_sz = nh_sz;
+	dp->number_tbl8s = num_tbl8;
+
+	/* Allocate per-VRF default nexthop array */
+	snprintf(mem_name, sizeof(mem_name), "DEFNH_%p", dp);
+	dp->def_nh = rte_zmalloc_socket(mem_name, num_vrfs * sizeof(uint64_t),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (dp->def_nh == NULL) {
+		rte_errno = ENOMEM;
+		rte_free(dp);
+		return NULL;
+	}
+
+	/* Initialize all VRFs with default nexthop */
+	for (vrf = 0; vrf < num_vrfs; vrf++) {
+		uint64_t vrf_def_nh = (fib_conf->vrf_default_nh != NULL) ?
+			fib_conf->vrf_default_nh[vrf] : def_nh;
+		dp->def_nh[vrf] = vrf_def_nh;
 
+		/* Init TBL24 for this VRF with default value */
+		uint64_t vrf_offset = (uint64_t)vrf * DIR24_8_TBL24_NUM_ENT;
+		void *vrf_tbl24 = (void *)&((uint8_t *)dp->tbl24)[vrf_offset << nh_sz];
+		write_to_fib(vrf_tbl24, (vrf_def_nh << 1), nh_sz, 1 << 24);
+	}
+
+	/* Allocate shared TBL8 for all VRFs */
 	snprintf(mem_name, sizeof(mem_name), "TBL8_%p", dp);
 	uint64_t tbl8_sz = DIR24_8_TBL8_GRP_NUM_ENT * (1ULL << nh_sz) *
 			(num_tbl8 + 1);
@@ -600,12 +727,10 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf)
 			RTE_CACHE_LINE_SIZE, socket_id);
 	if (dp->tbl8 == NULL) {
 		rte_errno = ENOMEM;
+		rte_free(dp->def_nh);
 		rte_free(dp);
 		return NULL;
 	}
-	dp->def_nh = def_nh;
-	dp->nh_sz = nh_sz;
-	dp->number_tbl8s = num_tbl8;
 
 	snprintf(mem_name, sizeof(mem_name), "TBL8_idxes_%p", dp);
 	dp->tbl8_idxes = rte_zmalloc_socket(mem_name,
@@ -614,6 +739,7 @@ dir24_8_create(const char *name, int socket_id, struct rte_fib_conf *fib_conf)
 	if (dp->tbl8_idxes == NULL) {
 		rte_errno = ENOMEM;
 		rte_free(dp->tbl8);
+		rte_free(dp->def_nh);
 		rte_free(dp);
 		return NULL;
 	}
@@ -629,6 +755,7 @@ dir24_8_free(void *p)
 	rte_rcu_qsbr_dq_delete(dp->dq);
 	rte_free(dp->tbl8_idxes);
 	rte_free(dp->tbl8);
+	rte_free(dp->def_nh);
 	rte_free(dp);
 }
 
diff --git a/lib/fib/dir24_8.h b/lib/fib/dir24_8.h
index b343b5d686..37a73a3cc2 100644
--- a/lib/fib/dir24_8.h
+++ b/lib/fib/dir24_8.h
@@ -12,6 +12,7 @@
 #include <rte_byteorder.h>
 #include <rte_prefetch.h>
 #include <rte_branch_prediction.h>
+#include <rte_debug.h>
 #include <rte_rcu_qsbr.h>
 
 /**
@@ -32,24 +33,19 @@ struct dir24_8_tbl {
 	uint32_t	number_tbl8s;	/**< Total number of tbl8s */
 	uint32_t	rsvd_tbl8s;	/**< Number of reserved tbl8s */
 	uint32_t	cur_tbl8s;	/**< Current number of tbl8s */
+	uint16_t	num_vrfs;	/**< Number of VRFs */
 	enum rte_fib_dir24_8_nh_sz	nh_sz;	/**< Size of nexthop entry */
 	/* RCU config. */
 	enum rte_fib_qsbr_mode rcu_mode;/* Blocking, defer queue. */
 	struct rte_rcu_qsbr *v;		/* RCU QSBR variable. */
 	struct rte_rcu_qsbr_dq *dq;	/* RCU QSBR defer queue. */
-	uint64_t	def_nh;		/**< Default next hop */
+	uint64_t	*def_nh;	/**< Per-VRF default next hop array */
 	uint64_t	*tbl8;		/**< tbl8 table. */
 	uint64_t	*tbl8_idxes;	/**< bitmap containing free tbl8 idxes*/
 	/* tbl24 table. */
 	alignas(RTE_CACHE_LINE_SIZE) uint64_t	tbl24[];
 };
 
-static inline void *
-get_tbl24_p(struct dir24_8_tbl *dp, uint32_t ip, uint8_t nh_sz)
-{
-	return (void *)&((uint8_t *)dp->tbl24)[(ip &
-		DIR24_8_TBL24_MASK) >> (8 - nh_sz)];
-}
 
 static inline  uint8_t
 bits_in_nh(uint8_t nh_sz)
@@ -63,14 +59,21 @@ get_max_nh(uint8_t nh_sz)
 	return ((1ULL << (bits_in_nh(nh_sz) - 1)) - 1);
 }
 
-static  inline uint32_t
-get_tbl24_idx(uint32_t ip)
+static  inline uint64_t
+get_tbl24_idx(uint16_t vrf_id, uint32_t ip)
+{
+	return ((uint64_t)vrf_id << 24) + (ip >> 8);
+}
+
+static inline void *
+get_tbl24_p(struct dir24_8_tbl *dp, uint16_t vrf_id, uint32_t ip, uint8_t nh_sz)
 {
-	return ip >> 8;
+	uint64_t idx = get_tbl24_idx(vrf_id, ip);
+	return (void *)&((uint8_t *)dp->tbl24)[idx << nh_sz];
 }
 
-static  inline uint32_t
-get_tbl8_idx(uint32_t res, uint32_t ip)
+static  inline uint64_t
+get_tbl8_idx(uint64_t res, uint32_t ip)
 {
 	return (res >> 1) * DIR24_8_TBL8_GRP_NUM_ENT + (uint8_t)ip;
 }
@@ -87,17 +90,18 @@ get_psd_idx(uint32_t val, uint8_t nh_sz)
 	return val & ((1 << (3 - nh_sz)) - 1);
 }
 
-static inline uint32_t
-get_tbl_idx(uint32_t val, uint8_t nh_sz)
+static inline uint64_t
+get_tbl_idx(uint64_t val, uint8_t nh_sz)
 {
 	return val >> (3 - nh_sz);
 }
 
 static inline uint64_t
-get_tbl24(struct dir24_8_tbl *dp, uint32_t ip, uint8_t nh_sz)
+get_tbl24(struct dir24_8_tbl *dp, uint16_t vrf_id, uint32_t ip, uint8_t nh_sz)
 {
-	return ((dp->tbl24[get_tbl_idx(get_tbl24_idx(ip), nh_sz)] >>
-		(get_psd_idx(get_tbl24_idx(ip), nh_sz) *
+	uint64_t idx = get_tbl24_idx(vrf_id, ip);
+	return ((dp->tbl24[get_tbl_idx(idx, nh_sz)] >>
+		(get_psd_idx(idx, nh_sz) *
 		bits_in_nh(nh_sz))) & lookup_msk(nh_sz));
 }
 
@@ -115,62 +119,92 @@ is_entry_extended(uint64_t ent)
 	return (ent & DIR24_8_EXT_ENT) == DIR24_8_EXT_ENT;
 }
 
-#define LOOKUP_FUNC(suffix, type, bulk_prefetch, nh_sz)			\
-static inline void dir24_8_lookup_bulk_##suffix(void *p, const uint32_t *ips, \
-	uint64_t *next_hops, const unsigned int n)			\
-{									\
-	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;		\
-	uint64_t tmp;							\
-	uint32_t i;							\
-	uint32_t prefetch_offset =					\
-		RTE_MIN((unsigned int)bulk_prefetch, n);		\
-									\
-	for (i = 0; i < prefetch_offset; i++)				\
-		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));		\
-	for (i = 0; i < (n - prefetch_offset); i++) {			\
-		rte_prefetch0(get_tbl24_p(dp,				\
-			ips[i + prefetch_offset], nh_sz));		\
-		tmp = ((type *)dp->tbl24)[ips[i] >> 8];			\
-		if (unlikely(is_entry_extended(tmp)))			\
-			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +	\
-				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)]; \
-		next_hops[i] = tmp >> 1;				\
-	}								\
-	for (; i < n; i++) {						\
-		tmp = ((type *)dp->tbl24)[ips[i] >> 8];			\
-		if (unlikely(is_entry_extended(tmp)))			\
-			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +	\
-				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)]; \
-		next_hops[i] = tmp >> 1;				\
-	}								\
-}									\
-
-LOOKUP_FUNC(1b, uint8_t, 5, 0)
-LOOKUP_FUNC(2b, uint16_t, 6, 1)
-LOOKUP_FUNC(4b, uint32_t, 15, 2)
-LOOKUP_FUNC(8b, uint64_t, 12, 3)
+
+#define LOOKUP_FUNC(suffix, type, bulk_prefetch, nh_sz, is_vrf)			\
+static inline void dir24_8_lookup_bulk_##suffix(void *p,			\
+	const uint16_t *vrf_ids, const uint32_t *ips,				\
+	uint64_t *next_hops, const unsigned int n)				\
+{										\
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;			\
+	uint64_t tmp;								\
+	uint32_t i;								\
+	uint32_t prefetch_offset = RTE_MIN((unsigned int)bulk_prefetch, n);	\
+										\
+	if (!is_vrf)								\
+		RTE_SET_USED(vrf_ids);						\
+										\
+	for (i = 0; i < prefetch_offset; i++) {					\
+		uint16_t vid = is_vrf ? vrf_ids[i] : 0;				\
+		RTE_ASSERT(vid < dp->num_vrfs);					\
+		rte_prefetch0(get_tbl24_p(dp, vid, ips[i], nh_sz));		\
+	}									\
+	for (i = 0; i < (n - prefetch_offset); i++) {				\
+		uint16_t vid = is_vrf ? vrf_ids[i] : 0;				\
+		uint16_t vid_next = is_vrf ? vrf_ids[i + prefetch_offset] : 0;	\
+		RTE_ASSERT(vid < dp->num_vrfs);					\
+		RTE_ASSERT(vid_next < dp->num_vrfs);				\
+		rte_prefetch0(get_tbl24_p(dp, vid_next,				\
+			ips[i + prefetch_offset], nh_sz));			\
+		tmp = ((type *)dp->tbl24)[get_tbl24_idx(vid, ips[i])];		\
+		if (unlikely(is_entry_extended(tmp)))				\
+			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +		\
+				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)];	\
+		next_hops[i] = tmp >> 1;					\
+	}									\
+	for (; i < n; i++) {							\
+		uint16_t vid = is_vrf ? vrf_ids[i] : 0;				\
+		RTE_ASSERT(vid < dp->num_vrfs);				\
+		tmp = ((type *)dp->tbl24)[get_tbl24_idx(vid, ips[i])];		\
+		if (unlikely(is_entry_extended(tmp)))				\
+			tmp = ((type *)dp->tbl8)[(uint8_t)ips[i] +		\
+				((tmp >> 1) * DIR24_8_TBL8_GRP_NUM_ENT)];	\
+		next_hops[i] = tmp >> 1;					\
+	}									\
+}
+
+LOOKUP_FUNC(1b, uint8_t, 5, 0, false)
+LOOKUP_FUNC(2b, uint16_t, 6, 1, false)
+LOOKUP_FUNC(4b, uint32_t, 15, 2, false)
+LOOKUP_FUNC(8b, uint64_t, 12, 3, false)
+LOOKUP_FUNC(vrf_1b, uint8_t, 5, 0, true)
+LOOKUP_FUNC(vrf_2b, uint16_t, 6, 1, true)
+LOOKUP_FUNC(vrf_4b, uint32_t, 15, 2, true)
+LOOKUP_FUNC(vrf_8b, uint64_t, 12, 3, true)
 
 static inline void
-dir24_8_lookup_bulk(struct dir24_8_tbl *dp, const uint32_t *ips,
-	uint64_t *next_hops, const unsigned int n, uint8_t nh_sz)
+__dir24_8_lookup_bulk(struct dir24_8_tbl *dp, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n,
+	uint8_t nh_sz, bool is_vrf)
 {
 	uint64_t tmp;
 	uint32_t i;
 	uint32_t prefetch_offset = RTE_MIN(15U, n);
 
-	for (i = 0; i < prefetch_offset; i++)
-		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));
+	if (!is_vrf)
+		RTE_SET_USED(vrf_ids);
+
+	for (i = 0; i < prefetch_offset; i++) {
+		uint16_t vid = is_vrf ? vrf_ids[i] : 0;
+		RTE_ASSERT(vid < dp->num_vrfs);
+		rte_prefetch0(get_tbl24_p(dp, vid, ips[i], nh_sz));
+	}
 	for (i = 0; i < (n - prefetch_offset); i++) {
-		rte_prefetch0(get_tbl24_p(dp, ips[i + prefetch_offset],
-			nh_sz));
-		tmp = get_tbl24(dp, ips[i], nh_sz);
+		uint16_t vid = is_vrf ? vrf_ids[i] : 0;
+		uint16_t vid_next = is_vrf ? vrf_ids[i + prefetch_offset] : 0;
+		RTE_ASSERT(vid < dp->num_vrfs);
+		RTE_ASSERT(vid_next < dp->num_vrfs);
+		rte_prefetch0(get_tbl24_p(dp, vid_next,
+			ips[i + prefetch_offset], nh_sz));
+		tmp = get_tbl24(dp, vid, ips[i], nh_sz);
 		if (unlikely(is_entry_extended(tmp)))
 			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
 
 		next_hops[i] = tmp >> 1;
 	}
 	for (; i < n; i++) {
-		tmp = get_tbl24(dp, ips[i], nh_sz);
+		uint16_t vid = is_vrf ? vrf_ids[i] : 0;
+		RTE_ASSERT(vid < dp->num_vrfs);
+		tmp = get_tbl24(dp, vid, ips[i], nh_sz);
 		if (unlikely(is_entry_extended(tmp)))
 			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
 
@@ -179,43 +213,79 @@ dir24_8_lookup_bulk(struct dir24_8_tbl *dp, const uint32_t *ips,
 }
 
 static inline void
-dir24_8_lookup_bulk_0(void *p, const uint32_t *ips,
+dir24_8_lookup_bulk_0(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n)
 {
 	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
 
-	dir24_8_lookup_bulk(dp, ips, next_hops, n, 0);
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 0, false);
+}
+
+static inline void
+dir24_8_lookup_bulk_vrf_0(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 0, true);
 }
 
 static inline void
-dir24_8_lookup_bulk_1(void *p, const uint32_t *ips,
+dir24_8_lookup_bulk_1(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n)
 {
 	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
 
-	dir24_8_lookup_bulk(dp, ips, next_hops, n, 1);
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 1, false);
 }
 
 static inline void
-dir24_8_lookup_bulk_2(void *p, const uint32_t *ips,
+dir24_8_lookup_bulk_vrf_1(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 1, true);
+}
+
+static inline void
+dir24_8_lookup_bulk_2(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n)
 {
 	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
 
-	dir24_8_lookup_bulk(dp, ips, next_hops, n, 2);
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 2, false);
 }
 
 static inline void
-dir24_8_lookup_bulk_3(void *p, const uint32_t *ips,
+dir24_8_lookup_bulk_vrf_2(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 2, true);
+}
+
+static inline void
+dir24_8_lookup_bulk_3(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n)
 {
 	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
 
-	dir24_8_lookup_bulk(dp, ips, next_hops, n, 3);
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 3, false);
 }
 
 static inline void
-dir24_8_lookup_bulk_uni(void *p, const uint32_t *ips,
+dir24_8_lookup_bulk_vrf_3(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+
+	__dir24_8_lookup_bulk(dp, vrf_ids, ips, next_hops, n, 3, true);
+}
+
+static inline void
+dir24_8_lookup_bulk_uni(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n)
 {
 	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
@@ -224,66 +294,83 @@ dir24_8_lookup_bulk_uni(void *p, const uint32_t *ips,
 	uint32_t prefetch_offset = RTE_MIN(15U, n);
 	uint8_t nh_sz = dp->nh_sz;
 
-	for (i = 0; i < prefetch_offset; i++)
-		rte_prefetch0(get_tbl24_p(dp, ips[i], nh_sz));
+	for (i = 0; i < prefetch_offset; i++) {
+		uint16_t vid = vrf_ids[i];
+		RTE_ASSERT(vid < dp->num_vrfs);
+		rte_prefetch0(get_tbl24_p(dp, vid, ips[i], nh_sz));
+	}
 	for (i = 0; i < (n - prefetch_offset); i++) {
-		rte_prefetch0(get_tbl24_p(dp, ips[i + prefetch_offset],
-			nh_sz));
-		tmp = get_tbl24(dp, ips[i], nh_sz);
+		uint16_t vid = vrf_ids[i];
+		uint16_t vid_next = vrf_ids[i + prefetch_offset];
+		RTE_ASSERT(vid < dp->num_vrfs);
+		RTE_ASSERT(vid_next < dp->num_vrfs);
+		rte_prefetch0(get_tbl24_p(dp, vid_next,
+			ips[i + prefetch_offset], nh_sz));
+		tmp = get_tbl24(dp, vid, ips[i], nh_sz);
 		if (unlikely(is_entry_extended(tmp)))
 			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
 
 		next_hops[i] = tmp >> 1;
 	}
 	for (; i < n; i++) {
-		tmp = get_tbl24(dp, ips[i], nh_sz);
+		uint16_t vid = vrf_ids[i];
+		RTE_ASSERT(vid < dp->num_vrfs);
+		tmp = get_tbl24(dp, vid, ips[i], nh_sz);
 		if (unlikely(is_entry_extended(tmp)))
 			tmp = get_tbl8(dp, tmp, ips[i], nh_sz);
 
 		next_hops[i] = tmp >> 1;
 	}
 }
-
 #define BSWAP_MAX_LENGTH	64
 
-typedef void (*dir24_8_lookup_bulk_be_cb)(void *p, const uint32_t *ips, uint64_t *next_hops,
-	const unsigned int n);
+typedef void (*dir24_8_lookup_bulk_be_cb)(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
 
 static inline void
-dir24_8_lookup_bulk_be(void *p, const uint32_t *ips, uint64_t *next_hops, const unsigned int n,
-	dir24_8_lookup_bulk_be_cb cb)
+dir24_8_lookup_bulk_be(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
+	uint64_t *next_hops, const unsigned int n, dir24_8_lookup_bulk_be_cb cb)
 {
 	uint32_t le_ips[BSWAP_MAX_LENGTH];
 	unsigned int i;
 
 #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
-	cb(p, ips, next_hops, n);
+	cb(p, vrf_ids, ips, next_hops, n);
 #else
 	for (i = 0; i < n; i += BSWAP_MAX_LENGTH) {
 		int j;
 		for (j = 0; j < BSWAP_MAX_LENGTH && i + j < n; j++)
 			le_ips[j] = rte_be_to_cpu_32(ips[i + j]);
 
-		cb(p, le_ips, next_hops + i, j);
+		cb(p, vrf_ids + i, le_ips, next_hops + i, j);
 	}
 #endif
 }
 
 #define DECLARE_BE_LOOKUP_FN(name) \
 static inline void \
-name##_be(void *p, const uint32_t *ips, uint64_t *next_hops, const unsigned int n) \
+name##_be(void *p, const uint16_t *vrf_ids, const uint32_t *ips, \
+	uint64_t *next_hops, const unsigned int n) \
 { \
-	dir24_8_lookup_bulk_be(p, ips, next_hops, n, name); \
+	dir24_8_lookup_bulk_be(p, vrf_ids, ips, next_hops, n, name); \
 }
 
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_1b)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_2b)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_4b)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_8b)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_1b)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_2b)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_4b)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_8b)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_0)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_1)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_2)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_3)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_0)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_1)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_2)
+DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_vrf_3)
 DECLARE_BE_LOOKUP_FN(dir24_8_lookup_bulk_uni)
 
 void *
@@ -296,7 +383,7 @@ rte_fib_lookup_fn_t
 dir24_8_get_lookup_fn(void *p, enum rte_fib_lookup_type type, bool be_addr);
 
 int
-dir24_8_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
+dir24_8_modify(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip, uint8_t depth,
 	uint64_t next_hop, int op);
 
 int
diff --git a/lib/fib/dir24_8_avx512.c b/lib/fib/dir24_8_avx512.c
index 89b43583c7..3e576e410e 100644
--- a/lib/fib/dir24_8_avx512.c
+++ b/lib/fib/dir24_8_avx512.c
@@ -4,75 +4,132 @@
 
 #include <rte_vect.h>
 #include <rte_fib.h>
+#include <rte_debug.h>
 
 #include "dir24_8.h"
 #include "dir24_8_avx512.h"
 
+enum vrf_scale {
+	VRF_SCALE_SINGLE = 0,
+	VRF_SCALE_SMALL = 1,
+	VRF_SCALE_LARGE = 2,
+};
+
 static __rte_always_inline void
-dir24_8_vec_lookup_x16(void *p, const uint32_t *ips,
-	uint64_t *next_hops, int size, bool be_addr)
+dir24_8_vec_lookup_x8_64b_path(struct dir24_8_tbl *dp, __m256i ip_vec_256,
+	__m256i vrf32_256, uint64_t *next_hops, int size)
 {
-	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
-	__mmask16 msk_ext;
-	__mmask16 exp_msk = 0x5555;
-	__m512i ip_vec, idxes, res, bytes;
-	const __m512i zero = _mm512_set1_epi32(0);
-	const __m512i lsb = _mm512_set1_epi32(1);
-	const __m512i lsbyte_msk = _mm512_set1_epi32(0xff);
-	__m512i tmp1, tmp2, res_msk;
-	__m256i tmp256;
-	/* used to mask gather values if size is 1/2 (8/16 bit next hops) */
+	const __m512i zero_64 = _mm512_set1_epi64(0);
+	const __m512i lsb_64 = _mm512_set1_epi64(1);
+	const __m512i lsbyte_msk_64 = _mm512_set1_epi64(0xff);
+	__m512i res_msk_64, vrf64, idxes_64, res, bytes_64;
+	__mmask8 msk_ext_64;
+
 	if (size == sizeof(uint8_t))
-		res_msk = _mm512_set1_epi32(UINT8_MAX);
+		res_msk_64 = _mm512_set1_epi64(UINT8_MAX);
 	else if (size == sizeof(uint16_t))
-		res_msk = _mm512_set1_epi32(UINT16_MAX);
+		res_msk_64 = _mm512_set1_epi64(UINT16_MAX);
+	else if (size == sizeof(uint32_t))
+		res_msk_64 = _mm512_set1_epi64(UINT32_MAX);
 
-	ip_vec = _mm512_loadu_si512(ips);
-	if (be_addr) {
-		const __m512i bswap32 = _mm512_set_epi32(
-			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203,
-			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203,
-			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203,
-			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203
-		);
-		ip_vec = _mm512_shuffle_epi8(ip_vec, bswap32);
+	vrf64 = _mm512_cvtepu32_epi64(vrf32_256);
+
+	/* Compute index: (vrf_id << 24) + (ip >> 8) using 64-bit shift */
+	idxes_64 = _mm512_slli_epi64(vrf64, 24);
+	idxes_64 = _mm512_add_epi64(idxes_64, _mm512_cvtepu32_epi64(
+		_mm256_srli_epi32(ip_vec_256, 8)));
+
+	/* lookup in tbl24 */
+	if (size == sizeof(uint8_t)) {
+		res = _mm512_i64gather_epi64(idxes_64, (const void *)dp->tbl24, 1);
+		res = _mm512_and_epi64(res, res_msk_64);
+	} else if (size == sizeof(uint16_t)) {
+		res = _mm512_i64gather_epi64(idxes_64, (const void *)dp->tbl24, 2);
+		res = _mm512_and_epi64(res, res_msk_64);
+	} else {
+		res = _mm512_i64gather_epi64(idxes_64, (const void *)dp->tbl24, 4);
+		res = _mm512_and_epi64(res, res_msk_64);
+	}
+
+	/* get extended entries indexes */
+	msk_ext_64 = _mm512_test_epi64_mask(res, lsb_64);
+
+	if (msk_ext_64 != 0) {
+		bytes_64 = _mm512_cvtepu32_epi64(ip_vec_256);
+		idxes_64 = _mm512_srli_epi64(res, 1);
+		idxes_64 = _mm512_slli_epi64(idxes_64, 8);
+		bytes_64 = _mm512_and_epi64(bytes_64, lsbyte_msk_64);
+		idxes_64 = _mm512_maskz_add_epi64(msk_ext_64, idxes_64, bytes_64);
+
+		if (size == sizeof(uint8_t))
+			idxes_64 = _mm512_mask_i64gather_epi64(zero_64, msk_ext_64,
+				idxes_64, (const void *)dp->tbl8, 1);
+		else if (size == sizeof(uint16_t))
+			idxes_64 = _mm512_mask_i64gather_epi64(zero_64, msk_ext_64,
+				idxes_64, (const void *)dp->tbl8, 2);
+		else
+			idxes_64 = _mm512_mask_i64gather_epi64(zero_64, msk_ext_64,
+				idxes_64, (const void *)dp->tbl8, 4);
+
+		res = _mm512_mask_blend_epi64(msk_ext_64, res, idxes_64);
 	}
 
-	/* mask 24 most significant bits */
-	idxes = _mm512_srli_epi32(ip_vec, 8);
+	res = _mm512_srli_epi64(res, 1);
+	_mm512_storeu_si512(next_hops, res);
+}
+
+static __rte_always_inline void
+dir24_8_vec_lookup_x16_32b_path(struct dir24_8_tbl *dp, __m512i ip_vec,
+	__m512i idxes, uint64_t *next_hops, int size)
+{
+	__mmask16 msk_ext;
+	const __mmask16 exp_msk = 0x5555;
+	const __m512i zero_32 = _mm512_set1_epi32(0);
+	const __m512i lsb_32 = _mm512_set1_epi32(1);
+	const __m512i lsbyte_msk_32 = _mm512_set1_epi32(0xff);
+	__m512i res, bytes, tmp1, tmp2;
+	__m256i tmp256;
+	__m512i res_msk_32;
+
+	if (size == sizeof(uint8_t))
+		res_msk_32 = _mm512_set1_epi32(UINT8_MAX);
+	else if (size == sizeof(uint16_t))
+		res_msk_32 = _mm512_set1_epi32(UINT16_MAX);
 
-	/**
+	/*
 	 * lookup in tbl24
 	 * Put it inside branch to make compiler happy with -O0
 	 */
 	if (size == sizeof(uint8_t)) {
 		res = _mm512_i32gather_epi32(idxes, (const int *)dp->tbl24, 1);
-		res = _mm512_and_epi32(res, res_msk);
+		res = _mm512_and_epi32(res, res_msk_32);
 	} else if (size == sizeof(uint16_t)) {
 		res = _mm512_i32gather_epi32(idxes, (const int *)dp->tbl24, 2);
-		res = _mm512_and_epi32(res, res_msk);
-	} else
+		res = _mm512_and_epi32(res, res_msk_32);
+	} else {
 		res = _mm512_i32gather_epi32(idxes, (const int *)dp->tbl24, 4);
+	}
 
 	/* get extended entries indexes */
-	msk_ext = _mm512_test_epi32_mask(res, lsb);
+	msk_ext = _mm512_test_epi32_mask(res, lsb_32);
 
 	if (msk_ext != 0) {
 		idxes = _mm512_srli_epi32(res, 1);
 		idxes = _mm512_slli_epi32(idxes, 8);
-		bytes = _mm512_and_epi32(ip_vec, lsbyte_msk);
+		bytes = _mm512_and_epi32(ip_vec, lsbyte_msk_32);
 		idxes = _mm512_maskz_add_epi32(msk_ext, idxes, bytes);
 		if (size == sizeof(uint8_t)) {
-			idxes = _mm512_mask_i32gather_epi32(zero, msk_ext,
+			idxes = _mm512_mask_i32gather_epi32(zero_32, msk_ext,
 				idxes, (const int *)dp->tbl8, 1);
-			idxes = _mm512_and_epi32(idxes, res_msk);
+			idxes = _mm512_and_epi32(idxes, res_msk_32);
 		} else if (size == sizeof(uint16_t)) {
-			idxes = _mm512_mask_i32gather_epi32(zero, msk_ext,
+			idxes = _mm512_mask_i32gather_epi32(zero_32, msk_ext,
 				idxes, (const int *)dp->tbl8, 2);
-			idxes = _mm512_and_epi32(idxes, res_msk);
-		} else
-			idxes = _mm512_mask_i32gather_epi32(zero, msk_ext,
+			idxes = _mm512_and_epi32(idxes, res_msk_32);
+		} else {
+			idxes = _mm512_mask_i32gather_epi32(zero_32, msk_ext,
 				idxes, (const int *)dp->tbl8, 4);
+		}
 
 		res = _mm512_mask_blend_epi32(msk_ext, res, idxes);
 	}
@@ -86,16 +143,74 @@ dir24_8_vec_lookup_x16(void *p, const uint32_t *ips,
 	_mm512_storeu_si512(next_hops + 8, tmp2);
 }
 
+/* Unified function with vrf_scale parameter similar to be_addr */
+static __rte_always_inline void
+dir24_8_vec_lookup_x16(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
+	uint64_t *next_hops, int size, bool be_addr, enum vrf_scale vrf_scale)
+{
+	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
+	__m512i ip_vec, idxes;
+	__m256i ip_vec_256, vrf32_256;
+
+	ip_vec = _mm512_loadu_si512(ips);
+	if (be_addr) {
+		const __m512i bswap32 = _mm512_set_epi32(
+			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203,
+			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203,
+			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203,
+			0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203
+		);
+		ip_vec = _mm512_shuffle_epi8(ip_vec, bswap32);
+	}
+
+	if (vrf_scale == VRF_SCALE_SINGLE) {
+		/* mask 24 most significant bits */
+		idxes = _mm512_srli_epi32(ip_vec, 8);
+		dir24_8_vec_lookup_x16_32b_path(dp, ip_vec, idxes, next_hops, size);
+	} else if (vrf_scale == VRF_SCALE_SMALL) {
+		/* For < 256 VRFs: use 32-bit indices with 32-bit shift */
+		__m512i vrf32;
+		uint32_t i;
+
+		for (i = 0; i < 16; i++)
+			RTE_ASSERT(vrf_ids[i] < dp->num_vrfs);
+
+		vrf32 = _mm512_cvtepu16_epi32(_mm256_loadu_si256((const void *)vrf_ids));
+
+		/* mask 24 most significant bits */
+		idxes = _mm512_srli_epi32(ip_vec, 8);
+		idxes = _mm512_add_epi32(idxes, _mm512_slli_epi32(vrf32, 24));
+		dir24_8_vec_lookup_x16_32b_path(dp, ip_vec, idxes, next_hops, size);
+	} else {
+		/* For >= 256 VRFs: use 64-bit indices to avoid overflow */
+		uint32_t i;
+
+		for (i = 0; i < 16; i++)
+			RTE_ASSERT(vrf_ids[i] < dp->num_vrfs);
+
+		/* Extract first 8 IPs and VRF IDs */
+		ip_vec_256 = _mm512_castsi512_si256(ip_vec);
+		vrf32_256 = _mm256_cvtepu16_epi32(_mm_loadu_si128((const void *)vrf_ids));
+		dir24_8_vec_lookup_x8_64b_path(dp, ip_vec_256, vrf32_256, next_hops, size);
+
+		/* Process next 8 IPs from the second half of the vector */
+		ip_vec_256 = _mm512_extracti32x8_epi32(ip_vec, 1);
+		vrf32_256 = _mm256_cvtepu16_epi32(_mm_loadu_si128((const void *)(vrf_ids + 8)));
+		dir24_8_vec_lookup_x8_64b_path(dp, ip_vec_256, vrf32_256, next_hops + 8, size);
+	}
+}
+
+/* Unified function with vrf_scale parameter */
 static __rte_always_inline void
-dir24_8_vec_lookup_x8_8b(void *p, const uint32_t *ips,
-	uint64_t *next_hops, bool be_addr)
+dir24_8_vec_lookup_x8_8b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, bool be_addr, enum vrf_scale vrf_scale)
 {
 	struct dir24_8_tbl *dp = (struct dir24_8_tbl *)p;
-	const __m512i zero = _mm512_set1_epi32(0);
-	const __m512i lsbyte_msk = _mm512_set1_epi64(0xff);
-	const __m512i lsb = _mm512_set1_epi64(1);
+	const __m512i zero_64 = _mm512_set1_epi64(0);
+	const __m512i lsbyte_msk_64 = _mm512_set1_epi64(0xff);
+	const __m512i lsb_64 = _mm512_set1_epi64(1);
 	__m512i res, idxes, bytes;
-	__m256i idxes_256, ip_vec;
+	__m256i ip_vec, vrf32_256;
 	__mmask8 msk_ext;
 
 	ip_vec = _mm256_loadu_si256((const void *)ips);
@@ -106,66 +221,207 @@ dir24_8_vec_lookup_x8_8b(void *p, const uint32_t *ips,
 		);
 		ip_vec = _mm256_shuffle_epi8(ip_vec, bswap32);
 	}
-	/* mask 24 most significant bits */
-	idxes_256 = _mm256_srli_epi32(ip_vec, 8);
 
-	/* lookup in tbl24 */
-	res = _mm512_i32gather_epi64(idxes_256, (const void *)dp->tbl24, 8);
+	if (vrf_scale == VRF_SCALE_SINGLE) {
+		/* For single VRF: use 32-bit indices without vrf_ids */
+		__m256i idxes_256;
 
-	/* get extended entries indexes */
-	msk_ext = _mm512_test_epi64_mask(res, lsb);
+		/* mask 24 most significant bits */
+		idxes_256 = _mm256_srli_epi32(ip_vec, 8);
 
-	if (msk_ext != 0) {
-		bytes = _mm512_cvtepi32_epi64(ip_vec);
-		idxes = _mm512_srli_epi64(res, 1);
-		idxes = _mm512_slli_epi64(idxes, 8);
-		bytes = _mm512_and_epi64(bytes, lsbyte_msk);
-		idxes = _mm512_maskz_add_epi64(msk_ext, idxes, bytes);
-		idxes = _mm512_mask_i64gather_epi64(zero, msk_ext, idxes,
-			(const void *)dp->tbl8, 8);
-
-		res = _mm512_mask_blend_epi64(msk_ext, res, idxes);
-	}
+		/* lookup in tbl24 */
+		res = _mm512_i32gather_epi64(idxes_256, (const void *)dp->tbl24, 8);
 
-	res = _mm512_srli_epi64(res, 1);
-	_mm512_storeu_si512(next_hops, res);
+		/* get extended entries indexes */
+		msk_ext = _mm512_test_epi64_mask(res, lsb_64);
+
+		if (msk_ext != 0) {
+			bytes = _mm512_cvtepu32_epi64(ip_vec);
+			idxes = _mm512_srli_epi64(res, 1);
+			idxes = _mm512_slli_epi64(idxes, 8);
+			bytes = _mm512_and_epi64(bytes, lsbyte_msk_64);
+			idxes = _mm512_maskz_add_epi64(msk_ext, idxes, bytes);
+			idxes = _mm512_mask_i64gather_epi64(zero_64, msk_ext, idxes,
+				(const void *)dp->tbl8, 8);
+
+			res = _mm512_mask_blend_epi64(msk_ext, res, idxes);
+		}
+
+		res = _mm512_srli_epi64(res, 1);
+		_mm512_storeu_si512(next_hops, res);
+	} else if (vrf_scale == VRF_SCALE_SMALL) {
+		/* For < 256 VRFs: use 32-bit indices with 32-bit shift */
+		__m256i idxes_256;
+		uint32_t i;
+
+		for (i = 0; i < 8; i++)
+			RTE_ASSERT(vrf_ids[i] < dp->num_vrfs);
+
+		/* mask 24 most significant bits */
+		idxes_256 = _mm256_srli_epi32(ip_vec, 8);
+		vrf32_256 = _mm256_cvtepu16_epi32(_mm_loadu_si128((const void *)vrf_ids));
+		idxes_256 = _mm256_add_epi32(idxes_256, _mm256_slli_epi32(vrf32_256, 24));
+
+		/* lookup in tbl24 */
+		res = _mm512_i32gather_epi64(idxes_256, (const void *)dp->tbl24, 8);
+
+		/* get extended entries indexes */
+		msk_ext = _mm512_test_epi64_mask(res, lsb_64);
+
+		if (msk_ext != 0) {
+			bytes = _mm512_cvtepu32_epi64(ip_vec);
+			idxes = _mm512_srli_epi64(res, 1);
+			idxes = _mm512_slli_epi64(idxes, 8);
+			bytes = _mm512_and_epi64(bytes, lsbyte_msk_64);
+			idxes = _mm512_maskz_add_epi64(msk_ext, idxes, bytes);
+			idxes = _mm512_mask_i64gather_epi64(zero_64, msk_ext, idxes,
+				(const void *)dp->tbl8, 8);
+
+			res = _mm512_mask_blend_epi64(msk_ext, res, idxes);
+		}
+
+		res = _mm512_srli_epi64(res, 1);
+		_mm512_storeu_si512(next_hops, res);
+	} else {
+		/* For >= 256 VRFs: use 64-bit indices to avoid overflow */
+		uint32_t i;
+
+		for (i = 0; i < 8; i++)
+			RTE_ASSERT(vrf_ids[i] < dp->num_vrfs);
+
+		vrf32_256 = _mm256_cvtepu16_epi32(_mm_loadu_si128((const void *)vrf_ids));
+		__m512i vrf64 = _mm512_cvtepu32_epi64(vrf32_256);
+
+		/* Compute index: (vrf_id << 24) + (ip >> 8) using 64-bit arithmetic */
+		idxes = _mm512_slli_epi64(vrf64, 24);
+		idxes = _mm512_add_epi64(idxes, _mm512_cvtepu32_epi64(
+			_mm256_srli_epi32(ip_vec, 8)));
+
+		/* lookup in tbl24 with 64-bit gather */
+		res = _mm512_i64gather_epi64(idxes, (const void *)dp->tbl24, 8);
+
+		/* get extended entries indexes */
+		msk_ext = _mm512_test_epi64_mask(res, lsb_64);
+
+		if (msk_ext != 0) {
+			bytes = _mm512_cvtepu32_epi64(ip_vec);
+			idxes = _mm512_srli_epi64(res, 1);
+			idxes = _mm512_slli_epi64(idxes, 8);
+			bytes = _mm512_and_epi64(bytes, lsbyte_msk_64);
+			idxes = _mm512_maskz_add_epi64(msk_ext, idxes, bytes);
+			idxes = _mm512_mask_i64gather_epi64(zero_64, msk_ext, idxes,
+				(const void *)dp->tbl8, 8);
+
+			res = _mm512_mask_blend_epi64(msk_ext, res, idxes);
+		}
+
+		res = _mm512_srli_epi64(res, 1);
+		_mm512_storeu_si512(next_hops, res);
+	}
 }
 
-#define DECLARE_VECTOR_FN(suffix, nh_type, be_addr) \
+#define DECLARE_VECTOR_FN(suffix, scalar_suffix, nh_type, be_addr, vrf_scale) \
 void \
-rte_dir24_8_vec_lookup_bulk_##suffix(void *p, const uint32_t *ips, uint64_t *next_hops, \
-	const unsigned int n) \
+rte_dir24_8_vec_lookup_bulk_##suffix(void *p, const uint16_t *vrf_ids, \
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n) \
 { \
 	uint32_t i; \
 	for (i = 0; i < (n / 16); i++) \
-		dir24_8_vec_lookup_x16(p, ips + i * 16, next_hops + i * 16, sizeof(nh_type), \
-			be_addr); \
-	dir24_8_lookup_bulk_##suffix(p, ips + i * 16, next_hops + i * 16, n - i * 16); \
+		dir24_8_vec_lookup_x16(p, vrf_ids + i * 16, ips + i * 16, \
+			next_hops + i * 16, sizeof(nh_type), be_addr, vrf_scale); \
+	dir24_8_lookup_bulk_##scalar_suffix(p, vrf_ids + i * 16, ips + i * 16, \
+		next_hops + i * 16, n - i * 16); \
+}
+
+DECLARE_VECTOR_FN(1b, 1b, uint8_t, false, VRF_SCALE_SINGLE)
+DECLARE_VECTOR_FN(1b_be, 1b_be, uint8_t, true, VRF_SCALE_SINGLE)
+DECLARE_VECTOR_FN(2b, 2b, uint16_t, false, VRF_SCALE_SINGLE)
+DECLARE_VECTOR_FN(2b_be, 2b_be, uint16_t, true, VRF_SCALE_SINGLE)
+DECLARE_VECTOR_FN(4b, 4b, uint32_t, false, VRF_SCALE_SINGLE)
+DECLARE_VECTOR_FN(4b_be, 4b_be, uint32_t, true, VRF_SCALE_SINGLE)
+
+DECLARE_VECTOR_FN(vrf_1b, vrf_1b, uint8_t, false, VRF_SCALE_SMALL)
+DECLARE_VECTOR_FN(vrf_1b_be, vrf_1b_be, uint8_t, true, VRF_SCALE_SMALL)
+DECLARE_VECTOR_FN(vrf_2b, vrf_2b, uint16_t, false, VRF_SCALE_SMALL)
+DECLARE_VECTOR_FN(vrf_2b_be, vrf_2b_be, uint16_t, true, VRF_SCALE_SMALL)
+DECLARE_VECTOR_FN(vrf_4b, vrf_4b, uint32_t, false, VRF_SCALE_SMALL)
+DECLARE_VECTOR_FN(vrf_4b_be, vrf_4b_be, uint32_t, true, VRF_SCALE_SMALL)
+
+DECLARE_VECTOR_FN(vrf_1b_large, vrf_1b, uint8_t, false, VRF_SCALE_LARGE)
+DECLARE_VECTOR_FN(vrf_1b_be_large, vrf_1b_be, uint8_t, true, VRF_SCALE_LARGE)
+DECLARE_VECTOR_FN(vrf_2b_large, vrf_2b, uint16_t, false, VRF_SCALE_LARGE)
+DECLARE_VECTOR_FN(vrf_2b_be_large, vrf_2b_be, uint16_t, true, VRF_SCALE_LARGE)
+DECLARE_VECTOR_FN(vrf_4b_large, vrf_4b, uint32_t, false, VRF_SCALE_LARGE)
+DECLARE_VECTOR_FN(vrf_4b_be_large, vrf_4b_be, uint32_t, true, VRF_SCALE_LARGE)
+
+void
+rte_dir24_8_vec_lookup_bulk_8b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	uint32_t i;
+	for (i = 0; i < (n / 8); i++)
+		dir24_8_vec_lookup_x8_8b(p, vrf_ids + i * 8, ips + i * 8,
+			next_hops + i * 8, false, VRF_SCALE_SINGLE);
+	dir24_8_lookup_bulk_8b(p, vrf_ids + i * 8, ips + i * 8,
+		next_hops + i * 8, n - i * 8);
+}
+
+void
+rte_dir24_8_vec_lookup_bulk_8b_be(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	uint32_t i;
+	for (i = 0; i < (n / 8); i++)
+		dir24_8_vec_lookup_x8_8b(p, vrf_ids + i * 8, ips + i * 8,
+			next_hops + i * 8, true, VRF_SCALE_SINGLE);
+	dir24_8_lookup_bulk_8b_be(p, vrf_ids + i * 8, ips + i * 8,
+		next_hops + i * 8, n - i * 8);
+}
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_8b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	uint32_t i;
+	for (i = 0; i < (n / 8); i++)
+		dir24_8_vec_lookup_x8_8b(p, vrf_ids + i * 8, ips + i * 8,
+			next_hops + i * 8, false, VRF_SCALE_SMALL);
+	dir24_8_lookup_bulk_vrf_8b(p, vrf_ids + i * 8, ips + i * 8,
+		next_hops + i * 8, n - i * 8);
 }
 
-DECLARE_VECTOR_FN(1b, uint8_t, false)
-DECLARE_VECTOR_FN(1b_be, uint8_t, true)
-DECLARE_VECTOR_FN(2b, uint16_t, false)
-DECLARE_VECTOR_FN(2b_be, uint16_t, true)
-DECLARE_VECTOR_FN(4b, uint32_t, false)
-DECLARE_VECTOR_FN(4b_be, uint32_t, true)
+void
+rte_dir24_8_vec_lookup_bulk_vrf_8b_be(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
+{
+	uint32_t i;
+	for (i = 0; i < (n / 8); i++)
+		dir24_8_vec_lookup_x8_8b(p, vrf_ids + i * 8, ips + i * 8,
+			next_hops + i * 8, true, VRF_SCALE_SMALL);
+	dir24_8_lookup_bulk_vrf_8b_be(p, vrf_ids + i * 8, ips + i * 8,
+		next_hops + i * 8, n - i * 8);
+}
 
 void
-rte_dir24_8_vec_lookup_bulk_8b(void *p, const uint32_t *ips,
-	uint64_t *next_hops, const unsigned int n)
+rte_dir24_8_vec_lookup_bulk_vrf_8b_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
 {
 	uint32_t i;
 	for (i = 0; i < (n / 8); i++)
-		dir24_8_vec_lookup_x8_8b(p, ips + i * 8, next_hops + i * 8, false);
-	dir24_8_lookup_bulk_8b(p, ips + i * 8, next_hops + i * 8, n - i * 8);
+		dir24_8_vec_lookup_x8_8b(p, vrf_ids + i * 8, ips + i * 8,
+			next_hops + i * 8, false, VRF_SCALE_LARGE);
+	dir24_8_lookup_bulk_vrf_8b(p, vrf_ids + i * 8, ips + i * 8,
+		next_hops + i * 8, n - i * 8);
 }
 
 void
-rte_dir24_8_vec_lookup_bulk_8b_be(void *p, const uint32_t *ips,
-	uint64_t *next_hops, const unsigned int n)
+rte_dir24_8_vec_lookup_bulk_vrf_8b_be_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
 {
 	uint32_t i;
 	for (i = 0; i < (n / 8); i++)
-		dir24_8_vec_lookup_x8_8b(p, ips + i * 8, next_hops + i * 8, true);
-	dir24_8_lookup_bulk_8b_be(p, ips + i * 8, next_hops + i * 8, n - i * 8);
+		dir24_8_vec_lookup_x8_8b(p, vrf_ids + i * 8, ips + i * 8,
+			next_hops + i * 8, true, VRF_SCALE_LARGE);
+	dir24_8_lookup_bulk_vrf_8b_be(p, vrf_ids + i * 8, ips + i * 8,
+		next_hops + i * 8, n - i * 8);
 }
diff --git a/lib/fib/dir24_8_avx512.h b/lib/fib/dir24_8_avx512.h
index 3e2bbc2490..d42ef1d17f 100644
--- a/lib/fib/dir24_8_avx512.h
+++ b/lib/fib/dir24_8_avx512.h
@@ -6,35 +6,99 @@
 #define _DIR248_AVX512_H_
 
 void
-rte_dir24_8_vec_lookup_bulk_1b(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_1b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_1b_be(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_1b(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_2b(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_1b_be(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_4b(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_1b_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_1b_be_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_2b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_2b_be(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_2b(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_8b(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_2b_be(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_1b_be(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_2b_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_2b_be_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_4b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_4b_be(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_4b(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_2b_be(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_4b_be(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_4b_be(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_4b_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_4b_be_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_8b(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_8b_be(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_8b(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
 void
-rte_dir24_8_vec_lookup_bulk_8b_be(void *p, const uint32_t *ips,
+rte_dir24_8_vec_lookup_bulk_vrf_8b_be(void *p, const uint16_t *vrf_ids, const uint32_t *ips,
 	uint64_t *next_hops, const unsigned int n);
 
+void
+rte_dir24_8_vec_lookup_bulk_vrf_8b_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
+void
+rte_dir24_8_vec_lookup_bulk_vrf_8b_be_large(void *p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
+
 #endif /* _DIR248_AVX512_H_ */
diff --git a/lib/fib/rte_fib.c b/lib/fib/rte_fib.c
index 184210f380..efc0595a7f 100644
--- a/lib/fib/rte_fib.c
+++ b/lib/fib/rte_fib.c
@@ -14,12 +14,15 @@
 #include <rte_string_fns.h>
 #include <rte_tailq.h>
 
+#include <rte_debug.h>
 #include <rte_rib.h>
 #include <rte_fib.h>
 
 #include "dir24_8.h"
 #include "fib_log.h"
 
+#define FIB_MAX_LOOKUP_BULK 64U
+
 RTE_LOG_REGISTER_DEFAULT(fib_logtype, INFO);
 
 TAILQ_HEAD(rte_fib_list, rte_tailq_entry);
@@ -40,52 +43,61 @@ EAL_REGISTER_TAILQ(rte_fib_tailq)
 struct rte_fib {
 	char			name[RTE_FIB_NAMESIZE];
 	enum rte_fib_type	type;	/**< Type of FIB struct */
-	unsigned int flags;		/**< Flags */
-	struct rte_rib		*rib;	/**< RIB helper datastructure */
+	uint16_t flags;			/**< Flags */
+	uint16_t		num_vrfs;/**< Number of VRFs */
+	struct rte_rib		**ribs;	/**< RIB helper datastructures per VRF */
 	void			*dp;	/**< pointer to the dataplane struct*/
 	rte_fib_lookup_fn_t	lookup;	/**< FIB lookup function */
 	rte_fib_modify_fn_t	modify; /**< modify FIB datastructure */
-	uint64_t		def_nh;
+	uint64_t		*def_nh;/**< Per-VRF default next hop array */
 };
 
 static void
-dummy_lookup(void *fib_p, const uint32_t *ips, uint64_t *next_hops,
-	const unsigned int n)
+dummy_lookup(void *fib_p, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n)
 {
 	unsigned int i;
 	struct rte_fib *fib = fib_p;
 	struct rte_rib_node *node;
+	struct rte_rib *rib;
 
 	for (i = 0; i < n; i++) {
-		node = rte_rib_lookup(fib->rib, ips[i]);
+		RTE_ASSERT(vrf_ids[i] < fib->num_vrfs);
+		rib = rte_fib_vrf_get_rib(fib, vrf_ids[i]);
+		node = rte_rib_lookup(rib, ips[i]);
 		if (node != NULL)
 			rte_rib_get_nh(node, &next_hops[i]);
 		else
-			next_hops[i] = fib->def_nh;
+			next_hops[i] = fib->def_nh[vrf_ids[i]];
 	}
 }
 
 static int
-dummy_modify(struct rte_fib *fib, uint32_t ip, uint8_t depth,
-	uint64_t next_hop, int op)
+dummy_modify(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip,
+	uint8_t depth, uint64_t next_hop, int op)
 {
 	struct rte_rib_node *node;
+	struct rte_rib *rib;
 	if ((fib == NULL) || (depth > RTE_FIB_MAXDEPTH))
 		return -EINVAL;
 
-	node = rte_rib_lookup_exact(fib->rib, ip, depth);
+	rib = rte_fib_vrf_get_rib(fib, vrf_id);
+	if (rib == NULL)
+		return -EINVAL;
+
+	node = rte_rib_lookup_exact(rib, ip, depth);
 
 	switch (op) {
 	case RTE_FIB_ADD:
 		if (node == NULL)
-			node = rte_rib_insert(fib->rib, ip, depth);
+			node = rte_rib_insert(rib, ip, depth);
 		if (node == NULL)
 			return -rte_errno;
 		return rte_rib_set_nh(node, next_hop);
 	case RTE_FIB_DEL:
 		if (node == NULL)
 			return -ENOENT;
-		rte_rib_remove(fib->rib, ip, depth);
+		rte_rib_remove(rib, ip, depth);
 		return 0;
 	}
 	return -EINVAL;
@@ -125,7 +137,7 @@ rte_fib_add(struct rte_fib *fib, uint32_t ip, uint8_t depth, uint64_t next_hop)
 	if ((fib == NULL) || (fib->modify == NULL) ||
 			(depth > RTE_FIB_MAXDEPTH))
 		return -EINVAL;
-	return fib->modify(fib, ip, depth, next_hop, RTE_FIB_ADD);
+	return fib->modify(fib, 0, ip, depth, next_hop, RTE_FIB_ADD);
 }
 
 RTE_EXPORT_SYMBOL(rte_fib_delete)
@@ -135,7 +147,7 @@ rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth)
 	if ((fib == NULL) || (fib->modify == NULL) ||
 			(depth > RTE_FIB_MAXDEPTH))
 		return -EINVAL;
-	return fib->modify(fib, ip, depth, 0, RTE_FIB_DEL);
+	return fib->modify(fib, 0, ip, depth, 0, RTE_FIB_DEL);
 }
 
 RTE_EXPORT_SYMBOL(rte_fib_lookup_bulk)
@@ -143,24 +155,73 @@ int
 rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
 	uint64_t *next_hops, int n)
 {
+	static const uint16_t zero_vrf_ids[FIB_MAX_LOOKUP_BULK];
+	unsigned int off = 0;
+	unsigned int total = (unsigned int)n;
+
 	FIB_RETURN_IF_TRUE(((fib == NULL) || (ips == NULL) ||
 		(next_hops == NULL) || (fib->lookup == NULL)), -EINVAL);
 
-	fib->lookup(fib->dp, ips, next_hops, n);
+	while (off < total) {
+		unsigned int chunk = RTE_MIN(total - off,
+			FIB_MAX_LOOKUP_BULK);
+		fib->lookup(fib->dp, zero_vrf_ids, ips + off,
+			next_hops + off, chunk);
+		off += chunk;
+	}
+
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_vrf_lookup_bulk, 26.07)
+int
+rte_fib_vrf_lookup_bulk(struct rte_fib *fib, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, int n)
+{
+	FIB_RETURN_IF_TRUE(((fib == NULL) || (vrf_ids == NULL) ||
+		(ips == NULL) || (next_hops == NULL) ||
+		(fib->lookup == NULL)), -EINVAL);
+
+	fib->lookup(fib->dp, vrf_ids, ips, next_hops, (unsigned int)n);
 	return 0;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_vrf_add, 26.07)
+int
+rte_fib_vrf_add(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip,
+	uint8_t depth, uint64_t next_hop)
+{
+	if ((fib == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, vrf_id, ip, depth, next_hop, RTE_FIB_ADD);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_vrf_delete, 26.07)
+int
+rte_fib_vrf_delete(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip,
+	uint8_t depth)
+{
+	if ((fib == NULL) || (fib->modify == NULL) ||
+			(depth > RTE_FIB_MAXDEPTH))
+		return -EINVAL;
+	return fib->modify(fib, vrf_id, ip, depth, 0, RTE_FIB_DEL);
+}
+
 RTE_EXPORT_SYMBOL(rte_fib_create)
 struct rte_fib *
 rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf)
 {
 	char mem_name[RTE_FIB_NAMESIZE];
+	char rib_name[RTE_FIB_NAMESIZE];
 	int ret;
 	struct rte_fib *fib = NULL;
 	struct rte_rib *rib = NULL;
 	struct rte_tailq_entry *te;
 	struct rte_fib_list *fib_list;
 	struct rte_rib_conf rib_conf;
+	uint16_t num_vrfs;
+	uint16_t vrf;
 
 	/* Check user arguments. */
 	if ((name == NULL) || (conf == NULL) ||	(conf->max_routes < 0) ||
@@ -170,16 +231,42 @@ rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf)
 		return NULL;
 	}
 
+	num_vrfs = (conf->max_vrfs == 0) ? 1 : conf->max_vrfs;
 	rib_conf.ext_sz = conf->rib_ext_sz;
 	rib_conf.max_nodes = conf->max_routes * 2;
 
-	rib = rte_rib_create(name, socket_id, &rib_conf);
-	if (rib == NULL) {
-		FIB_LOG(ERR,
-			"Can not allocate RIB %s", name);
+	struct rte_rib **ribs = rte_zmalloc_socket("FIB_RIBS",
+		num_vrfs * sizeof(*fib->ribs), RTE_CACHE_LINE_SIZE, socket_id);
+	if (ribs == NULL) {
+		FIB_LOG(ERR, "FIB %s RIB array allocation failed", name);
+		rte_errno = ENOMEM;
 		return NULL;
 	}
 
+	uint64_t *def_nh = rte_zmalloc_socket("FIB_DEF_NH",
+		num_vrfs * sizeof(*def_nh), RTE_CACHE_LINE_SIZE, socket_id);
+	if (def_nh == NULL) {
+		FIB_LOG(ERR, "FIB %s default nexthop array allocation failed", name);
+		rte_errno = ENOMEM;
+		rte_free(ribs);
+		return NULL;
+	}
+
+	for (vrf = 0; vrf < num_vrfs; vrf++) {
+		if (num_vrfs == 1)
+			snprintf(rib_name, sizeof(rib_name), "%s", name);
+		else
+			snprintf(rib_name, sizeof(rib_name), "%s_vrf%u", name, vrf);
+		rib = rte_rib_create(rib_name, socket_id, &rib_conf);
+		if (rib == NULL) {
+			FIB_LOG(ERR, "Can not allocate RIB %s", rib_name);
+			goto free_ribs;
+		}
+		ribs[vrf] = rib;
+		def_nh[vrf] = (conf->vrf_default_nh != NULL) ?
+			conf->vrf_default_nh[vrf] : conf->default_nh;
+	}
+
 	snprintf(mem_name, sizeof(mem_name), "FIB_%s", name);
 	fib_list = RTE_TAILQ_CAST(rte_fib_tailq.head, rte_fib_list);
 
@@ -215,11 +302,13 @@ rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf)
 		goto free_te;
 	}
 
+	fib->num_vrfs = num_vrfs;
+	fib->ribs = ribs;
+	fib->def_nh = def_nh;
+
 	rte_strlcpy(fib->name, name, sizeof(fib->name));
-	fib->rib = rib;
 	fib->type = conf->type;
 	fib->flags = conf->flags;
-	fib->def_nh = conf->default_nh;
 	ret = init_dataplane(fib, socket_id, conf);
 	if (ret < 0) {
 		FIB_LOG(ERR,
@@ -242,8 +331,12 @@ rte_fib_create(const char *name, int socket_id, struct rte_fib_conf *conf)
 	rte_free(te);
 exit:
 	rte_mcfg_tailq_write_unlock();
-	rte_rib_free(rib);
+free_ribs:
+	for (vrf = 0; vrf < num_vrfs; vrf++)
+		rte_rib_free(ribs[vrf]);
 
+	rte_free(def_nh);
+	rte_free(ribs);
 	return NULL;
 }
 
@@ -311,7 +404,13 @@ rte_fib_free(struct rte_fib *fib)
 	rte_mcfg_tailq_write_unlock();
 
 	free_dataplane(fib);
-	rte_rib_free(fib->rib);
+	if (fib->ribs != NULL) {
+		uint16_t vrf;
+		for (vrf = 0; vrf < fib->num_vrfs; vrf++)
+			rte_rib_free(fib->ribs[vrf]);
+	}
+	rte_free(fib->ribs);
+	rte_free(fib->def_nh);
 	rte_free(fib);
 	rte_free(te);
 }
@@ -327,7 +426,18 @@ RTE_EXPORT_SYMBOL(rte_fib_get_rib)
 struct rte_rib *
 rte_fib_get_rib(struct rte_fib *fib)
 {
-	return (fib == NULL) ? NULL : fib->rib;
+	return (fib == NULL || fib->ribs == NULL) ? NULL : fib->ribs[0];
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_fib_vrf_get_rib, 26.07)
+struct rte_rib *
+rte_fib_vrf_get_rib(struct rte_fib *fib, uint16_t vrf_id)
+{
+	if (fib == NULL || fib->ribs == NULL)
+		return NULL;
+	if (vrf_id >= fib->num_vrfs)
+		return NULL;
+	return fib->ribs[vrf_id];
 }
 
 RTE_EXPORT_SYMBOL(rte_fib_select_lookup)
diff --git a/lib/fib/rte_fib.h b/lib/fib/rte_fib.h
index b16a653535..883195c7d6 100644
--- a/lib/fib/rte_fib.h
+++ b/lib/fib/rte_fib.h
@@ -53,11 +53,11 @@ enum rte_fib_type {
 };
 
 /** Modify FIB function */
-typedef int (*rte_fib_modify_fn_t)(struct rte_fib *fib, uint32_t ip,
-	uint8_t depth, uint64_t next_hop, int op);
+typedef int (*rte_fib_modify_fn_t)(struct rte_fib *fib, uint16_t vrf_id,
+	uint32_t ip, uint8_t depth, uint64_t next_hop, int op);
 /** FIB bulk lookup function */
-typedef void (*rte_fib_lookup_fn_t)(void *fib, const uint32_t *ips,
-	uint64_t *next_hops, const unsigned int n);
+typedef void (*rte_fib_lookup_fn_t)(void *fib, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, const unsigned int n);
 
 enum rte_fib_op {
 	RTE_FIB_ADD,
@@ -110,6 +110,10 @@ struct rte_fib_conf {
 		} dir24_8;
 	};
 	unsigned int flags; /**< Optional feature flags from RTE_FIB_F_* */
+	/** Number of VRFs to support (0 or 1 = single VRF for backward compat) */
+	uint16_t max_vrfs;
+	/** Per-VRF default nexthops (NULL = use default_nh for all) */
+	uint64_t *vrf_default_nh;
 };
 
 /** FIB RCU QSBR configuration structure. */
@@ -224,6 +228,71 @@ rte_fib_delete(struct rte_fib *fib, uint32_t ip, uint8_t depth);
 int
 rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips,
 		uint64_t *next_hops, int n);
+
+/**
+ * Add a route to the FIB with VRF ID.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param vrf_id
+ *   VRF ID (0 to max_vrfs-1)
+ * @param ip
+ *   IPv4 prefix address to be added to the FIB
+ * @param depth
+ *   Prefix length
+ * @param next_hop
+ *   Next hop to be added to the FIB
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib_vrf_add(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip,
+	uint8_t depth, uint64_t next_hop);
+
+/**
+ * Delete a rule from the FIB with VRF ID.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param vrf_id
+ *   VRF ID (0 to max_vrfs-1)
+ * @param ip
+ *   IPv4 prefix address to be deleted from the FIB
+ * @param depth
+ *   Prefix length
+ * @return
+ *   0 on success, negative value otherwise
+ */
+__rte_experimental
+int
+rte_fib_vrf_delete(struct rte_fib *fib, uint16_t vrf_id, uint32_t ip,
+	uint8_t depth);
+
+/**
+ * Lookup multiple IP addresses in the FIB with per-packet VRF IDs.
+ *
+ * @param fib
+ *   FIB object handle
+ * @param vrf_ids
+ *   Array of VRF IDs
+ * @param ips
+ *   Array of IPs to be looked up in the FIB
+ * @param next_hops
+ *   Next hop of the most specific rule found for IP in the corresponding VRF.
+ *   This is an array of eight byte values.
+ *   If the lookup for the given IP failed, then corresponding element would
+ *   contain default nexthop value configured for that VRF.
+ * @param n
+ *   Number of elements in vrf_ids, ips (and next_hops) arrays to lookup.
+ * @return
+ *   -EINVAL for incorrect arguments, otherwise 0
+ */
+__rte_experimental
+int
+rte_fib_vrf_lookup_bulk(struct rte_fib *fib, const uint16_t *vrf_ids,
+	const uint32_t *ips, uint64_t *next_hops, int n);
+
 /**
  * Get pointer to the dataplane specific struct
  *
@@ -237,7 +306,7 @@ void *
 rte_fib_get_dp(struct rte_fib *fib);
 
 /**
- * Get pointer to the RIB
+ * Get pointer to the RIB for VRF 0
  *
  * @param fib
  *   FIB object handle
@@ -248,6 +317,21 @@ rte_fib_get_dp(struct rte_fib *fib);
 struct rte_rib *
 rte_fib_get_rib(struct rte_fib *fib);
 
+/**
+ * Get pointer to the RIB for a specific VRF
+ *
+ * @param fib
+ *   FIB object handle
+ * @param vrf_id
+ *   VRF ID (0 to max_vrfs-1)
+ * @return
+ *   Pointer on the RIB on success
+ *   NULL otherwise
+ */
+__rte_experimental
+struct rte_rib *
+rte_fib_vrf_get_rib(struct rte_fib *fib, uint16_t vrf_id);
+
 /**
  * Set lookup function based on type
  *
-- 
2.43.0



More information about the dev mailing list