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

Konstantin Ananyev konstantin.ananyev at huawei.com
Mon Mar 23 16:48:37 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.

Thanks Vladimir, allowing multiple VRFs per same LPM table will
definitely be a useful thing to have.
Though, I have the same concern as Maxime:
memory requirements are just overwhelming.
Stupid q - why just not to store a pointer to a vector of next-hops
within the table entry?
And we can provide to the user with ability to specify custom
alloc/free function for these vectors.
That would help to avoid allocating huge chunks of memory at startup.
I understand that it will be one extra memory dereference,
but probably it will be not that critical in terms of performance .
Again for bulk function  we might be able to pipeline lookups and
de-references and hide that extra load latency.  

> 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