xref: /dpdk/lib/lpm/rte_lpm6.c (revision e9fd1ebf981f361844aea9ec94e17f4bda5e1479)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson #include <string.h>
5*e9fd1ebfSTyler Retzlaff #include <stdalign.h>
699a2dd95SBruce Richardson #include <stdint.h>
799a2dd95SBruce Richardson #include <errno.h>
899a2dd95SBruce Richardson #include <stdio.h>
999a2dd95SBruce Richardson #include <sys/queue.h>
1099a2dd95SBruce Richardson 
1199a2dd95SBruce Richardson #include <rte_log.h>
1299a2dd95SBruce Richardson #include <rte_common.h>
1399a2dd95SBruce Richardson #include <rte_malloc.h>
1499a2dd95SBruce Richardson #include <rte_memcpy.h>
1599a2dd95SBruce Richardson #include <rte_eal_memconfig.h>
1699a2dd95SBruce Richardson #include <rte_string_fns.h>
1799a2dd95SBruce Richardson #include <rte_errno.h>
1899a2dd95SBruce Richardson #include <rte_hash.h>
1999a2dd95SBruce Richardson #include <assert.h>
2099a2dd95SBruce Richardson #include <rte_jhash.h>
2199a2dd95SBruce Richardson #include <rte_tailq.h>
2299a2dd95SBruce Richardson 
2399a2dd95SBruce Richardson #include "rte_lpm6.h"
24fdb83ffbSStephen Hemminger #include "lpm_log.h"
2599a2dd95SBruce Richardson 
2699a2dd95SBruce Richardson #define RTE_LPM6_TBL24_NUM_ENTRIES        (1 << 24)
2799a2dd95SBruce Richardson #define RTE_LPM6_TBL8_GROUP_NUM_ENTRIES         256
2899a2dd95SBruce Richardson #define RTE_LPM6_TBL8_MAX_NUM_GROUPS      (1 << 21)
2999a2dd95SBruce Richardson 
3099a2dd95SBruce Richardson #define RTE_LPM6_VALID_EXT_ENTRY_BITMASK 0xA0000000
3199a2dd95SBruce Richardson #define RTE_LPM6_LOOKUP_SUCCESS          0x20000000
3299a2dd95SBruce Richardson #define RTE_LPM6_TBL8_BITMASK            0x001FFFFF
3399a2dd95SBruce Richardson 
3499a2dd95SBruce Richardson #define ADD_FIRST_BYTE                            3
3599a2dd95SBruce Richardson #define LOOKUP_FIRST_BYTE                         4
3699a2dd95SBruce Richardson #define BYTE_SIZE                                 8
3799a2dd95SBruce Richardson #define BYTES2_SIZE                              16
3899a2dd95SBruce Richardson 
3999a2dd95SBruce Richardson #define RULE_HASH_TABLE_EXTRA_SPACE              64
4099a2dd95SBruce Richardson #define TBL24_IND                        UINT32_MAX
4199a2dd95SBruce Richardson 
4299a2dd95SBruce Richardson #define lpm6_tbl8_gindex next_hop
4399a2dd95SBruce Richardson 
4499a2dd95SBruce Richardson /** Flags for setting an entry as valid/invalid. */
4599a2dd95SBruce Richardson enum valid_flag {
4699a2dd95SBruce Richardson 	INVALID = 0,
4799a2dd95SBruce Richardson 	VALID
4899a2dd95SBruce Richardson };
4999a2dd95SBruce Richardson 
5099a2dd95SBruce Richardson TAILQ_HEAD(rte_lpm6_list, rte_tailq_entry);
5199a2dd95SBruce Richardson 
5299a2dd95SBruce Richardson static struct rte_tailq_elem rte_lpm6_tailq = {
5399a2dd95SBruce Richardson 	.name = "RTE_LPM6",
5499a2dd95SBruce Richardson };
5599a2dd95SBruce Richardson EAL_REGISTER_TAILQ(rte_lpm6_tailq)
5699a2dd95SBruce Richardson 
5799a2dd95SBruce Richardson /** Tbl entry structure. It is the same for both tbl24 and tbl8 */
5899a2dd95SBruce Richardson struct rte_lpm6_tbl_entry {
5999a2dd95SBruce Richardson 	uint32_t next_hop:	21;  /**< Next hop / next table to be checked. */
6099a2dd95SBruce Richardson 	uint32_t depth	:8;      /**< Rule depth. */
6199a2dd95SBruce Richardson 
6299a2dd95SBruce Richardson 	/* Flags. */
6399a2dd95SBruce Richardson 	uint32_t valid     :1;   /**< Validation flag. */
6499a2dd95SBruce Richardson 	uint32_t valid_group :1; /**< Group validation flag. */
6599a2dd95SBruce Richardson 	uint32_t ext_entry :1;   /**< External entry. */
6699a2dd95SBruce Richardson };
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson /** Rules tbl entry structure. */
6999a2dd95SBruce Richardson struct rte_lpm6_rule {
7099a2dd95SBruce Richardson 	uint8_t ip[RTE_LPM6_IPV6_ADDR_SIZE]; /**< Rule IP address. */
7199a2dd95SBruce Richardson 	uint32_t next_hop; /**< Rule next hop. */
7299a2dd95SBruce Richardson 	uint8_t depth; /**< Rule depth. */
7399a2dd95SBruce Richardson };
7499a2dd95SBruce Richardson 
7599a2dd95SBruce Richardson /** Rules tbl entry key. */
7699a2dd95SBruce Richardson struct rte_lpm6_rule_key {
7799a2dd95SBruce Richardson 	uint8_t ip[RTE_LPM6_IPV6_ADDR_SIZE]; /**< Rule IP address. */
78b16ac536SVladimir Medvedkin 	uint32_t depth; /**< Rule depth. */
7999a2dd95SBruce Richardson };
8099a2dd95SBruce Richardson 
8199a2dd95SBruce Richardson /* Header of tbl8 */
8299a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr {
8399a2dd95SBruce Richardson 	uint32_t owner_tbl_ind; /**< owner table: TBL24_IND if owner is tbl24,
8499a2dd95SBruce Richardson 				  *  otherwise index of tbl8
8599a2dd95SBruce Richardson 				  */
8699a2dd95SBruce Richardson 	uint32_t owner_entry_ind; /**< index of the owner table entry where
8799a2dd95SBruce Richardson 				    *  pointer to the tbl8 is stored
8899a2dd95SBruce Richardson 				    */
8999a2dd95SBruce Richardson 	uint32_t ref_cnt; /**< table reference counter */
9099a2dd95SBruce Richardson };
9199a2dd95SBruce Richardson 
9299a2dd95SBruce Richardson /** LPM6 structure. */
9399a2dd95SBruce Richardson struct rte_lpm6 {
9499a2dd95SBruce Richardson 	/* LPM metadata. */
9599a2dd95SBruce Richardson 	char name[RTE_LPM6_NAMESIZE];    /**< Name of the lpm. */
9699a2dd95SBruce Richardson 	uint32_t max_rules;              /**< Max number of rules. */
9799a2dd95SBruce Richardson 	uint32_t used_rules;             /**< Used rules so far. */
9899a2dd95SBruce Richardson 	uint32_t number_tbl8s;           /**< Number of tbl8s to allocate. */
9999a2dd95SBruce Richardson 
10099a2dd95SBruce Richardson 	/* LPM Tables. */
10199a2dd95SBruce Richardson 	struct rte_hash *rules_tbl; /**< LPM rules. */
102*e9fd1ebfSTyler Retzlaff 	alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl24[RTE_LPM6_TBL24_NUM_ENTRIES];
103*e9fd1ebfSTyler Retzlaff 			/**< LPM tbl24 table. */
10499a2dd95SBruce Richardson 
10599a2dd95SBruce Richardson 	uint32_t *tbl8_pool; /**< pool of indexes of free tbl8s */
10699a2dd95SBruce Richardson 	uint32_t tbl8_pool_pos; /**< current position in the tbl8 pool */
10799a2dd95SBruce Richardson 
10899a2dd95SBruce Richardson 	struct rte_lpm_tbl8_hdr *tbl8_hdrs; /* array of tbl8 headers */
10999a2dd95SBruce Richardson 
110*e9fd1ebfSTyler Retzlaff 	alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl8[0];
111*e9fd1ebfSTyler Retzlaff 			/**< LPM tbl8 table. */
11299a2dd95SBruce Richardson };
11399a2dd95SBruce Richardson 
11499a2dd95SBruce Richardson /*
11599a2dd95SBruce Richardson  * Takes an array of uint8_t (IPv6 address) and masks it using the depth.
11699a2dd95SBruce Richardson  * It leaves untouched one bit per unit in the depth variable
11799a2dd95SBruce Richardson  * and set the rest to 0.
11899a2dd95SBruce Richardson  */
11999a2dd95SBruce Richardson static inline void
12099a2dd95SBruce Richardson ip6_mask_addr(uint8_t *ip, uint8_t depth)
12199a2dd95SBruce Richardson {
12299a2dd95SBruce Richardson 	int16_t part_depth, mask;
12399a2dd95SBruce Richardson 	int i;
12499a2dd95SBruce Richardson 
12599a2dd95SBruce Richardson 	part_depth = depth;
12699a2dd95SBruce Richardson 
12799a2dd95SBruce Richardson 	for (i = 0; i < RTE_LPM6_IPV6_ADDR_SIZE; i++) {
12899a2dd95SBruce Richardson 		if (part_depth < BYTE_SIZE && part_depth >= 0) {
12999a2dd95SBruce Richardson 			mask = (uint16_t)(~(UINT8_MAX >> part_depth));
13099a2dd95SBruce Richardson 			ip[i] = (uint8_t)(ip[i] & mask);
13199a2dd95SBruce Richardson 		} else if (part_depth < 0)
13299a2dd95SBruce Richardson 			ip[i] = 0;
13399a2dd95SBruce Richardson 
13499a2dd95SBruce Richardson 		part_depth -= BYTE_SIZE;
13599a2dd95SBruce Richardson 	}
13699a2dd95SBruce Richardson }
13799a2dd95SBruce Richardson 
13899a2dd95SBruce Richardson /* copy ipv6 address */
13999a2dd95SBruce Richardson static inline void
14099a2dd95SBruce Richardson ip6_copy_addr(uint8_t *dst, const uint8_t *src)
14199a2dd95SBruce Richardson {
14299a2dd95SBruce Richardson 	rte_memcpy(dst, src, RTE_LPM6_IPV6_ADDR_SIZE);
14399a2dd95SBruce Richardson }
14499a2dd95SBruce Richardson 
14599a2dd95SBruce Richardson /*
14699a2dd95SBruce Richardson  * LPM6 rule hash function
14799a2dd95SBruce Richardson  *
14899a2dd95SBruce Richardson  * It's used as a hash function for the rte_hash
14999a2dd95SBruce Richardson  *	containing rules
15099a2dd95SBruce Richardson  */
15199a2dd95SBruce Richardson static inline uint32_t
15299a2dd95SBruce Richardson rule_hash(const void *data, __rte_unused uint32_t data_len,
15399a2dd95SBruce Richardson 		  uint32_t init_val)
15499a2dd95SBruce Richardson {
15599a2dd95SBruce Richardson 	return rte_jhash(data, sizeof(struct rte_lpm6_rule_key), init_val);
15699a2dd95SBruce Richardson }
15799a2dd95SBruce Richardson 
15899a2dd95SBruce Richardson /*
15999a2dd95SBruce Richardson  * Init pool of free tbl8 indexes
16099a2dd95SBruce Richardson  */
16199a2dd95SBruce Richardson static void
16299a2dd95SBruce Richardson tbl8_pool_init(struct rte_lpm6 *lpm)
16399a2dd95SBruce Richardson {
16499a2dd95SBruce Richardson 	uint32_t i;
16599a2dd95SBruce Richardson 
16699a2dd95SBruce Richardson 	/* put entire range of indexes to the tbl8 pool */
16799a2dd95SBruce Richardson 	for (i = 0; i < lpm->number_tbl8s; i++)
16899a2dd95SBruce Richardson 		lpm->tbl8_pool[i] = i;
16999a2dd95SBruce Richardson 
17099a2dd95SBruce Richardson 	lpm->tbl8_pool_pos = 0;
17199a2dd95SBruce Richardson }
17299a2dd95SBruce Richardson 
17399a2dd95SBruce Richardson /*
17499a2dd95SBruce Richardson  * Get an index of a free tbl8 from the pool
17599a2dd95SBruce Richardson  */
17699a2dd95SBruce Richardson static inline uint32_t
17799a2dd95SBruce Richardson tbl8_get(struct rte_lpm6 *lpm, uint32_t *tbl8_ind)
17899a2dd95SBruce Richardson {
17999a2dd95SBruce Richardson 	if (lpm->tbl8_pool_pos == lpm->number_tbl8s)
18099a2dd95SBruce Richardson 		/* no more free tbl8 */
18199a2dd95SBruce Richardson 		return -ENOSPC;
18299a2dd95SBruce Richardson 
18399a2dd95SBruce Richardson 	/* next index */
18499a2dd95SBruce Richardson 	*tbl8_ind = lpm->tbl8_pool[lpm->tbl8_pool_pos++];
18599a2dd95SBruce Richardson 	return 0;
18699a2dd95SBruce Richardson }
18799a2dd95SBruce Richardson 
18899a2dd95SBruce Richardson /*
18999a2dd95SBruce Richardson  * Put an index of a free tbl8 back to the pool
19099a2dd95SBruce Richardson  */
19199a2dd95SBruce Richardson static inline uint32_t
19299a2dd95SBruce Richardson tbl8_put(struct rte_lpm6 *lpm, uint32_t tbl8_ind)
19399a2dd95SBruce Richardson {
19499a2dd95SBruce Richardson 	if (lpm->tbl8_pool_pos == 0)
19599a2dd95SBruce Richardson 		/* pool is full */
19699a2dd95SBruce Richardson 		return -ENOSPC;
19799a2dd95SBruce Richardson 
19899a2dd95SBruce Richardson 	lpm->tbl8_pool[--lpm->tbl8_pool_pos] = tbl8_ind;
19999a2dd95SBruce Richardson 	return 0;
20099a2dd95SBruce Richardson }
20199a2dd95SBruce Richardson 
20299a2dd95SBruce Richardson /*
20399a2dd95SBruce Richardson  * Returns number of tbl8s available in the pool
20499a2dd95SBruce Richardson  */
20599a2dd95SBruce Richardson static inline uint32_t
20699a2dd95SBruce Richardson tbl8_available(struct rte_lpm6 *lpm)
20799a2dd95SBruce Richardson {
20899a2dd95SBruce Richardson 	return lpm->number_tbl8s - lpm->tbl8_pool_pos;
20999a2dd95SBruce Richardson }
21099a2dd95SBruce Richardson 
21199a2dd95SBruce Richardson /*
21299a2dd95SBruce Richardson  * Init a rule key.
21399a2dd95SBruce Richardson  *	  note that ip must be already masked
21499a2dd95SBruce Richardson  */
21599a2dd95SBruce Richardson static inline void
21699a2dd95SBruce Richardson rule_key_init(struct rte_lpm6_rule_key *key, uint8_t *ip, uint8_t depth)
21799a2dd95SBruce Richardson {
21899a2dd95SBruce Richardson 	ip6_copy_addr(key->ip, ip);
21999a2dd95SBruce Richardson 	key->depth = depth;
22099a2dd95SBruce Richardson }
22199a2dd95SBruce Richardson 
22299a2dd95SBruce Richardson /*
22399a2dd95SBruce Richardson  * Rebuild the entire LPM tree by reinserting all rules
22499a2dd95SBruce Richardson  */
22599a2dd95SBruce Richardson static void
22699a2dd95SBruce Richardson rebuild_lpm(struct rte_lpm6 *lpm)
22799a2dd95SBruce Richardson {
22899a2dd95SBruce Richardson 	uint64_t next_hop;
22999a2dd95SBruce Richardson 	struct rte_lpm6_rule_key *rule_key;
23099a2dd95SBruce Richardson 	uint32_t iter = 0;
23199a2dd95SBruce Richardson 
23299a2dd95SBruce Richardson 	while (rte_hash_iterate(lpm->rules_tbl, (void *) &rule_key,
23399a2dd95SBruce Richardson 			(void **) &next_hop, &iter) >= 0)
23499a2dd95SBruce Richardson 		rte_lpm6_add(lpm, rule_key->ip, rule_key->depth,
23599a2dd95SBruce Richardson 			(uint32_t) next_hop);
23699a2dd95SBruce Richardson }
23799a2dd95SBruce Richardson 
23899a2dd95SBruce Richardson /*
23999a2dd95SBruce Richardson  * Allocates memory for LPM object
24099a2dd95SBruce Richardson  */
24199a2dd95SBruce Richardson struct rte_lpm6 *
24299a2dd95SBruce Richardson rte_lpm6_create(const char *name, int socket_id,
24399a2dd95SBruce Richardson 		const struct rte_lpm6_config *config)
24499a2dd95SBruce Richardson {
24599a2dd95SBruce Richardson 	char mem_name[RTE_LPM6_NAMESIZE];
24699a2dd95SBruce Richardson 	struct rte_lpm6 *lpm = NULL;
24799a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
24899a2dd95SBruce Richardson 	uint64_t mem_size;
24999a2dd95SBruce Richardson 	struct rte_lpm6_list *lpm_list;
25099a2dd95SBruce Richardson 	struct rte_hash *rules_tbl = NULL;
25199a2dd95SBruce Richardson 	uint32_t *tbl8_pool = NULL;
25299a2dd95SBruce Richardson 	struct rte_lpm_tbl8_hdr *tbl8_hdrs = NULL;
25399a2dd95SBruce Richardson 
25499a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
25599a2dd95SBruce Richardson 
25699a2dd95SBruce Richardson 	RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_tbl_entry) != sizeof(uint32_t));
257b16ac536SVladimir Medvedkin 	RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_rule_key) %
258b16ac536SVladimir Medvedkin 		sizeof(uint32_t) != 0);
25999a2dd95SBruce Richardson 
26099a2dd95SBruce Richardson 	/* Check user arguments. */
26199a2dd95SBruce Richardson 	if ((name == NULL) || (socket_id < -1) || (config == NULL) ||
26299a2dd95SBruce Richardson 			(config->max_rules == 0) ||
26399a2dd95SBruce Richardson 			config->number_tbl8s > RTE_LPM6_TBL8_MAX_NUM_GROUPS) {
26499a2dd95SBruce Richardson 		rte_errno = EINVAL;
26599a2dd95SBruce Richardson 		return NULL;
26699a2dd95SBruce Richardson 	}
26799a2dd95SBruce Richardson 
26899a2dd95SBruce Richardson 	/* create rules hash table */
26999a2dd95SBruce Richardson 	snprintf(mem_name, sizeof(mem_name), "LRH_%s", name);
27099a2dd95SBruce Richardson 	struct rte_hash_parameters rule_hash_tbl_params = {
27199a2dd95SBruce Richardson 		.entries = config->max_rules * 1.2 +
27299a2dd95SBruce Richardson 			RULE_HASH_TABLE_EXTRA_SPACE,
27399a2dd95SBruce Richardson 		.key_len = sizeof(struct rte_lpm6_rule_key),
27499a2dd95SBruce Richardson 		.hash_func = rule_hash,
27599a2dd95SBruce Richardson 		.hash_func_init_val = 0,
27699a2dd95SBruce Richardson 		.name = mem_name,
27799a2dd95SBruce Richardson 		.reserved = 0,
27899a2dd95SBruce Richardson 		.socket_id = socket_id,
27999a2dd95SBruce Richardson 		.extra_flag = 0
28099a2dd95SBruce Richardson 	};
28199a2dd95SBruce Richardson 
28299a2dd95SBruce Richardson 	rules_tbl = rte_hash_create(&rule_hash_tbl_params);
28399a2dd95SBruce Richardson 	if (rules_tbl == NULL) {
284ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM rules hash table allocation failed: %s (%d)",
28599a2dd95SBruce Richardson 				  rte_strerror(rte_errno), rte_errno);
28699a2dd95SBruce Richardson 		goto fail_wo_unlock;
28799a2dd95SBruce Richardson 	}
28899a2dd95SBruce Richardson 
28999a2dd95SBruce Richardson 	/* allocate tbl8 indexes pool */
29099a2dd95SBruce Richardson 	tbl8_pool = rte_malloc(NULL,
29199a2dd95SBruce Richardson 			sizeof(uint32_t) * config->number_tbl8s,
29299a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE);
29399a2dd95SBruce Richardson 	if (tbl8_pool == NULL) {
294ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM tbl8 pool allocation failed: %s (%d)",
29599a2dd95SBruce Richardson 				  rte_strerror(rte_errno), rte_errno);
29699a2dd95SBruce Richardson 		rte_errno = ENOMEM;
29799a2dd95SBruce Richardson 		goto fail_wo_unlock;
29899a2dd95SBruce Richardson 	}
29999a2dd95SBruce Richardson 
30099a2dd95SBruce Richardson 	/* allocate tbl8 headers */
30199a2dd95SBruce Richardson 	tbl8_hdrs = rte_malloc(NULL,
30299a2dd95SBruce Richardson 			sizeof(struct rte_lpm_tbl8_hdr) * config->number_tbl8s,
30399a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE);
30499a2dd95SBruce Richardson 	if (tbl8_hdrs == NULL) {
305ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM tbl8 headers allocation failed: %s (%d)",
30699a2dd95SBruce Richardson 				  rte_strerror(rte_errno), rte_errno);
30799a2dd95SBruce Richardson 		rte_errno = ENOMEM;
30899a2dd95SBruce Richardson 		goto fail_wo_unlock;
30999a2dd95SBruce Richardson 	}
31099a2dd95SBruce Richardson 
31199a2dd95SBruce Richardson 	snprintf(mem_name, sizeof(mem_name), "LPM_%s", name);
31299a2dd95SBruce Richardson 
31399a2dd95SBruce Richardson 	/* Determine the amount of memory to allocate. */
31499a2dd95SBruce Richardson 	mem_size = sizeof(*lpm) + (sizeof(lpm->tbl8[0]) *
31599a2dd95SBruce Richardson 			RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * config->number_tbl8s);
31699a2dd95SBruce Richardson 
31799a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
31899a2dd95SBruce Richardson 
31999a2dd95SBruce Richardson 	/* Guarantee there's no existing */
32099a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
32199a2dd95SBruce Richardson 		lpm = (struct rte_lpm6 *) te->data;
32299a2dd95SBruce Richardson 		if (strncmp(name, lpm->name, RTE_LPM6_NAMESIZE) == 0)
32399a2dd95SBruce Richardson 			break;
32499a2dd95SBruce Richardson 	}
32599a2dd95SBruce Richardson 	lpm = NULL;
32699a2dd95SBruce Richardson 	if (te != NULL) {
32799a2dd95SBruce Richardson 		rte_errno = EEXIST;
32899a2dd95SBruce Richardson 		goto fail;
32999a2dd95SBruce Richardson 	}
33099a2dd95SBruce Richardson 
33199a2dd95SBruce Richardson 	/* allocate tailq entry */
33299a2dd95SBruce Richardson 	te = rte_zmalloc("LPM6_TAILQ_ENTRY", sizeof(*te), 0);
33399a2dd95SBruce Richardson 	if (te == NULL) {
334ae67895bSDavid Marchand 		LPM_LOG(ERR, "Failed to allocate tailq entry!");
33599a2dd95SBruce Richardson 		rte_errno = ENOMEM;
33699a2dd95SBruce Richardson 		goto fail;
33799a2dd95SBruce Richardson 	}
33899a2dd95SBruce Richardson 
33999a2dd95SBruce Richardson 	/* Allocate memory to store the LPM data structures. */
34099a2dd95SBruce Richardson 	lpm = rte_zmalloc_socket(mem_name, (size_t)mem_size,
34199a2dd95SBruce Richardson 			RTE_CACHE_LINE_SIZE, socket_id);
34299a2dd95SBruce Richardson 
34399a2dd95SBruce Richardson 	if (lpm == NULL) {
344ae67895bSDavid Marchand 		LPM_LOG(ERR, "LPM memory allocation failed");
34599a2dd95SBruce Richardson 		rte_free(te);
34699a2dd95SBruce Richardson 		rte_errno = ENOMEM;
34799a2dd95SBruce Richardson 		goto fail;
34899a2dd95SBruce Richardson 	}
34999a2dd95SBruce Richardson 
35099a2dd95SBruce Richardson 	/* Save user arguments. */
35199a2dd95SBruce Richardson 	lpm->max_rules = config->max_rules;
35299a2dd95SBruce Richardson 	lpm->number_tbl8s = config->number_tbl8s;
35399a2dd95SBruce Richardson 	strlcpy(lpm->name, name, sizeof(lpm->name));
35499a2dd95SBruce Richardson 	lpm->rules_tbl = rules_tbl;
35599a2dd95SBruce Richardson 	lpm->tbl8_pool = tbl8_pool;
35699a2dd95SBruce Richardson 	lpm->tbl8_hdrs = tbl8_hdrs;
35799a2dd95SBruce Richardson 
35899a2dd95SBruce Richardson 	/* init the stack */
35999a2dd95SBruce Richardson 	tbl8_pool_init(lpm);
36099a2dd95SBruce Richardson 
36199a2dd95SBruce Richardson 	te->data = (void *) lpm;
36299a2dd95SBruce Richardson 
36399a2dd95SBruce Richardson 	TAILQ_INSERT_TAIL(lpm_list, te, next);
36499a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
36599a2dd95SBruce Richardson 	return lpm;
36699a2dd95SBruce Richardson 
36799a2dd95SBruce Richardson fail:
36899a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
36999a2dd95SBruce Richardson 
37099a2dd95SBruce Richardson fail_wo_unlock:
37199a2dd95SBruce Richardson 	rte_free(tbl8_hdrs);
37299a2dd95SBruce Richardson 	rte_free(tbl8_pool);
37399a2dd95SBruce Richardson 	rte_hash_free(rules_tbl);
37499a2dd95SBruce Richardson 
37599a2dd95SBruce Richardson 	return NULL;
37699a2dd95SBruce Richardson }
37799a2dd95SBruce Richardson 
37899a2dd95SBruce Richardson /*
37999a2dd95SBruce Richardson  * Find an existing lpm table and return a pointer to it.
38099a2dd95SBruce Richardson  */
38199a2dd95SBruce Richardson struct rte_lpm6 *
38299a2dd95SBruce Richardson rte_lpm6_find_existing(const char *name)
38399a2dd95SBruce Richardson {
38499a2dd95SBruce Richardson 	struct rte_lpm6 *l = NULL;
38599a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
38699a2dd95SBruce Richardson 	struct rte_lpm6_list *lpm_list;
38799a2dd95SBruce Richardson 
38899a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
38999a2dd95SBruce Richardson 
39099a2dd95SBruce Richardson 	rte_mcfg_tailq_read_lock();
39199a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
39299a2dd95SBruce Richardson 		l = (struct rte_lpm6 *) te->data;
39399a2dd95SBruce Richardson 		if (strncmp(name, l->name, RTE_LPM6_NAMESIZE) == 0)
39499a2dd95SBruce Richardson 			break;
39599a2dd95SBruce Richardson 	}
39699a2dd95SBruce Richardson 	rte_mcfg_tailq_read_unlock();
39799a2dd95SBruce Richardson 
39899a2dd95SBruce Richardson 	if (te == NULL) {
39999a2dd95SBruce Richardson 		rte_errno = ENOENT;
40099a2dd95SBruce Richardson 		return NULL;
40199a2dd95SBruce Richardson 	}
40299a2dd95SBruce Richardson 
40399a2dd95SBruce Richardson 	return l;
40499a2dd95SBruce Richardson }
40599a2dd95SBruce Richardson 
40699a2dd95SBruce Richardson /*
40799a2dd95SBruce Richardson  * Deallocates memory for given LPM table.
40899a2dd95SBruce Richardson  */
40999a2dd95SBruce Richardson void
41099a2dd95SBruce Richardson rte_lpm6_free(struct rte_lpm6 *lpm)
41199a2dd95SBruce Richardson {
41299a2dd95SBruce Richardson 	struct rte_lpm6_list *lpm_list;
41399a2dd95SBruce Richardson 	struct rte_tailq_entry *te;
41499a2dd95SBruce Richardson 
41599a2dd95SBruce Richardson 	/* Check user arguments. */
41699a2dd95SBruce Richardson 	if (lpm == NULL)
41799a2dd95SBruce Richardson 		return;
41899a2dd95SBruce Richardson 
41999a2dd95SBruce Richardson 	lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
42099a2dd95SBruce Richardson 
42199a2dd95SBruce Richardson 	rte_mcfg_tailq_write_lock();
42299a2dd95SBruce Richardson 
42399a2dd95SBruce Richardson 	/* find our tailq entry */
42499a2dd95SBruce Richardson 	TAILQ_FOREACH(te, lpm_list, next) {
42599a2dd95SBruce Richardson 		if (te->data == (void *) lpm)
42699a2dd95SBruce Richardson 			break;
42799a2dd95SBruce Richardson 	}
42899a2dd95SBruce Richardson 
42999a2dd95SBruce Richardson 	if (te != NULL)
43099a2dd95SBruce Richardson 		TAILQ_REMOVE(lpm_list, te, next);
43199a2dd95SBruce Richardson 
43299a2dd95SBruce Richardson 	rte_mcfg_tailq_write_unlock();
43399a2dd95SBruce Richardson 
43499a2dd95SBruce Richardson 	rte_free(lpm->tbl8_hdrs);
43599a2dd95SBruce Richardson 	rte_free(lpm->tbl8_pool);
43699a2dd95SBruce Richardson 	rte_hash_free(lpm->rules_tbl);
43799a2dd95SBruce Richardson 	rte_free(lpm);
43899a2dd95SBruce Richardson 	rte_free(te);
43999a2dd95SBruce Richardson }
44099a2dd95SBruce Richardson 
44199a2dd95SBruce Richardson /* Find a rule */
44299a2dd95SBruce Richardson static inline int
44399a2dd95SBruce Richardson rule_find_with_key(struct rte_lpm6 *lpm,
44499a2dd95SBruce Richardson 		  const struct rte_lpm6_rule_key *rule_key,
44599a2dd95SBruce Richardson 		  uint32_t *next_hop)
44699a2dd95SBruce Richardson {
44799a2dd95SBruce Richardson 	uint64_t hash_val;
44899a2dd95SBruce Richardson 	int ret;
44999a2dd95SBruce Richardson 
45099a2dd95SBruce Richardson 	/* lookup for a rule */
45199a2dd95SBruce Richardson 	ret = rte_hash_lookup_data(lpm->rules_tbl, (const void *) rule_key,
45299a2dd95SBruce Richardson 		(void **) &hash_val);
45399a2dd95SBruce Richardson 	if (ret >= 0) {
45499a2dd95SBruce Richardson 		*next_hop = (uint32_t) hash_val;
45599a2dd95SBruce Richardson 		return 1;
45699a2dd95SBruce Richardson 	}
45799a2dd95SBruce Richardson 
45899a2dd95SBruce Richardson 	return 0;
45999a2dd95SBruce Richardson }
46099a2dd95SBruce Richardson 
46199a2dd95SBruce Richardson /* Find a rule */
46299a2dd95SBruce Richardson static int
46399a2dd95SBruce Richardson rule_find(struct rte_lpm6 *lpm, uint8_t *ip, uint8_t depth,
46499a2dd95SBruce Richardson 		  uint32_t *next_hop)
46599a2dd95SBruce Richardson {
46699a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
46799a2dd95SBruce Richardson 
46899a2dd95SBruce Richardson 	/* init a rule key */
46999a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
47099a2dd95SBruce Richardson 
47199a2dd95SBruce Richardson 	return rule_find_with_key(lpm, &rule_key, next_hop);
47299a2dd95SBruce Richardson }
47399a2dd95SBruce Richardson 
47499a2dd95SBruce Richardson /*
47599a2dd95SBruce Richardson  * Checks if a rule already exists in the rules table and updates
47699a2dd95SBruce Richardson  * the nexthop if so. Otherwise it adds a new rule if enough space is available.
47799a2dd95SBruce Richardson  *
47899a2dd95SBruce Richardson  * Returns:
47999a2dd95SBruce Richardson  *    0 - next hop of existed rule is updated
48099a2dd95SBruce Richardson  *    1 - new rule successfully added
48199a2dd95SBruce Richardson  *   <0 - error
48299a2dd95SBruce Richardson  */
48399a2dd95SBruce Richardson static inline int
48499a2dd95SBruce Richardson rule_add(struct rte_lpm6 *lpm, uint8_t *ip, uint8_t depth, uint32_t next_hop)
48599a2dd95SBruce Richardson {
48699a2dd95SBruce Richardson 	int ret, rule_exist;
48799a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
48899a2dd95SBruce Richardson 	uint32_t unused;
48999a2dd95SBruce Richardson 
49099a2dd95SBruce Richardson 	/* init a rule key */
49199a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
49299a2dd95SBruce Richardson 
49399a2dd95SBruce Richardson 	/* Scan through rule list to see if rule already exists. */
49499a2dd95SBruce Richardson 	rule_exist = rule_find_with_key(lpm, &rule_key, &unused);
49599a2dd95SBruce Richardson 
49699a2dd95SBruce Richardson 	/*
49799a2dd95SBruce Richardson 	 * If rule does not exist check if there is space to add a new rule to
49899a2dd95SBruce Richardson 	 * this rule group. If there is no space return error.
49999a2dd95SBruce Richardson 	 */
50099a2dd95SBruce Richardson 	if (!rule_exist && lpm->used_rules == lpm->max_rules)
50199a2dd95SBruce Richardson 		return -ENOSPC;
50299a2dd95SBruce Richardson 
50399a2dd95SBruce Richardson 	/* add the rule or update rules next hop */
50499a2dd95SBruce Richardson 	ret = rte_hash_add_key_data(lpm->rules_tbl, &rule_key,
50599a2dd95SBruce Richardson 		(void *)(uintptr_t) next_hop);
50699a2dd95SBruce Richardson 	if (ret < 0)
50799a2dd95SBruce Richardson 		return ret;
50899a2dd95SBruce Richardson 
50999a2dd95SBruce Richardson 	/* Increment the used rules counter for this rule group. */
51099a2dd95SBruce Richardson 	if (!rule_exist) {
51199a2dd95SBruce Richardson 		lpm->used_rules++;
51299a2dd95SBruce Richardson 		return 1;
51399a2dd95SBruce Richardson 	}
51499a2dd95SBruce Richardson 
51599a2dd95SBruce Richardson 	return 0;
51699a2dd95SBruce Richardson }
51799a2dd95SBruce Richardson 
51899a2dd95SBruce Richardson /*
51999a2dd95SBruce Richardson  * Function that expands a rule across the data structure when a less-generic
52099a2dd95SBruce Richardson  * one has been added before. It assures that every possible combination of bits
52199a2dd95SBruce Richardson  * in the IP address returns a match.
52299a2dd95SBruce Richardson  */
52399a2dd95SBruce Richardson static void
52499a2dd95SBruce Richardson expand_rule(struct rte_lpm6 *lpm, uint32_t tbl8_gindex, uint8_t old_depth,
52599a2dd95SBruce Richardson 		uint8_t new_depth, uint32_t next_hop, uint8_t valid)
52699a2dd95SBruce Richardson {
52799a2dd95SBruce Richardson 	uint32_t tbl8_group_end, tbl8_gindex_next, j;
52899a2dd95SBruce Richardson 
52999a2dd95SBruce Richardson 	tbl8_group_end = tbl8_gindex + RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
53099a2dd95SBruce Richardson 
53199a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry new_tbl8_entry = {
53299a2dd95SBruce Richardson 		.valid = valid,
53399a2dd95SBruce Richardson 		.valid_group = valid,
53499a2dd95SBruce Richardson 		.depth = new_depth,
53599a2dd95SBruce Richardson 		.next_hop = next_hop,
53699a2dd95SBruce Richardson 		.ext_entry = 0,
53799a2dd95SBruce Richardson 	};
53899a2dd95SBruce Richardson 
53999a2dd95SBruce Richardson 	for (j = tbl8_gindex; j < tbl8_group_end; j++) {
54099a2dd95SBruce Richardson 		if (!lpm->tbl8[j].valid || (lpm->tbl8[j].ext_entry == 0
54199a2dd95SBruce Richardson 				&& lpm->tbl8[j].depth <= old_depth)) {
54299a2dd95SBruce Richardson 
54399a2dd95SBruce Richardson 			lpm->tbl8[j] = new_tbl8_entry;
54499a2dd95SBruce Richardson 
54599a2dd95SBruce Richardson 		} else if (lpm->tbl8[j].ext_entry == 1) {
54699a2dd95SBruce Richardson 
54799a2dd95SBruce Richardson 			tbl8_gindex_next = lpm->tbl8[j].lpm6_tbl8_gindex
54899a2dd95SBruce Richardson 					* RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
54999a2dd95SBruce Richardson 			expand_rule(lpm, tbl8_gindex_next, old_depth, new_depth,
55099a2dd95SBruce Richardson 					next_hop, valid);
55199a2dd95SBruce Richardson 		}
55299a2dd95SBruce Richardson 	}
55399a2dd95SBruce Richardson }
55499a2dd95SBruce Richardson 
55599a2dd95SBruce Richardson /*
55699a2dd95SBruce Richardson  * Init a tbl8 header
55799a2dd95SBruce Richardson  */
55899a2dd95SBruce Richardson static inline void
55999a2dd95SBruce Richardson init_tbl8_header(struct rte_lpm6 *lpm, uint32_t tbl_ind,
56099a2dd95SBruce Richardson 		uint32_t owner_tbl_ind, uint32_t owner_entry_ind)
56199a2dd95SBruce Richardson {
56299a2dd95SBruce Richardson 	struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind];
56399a2dd95SBruce Richardson 	tbl_hdr->owner_tbl_ind = owner_tbl_ind;
56499a2dd95SBruce Richardson 	tbl_hdr->owner_entry_ind = owner_entry_ind;
56599a2dd95SBruce Richardson 	tbl_hdr->ref_cnt = 0;
56699a2dd95SBruce Richardson }
56799a2dd95SBruce Richardson 
56899a2dd95SBruce Richardson /*
56999a2dd95SBruce Richardson  * Calculate index to the table based on the number and position
57099a2dd95SBruce Richardson  * of the bytes being inspected in this step.
57199a2dd95SBruce Richardson  */
57299a2dd95SBruce Richardson static uint32_t
57399a2dd95SBruce Richardson get_bitshift(const uint8_t *ip, uint8_t first_byte, uint8_t bytes)
57499a2dd95SBruce Richardson {
57599a2dd95SBruce Richardson 	uint32_t entry_ind, i;
57699a2dd95SBruce Richardson 	int8_t bitshift;
57799a2dd95SBruce Richardson 
57899a2dd95SBruce Richardson 	entry_ind = 0;
57999a2dd95SBruce Richardson 	for (i = first_byte; i < (uint32_t)(first_byte + bytes); i++) {
58099a2dd95SBruce Richardson 		bitshift = (int8_t)((bytes - i)*BYTE_SIZE);
58199a2dd95SBruce Richardson 
58299a2dd95SBruce Richardson 		if (bitshift < 0)
58399a2dd95SBruce Richardson 			bitshift = 0;
58499a2dd95SBruce Richardson 		entry_ind = entry_ind | ip[i-1] << bitshift;
58599a2dd95SBruce Richardson 	}
58699a2dd95SBruce Richardson 
58799a2dd95SBruce Richardson 	return entry_ind;
58899a2dd95SBruce Richardson }
58999a2dd95SBruce Richardson 
59099a2dd95SBruce Richardson /*
59199a2dd95SBruce Richardson  * Simulate adding a new route to the LPM counting number
59299a2dd95SBruce Richardson  * of new tables that will be needed
59399a2dd95SBruce Richardson  *
59499a2dd95SBruce Richardson  * It returns 0 on success, or 1 if
59599a2dd95SBruce Richardson  * the process needs to be continued by calling the function again.
59699a2dd95SBruce Richardson  */
59799a2dd95SBruce Richardson static inline int
59899a2dd95SBruce Richardson simulate_add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl,
59999a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry **next_tbl, const uint8_t *ip,
60099a2dd95SBruce Richardson 		uint8_t bytes, uint8_t first_byte, uint8_t depth,
60199a2dd95SBruce Richardson 		uint32_t *need_tbl_nb)
60299a2dd95SBruce Richardson {
60399a2dd95SBruce Richardson 	uint32_t entry_ind;
60499a2dd95SBruce Richardson 	uint8_t bits_covered;
60599a2dd95SBruce Richardson 	uint32_t next_tbl_ind;
60699a2dd95SBruce Richardson 
60799a2dd95SBruce Richardson 	/*
60899a2dd95SBruce Richardson 	 * Calculate index to the table based on the number and position
60999a2dd95SBruce Richardson 	 * of the bytes being inspected in this step.
61099a2dd95SBruce Richardson 	 */
61199a2dd95SBruce Richardson 	entry_ind = get_bitshift(ip, first_byte, bytes);
61299a2dd95SBruce Richardson 
61399a2dd95SBruce Richardson 	/* Number of bits covered in this step */
61499a2dd95SBruce Richardson 	bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE);
61599a2dd95SBruce Richardson 
61699a2dd95SBruce Richardson 	if (depth <= bits_covered) {
61799a2dd95SBruce Richardson 		*need_tbl_nb = 0;
61899a2dd95SBruce Richardson 		return 0;
61999a2dd95SBruce Richardson 	}
62099a2dd95SBruce Richardson 
62199a2dd95SBruce Richardson 	if (tbl[entry_ind].valid == 0 || tbl[entry_ind].ext_entry == 0) {
62299a2dd95SBruce Richardson 		/* from this point on a new table is needed on each level
62399a2dd95SBruce Richardson 		 * that is not covered yet
62499a2dd95SBruce Richardson 		 */
62599a2dd95SBruce Richardson 		depth -= bits_covered;
62699a2dd95SBruce Richardson 		uint32_t cnt = depth >> 3; /* depth / BYTE_SIZE */
62799a2dd95SBruce Richardson 		if (depth & 7) /* 0b00000111 */
62899a2dd95SBruce Richardson 			/* if depth % 8 > 0 then one more table is needed
62999a2dd95SBruce Richardson 			 * for those last bits
63099a2dd95SBruce Richardson 			 */
63199a2dd95SBruce Richardson 			cnt++;
63299a2dd95SBruce Richardson 
63399a2dd95SBruce Richardson 		*need_tbl_nb = cnt;
63499a2dd95SBruce Richardson 		return 0;
63599a2dd95SBruce Richardson 	}
63699a2dd95SBruce Richardson 
63799a2dd95SBruce Richardson 	next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex;
63899a2dd95SBruce Richardson 	*next_tbl = &(lpm->tbl8[next_tbl_ind *
63999a2dd95SBruce Richardson 		RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]);
64099a2dd95SBruce Richardson 	*need_tbl_nb = 0;
64199a2dd95SBruce Richardson 	return 1;
64299a2dd95SBruce Richardson }
64399a2dd95SBruce Richardson 
64499a2dd95SBruce Richardson /*
64599a2dd95SBruce Richardson  * Partially adds a new route to the data structure (tbl24+tbl8s).
64699a2dd95SBruce Richardson  * It returns 0 on success, a negative number on failure, or 1 if
64799a2dd95SBruce Richardson  * the process needs to be continued by calling the function again.
64899a2dd95SBruce Richardson  */
64999a2dd95SBruce Richardson static inline int
65099a2dd95SBruce Richardson add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl,
65199a2dd95SBruce Richardson 		uint32_t tbl_ind, struct rte_lpm6_tbl_entry **next_tbl,
65299a2dd95SBruce Richardson 		uint32_t *next_tbl_ind, uint8_t *ip, uint8_t bytes,
65399a2dd95SBruce Richardson 		uint8_t first_byte, uint8_t depth, uint32_t next_hop,
65499a2dd95SBruce Richardson 		uint8_t is_new_rule)
65599a2dd95SBruce Richardson {
65699a2dd95SBruce Richardson 	uint32_t entry_ind, tbl_range, tbl8_group_start, tbl8_group_end, i;
65799a2dd95SBruce Richardson 	uint32_t tbl8_gindex;
65899a2dd95SBruce Richardson 	uint8_t bits_covered;
65999a2dd95SBruce Richardson 	int ret;
66099a2dd95SBruce Richardson 
66199a2dd95SBruce Richardson 	/*
66299a2dd95SBruce Richardson 	 * Calculate index to the table based on the number and position
66399a2dd95SBruce Richardson 	 * of the bytes being inspected in this step.
66499a2dd95SBruce Richardson 	 */
66599a2dd95SBruce Richardson 	entry_ind = get_bitshift(ip, first_byte, bytes);
66699a2dd95SBruce Richardson 
66799a2dd95SBruce Richardson 	/* Number of bits covered in this step */
66899a2dd95SBruce Richardson 	bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE);
66999a2dd95SBruce Richardson 
67099a2dd95SBruce Richardson 	/*
67199a2dd95SBruce Richardson 	 * If depth if smaller than this number (ie this is the last step)
67299a2dd95SBruce Richardson 	 * expand the rule across the relevant positions in the table.
67399a2dd95SBruce Richardson 	 */
67499a2dd95SBruce Richardson 	if (depth <= bits_covered) {
67599a2dd95SBruce Richardson 		tbl_range = 1 << (bits_covered - depth);
67699a2dd95SBruce Richardson 
67799a2dd95SBruce Richardson 		for (i = entry_ind; i < (entry_ind + tbl_range); i++) {
67899a2dd95SBruce Richardson 			if (!tbl[i].valid || (tbl[i].ext_entry == 0 &&
67999a2dd95SBruce Richardson 					tbl[i].depth <= depth)) {
68099a2dd95SBruce Richardson 
68199a2dd95SBruce Richardson 				struct rte_lpm6_tbl_entry new_tbl_entry = {
68299a2dd95SBruce Richardson 					.next_hop = next_hop,
68399a2dd95SBruce Richardson 					.depth = depth,
68499a2dd95SBruce Richardson 					.valid = VALID,
68599a2dd95SBruce Richardson 					.valid_group = VALID,
68699a2dd95SBruce Richardson 					.ext_entry = 0,
68799a2dd95SBruce Richardson 				};
68899a2dd95SBruce Richardson 
68999a2dd95SBruce Richardson 				tbl[i] = new_tbl_entry;
69099a2dd95SBruce Richardson 
69199a2dd95SBruce Richardson 			} else if (tbl[i].ext_entry == 1) {
69299a2dd95SBruce Richardson 
69399a2dd95SBruce Richardson 				/*
69499a2dd95SBruce Richardson 				 * If tbl entry is valid and extended calculate the index
69599a2dd95SBruce Richardson 				 * into next tbl8 and expand the rule across the data structure.
69699a2dd95SBruce Richardson 				 */
69799a2dd95SBruce Richardson 				tbl8_gindex = tbl[i].lpm6_tbl8_gindex *
69899a2dd95SBruce Richardson 						RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
69999a2dd95SBruce Richardson 				expand_rule(lpm, tbl8_gindex, depth, depth,
70099a2dd95SBruce Richardson 						next_hop, VALID);
70199a2dd95SBruce Richardson 			}
70299a2dd95SBruce Richardson 		}
70399a2dd95SBruce Richardson 
70499a2dd95SBruce Richardson 		/* update tbl8 rule reference counter */
70599a2dd95SBruce Richardson 		if (tbl_ind != TBL24_IND && is_new_rule)
70699a2dd95SBruce Richardson 			lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
70799a2dd95SBruce Richardson 
70899a2dd95SBruce Richardson 		return 0;
70999a2dd95SBruce Richardson 	}
71099a2dd95SBruce Richardson 	/*
71199a2dd95SBruce Richardson 	 * If this is not the last step just fill one position
71299a2dd95SBruce Richardson 	 * and calculate the index to the next table.
71399a2dd95SBruce Richardson 	 */
71499a2dd95SBruce Richardson 	else {
71599a2dd95SBruce Richardson 		/* If it's invalid a new tbl8 is needed */
71699a2dd95SBruce Richardson 		if (!tbl[entry_ind].valid) {
71799a2dd95SBruce Richardson 			/* get a new table */
71899a2dd95SBruce Richardson 			ret = tbl8_get(lpm, &tbl8_gindex);
71999a2dd95SBruce Richardson 			if (ret != 0)
72099a2dd95SBruce Richardson 				return -ENOSPC;
72199a2dd95SBruce Richardson 
72299a2dd95SBruce Richardson 			/* invalidate all new tbl8 entries */
72399a2dd95SBruce Richardson 			tbl8_group_start = tbl8_gindex *
72499a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
72599a2dd95SBruce Richardson 			memset(&lpm->tbl8[tbl8_group_start], 0,
72699a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES *
72799a2dd95SBruce Richardson 					sizeof(struct rte_lpm6_tbl_entry));
72899a2dd95SBruce Richardson 
72999a2dd95SBruce Richardson 			/* init the new table's header:
73099a2dd95SBruce Richardson 			 *   save the reference to the owner table
73199a2dd95SBruce Richardson 			 */
73299a2dd95SBruce Richardson 			init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind);
73399a2dd95SBruce Richardson 
73499a2dd95SBruce Richardson 			/* reference to a new tbl8 */
73599a2dd95SBruce Richardson 			struct rte_lpm6_tbl_entry new_tbl_entry = {
73699a2dd95SBruce Richardson 				.lpm6_tbl8_gindex = tbl8_gindex,
73799a2dd95SBruce Richardson 				.depth = 0,
73899a2dd95SBruce Richardson 				.valid = VALID,
73999a2dd95SBruce Richardson 				.valid_group = VALID,
74099a2dd95SBruce Richardson 				.ext_entry = 1,
74199a2dd95SBruce Richardson 			};
74299a2dd95SBruce Richardson 
74399a2dd95SBruce Richardson 			tbl[entry_ind] = new_tbl_entry;
74499a2dd95SBruce Richardson 
74599a2dd95SBruce Richardson 			/* update the current table's reference counter */
74699a2dd95SBruce Richardson 			if (tbl_ind != TBL24_IND)
74799a2dd95SBruce Richardson 				lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
74899a2dd95SBruce Richardson 		}
74999a2dd95SBruce Richardson 		/*
75099a2dd95SBruce Richardson 		 * If it's valid but not extended the rule that was stored
75199a2dd95SBruce Richardson 		 * here needs to be moved to the next table.
75299a2dd95SBruce Richardson 		 */
75399a2dd95SBruce Richardson 		else if (tbl[entry_ind].ext_entry == 0) {
75499a2dd95SBruce Richardson 			/* get a new tbl8 index */
75599a2dd95SBruce Richardson 			ret = tbl8_get(lpm, &tbl8_gindex);
75699a2dd95SBruce Richardson 			if (ret != 0)
75799a2dd95SBruce Richardson 				return -ENOSPC;
75899a2dd95SBruce Richardson 
75999a2dd95SBruce Richardson 			tbl8_group_start = tbl8_gindex *
76099a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
76199a2dd95SBruce Richardson 			tbl8_group_end = tbl8_group_start +
76299a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
76399a2dd95SBruce Richardson 
76499a2dd95SBruce Richardson 			struct rte_lpm6_tbl_entry tbl_entry = {
76599a2dd95SBruce Richardson 				.next_hop = tbl[entry_ind].next_hop,
76699a2dd95SBruce Richardson 				.depth = tbl[entry_ind].depth,
76799a2dd95SBruce Richardson 				.valid = VALID,
76899a2dd95SBruce Richardson 				.valid_group = VALID,
76999a2dd95SBruce Richardson 				.ext_entry = 0
77099a2dd95SBruce Richardson 			};
77199a2dd95SBruce Richardson 
77299a2dd95SBruce Richardson 			/* Populate new tbl8 with tbl value. */
77399a2dd95SBruce Richardson 			for (i = tbl8_group_start; i < tbl8_group_end; i++)
77499a2dd95SBruce Richardson 				lpm->tbl8[i] = tbl_entry;
77599a2dd95SBruce Richardson 
77699a2dd95SBruce Richardson 			/* init the new table's header:
77799a2dd95SBruce Richardson 			 *   save the reference to the owner table
77899a2dd95SBruce Richardson 			 */
77999a2dd95SBruce Richardson 			init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind);
78099a2dd95SBruce Richardson 
78199a2dd95SBruce Richardson 			/*
78299a2dd95SBruce Richardson 			 * Update tbl entry to point to new tbl8 entry. Note: The
78399a2dd95SBruce Richardson 			 * ext_flag and tbl8_index need to be updated simultaneously,
78499a2dd95SBruce Richardson 			 * so assign whole structure in one go.
78599a2dd95SBruce Richardson 			 */
78699a2dd95SBruce Richardson 			struct rte_lpm6_tbl_entry new_tbl_entry = {
78799a2dd95SBruce Richardson 				.lpm6_tbl8_gindex = tbl8_gindex,
78899a2dd95SBruce Richardson 				.depth = 0,
78999a2dd95SBruce Richardson 				.valid = VALID,
79099a2dd95SBruce Richardson 				.valid_group = VALID,
79199a2dd95SBruce Richardson 				.ext_entry = 1,
79299a2dd95SBruce Richardson 			};
79399a2dd95SBruce Richardson 
79499a2dd95SBruce Richardson 			tbl[entry_ind] = new_tbl_entry;
79599a2dd95SBruce Richardson 
79699a2dd95SBruce Richardson 			/* update the current table's reference counter */
79799a2dd95SBruce Richardson 			if (tbl_ind != TBL24_IND)
79899a2dd95SBruce Richardson 				lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
79999a2dd95SBruce Richardson 		}
80099a2dd95SBruce Richardson 
80199a2dd95SBruce Richardson 		*next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex;
80299a2dd95SBruce Richardson 		*next_tbl = &(lpm->tbl8[*next_tbl_ind *
80399a2dd95SBruce Richardson 				  RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]);
80499a2dd95SBruce Richardson 	}
80599a2dd95SBruce Richardson 
80699a2dd95SBruce Richardson 	return 1;
80799a2dd95SBruce Richardson }
80899a2dd95SBruce Richardson 
80999a2dd95SBruce Richardson /*
81099a2dd95SBruce Richardson  * Simulate adding a route to LPM
81199a2dd95SBruce Richardson  *
81299a2dd95SBruce Richardson  *	Returns:
81399a2dd95SBruce Richardson  *    0 on success
81499a2dd95SBruce Richardson  *    -ENOSPC not enough tbl8 left
81599a2dd95SBruce Richardson  */
81699a2dd95SBruce Richardson static int
81799a2dd95SBruce Richardson simulate_add(struct rte_lpm6 *lpm, const uint8_t *masked_ip, uint8_t depth)
81899a2dd95SBruce Richardson {
81999a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl;
82099a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl_next = NULL;
82199a2dd95SBruce Richardson 	int ret, i;
82299a2dd95SBruce Richardson 
82399a2dd95SBruce Richardson 	/* number of new tables needed for a step */
82499a2dd95SBruce Richardson 	uint32_t need_tbl_nb;
82599a2dd95SBruce Richardson 	/* total number of new tables needed */
82699a2dd95SBruce Richardson 	uint32_t total_need_tbl_nb;
82799a2dd95SBruce Richardson 
82899a2dd95SBruce Richardson 	/* Inspect the first three bytes through tbl24 on the first step. */
82999a2dd95SBruce Richardson 	ret = simulate_add_step(lpm, lpm->tbl24, &tbl_next, masked_ip,
83099a2dd95SBruce Richardson 		ADD_FIRST_BYTE, 1, depth, &need_tbl_nb);
83199a2dd95SBruce Richardson 	total_need_tbl_nb = need_tbl_nb;
83299a2dd95SBruce Richardson 	/*
83399a2dd95SBruce Richardson 	 * Inspect one by one the rest of the bytes until
83499a2dd95SBruce Richardson 	 * the process is completed.
83599a2dd95SBruce Richardson 	 */
83699a2dd95SBruce Richardson 	for (i = ADD_FIRST_BYTE; i < RTE_LPM6_IPV6_ADDR_SIZE && ret == 1; i++) {
83799a2dd95SBruce Richardson 		tbl = tbl_next;
83899a2dd95SBruce Richardson 		ret = simulate_add_step(lpm, tbl, &tbl_next, masked_ip, 1,
83999a2dd95SBruce Richardson 			(uint8_t)(i + 1), depth, &need_tbl_nb);
84099a2dd95SBruce Richardson 		total_need_tbl_nb += need_tbl_nb;
84199a2dd95SBruce Richardson 	}
84299a2dd95SBruce Richardson 
84399a2dd95SBruce Richardson 	if (tbl8_available(lpm) < total_need_tbl_nb)
84499a2dd95SBruce Richardson 		/* not enough tbl8 to add a rule */
84599a2dd95SBruce Richardson 		return -ENOSPC;
84699a2dd95SBruce Richardson 
84799a2dd95SBruce Richardson 	return 0;
84899a2dd95SBruce Richardson }
84999a2dd95SBruce Richardson 
85099a2dd95SBruce Richardson /*
85199a2dd95SBruce Richardson  * Add a route
85299a2dd95SBruce Richardson  */
85399a2dd95SBruce Richardson int
85499a2dd95SBruce Richardson rte_lpm6_add(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth,
85599a2dd95SBruce Richardson 	     uint32_t next_hop)
85699a2dd95SBruce Richardson {
85799a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl;
85899a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *tbl_next = NULL;
85999a2dd95SBruce Richardson 	/* init to avoid compiler warning */
86099a2dd95SBruce Richardson 	uint32_t tbl_next_num = 123456;
86199a2dd95SBruce Richardson 	int status;
86299a2dd95SBruce Richardson 	uint8_t masked_ip[RTE_LPM6_IPV6_ADDR_SIZE];
86399a2dd95SBruce Richardson 	int i;
86499a2dd95SBruce Richardson 
86599a2dd95SBruce Richardson 	/* Check user arguments. */
86699a2dd95SBruce Richardson 	if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM6_MAX_DEPTH))
86799a2dd95SBruce Richardson 		return -EINVAL;
86899a2dd95SBruce Richardson 
86999a2dd95SBruce Richardson 	/* Copy the IP and mask it to avoid modifying user's input data. */
87099a2dd95SBruce Richardson 	ip6_copy_addr(masked_ip, ip);
87199a2dd95SBruce Richardson 	ip6_mask_addr(masked_ip, depth);
87299a2dd95SBruce Richardson 
87399a2dd95SBruce Richardson 	/* Simulate adding a new route */
87499a2dd95SBruce Richardson 	int ret = simulate_add(lpm, masked_ip, depth);
87599a2dd95SBruce Richardson 	if (ret < 0)
87699a2dd95SBruce Richardson 		return ret;
87799a2dd95SBruce Richardson 
87899a2dd95SBruce Richardson 	/* Add the rule to the rule table. */
87999a2dd95SBruce Richardson 	int is_new_rule = rule_add(lpm, masked_ip, depth, next_hop);
88099a2dd95SBruce Richardson 	/* If there is no space available for new rule return error. */
88199a2dd95SBruce Richardson 	if (is_new_rule < 0)
88299a2dd95SBruce Richardson 		return is_new_rule;
88399a2dd95SBruce Richardson 
88499a2dd95SBruce Richardson 	/* Inspect the first three bytes through tbl24 on the first step. */
88599a2dd95SBruce Richardson 	tbl = lpm->tbl24;
88699a2dd95SBruce Richardson 	status = add_step(lpm, tbl, TBL24_IND, &tbl_next, &tbl_next_num,
88799a2dd95SBruce Richardson 		masked_ip, ADD_FIRST_BYTE, 1, depth, next_hop,
88899a2dd95SBruce Richardson 		is_new_rule);
88999a2dd95SBruce Richardson 	assert(status >= 0);
89099a2dd95SBruce Richardson 
89199a2dd95SBruce Richardson 	/*
89299a2dd95SBruce Richardson 	 * Inspect one by one the rest of the bytes until
89399a2dd95SBruce Richardson 	 * the process is completed.
89499a2dd95SBruce Richardson 	 */
89599a2dd95SBruce Richardson 	for (i = ADD_FIRST_BYTE; i < RTE_LPM6_IPV6_ADDR_SIZE && status == 1; i++) {
89699a2dd95SBruce Richardson 		tbl = tbl_next;
89799a2dd95SBruce Richardson 		status = add_step(lpm, tbl, tbl_next_num, &tbl_next,
89899a2dd95SBruce Richardson 			&tbl_next_num, masked_ip, 1, (uint8_t)(i + 1),
89999a2dd95SBruce Richardson 			depth, next_hop, is_new_rule);
90099a2dd95SBruce Richardson 		assert(status >= 0);
90199a2dd95SBruce Richardson 	}
90299a2dd95SBruce Richardson 
90399a2dd95SBruce Richardson 	return status;
90499a2dd95SBruce Richardson }
90599a2dd95SBruce Richardson 
90699a2dd95SBruce Richardson /*
90799a2dd95SBruce Richardson  * Takes a pointer to a table entry and inspect one level.
90899a2dd95SBruce Richardson  * The function returns 0 on lookup success, ENOENT if no match was found
90999a2dd95SBruce Richardson  * or 1 if the process needs to be continued by calling the function again.
91099a2dd95SBruce Richardson  */
91199a2dd95SBruce Richardson static inline int
91299a2dd95SBruce Richardson lookup_step(const struct rte_lpm6 *lpm, const struct rte_lpm6_tbl_entry *tbl,
91399a2dd95SBruce Richardson 		const struct rte_lpm6_tbl_entry **tbl_next, const uint8_t *ip,
91499a2dd95SBruce Richardson 		uint8_t first_byte, uint32_t *next_hop)
91599a2dd95SBruce Richardson {
91699a2dd95SBruce Richardson 	uint32_t tbl8_index, tbl_entry;
91799a2dd95SBruce Richardson 
91899a2dd95SBruce Richardson 	/* Take the integer value from the pointer. */
91999a2dd95SBruce Richardson 	tbl_entry = *(const uint32_t *)tbl;
92099a2dd95SBruce Richardson 
92199a2dd95SBruce Richardson 	/* If it is valid and extended we calculate the new pointer to return. */
92299a2dd95SBruce Richardson 	if ((tbl_entry & RTE_LPM6_VALID_EXT_ENTRY_BITMASK) ==
92399a2dd95SBruce Richardson 			RTE_LPM6_VALID_EXT_ENTRY_BITMASK) {
92499a2dd95SBruce Richardson 
92599a2dd95SBruce Richardson 		tbl8_index = ip[first_byte-1] +
92699a2dd95SBruce Richardson 				((tbl_entry & RTE_LPM6_TBL8_BITMASK) *
92799a2dd95SBruce Richardson 				RTE_LPM6_TBL8_GROUP_NUM_ENTRIES);
92899a2dd95SBruce Richardson 
92999a2dd95SBruce Richardson 		*tbl_next = &lpm->tbl8[tbl8_index];
93099a2dd95SBruce Richardson 
93199a2dd95SBruce Richardson 		return 1;
93299a2dd95SBruce Richardson 	} else {
93399a2dd95SBruce Richardson 		/* If not extended then we can have a match. */
93499a2dd95SBruce Richardson 		*next_hop = ((uint32_t)tbl_entry & RTE_LPM6_TBL8_BITMASK);
93599a2dd95SBruce Richardson 		return (tbl_entry & RTE_LPM6_LOOKUP_SUCCESS) ? 0 : -ENOENT;
93699a2dd95SBruce Richardson 	}
93799a2dd95SBruce Richardson }
93899a2dd95SBruce Richardson 
93999a2dd95SBruce Richardson /*
94099a2dd95SBruce Richardson  * Looks up an IP
94199a2dd95SBruce Richardson  */
94299a2dd95SBruce Richardson int
94399a2dd95SBruce Richardson rte_lpm6_lookup(const struct rte_lpm6 *lpm, const uint8_t *ip,
94499a2dd95SBruce Richardson 		uint32_t *next_hop)
94599a2dd95SBruce Richardson {
94699a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl;
94799a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl_next = NULL;
94899a2dd95SBruce Richardson 	int status;
94999a2dd95SBruce Richardson 	uint8_t first_byte;
95099a2dd95SBruce Richardson 	uint32_t tbl24_index;
95199a2dd95SBruce Richardson 
95299a2dd95SBruce Richardson 	/* DEBUG: Check user input arguments. */
95399a2dd95SBruce Richardson 	if ((lpm == NULL) || (ip == NULL) || (next_hop == NULL))
95499a2dd95SBruce Richardson 		return -EINVAL;
95599a2dd95SBruce Richardson 
95699a2dd95SBruce Richardson 	first_byte = LOOKUP_FIRST_BYTE;
95799a2dd95SBruce Richardson 	tbl24_index = (ip[0] << BYTES2_SIZE) | (ip[1] << BYTE_SIZE) | ip[2];
95899a2dd95SBruce Richardson 
95999a2dd95SBruce Richardson 	/* Calculate pointer to the first entry to be inspected */
96099a2dd95SBruce Richardson 	tbl = &lpm->tbl24[tbl24_index];
96199a2dd95SBruce Richardson 
96299a2dd95SBruce Richardson 	do {
96399a2dd95SBruce Richardson 		/* Continue inspecting following levels until success or failure */
96499a2dd95SBruce Richardson 		status = lookup_step(lpm, tbl, &tbl_next, ip, first_byte++, next_hop);
96599a2dd95SBruce Richardson 		tbl = tbl_next;
96699a2dd95SBruce Richardson 	} while (status == 1);
96799a2dd95SBruce Richardson 
96899a2dd95SBruce Richardson 	return status;
96999a2dd95SBruce Richardson }
97099a2dd95SBruce Richardson 
97199a2dd95SBruce Richardson /*
97299a2dd95SBruce Richardson  * Looks up a group of IP addresses
97399a2dd95SBruce Richardson  */
97499a2dd95SBruce Richardson int
97599a2dd95SBruce Richardson rte_lpm6_lookup_bulk_func(const struct rte_lpm6 *lpm,
97699a2dd95SBruce Richardson 		uint8_t ips[][RTE_LPM6_IPV6_ADDR_SIZE],
97799a2dd95SBruce Richardson 		int32_t *next_hops, unsigned int n)
97899a2dd95SBruce Richardson {
97999a2dd95SBruce Richardson 	unsigned int i;
98099a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl;
98199a2dd95SBruce Richardson 	const struct rte_lpm6_tbl_entry *tbl_next = NULL;
98299a2dd95SBruce Richardson 	uint32_t tbl24_index, next_hop;
98399a2dd95SBruce Richardson 	uint8_t first_byte;
98499a2dd95SBruce Richardson 	int status;
98599a2dd95SBruce Richardson 
98699a2dd95SBruce Richardson 	/* DEBUG: Check user input arguments. */
98799a2dd95SBruce Richardson 	if ((lpm == NULL) || (ips == NULL) || (next_hops == NULL))
98899a2dd95SBruce Richardson 		return -EINVAL;
98999a2dd95SBruce Richardson 
99099a2dd95SBruce Richardson 	for (i = 0; i < n; i++) {
99199a2dd95SBruce Richardson 		first_byte = LOOKUP_FIRST_BYTE;
99299a2dd95SBruce Richardson 		tbl24_index = (ips[i][0] << BYTES2_SIZE) |
99399a2dd95SBruce Richardson 				(ips[i][1] << BYTE_SIZE) | ips[i][2];
99499a2dd95SBruce Richardson 
99599a2dd95SBruce Richardson 		/* Calculate pointer to the first entry to be inspected */
99699a2dd95SBruce Richardson 		tbl = &lpm->tbl24[tbl24_index];
99799a2dd95SBruce Richardson 
99899a2dd95SBruce Richardson 		do {
99999a2dd95SBruce Richardson 			/* Continue inspecting following levels
100099a2dd95SBruce Richardson 			 * until success or failure
100199a2dd95SBruce Richardson 			 */
100299a2dd95SBruce Richardson 			status = lookup_step(lpm, tbl, &tbl_next, ips[i],
100399a2dd95SBruce Richardson 					first_byte++, &next_hop);
100499a2dd95SBruce Richardson 			tbl = tbl_next;
100599a2dd95SBruce Richardson 		} while (status == 1);
100699a2dd95SBruce Richardson 
100799a2dd95SBruce Richardson 		if (status < 0)
100899a2dd95SBruce Richardson 			next_hops[i] = -1;
100999a2dd95SBruce Richardson 		else
101099a2dd95SBruce Richardson 			next_hops[i] = (int32_t)next_hop;
101199a2dd95SBruce Richardson 	}
101299a2dd95SBruce Richardson 
101399a2dd95SBruce Richardson 	return 0;
101499a2dd95SBruce Richardson }
101599a2dd95SBruce Richardson 
101699a2dd95SBruce Richardson /*
101799a2dd95SBruce Richardson  * Look for a rule in the high-level rules table
101899a2dd95SBruce Richardson  */
101999a2dd95SBruce Richardson int
102099a2dd95SBruce Richardson rte_lpm6_is_rule_present(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth,
102199a2dd95SBruce Richardson 			 uint32_t *next_hop)
102299a2dd95SBruce Richardson {
102399a2dd95SBruce Richardson 	uint8_t masked_ip[RTE_LPM6_IPV6_ADDR_SIZE];
102499a2dd95SBruce Richardson 
102599a2dd95SBruce Richardson 	/* Check user arguments. */
102699a2dd95SBruce Richardson 	if ((lpm == NULL) || next_hop == NULL || ip == NULL ||
102799a2dd95SBruce Richardson 			(depth < 1) || (depth > RTE_LPM6_MAX_DEPTH))
102899a2dd95SBruce Richardson 		return -EINVAL;
102999a2dd95SBruce Richardson 
103099a2dd95SBruce Richardson 	/* Copy the IP and mask it to avoid modifying user's input data. */
103199a2dd95SBruce Richardson 	ip6_copy_addr(masked_ip, ip);
103299a2dd95SBruce Richardson 	ip6_mask_addr(masked_ip, depth);
103399a2dd95SBruce Richardson 
103499a2dd95SBruce Richardson 	return rule_find(lpm, masked_ip, depth, next_hop);
103599a2dd95SBruce Richardson }
103699a2dd95SBruce Richardson 
103799a2dd95SBruce Richardson /*
103899a2dd95SBruce Richardson  * Delete a rule from the rule table.
103999a2dd95SBruce Richardson  * NOTE: Valid range for depth parameter is 1 .. 128 inclusive.
104099a2dd95SBruce Richardson  * return
104199a2dd95SBruce Richardson  *	  0 on success
104299a2dd95SBruce Richardson  *   <0 on failure
104399a2dd95SBruce Richardson  */
104499a2dd95SBruce Richardson static inline int
104599a2dd95SBruce Richardson rule_delete(struct rte_lpm6 *lpm, uint8_t *ip, uint8_t depth)
104699a2dd95SBruce Richardson {
104799a2dd95SBruce Richardson 	int ret;
104899a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
104999a2dd95SBruce Richardson 
105099a2dd95SBruce Richardson 	/* init rule key */
105199a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
105299a2dd95SBruce Richardson 
105399a2dd95SBruce Richardson 	/* delete the rule */
105499a2dd95SBruce Richardson 	ret = rte_hash_del_key(lpm->rules_tbl, (void *) &rule_key);
105599a2dd95SBruce Richardson 	if (ret >= 0)
105699a2dd95SBruce Richardson 		lpm->used_rules--;
105799a2dd95SBruce Richardson 
105899a2dd95SBruce Richardson 	return ret;
105999a2dd95SBruce Richardson }
106099a2dd95SBruce Richardson 
106199a2dd95SBruce Richardson /*
106299a2dd95SBruce Richardson  * Deletes a group of rules
106399a2dd95SBruce Richardson  *
106499a2dd95SBruce Richardson  * Note that the function rebuilds the lpm table,
106599a2dd95SBruce Richardson  * rather than doing incremental updates like
106699a2dd95SBruce Richardson  * the regular delete function
106799a2dd95SBruce Richardson  */
106899a2dd95SBruce Richardson int
106999a2dd95SBruce Richardson rte_lpm6_delete_bulk_func(struct rte_lpm6 *lpm,
107099a2dd95SBruce Richardson 		uint8_t ips[][RTE_LPM6_IPV6_ADDR_SIZE], uint8_t *depths,
107199a2dd95SBruce Richardson 		unsigned n)
107299a2dd95SBruce Richardson {
107399a2dd95SBruce Richardson 	uint8_t masked_ip[RTE_LPM6_IPV6_ADDR_SIZE];
107499a2dd95SBruce Richardson 	unsigned i;
107599a2dd95SBruce Richardson 
107699a2dd95SBruce Richardson 	/* Check input arguments. */
107799a2dd95SBruce Richardson 	if ((lpm == NULL) || (ips == NULL) || (depths == NULL))
107899a2dd95SBruce Richardson 		return -EINVAL;
107999a2dd95SBruce Richardson 
108099a2dd95SBruce Richardson 	for (i = 0; i < n; i++) {
108199a2dd95SBruce Richardson 		ip6_copy_addr(masked_ip, ips[i]);
108299a2dd95SBruce Richardson 		ip6_mask_addr(masked_ip, depths[i]);
108399a2dd95SBruce Richardson 		rule_delete(lpm, masked_ip, depths[i]);
108499a2dd95SBruce Richardson 	}
108599a2dd95SBruce Richardson 
108699a2dd95SBruce Richardson 	/*
108799a2dd95SBruce Richardson 	 * Set all the table entries to 0 (ie delete every rule
108899a2dd95SBruce Richardson 	 * from the data structure.
108999a2dd95SBruce Richardson 	 */
109099a2dd95SBruce Richardson 	memset(lpm->tbl24, 0, sizeof(lpm->tbl24));
109199a2dd95SBruce Richardson 	memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0])
109299a2dd95SBruce Richardson 			* RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s);
109399a2dd95SBruce Richardson 	tbl8_pool_init(lpm);
109499a2dd95SBruce Richardson 
109599a2dd95SBruce Richardson 	/*
109699a2dd95SBruce Richardson 	 * Add every rule again (except for the ones that were removed from
109799a2dd95SBruce Richardson 	 * the rules table).
109899a2dd95SBruce Richardson 	 */
109999a2dd95SBruce Richardson 	rebuild_lpm(lpm);
110099a2dd95SBruce Richardson 
110199a2dd95SBruce Richardson 	return 0;
110299a2dd95SBruce Richardson }
110399a2dd95SBruce Richardson 
110499a2dd95SBruce Richardson /*
110599a2dd95SBruce Richardson  * Delete all rules from the LPM table.
110699a2dd95SBruce Richardson  */
110799a2dd95SBruce Richardson void
110899a2dd95SBruce Richardson rte_lpm6_delete_all(struct rte_lpm6 *lpm)
110999a2dd95SBruce Richardson {
111099a2dd95SBruce Richardson 	/* Zero used rules counter. */
111199a2dd95SBruce Richardson 	lpm->used_rules = 0;
111299a2dd95SBruce Richardson 
111399a2dd95SBruce Richardson 	/* Zero tbl24. */
111499a2dd95SBruce Richardson 	memset(lpm->tbl24, 0, sizeof(lpm->tbl24));
111599a2dd95SBruce Richardson 
111699a2dd95SBruce Richardson 	/* Zero tbl8. */
111799a2dd95SBruce Richardson 	memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0]) *
111899a2dd95SBruce Richardson 			RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s);
111999a2dd95SBruce Richardson 
112099a2dd95SBruce Richardson 	/* init pool of free tbl8 indexes */
112199a2dd95SBruce Richardson 	tbl8_pool_init(lpm);
112299a2dd95SBruce Richardson 
112399a2dd95SBruce Richardson 	/* Delete all rules form the rules table. */
112499a2dd95SBruce Richardson 	rte_hash_reset(lpm->rules_tbl);
112599a2dd95SBruce Richardson }
112699a2dd95SBruce Richardson 
112799a2dd95SBruce Richardson /*
112899a2dd95SBruce Richardson  * Convert a depth to a one byte long mask
112999a2dd95SBruce Richardson  *   Example: 4 will be converted to 0xF0
113099a2dd95SBruce Richardson  */
113199a2dd95SBruce Richardson static uint8_t __attribute__((pure))
113299a2dd95SBruce Richardson depth_to_mask_1b(uint8_t depth)
113399a2dd95SBruce Richardson {
113499a2dd95SBruce Richardson 	/* To calculate a mask start with a 1 on the left hand side and right
113599a2dd95SBruce Richardson 	 * shift while populating the left hand side with 1's
113699a2dd95SBruce Richardson 	 */
113799a2dd95SBruce Richardson 	return (signed char)0x80 >> (depth - 1);
113899a2dd95SBruce Richardson }
113999a2dd95SBruce Richardson 
114099a2dd95SBruce Richardson /*
114199a2dd95SBruce Richardson  * Find a less specific rule
114299a2dd95SBruce Richardson  */
114399a2dd95SBruce Richardson static int
114499a2dd95SBruce Richardson rule_find_less_specific(struct rte_lpm6 *lpm, uint8_t *ip, uint8_t depth,
114599a2dd95SBruce Richardson 	struct rte_lpm6_rule *rule)
114699a2dd95SBruce Richardson {
114799a2dd95SBruce Richardson 	int ret;
114899a2dd95SBruce Richardson 	uint32_t next_hop;
114999a2dd95SBruce Richardson 	uint8_t mask;
115099a2dd95SBruce Richardson 	struct rte_lpm6_rule_key rule_key;
115199a2dd95SBruce Richardson 
115299a2dd95SBruce Richardson 	if (depth == 1)
115399a2dd95SBruce Richardson 		return 0;
115499a2dd95SBruce Richardson 
115599a2dd95SBruce Richardson 	rule_key_init(&rule_key, ip, depth);
115699a2dd95SBruce Richardson 
115799a2dd95SBruce Richardson 	while (depth > 1) {
115899a2dd95SBruce Richardson 		depth--;
115999a2dd95SBruce Richardson 
116099a2dd95SBruce Richardson 		/* each iteration zero one more bit of the key */
116199a2dd95SBruce Richardson 		mask = depth & 7; /* depth % BYTE_SIZE */
116299a2dd95SBruce Richardson 		if (mask > 0)
116399a2dd95SBruce Richardson 			mask = depth_to_mask_1b(mask);
116499a2dd95SBruce Richardson 
116599a2dd95SBruce Richardson 		rule_key.depth = depth;
116699a2dd95SBruce Richardson 		rule_key.ip[depth >> 3] &= mask;
116799a2dd95SBruce Richardson 
116899a2dd95SBruce Richardson 		ret = rule_find_with_key(lpm, &rule_key, &next_hop);
116999a2dd95SBruce Richardson 		if (ret) {
117099a2dd95SBruce Richardson 			rule->depth = depth;
117199a2dd95SBruce Richardson 			ip6_copy_addr(rule->ip, rule_key.ip);
117299a2dd95SBruce Richardson 			rule->next_hop = next_hop;
117399a2dd95SBruce Richardson 			return 1;
117499a2dd95SBruce Richardson 		}
117599a2dd95SBruce Richardson 	}
117699a2dd95SBruce Richardson 
117799a2dd95SBruce Richardson 	return 0;
117899a2dd95SBruce Richardson }
117999a2dd95SBruce Richardson 
118099a2dd95SBruce Richardson /*
118199a2dd95SBruce Richardson  * Find range of tbl8 cells occupied by a rule
118299a2dd95SBruce Richardson  */
118399a2dd95SBruce Richardson static void
118499a2dd95SBruce Richardson rule_find_range(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth,
118599a2dd95SBruce Richardson 		  struct rte_lpm6_tbl_entry **from,
118699a2dd95SBruce Richardson 		  struct rte_lpm6_tbl_entry **to,
118799a2dd95SBruce Richardson 		  uint32_t *out_tbl_ind)
118899a2dd95SBruce Richardson {
118999a2dd95SBruce Richardson 	uint32_t ind;
119099a2dd95SBruce Richardson 	uint32_t first_3bytes = (uint32_t)ip[0] << 16 | ip[1] << 8 | ip[2];
119199a2dd95SBruce Richardson 
119299a2dd95SBruce Richardson 	if (depth <= 24) {
119399a2dd95SBruce Richardson 		/* rule is within the top level */
119499a2dd95SBruce Richardson 		ind = first_3bytes;
119599a2dd95SBruce Richardson 		*from = &lpm->tbl24[ind];
119699a2dd95SBruce Richardson 		ind += (1 << (24 - depth)) - 1;
119799a2dd95SBruce Richardson 		*to = &lpm->tbl24[ind];
119899a2dd95SBruce Richardson 		*out_tbl_ind = TBL24_IND;
119999a2dd95SBruce Richardson 	} else {
120099a2dd95SBruce Richardson 		/* top level entry */
120199a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry *tbl = &lpm->tbl24[first_3bytes];
120299a2dd95SBruce Richardson 		assert(tbl->ext_entry == 1);
120399a2dd95SBruce Richardson 		/* first tbl8 */
120499a2dd95SBruce Richardson 		uint32_t tbl_ind = tbl->lpm6_tbl8_gindex;
120599a2dd95SBruce Richardson 		tbl = &lpm->tbl8[tbl_ind *
120699a2dd95SBruce Richardson 				RTE_LPM6_TBL8_GROUP_NUM_ENTRIES];
120799a2dd95SBruce Richardson 		/* current ip byte, the top level is already behind */
120899a2dd95SBruce Richardson 		uint8_t byte = 3;
120999a2dd95SBruce Richardson 		/* minus top level */
121099a2dd95SBruce Richardson 		depth -= 24;
121199a2dd95SBruce Richardson 
121299a2dd95SBruce Richardson 		/* iterate through levels (tbl8s)
121399a2dd95SBruce Richardson 		 * until we reach the last one
121499a2dd95SBruce Richardson 		 */
121599a2dd95SBruce Richardson 		while (depth > 8) {
121699a2dd95SBruce Richardson 			tbl += ip[byte];
121799a2dd95SBruce Richardson 			assert(tbl->ext_entry == 1);
121899a2dd95SBruce Richardson 			/* go to the next level/tbl8 */
121999a2dd95SBruce Richardson 			tbl_ind = tbl->lpm6_tbl8_gindex;
122099a2dd95SBruce Richardson 			tbl = &lpm->tbl8[tbl_ind *
122199a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES];
122299a2dd95SBruce Richardson 			byte += 1;
122399a2dd95SBruce Richardson 			depth -= 8;
122499a2dd95SBruce Richardson 		}
122599a2dd95SBruce Richardson 
122699a2dd95SBruce Richardson 		/* last level/tbl8 */
122799a2dd95SBruce Richardson 		ind = ip[byte] & depth_to_mask_1b(depth);
122899a2dd95SBruce Richardson 		*from = &tbl[ind];
122999a2dd95SBruce Richardson 		ind += (1 << (8 - depth)) - 1;
123099a2dd95SBruce Richardson 		*to = &tbl[ind];
123199a2dd95SBruce Richardson 		*out_tbl_ind = tbl_ind;
123299a2dd95SBruce Richardson 	}
123399a2dd95SBruce Richardson }
123499a2dd95SBruce Richardson 
123599a2dd95SBruce Richardson /*
123699a2dd95SBruce Richardson  * Remove a table from the LPM tree
123799a2dd95SBruce Richardson  */
123899a2dd95SBruce Richardson static void
123999a2dd95SBruce Richardson remove_tbl(struct rte_lpm6 *lpm, struct rte_lpm_tbl8_hdr *tbl_hdr,
124099a2dd95SBruce Richardson 		  uint32_t tbl_ind, struct rte_lpm6_rule *lsp_rule)
124199a2dd95SBruce Richardson {
124299a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *owner_entry;
124399a2dd95SBruce Richardson 
124499a2dd95SBruce Richardson 	if (tbl_hdr->owner_tbl_ind == TBL24_IND)
124599a2dd95SBruce Richardson 		owner_entry = &lpm->tbl24[tbl_hdr->owner_entry_ind];
124699a2dd95SBruce Richardson 	else {
124799a2dd95SBruce Richardson 		uint32_t owner_tbl_ind = tbl_hdr->owner_tbl_ind;
124899a2dd95SBruce Richardson 		owner_entry = &lpm->tbl8[
124999a2dd95SBruce Richardson 			owner_tbl_ind * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES +
125099a2dd95SBruce Richardson 			tbl_hdr->owner_entry_ind];
125199a2dd95SBruce Richardson 
125299a2dd95SBruce Richardson 		struct rte_lpm_tbl8_hdr *owner_tbl_hdr =
125399a2dd95SBruce Richardson 			&lpm->tbl8_hdrs[owner_tbl_ind];
125499a2dd95SBruce Richardson 		if (--owner_tbl_hdr->ref_cnt == 0)
125599a2dd95SBruce Richardson 			remove_tbl(lpm, owner_tbl_hdr, owner_tbl_ind, lsp_rule);
125699a2dd95SBruce Richardson 	}
125799a2dd95SBruce Richardson 
125899a2dd95SBruce Richardson 	assert(owner_entry->ext_entry == 1);
125999a2dd95SBruce Richardson 
126099a2dd95SBruce Richardson 	/* unlink the table */
126199a2dd95SBruce Richardson 	if (lsp_rule != NULL) {
126299a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry new_tbl_entry = {
126399a2dd95SBruce Richardson 			.next_hop = lsp_rule->next_hop,
126499a2dd95SBruce Richardson 			.depth = lsp_rule->depth,
126599a2dd95SBruce Richardson 			.valid = VALID,
126699a2dd95SBruce Richardson 			.valid_group = VALID,
126799a2dd95SBruce Richardson 			.ext_entry = 0
126899a2dd95SBruce Richardson 		};
126999a2dd95SBruce Richardson 
127099a2dd95SBruce Richardson 		*owner_entry = new_tbl_entry;
127199a2dd95SBruce Richardson 	} else {
127299a2dd95SBruce Richardson 		struct rte_lpm6_tbl_entry new_tbl_entry = {
127399a2dd95SBruce Richardson 			.next_hop = 0,
127499a2dd95SBruce Richardson 			.depth = 0,
127599a2dd95SBruce Richardson 			.valid = INVALID,
127699a2dd95SBruce Richardson 			.valid_group = INVALID,
127799a2dd95SBruce Richardson 			.ext_entry = 0
127899a2dd95SBruce Richardson 		};
127999a2dd95SBruce Richardson 
128099a2dd95SBruce Richardson 		*owner_entry = new_tbl_entry;
128199a2dd95SBruce Richardson 	}
128299a2dd95SBruce Richardson 
128399a2dd95SBruce Richardson 	/* return the table to the pool */
128499a2dd95SBruce Richardson 	tbl8_put(lpm, tbl_ind);
128599a2dd95SBruce Richardson }
128699a2dd95SBruce Richardson 
128799a2dd95SBruce Richardson /*
128899a2dd95SBruce Richardson  * Deletes a rule
128999a2dd95SBruce Richardson  */
129099a2dd95SBruce Richardson int
129199a2dd95SBruce Richardson rte_lpm6_delete(struct rte_lpm6 *lpm, const uint8_t *ip, uint8_t depth)
129299a2dd95SBruce Richardson {
129399a2dd95SBruce Richardson 	uint8_t masked_ip[RTE_LPM6_IPV6_ADDR_SIZE];
129499a2dd95SBruce Richardson 	struct rte_lpm6_rule lsp_rule_obj;
129599a2dd95SBruce Richardson 	struct rte_lpm6_rule *lsp_rule;
129699a2dd95SBruce Richardson 	int ret;
129799a2dd95SBruce Richardson 	uint32_t tbl_ind;
129899a2dd95SBruce Richardson 	struct rte_lpm6_tbl_entry *from, *to;
129999a2dd95SBruce Richardson 
130099a2dd95SBruce Richardson 	/* Check input arguments. */
130199a2dd95SBruce Richardson 	if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM6_MAX_DEPTH))
130299a2dd95SBruce Richardson 		return -EINVAL;
130399a2dd95SBruce Richardson 
130499a2dd95SBruce Richardson 	/* Copy the IP and mask it to avoid modifying user's input data. */
130599a2dd95SBruce Richardson 	ip6_copy_addr(masked_ip, ip);
130699a2dd95SBruce Richardson 	ip6_mask_addr(masked_ip, depth);
130799a2dd95SBruce Richardson 
130899a2dd95SBruce Richardson 	/* Delete the rule from the rule table. */
130999a2dd95SBruce Richardson 	ret = rule_delete(lpm, masked_ip, depth);
131099a2dd95SBruce Richardson 	if (ret < 0)
131199a2dd95SBruce Richardson 		return -ENOENT;
131299a2dd95SBruce Richardson 
131399a2dd95SBruce Richardson 	/* find rule cells */
131499a2dd95SBruce Richardson 	rule_find_range(lpm, masked_ip, depth, &from, &to, &tbl_ind);
131599a2dd95SBruce Richardson 
131699a2dd95SBruce Richardson 	/* find a less specific rule (a rule with smaller depth)
131799a2dd95SBruce Richardson 	 * note: masked_ip will be modified, don't use it anymore
131899a2dd95SBruce Richardson 	 */
131999a2dd95SBruce Richardson 	ret = rule_find_less_specific(lpm, masked_ip, depth,
132099a2dd95SBruce Richardson 			&lsp_rule_obj);
132199a2dd95SBruce Richardson 	lsp_rule = ret ? &lsp_rule_obj : NULL;
132299a2dd95SBruce Richardson 
132399a2dd95SBruce Richardson 	/* decrement the table rule counter,
132499a2dd95SBruce Richardson 	 * note that tbl24 doesn't have a header
132599a2dd95SBruce Richardson 	 */
132699a2dd95SBruce Richardson 	if (tbl_ind != TBL24_IND) {
132799a2dd95SBruce Richardson 		struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind];
132899a2dd95SBruce Richardson 		if (--tbl_hdr->ref_cnt == 0) {
132999a2dd95SBruce Richardson 			/* remove the table */
133099a2dd95SBruce Richardson 			remove_tbl(lpm, tbl_hdr, tbl_ind, lsp_rule);
133199a2dd95SBruce Richardson 			return 0;
133299a2dd95SBruce Richardson 		}
133399a2dd95SBruce Richardson 	}
133499a2dd95SBruce Richardson 
133599a2dd95SBruce Richardson 	/* iterate rule cells */
133699a2dd95SBruce Richardson 	for (; from <= to; from++)
133799a2dd95SBruce Richardson 		if (from->ext_entry == 1) {
133899a2dd95SBruce Richardson 			/* reference to a more specific space
133999a2dd95SBruce Richardson 			 * of the prefix/rule. Entries in a more
134099a2dd95SBruce Richardson 			 * specific space that are not used by
134199a2dd95SBruce Richardson 			 * a more specific prefix must be occupied
134299a2dd95SBruce Richardson 			 * by the prefix
134399a2dd95SBruce Richardson 			 */
134499a2dd95SBruce Richardson 			if (lsp_rule != NULL)
134599a2dd95SBruce Richardson 				expand_rule(lpm,
134699a2dd95SBruce Richardson 					from->lpm6_tbl8_gindex *
134799a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES,
134899a2dd95SBruce Richardson 					depth, lsp_rule->depth,
134999a2dd95SBruce Richardson 					lsp_rule->next_hop, VALID);
135099a2dd95SBruce Richardson 			else
135199a2dd95SBruce Richardson 				/* since the prefix has no less specific prefix,
135299a2dd95SBruce Richardson 				 * its more specific space must be invalidated
135399a2dd95SBruce Richardson 				 */
135499a2dd95SBruce Richardson 				expand_rule(lpm,
135599a2dd95SBruce Richardson 					from->lpm6_tbl8_gindex *
135699a2dd95SBruce Richardson 					RTE_LPM6_TBL8_GROUP_NUM_ENTRIES,
135799a2dd95SBruce Richardson 					depth, 0, 0, INVALID);
135899a2dd95SBruce Richardson 		} else if (from->depth == depth) {
135999a2dd95SBruce Richardson 			/* entry is not a reference and belongs to the prefix */
136099a2dd95SBruce Richardson 			if (lsp_rule != NULL) {
136199a2dd95SBruce Richardson 				struct rte_lpm6_tbl_entry new_tbl_entry = {
136299a2dd95SBruce Richardson 					.next_hop = lsp_rule->next_hop,
136399a2dd95SBruce Richardson 					.depth = lsp_rule->depth,
136499a2dd95SBruce Richardson 					.valid = VALID,
136599a2dd95SBruce Richardson 					.valid_group = VALID,
136699a2dd95SBruce Richardson 					.ext_entry = 0
136799a2dd95SBruce Richardson 				};
136899a2dd95SBruce Richardson 
136999a2dd95SBruce Richardson 				*from = new_tbl_entry;
137099a2dd95SBruce Richardson 			} else {
137199a2dd95SBruce Richardson 				struct rte_lpm6_tbl_entry new_tbl_entry = {
137299a2dd95SBruce Richardson 					.next_hop = 0,
137399a2dd95SBruce Richardson 					.depth = 0,
137499a2dd95SBruce Richardson 					.valid = INVALID,
137599a2dd95SBruce Richardson 					.valid_group = INVALID,
137699a2dd95SBruce Richardson 					.ext_entry = 0
137799a2dd95SBruce Richardson 				};
137899a2dd95SBruce Richardson 
137999a2dd95SBruce Richardson 				*from = new_tbl_entry;
138099a2dd95SBruce Richardson 			}
138199a2dd95SBruce Richardson 		}
138299a2dd95SBruce Richardson 
138399a2dd95SBruce Richardson 	return 0;
138499a2dd95SBruce Richardson }
1385