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