diff options
Diffstat (limited to 'plugins/vcgn-plugin/vcgn/cnat_db_v2.c')
-rw-r--r-- | plugins/vcgn-plugin/vcgn/cnat_db_v2.c | 3802 |
1 files changed, 3802 insertions, 0 deletions
diff --git a/plugins/vcgn-plugin/vcgn/cnat_db_v2.c b/plugins/vcgn-plugin/vcgn/cnat_db_v2.c new file mode 100644 index 00000000000..2b43849dca3 --- /dev/null +++ b/plugins/vcgn-plugin/vcgn/cnat_db_v2.c @@ -0,0 +1,3802 @@ +/* + *------------------------------------------------------------------ + * cnat_db_v2.c - translation database definitions + * + * Copyright (c) 2007-2013 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vppinfra/vec.h> +#include <vppinfra/bitmap.h> +#include <vppinfra/hash.h> +#include <vppinfra/pool.h> +#include <vppinfra/clib.h> +#include <vppinfra/error.h> + +#include "cnat_db.h" +#include "cnat_config.h" +#include "cnat_global.h" +#include "cnat_v4_functions.h" +#include "cnat_log_api.h" +#include "cnat_cli.h" +#include "spp_platform_trace_log.h" +#include "cnat_bulk_port.h" +#include "nat64_db.h" +#include "dslite_db.h" +#include "cnat_config_api.h" + +#define HASH_TABLE_SIZE 8192 // hash table size +#define THROTTLE_TIME 180 // throttle time value for out of port msg/user + +u8 cnat_db_init_done = 0; + +typedef struct { + /* Locks for multi thread support */ + CLIB_CACHE_LINE_ALIGN_MARK(cacheline0); + pthread_spinlock_t *main_db_lockp; + pthread_spinlock_t *user_db_lockp; + pthread_spinlock_t *session_db_lockp; + + u32 cached_next_index; + /* $$$$ add data here */ + + /* convenience variables */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; +} cnat_db_v2_main_t; + +cnat_db_v2_main_t cnat_db_v2_main; + +#if 1 +/* TOBE_PORTED : Remove the following once fixed */ +#undef PREDICT_TRUE +#undef PREDICT_FALSE +#define PREDICT_TRUE(x) (x) +#define PREDICT_FALSE(x) (x) +#endif + +#define foreach_cnat_db_v2_error \ +_(DROP, "error-drop packets") + +typedef enum { +#define _(sym,str) CNAT_DB_V2_##sym, + foreach_cnat_db_v2_error +#undef _ + CNAT_DB_V2_N_ERROR, +} cnat_db_v2_error_t; + +static char * cnat_db_v2_error_strings[] __attribute__((unused)) = { +#define _(sym,string) string, + foreach_cnat_db_v2_error +#undef _ +}; + + +void cnat_table_entry_fill_map(u32 start_addr, u32 end_addr, + cnat_portmap_v2_t **port_map_holder) +{ + u32 this_start_addr, this_end_addr, this_addr, new; + u32 loop_count; + u32 pm_len, i; + cnat_portmap_v2_t *my_pm =0; + cnat_portmap_v2_t *pm = 0; + + my_instance_number = 0; + + this_start_addr = start_addr; + this_end_addr = end_addr; + + /* + * How many new addresses are getting added ?? + */ + /* commenting this. Right now end - start will be for this vCGN instance */ + //new = ((this_end_addr - this_start_addr) / MAX_CORES_PER_PARTITION) + 1; + new = (this_end_addr - this_start_addr) + 1; + + pm = *port_map_holder; + pm_len = vec_len(pm); +#if DEBUG_NOT_COMMENTED + printf("this_start_addr = 0x%08X, this_end_addr = 0x%08X, Num Addr = %d\n", + this_start_addr, this_end_addr, new); + printf("pm_len = %d\n", pm_len); +#endif + /* Check whether the address pool add requested already exists */ + my_pm = pm; + for(i = 0; i< pm_len; i++) { + if(my_pm->ipv4_address == this_start_addr) { + printf("address pool with addr 0x%08X exists\n", this_start_addr); + return; + } + my_pm++; + } + + /* + * For now give a warning message only.... + */ +#if 0 + if ((total_address_pool_allocated + new) > + CNAT_MAX_ADDR_POOL_SIZE_PER_CORE) { + printf("address pool size (%d) would cross permissible limit (%u) \n", + (total_address_pool_allocated + new), + CNAT_MAX_ADDR_POOL_SIZE_PER_CORE); + } +#endif + + total_address_pool_allocated += new; + vec_add2(pm, my_pm, new); + +#if DEBUG_NOT_COMMENTED + printf("total_address_pool_allocated changed from %d to %d (added %d)", + (total_address_pool_allocated - new), + total_address_pool_allocated, new); + printf("vec add is ok\n"); +#endif + + memset(my_pm, 0, new*sizeof(*my_pm)); + this_addr = this_start_addr; + loop_count = 0; /* Sanity counter */ + + while (this_addr <= this_end_addr) { +#if DEBUG_NOT_COMMENTED + printf("loop %d: this addr = 0x%08X\n", loop_count+1, this_addr); +#endif + my_pm->ipv4_address = this_addr; + /* + * Set all bits to "1" indicating all ports are free + */ + memset(my_pm->bm, 0xff, + (((BITS_PER_INST + BITS(uword)-1)/BITS(uword))*(sizeof(uword)))); + //this_addr += MAX_CORES_PER_PARTITION; + this_addr += 1; + my_pm++; + loop_count++; + } + /* + * We should have loop_count same as the new value + */ + if (loop_count != new) { + printf("Mismatch in loop_count (%d) != new (%d)\n", + loop_count, new); + } + + *port_map_holder = pm; + +#if DEBUG_NOT_COMMENTED + printf("revised pm len %d\n", vec_len(*port_map_holder)); +#endif + + return; +} + + +void cnat_delete_session_db_entry (cnat_session_entry_t *ep, u8 log); +void handle_cnat_port_exceeded_logging( + cnat_user_db_entry_t *udb, + cnat_key_t * key, + cnat_vrfmap_t *vrfmap); + +cnat_global_counters_t cnat_global_counters; +u32 last_log_timestamp = 0; +u32 last_user_dyn_port_exc_timestamp = 0; +u32 last_user_stat_port_exc_timestamp = 0; + +index_slist_t *cnat_out2in_hash; +index_slist_t *cnat_in2out_hash; +index_slist_t *cnat_user_hash; +index_slist_t *cnat_timeout_hash; +index_slist_t *cnat_session_hash; + +cnat_main_db_entry_t *cnat_main_db; +cnat_user_db_entry_t *cnat_user_db; +cnat_session_entry_t *cnat_session_db; +cnat_timeout_db_entry_t *cnat_timeout_db; + +cgse_nat_db_entry_t *cgse_nat_db; +cgse_nat_user_db_entry_t *cgse_user_db; +cgse_nat_session_db_entry_t *cgse_session_db; + +nat44_dslite_common_stats_t nat44_dslite_common_stats[255]; /* 0 is for nat44 */ +nat44_dslite_global_stats_t nat44_dslite_global_stats[2]; /* 0 for nat44 and 1 for dslite */ +nat44_counters_stats_t nat44_counters_stats[CNAT_MAX_VRFMAP_ENTRIES]; +/*For displaying show cgn <cgn-name> inside-vrf <vrf-name> counters */ + +/* + * This is the pool of vrf map structures used by latest main-db functions + */ +cnat_vrfmap_t *cnat_map_by_vrf; + +/* + * Have a mapping table of vrf_id-->vrf_map_index + * This helps in easily getting the vrf_map structure during + * main-db create paths + */ +u16 vrf_map_array[CNAT_MAX_VRFMAP_ENTRIES]; +cnat_svi_params_entry svi_params_array[CNAT_MAX_VRFMAP_ENTRIES]; +cnat_ingress_vrfid_name_entry vrfid_name_map[MAX_VRFID] = {{0}}; +u64 in2out_drops_port_limit_exceeded; +u64 in2out_drops_system_limit_reached; +u64 in2out_drops_resource_depletion; +u64 no_translation_entry_drops; +u32 no_sessions; + +#define CNAT_SET_ICMP_MSG_INFO \ +if (PREDICT_TRUE((my_vrfmap->i_vrf < CNAT_MAX_VRFMAP_ENTRIES) && \ + (svi_params_array[my_vrfmap->i_vrf].ipv4_addr))) { \ + info->gen_icmp_msg = icmp_msg_gen_allowed(); \ + info->svi_addr = svi_params_array[my_vrfmap->i_vrf].ipv4_addr; \ +} + +#define CNAT_DEBUG_INSIDE_ERR(err) \ +if (((protocol == CNAT_UDP) && \ + (debug_i_flag & CNAT_DEBUG_ERR_UDP)) || \ + ((protocol == CNAT_TCP) && \ + (debug_i_flag & CNAT_DEBUG_ERR_TCP)) || \ + ((protocol == CNAT_ICMP) && \ + (debug_i_flag & CNAT_DEBUG_ERR_ICMP))) { \ + cnat_db_debug_error(&u_ki, err); \ +} + +#define DSLITE_DEBUG_INSIDE_ERR(err) \ +if (((protocol == CNAT_UDP) && \ + (debug_i_flag & CNAT_DEBUG_ERR_UDP)) || \ + ((protocol == CNAT_TCP) && \ + (debug_i_flag & CNAT_DEBUG_ERR_TCP)) || \ + ((protocol == CNAT_ICMP) && \ + (debug_i_flag & CNAT_DEBUG_ERR_ICMP))) { \ + dslite_db_debug_error(&u_ki, err); \ +} + +#define PORT_LIMIT_LOW_THRESHOLD_FOR_SYSLOG 7 +/* If the max_limit is less than 10, no meaningful throttling can be + * done.. so, log only once per user and never clear the flag + * once the user exceeds limit + */ +#define CHECK_CLEAR_PORT_LIMIT_EXCEED_FLAG(udb, max_limit) \ + if(PREDICT_FALSE(udb->flags & CNAT_USER_DB_PORT_LIMIT_EXCEEDED)) { \ + if(udb->ntranslations < \ + ((max_limit/10)*PORT_LIMIT_LOW_THRESHOLD_FOR_SYSLOG) && \ + max_limit >= 10) { \ + udb->flags = udb->flags & (~CNAT_USER_DB_PORT_LIMIT_EXCEEDED); \ + } \ + } + +#ifdef TOBE_PORTED +/* Commented to remove unused variable warning */ +static char *debug_db_error[] = { + "no error", /* CNAT_SUCCESS */ + "no config", /*CNAT_NO_CONFIG*/ + "not in run state", /*CNAT_NO_VRF_RUN*/ + "no pool for any", /*CNAT_NO_POOL_ANY*/ + "no port for any", /*CNAT_NO_PORT_ANY*/ + "bad in use for any", /*CNAT_BAD_INUSE_ANY*/ + "not found for any", /*CNAT_NOT_FOUND_ANY*/ + "invalid index for direct", /*CNAT_INV_PORT_DIRECT*/ + "deleted addr for direct", /*CNAT_DEL_PORT_DIRECT*/ + "bad in use for direct",/*CNAT_BAD_INUSE_DIRECT*/ + "not found for direct",/*CNAT_NOT_FOUND_DIRECT*/ + "out of port limit", /*CNAT_OUT_LIMIT*/ + "main db limit", /*CNAT_MAIN_DB_LIMIT*/ + "user db limit", /*CNAT_USER_DB_LIMIT*/ + "not static port", /*CNAT_NOT_STATIC_PORT*/ + "bad static port request", /*CNAT_BAD_STATIC_PORT_REQ*/ + "not this core", /*CNAT_NOT_THIS_CORE*/ + "parser error", /*CNAT_ERR_PARSER*/ + "invalid msg id", /*CNAT_ERR_INVALID_MSG_ID*/ + "invalid msg size", /*CNAT_ERR_INVALID_MSG_SIZE*/ + "invalid payload size", /*CNAT_ERR_INVALID_PAYLOAD_SIZE*/ + "bad tcp udp port", /*CNAT_ERR_BAD_TCP_UDP_PORT*/ + "bulk single failure", /*CNAT_ERR_BULK_SINGLE_FAILURE*/ + "xlat id invalid", /*CNAT_ERR_XLAT_ID_INVALID*/ + "xlat v6 prefix invalid", /*CNAT_ERR_XLAT_V6_PREFIX_INVALID*/ + "xlat v4 prefix invalid", /*CNAT_ERR_XLAT_V4_PREFIX_INVALID*/ + "xlat tcp mss invalid", /*CNAT_ERR_XLAT_TCP_MSS_INVALID*/ + "6rd id invalid", /*CNAT_ERR_6RD_ID_INVALID*/ + "6rd v4 tunnel src invalid", /*CNAT_ERR_6RD_V4_TUNNEL_SRC_INVALID*/ + "6rd v6 prefix invalid", /*CNAT_ERR_6RD_V6_PREFIX_INVALID*/ + "6rd v6 BR unicast invalid", /*CNAT_ERR_6RD_V6_BR_UNICAST_INVALID*/ + "6rd v4 prefix masklen invalid", /*CNAT_ERR_6RD_V4_PREFIX_MASK_LEN_INVALID*/ + "6rd v4 suffix masklen invalid", /*CNAT_ERR_6RD_V4_SUFFIX_MASK_LEN_INVALID*/ + "6rd v4 combo masklen invalid", /*CNAT_ERR_6RD_V4_COMBO_MASK_LEN_INVALID*/ + "6rd tunnel mtu invalid", /*CNAT_ERR_6RD_TUNNEL_MTU_INVALID*/ + "6rd tunnel ttl invalid", /*CNAT_ERR_6RD_TUNNEL_TTL_INVALID*/ + "6rd tunnel tos invalid", /*CNAT_ERR_6RD_TUNNEL_TOS_INVALID*/ +}; +#endif + +f64 port_log_timestamps[HASH_TABLE_SIZE]; /* 32 KB array per core */ + +void port_exceeded_msg_log (u32 src_addr, u16 i_vrf) +{ + u32 hash_value; + f64 current_timestamp; + vlib_main_t *vlib_main; + + vlib_main = vlib_get_main(); + current_timestamp = vlib_time_now((vlib_main_t *) vlib_main); + + hash_value = ((src_addr >> 16) ^ ((src_addr & 0xffff) ^ i_vrf)) % (1024*8); + + if (PREDICT_FALSE((current_timestamp - port_log_timestamps[hash_value]) > THROTTLE_TIME)) { + u32 arg[2] = {i_vrf, src_addr}; + /* update timestamp */ + port_log_timestamps[hash_value] = current_timestamp; + spp_printf(CNAT_USER_OUT_OF_PORTS, 2, arg); + } + + return ; +} + +static void log_port_alloc_error(cnat_errno_t error, cnat_key_t *k) +{ + u32 error_code; + u32 arr[] = {k->k.vrf, k->k.ipv4, k->k.port}; + switch (error) + { + case CNAT_NO_POOL_ANY: + error_code = CNAT_NO_POOL_FOR_ANY_ERROR; + break; + case CNAT_NO_PORT_ANY: + error_code = CNAT_NO_PORT_FOR_ANY_ERROR; + break; + case CNAT_ERR_PARSER: + error_code = CNAT_WRONG_PORT_ALLOC_TYPE; + break; + case CNAT_BAD_INUSE_ANY: + error_code = CNAT_BAD_INUSE_ANY_ERROR; + break; + case CNAT_BAD_INUSE_DIRECT: + error_code = CNAT_BAD_INUSE_DIRECT_ERROR; + break; + case CNAT_NOT_FOUND_ANY: + error_code = CNAT_NOT_FOUND_ANY_ERROR; + break; + case CNAT_NOT_FOUND_DIRECT: + error_code = CNAT_NOT_FOUND_DIRECT_ERROR; + break; + case CNAT_INV_PORT_DIRECT: + error_code = CNAT_INV_PORT_FOR_DIRECT_ERROR; + break; + default: + error_code = CNAT_NEW_PORT_ALLOC_ERROR; /* If this code is seen in the log, + it means, new error codes are to be added here */ + break; + } + spp_printf(error_code, 3, arr); +} + +void cnat_db_debug_error(cnat_db_key_bucket_t *u_ki, + cnat_errno_t error) +{ + if (PREDICT_FALSE((u_ki->k.k.vrf == debug_i_vrf) && + ((u_ki->k.k.ipv4 >= debug_i_addr_start) && + (u_ki->k.k.ipv4 <= debug_i_addr_end)))) { +#ifdef DEBUG_PRINTF_ENABLED + PLATFORM_DEBUG_PRINT("failed to allocate port due to %s " + "for i-vrf 0x%x addr 0x%x port 0x%x\n", + debug_db_error[error], u_ki->k.k.vrf, + u_ki->k.k.ipv4, u_ki->k.k.port); +#endif + { + u32 arg[] = {u_ki->k.k.vrf, u_ki->k.k.ipv4, u_ki->k.k.port}; + spp_printf(error, 3, arg); + } + } +} + +void dslite_db_debug_error(dslite_db_key_bucket_t *u_ki, + cnat_errno_t error) +{ + if (PREDICT_FALSE((u_ki->dk.ipv4_key.k.vrf == debug_i_vrf) && + ((u_ki->dk.ipv4_key.k.ipv4 >= debug_i_addr_start) && + (u_ki->dk.ipv4_key.k.ipv4 <= debug_i_addr_end)))) { +#ifdef DEBUG_PRINTF_ENABLED + PLATFORM_DEBUG_PRINT("failed to allocate port due to %s " + "for i-vrf 0x%x addr 0x%x port 0x%x\n", + debug_db_error[error], u_ki->dk.ipv4_key.k.vrf, + u_ki->dk.ipv4_key.k.ipv4, u_ki->dk.ipv4_key.k.port); +#endif + { + u32 arg[] = {u_ki->dk.ipv4_key.k.vrf, u_ki->dk.ipv4_key.k.ipv4, u_ki->dk.ipv4_key.k.port}; + spp_printf(error, 3, arg); + } + } +} + +void cnat_db_debug_i2o_drop(cnat_db_key_bucket_t *ki) +{ + if (PREDICT_FALSE(((ki->k.k.vrf & CNAT_VRF_MASK) == debug_i_vrf) && + ((ki->k.k.ipv4 >= debug_i_addr_start) && + (ki->k.k.ipv4 <= debug_i_addr_end)))) { +#ifdef DEBUG_PRINTF_ENABLED + PLATFORM_DEBUG_PRINT("pakcet[i-vrf 0x%x addr 0x%x port 0x%x] dropped\n", + ki->k.k.vrf, ki->k.k.ipv4, ki->k.k.port); +#endif + { + u32 arg[] = {ki->k.k.vrf, ki->k.k.ipv4, ki->k.k.port}; + spp_printf(CNAT_PACKET_DROP_ERROR, 3, arg); + } + } +} + +void cnat_db_in2out_hash_delete (cnat_main_db_entry_t *ep, cnat_user_db_entry_t *up) +{ + u64 a, b, c; + u32 index, bucket; + cnat_main_db_entry_t *this, *prev; + +#ifdef DSLITE_DEF + if (PREDICT_FALSE(ep->flags & CNAT_DB_DSLITE_FLAG)) { + dslite_key_t dk = { + {up->ipv6[0], up->ipv6[1], up->ipv6[2], up->ipv6[3]} , + {ep->in2out_key.k.ipv4, ep->in2out_key.k.port, ep->in2out_key.k.vrf} + }; + DSLITE_V6_GET_HASH((&dk), + bucket, + CNAT_MAIN_HASH_MASK); + DSLITE_PRINTF(1, "Delete1 DSL main hash bucket ..%u\n", bucket); + } else { + CNAT_V4_GET_HASH(ep->in2out_key.key64, + bucket, CNAT_MAIN_HASH_MASK) + DSLITE_PRINTF(1, "Delete1 NAT44 main hash bucket ..%u\n", bucket); + } +#else + CNAT_V4_GET_HASH(ep->in2out_key.key64, + bucket, CNAT_MAIN_HASH_MASK) +#endif + + index = cnat_in2out_hash[bucket].next; + + ASSERT(index != EMPTY); + + prev = 0; + do { + this = cnat_main_db + index; + if (PREDICT_TRUE(this == ep)) { + if (prev == 0) { + cnat_in2out_hash[bucket].next = ep->in2out_hash.next; + return; + } else { + prev->in2out_hash.next = ep->in2out_hash.next; + return; + } + } + prev = this; + index = this->in2out_hash.next; + } while (index != EMPTY); + + ASSERT(0); +} + +void cnat_db_out2in_hash_delete (cnat_main_db_entry_t *ep) +{ + u64 a, b, c; + u32 index, bucket; + cnat_main_db_entry_t *this, *prev; + + CNAT_V4_GET_HASH(ep->out2in_key.key64, + bucket, CNAT_MAIN_HASH_MASK) + + index = cnat_out2in_hash[bucket].next; + + ASSERT(index != EMPTY); + + prev = 0; + do { + this = cnat_main_db + index; + if (PREDICT_TRUE(this == ep)) { + if (prev == 0) { + cnat_out2in_hash[bucket].next = ep->out2in_hash.next; + return; + } else { + prev->out2in_hash.next = ep->out2in_hash.next; + return; + } + } + prev = this; + index = this->out2in_hash.next; + } while (index != EMPTY); + + ASSERT(0); +} + +cnat_main_db_entry_t* +cnat_main_db_lookup_entry(cnat_db_key_bucket_t *ki) +{ + u64 a, b, c; + u32 index; + cnat_main_db_entry_t *db; + + CNAT_V4_GET_HASH(ki->k.key64, + ki->bucket, + CNAT_MAIN_HASH_MASK); + + index = cnat_in2out_hash[ki->bucket].next; + if (PREDICT_TRUE(index == EMPTY)) { + return (NULL); + } + + do { + db = cnat_main_db + index; + if (PREDICT_TRUE(db->in2out_key.key64 == ki->k.key64)) { + return db; + } + index = db->in2out_hash.next; + } while (index != EMPTY); + + return (NULL); +} + +void cnat_user_db_delete (cnat_user_db_entry_t *up) +{ + u64 a, b, c; + u32 index, bucket; + cnat_user_db_entry_t *this, *prev; + + if (PREDICT_FALSE(up->flags & CNAT_USER_DB_NAT64_FLAG) != 0) { + /* Preventive check - Not a NAT44 entry */ + return; + } + + pthread_spin_lock(cnat_db_v2_main.user_db_lockp); +#if 1 + if(PREDICT_FALSE(up->flags & CNAT_USER_DB_DSLITE_FLAG)) { + dslite_key_t dk = { + {up->ipv6[0], up->ipv6[1], up->ipv6[2], up->ipv6[3]} , + {{up->key.k.ipv4, up->key.k.port, up->key.k.vrf}} + }; + + DSLITE_V6_GET_HASH((&dk), + bucket, + CNAT_USER_HASH_MASK); + DSLITE_PRINTF(1, "Delete1 DSL user hash bucket ..%u\n", bucket); + } else { + CNAT_V4_GET_HASH(up->key.key64, + bucket, CNAT_USER_HASH_MASK) + DSLITE_PRINTF(1, "Delete1 NAT44 user hash bucket ..%u\n", bucket); + } +#else + CNAT_V4_GET_HASH(up->key.key64, + bucket, CNAT_USER_HASH_MASK) + DSLITE_PRINTF(1, "Delete2 NAT44 user hash bucket ..%u\n", bucket); +#endif + + index = cnat_user_hash[bucket].next; + + ASSERT(index != EMPTY); + + prev = 0; + do { + this = cnat_user_db + index; + if (PREDICT_TRUE(this == up)) { + if (prev == 0) { + cnat_user_hash[bucket].next = up->user_hash.next; + goto found; + } else { + prev->user_hash.next = up->user_hash.next; + goto found; + } + } + prev = this; + index = this->user_hash.next; + } while (index != EMPTY); + + ASSERT(0); + + found: + pool_put(cnat_user_db, up); + pthread_spin_unlock(cnat_db_v2_main.user_db_lockp); +} + +cnat_user_db_entry_t* +cnat_user_db_lookup_entry(cnat_db_key_bucket_t *uki) +{ + u64 a, b, c; + u32 index; + cnat_user_db_entry_t *udb=NULL; + + CNAT_V4_GET_HASH(uki->k.key64, + uki->bucket, + CNAT_USER_HASH_MASK) + + /* now: index in user vector */ + index = cnat_user_hash[uki->bucket].next; + if (PREDICT_TRUE(index != EMPTY)) { + do { + udb = cnat_user_db + index; + if (PREDICT_FALSE(udb->key.key64 == uki->k.key64)) { + return udb; + } + index = udb->user_hash.next; + } while (index != EMPTY); + } + return (NULL); +} + +cnat_user_db_entry_t* +cnat_user_db_create_entry(cnat_db_key_bucket_t *uki, + u32 portmap_index) +{ + cnat_user_db_entry_t *udb = NULL; + + pthread_spin_lock(cnat_db_v2_main.user_db_lockp); + pool_get(cnat_user_db, udb); + memset(udb, 0, sizeof(*udb)); + + udb->ntranslations = 1; + udb->portmap_index = portmap_index; + udb->key.key64 = uki->k.key64; + /* Add this user to the head of the bucket chain */ + udb->user_hash.next = + cnat_user_hash[uki->bucket].next; + cnat_user_hash[uki->bucket].next = udb - cnat_user_db; + +#ifndef NO_BULK_LOGGING + INIT_BULK_CACHE(udb) +#endif /* NO_BULK_LOGGING */ + pthread_spin_unlock(cnat_db_v2_main.user_db_lockp); + return udb; +} + +cnat_main_db_entry_t* +cnat_create_main_db_entry_and_hash(cnat_db_key_bucket_t *ki, + cnat_db_key_bucket_t *ko, + cnat_user_db_entry_t *udb) +{ + u64 a, b, c; + u32 db_index; + cnat_main_db_entry_t *db = NULL; + + pool_get(cnat_main_db, db); + memset(db, 0, sizeof(*db)); + + db_index = db - cnat_main_db; + db->in2out_key.k.ipv4 = ki->k.k.ipv4; + db->in2out_key.k.port = ki->k.k.port; + db->in2out_key.k.vrf = ki->k.k.vrf; + db->out2in_key.k.ipv4 = ko->k.k.ipv4; + db->out2in_key.k.port = ko->k.k.port; + db->out2in_key.k.vrf = ko->k.k.vrf; + + db->user_ports.next = db_index; + db->user_ports.prev = db_index; + db->user_index = udb - cnat_user_db; + //db->portmap_index = udb->portmap_index; + db->flags &= ~(CNAT_DB_DSLITE_FLAG); // Mark that it is not dslite + if (PREDICT_FALSE(udb->ntranslations == 1)) { + /* + * first port for this src vrf/src ip addr + */ + udb->translation_list_head_index = db_index; + } else { + index_dlist_addtail(udb->translation_list_head_index, + (u8 *)cnat_main_db, sizeof(cnat_main_db[0]), + STRUCT_OFFSET_OF(cnat_main_db_entry_t, user_ports), + db_index); + } + + /* + * setup o2i hash key + */ + CNAT_V4_GET_HASH(ko->k.key64, + ko->bucket, + CNAT_MAIN_HASH_MASK) + db->out2in_hash.next = cnat_out2in_hash[ko->bucket].next; + cnat_out2in_hash[ko->bucket].next = db_index; + /* + * setup i2o hash key, bucket is already calculate + */ + db->in2out_hash.next = cnat_in2out_hash[ki->bucket].next; + cnat_in2out_hash[ki->bucket].next = db_index; + +#if DEBUG > 1 + printf("\nMy_Instance_Number %d: Bucket %d, Db_Index %d", + my_instance_number, ki->bucket, db_index); + printf("\nInside (VRF 0x%x, IP 0x%x, PORT 0x%x)", + db->in2out_key.k.vrf, db->in2out_key.k.ipv4, db->in2out_key.k.port); + printf("\nOutside (VRF 0x%x, IP 0x%x, PORT 0x%x)", + db->out2in_key.k.vrf, db->out2in_key.k.ipv4, db->out2in_key.k.port); + printf("\nUser Index %d, IP 0x%x", + db->user_index, udb->key.k.ipv4); +#endif + + NAT44_COMMON_STATS.active_translations++; + + return db; +} + +static inline void pptp_clear_all_channels( + cnat_main_db_entry_t *db) +{ + u32 db_index, current_db_index; + cnat_main_db_entry_t *temp_db; + + /* clear all channels */ + + db_index = db->proto_data.pptp_list.next; + current_db_index = db - cnat_main_db; + + while( db_index != EMPTY) { + temp_db = cnat_main_db + db_index; + db_index = temp_db->proto_data.pptp_list.next; + temp_db->entry_expires = 0; + if(PREDICT_FALSE(temp_db->proto_data.pptp_list.prev + == current_db_index)) { // Decouple child GREs from parent + temp_db->proto_data.pptp_list.prev = EMPTY; + } + } + + db->proto_data.pptp_list.next = EMPTY; +} + +void pptp_remove_channel_from_tunnel(cnat_main_db_entry_t *db) { + + cnat_main_db_entry_t *prev_db, *next_db; + + prev_db = cnat_main_db + db->proto_data.pptp_list.prev; + next_db = cnat_main_db + db->proto_data.pptp_list.next; + + /* remove entry from the tunnel list */ + if(PREDICT_TRUE(db->proto_data.pptp_list.prev != EMPTY)) { + prev_db->proto_data.pptp_list.next = + db->proto_data.pptp_list.next ; + } + + if(db->proto_data.pptp_list.next != EMPTY) { + next_db->proto_data.pptp_list.prev + = db->proto_data.pptp_list.prev; + } + +} + +void cnat_delete_main_db_entry_v2 (cnat_main_db_entry_t *ep) +{ + u32 main_db_index; + u32 vrfmap_len, udb_len; + cnat_user_db_entry_t *up =0; + cnat_portmap_v2_t *pm =0; + cnat_portmap_v2_t *my_pm =0; + cnat_vrfmap_t *my_vrfmap =0; + u16 static_port_range; +#ifndef NO_BULK_LOGGING + bulk_alloc_size_t bulk_size; + int nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED; +#endif + pool_header_t *h = pool_header(cnat_user_db); + u16 instance = 0; + u32 my_index; + + + if (PREDICT_FALSE(ep->flags & CNAT_DB_NAT64_FLAG) != 0) { + /* Preventive check - Not a NAT44 entry */ + return; + } + + pthread_spin_lock(cnat_db_v2_main.main_db_lockp); + if(PREDICT_FALSE(ep->flags & + CNAT_DB_FLAG_PPTP_TUNNEL_ACTIVE)) { + pptp_clear_all_channels(ep); + PPTP_DECR(active_tunnels); + } + + if(PREDICT_FALSE(ep->flags & + CNAT_DB_FLAG_PPTP_GRE_ENTRY)) { + pptp_remove_channel_from_tunnel(ep); + PPTP_DECR(active_channels); + } + + /* This function gets called from various locations.. + * many times from config handler.. so we + * to ensure that multiple sessions if any are + * released + */ + + if(PREDICT_FALSE(ep->nsessions > 1)) { + cnat_session_entry_t *sdb; + while(ep->nsessions > 1 && + ep->session_head_index != EMPTY) { + sdb = cnat_session_db + ep->session_head_index; + cnat_delete_session_db_entry(sdb, TRUE); + } + } + + /* Find the set of portmaps for the outside vrf */ + vrfmap_len = vec_len(cnat_map_by_vrf); + udb_len = vec_len(cnat_user_db); + + /* In case of invalid user just return, deleting only main db + * is not a good idea, since some valid user db entry might be pointing + * to that main db and hence leave the dbs in a inconsistent state + */ + if (PREDICT_FALSE((ep->user_index >= udb_len) || + (clib_bitmap_get(h->free_bitmap, ep->user_index)))) { +#ifdef DEBUG_PRINTF_ENABLED + printf("invalid/unused user index in db %d\n", ep->user_index); +#endif + spp_printf(CNAT_INV_UNUSED_USR_INDEX, 1, (u32 *) &(ep->user_index)); + cnat_main_db_entry_dump(ep); + goto unlock; + } + + up = cnat_user_db + ep->user_index; + +/* Point to the right portmap list */ +if (PREDICT_FALSE(ep->flags & CNAT_DB_DSLITE_FLAG)) { + instance = ep->dslite_nat44_inst_id; + pm = dslite_table_db_ptr[instance].portmap_list; + if(PREDICT_FALSE((pm == NULL))) { + DSLITE_PRINTF(3, "NULL portmap list for dslite_id %u, state %u\n", + instance, dslite_table_db_ptr[instance].state); + cnat_main_db_entry_dump(ep); + goto delete_entry; + } + static_port_range = + STAT_PORT_RANGE_FROM_INST_PTR(&(dslite_table_db_ptr[instance])); + /* + * Netflow logging API for delete event + */ + bulk_size = + BULKSIZE_FROM_VRFMAP(&(dslite_table_db_ptr[instance])); +} else { + if (PREDICT_FALSE(ep->vrfmap_index >= vrfmap_len)) { +#ifdef DEBUG_PRINTF_ENABLED + printf("invalid vrfmap index in db\n"); +#endif + spp_printf(CNAT_INVALID_VRFMAP_INDEX, 0, NULL); + cnat_main_db_entry_dump(ep); + goto delete_entry; + } + instance = NAT44_RESERVED_INST_ID; + my_vrfmap = cnat_map_by_vrf + ep->vrfmap_index; + pm = my_vrfmap->portmap_list; + static_port_range = cnat_static_port_range; + bulk_size = BULKSIZE_FROM_VRFMAP(my_vrfmap); +} + + if (PREDICT_FALSE(ep->flags & CNAT_DB_FLAG_PORT_PAIR)) { + /* Give back the port(s) */ + cnat_port_free_v2_bulk(pm, up->portmap_index, + PORT_PAIR, ep->out2in_key.k.port, up, static_port_range +#ifndef NO_BULK_LOGGING + , bulk_size, &nfv9_log_req +#endif + ); + } else { + /* Give back the port(s) */ + cnat_port_free_v2_bulk (pm, up->portmap_index, + PORT_SINGLE, ep->out2in_key.k.port, up, static_port_range +#ifndef NO_BULK_LOGGING + , bulk_size, &nfv9_log_req +#endif + ); + } + + if (PREDICT_TRUE(!(ep->flags & CNAT_DB_DSLITE_FLAG))) { + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + if(PREDICT_FALSE(my_vrfmap->nf_logging_policy == SESSION_LOG_ENABLE)) { + if(ep->nsessions != 0) { + cnat_nfv9_nat44_log_session_delete(ep, NULL, my_vrfmap); + } + } else { + cnat_nfv9_log_mapping_delete(ep, my_vrfmap +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + if(PREDICT_TRUE((my_vrfmap->syslog_logging_policy != SESSION_LOG_ENABLE) || + (ep->nsessions != 0))) { + cnat_syslog_nat44_mapping_delete(ep, my_vrfmap, NULL +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + } + } else { + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + if(PREDICT_FALSE( dslite_table_db_ptr[instance].nf_logging_policy == + SESSION_LOG_ENABLE)) { + cnat_nfv9_ds_lite_log_session_delete(ep, + (dslite_table_db_ptr + instance),NULL); + } else { + cnat_nfv9_ds_lite_mapping_delete(ep, + (dslite_table_db_ptr + instance) +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } +#ifdef TOBE_PORTED + cnat_syslog_ds_lite_mapping_delete(ep, + (dslite_table_db_ptr + instance), NULL +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); +#endif /* TOBE_PORTED */ + } + } + +delete_entry: + + main_db_index = ep - cnat_main_db; + + pthread_spin_lock(cnat_db_v2_main.user_db_lockp); + up->ntranslations--; + pthread_spin_unlock(cnat_db_v2_main.user_db_lockp); + + /* + * when user reaches max allowed port limit + * we generate icmp msg and inc the counter + * when counter reach the icmp msg rate limit + * we stop icmp msg gen + * when a user port is freed + * that means we need to clear the msg gen counter + * so that next time + * reach max port limit, we can generate new icmp msg again + */ + up->icmp_msg_count = 0; + + up->translation_list_head_index = index_dlist_remelem ( + up->translation_list_head_index, (u8 *)cnat_main_db, + sizeof (cnat_main_db[0]), + STRUCT_OFFSET_OF(cnat_main_db_entry_t, user_ports), + main_db_index); + + cnat_db_in2out_hash_delete(ep, up); + + if (PREDICT_FALSE(up->ntranslations == 0)) { + ASSERT(up->translation_list_head_index == EMPTY); + nat44_dslite_common_stats[instance].num_subscribers--; + my_index = up->portmap_index; + my_pm = pm + my_index; + if(PREDICT_TRUE(my_pm->private_ip_users_count)) { + my_pm->private_ip_users_count--; +#ifdef DEBUG_PRINTF_IP_N_TO_1_ENABLED + PLATFORM_DEBUG_PRINT("\n cnat_delete_main_db_entry_v2 " + "private_ip_users_count = %d", + my_pm->private_ip_users_count); +#endif + + } + cnat_user_db_delete(up); + + } + + /* Remove from main DB hashes */ + //cnat_db_in2out_hash_delete(ep); + cnat_db_out2in_hash_delete(ep); + + pool_put(cnat_main_db, ep); + + if(PREDICT_FALSE(ep->flags & CNAT_DB_FLAG_STATIC_PORT)) { + nat44_dslite_common_stats[instance].num_static_translations--; + } else { + nat44_dslite_common_stats[instance].num_dynamic_translations--; + } + nat44_dslite_common_stats[instance].active_translations--; + nat44_dslite_global_stats[!!(instance - 1)].translation_delete_count ++; +unlock: + pthread_spin_unlock(cnat_db_v2_main.main_db_lockp); +} + +cnat_main_db_entry_t* +cnat_main_db_lookup_entry_out2in (cnat_db_key_bucket_t *ko) +{ + u64 a, b, c; + u32 index; + cnat_main_db_entry_t *db; + + CNAT_V4_GET_HASH(ko->k.key64, + ko->bucket, + CNAT_MAIN_HASH_MASK); + + index = cnat_out2in_hash[ko->bucket].next; + if (PREDICT_TRUE(index == EMPTY)) { + return (NULL); + } + + do { + db = cnat_main_db + index; + if (PREDICT_TRUE(db->out2in_key.key64 == ko->k.key64)) { + return db; + } + index = db->out2in_hash.next; + } while (index != EMPTY); + + return (NULL); +} + +/* Creates 2 sessions. + * Moves the default dest info from mdb to first session + * Fills the dest_info details in to second session and + * returns the pointer to second session + */ +cnat_session_entry_t *cnat_handle_1to2_session( + cnat_main_db_entry_t *mdb, + cnat_key_t *dest_info) +{ + cnat_key_t old_dest_info; + pool_header_t *h; + u32 free_session = 0; + u16 instance; + cnat_session_entry_t *session_db1 = NULL, *session_db2 = NULL; + + h = pool_header(cnat_session_db); + free_session = vec_len(h->free_indices) - 1; + + if (PREDICT_FALSE(free_session < 2)) { + if (mdb->flags & CNAT_DB_DSLITE_FLAG) { + instance = mdb->dslite_nat44_inst_id; + } else { + instance = NAT44_RESERVED_INST_ID; + } + + /* we need 2 sessions here, return NULL */ + nat44_dslite_common_stats[instance].drops_sessiondb_limit_exceeded++; + return NULL; + } + + old_dest_info.k.ipv4 = mdb->dst_ipv4; + old_dest_info.k.port = mdb->dst_port; + old_dest_info.k.vrf = mdb->in2out_key.k.vrf; + + /* create 2 new sessions */ + session_db1 = cnat_create_session_db_entry(&old_dest_info, + mdb, FALSE); + + if(PREDICT_FALSE(session_db1 == NULL)) { + return NULL; + } + + /* update pkt info to session 2 */ + session_db2 = cnat_create_session_db_entry(dest_info, + mdb, TRUE); + + if(PREDICT_FALSE(session_db2 == NULL)) { + cnat_delete_session_db_entry(session_db1, FALSE); + return NULL; + } + /* update main db info to session 1 */ + cnat_dest_update_main2session(mdb, session_db1); + + return session_db2; +} + +/* The below function shold be called only + * when a NAT44 STATIC entry received traffic + * for the first time. This is to ensure + * the destination is noted and logged + */ +void cnat_add_dest_n_log( + cnat_main_db_entry_t *mdb, + cnat_key_t *dest_info) +{ + + if(PREDICT_FALSE(mdb->nsessions != 0)) { + return; /* Should not have been called */ + } + + mdb->dst_ipv4 = dest_info->k.ipv4; + mdb->dst_port = dest_info->k.port; + mdb->nsessions = 1; + mdb->entry_expires = cnat_current_time; + u16 instance; + + if (mdb->flags & CNAT_DB_DSLITE_FLAG) { + instance = mdb->dslite_nat44_inst_id; + cnat_session_log_ds_lite_mapping_create(mdb, + (dslite_table_db_ptr + instance),NULL); + } else { + instance = NAT44_RESERVED_INST_ID; + cnat_vrfmap_t *my_vrfmap = cnat_map_by_vrf + mdb->vrfmap_index; + cnat_session_log_nat44_mapping_create(mdb, 0, my_vrfmap); + } +} + +/* + * this function is called by exception node + * when lookup is fialed in i2o node + * + * if reash per user port limit, + * set user_db_entry pointer, and error == CNAT_OUT_LIMIT + */ +static cnat_main_db_entry_t* +_cnat_get_main_db_entry_v2(cnat_db_key_bucket_t *ki, + port_pair_t port_pair_type, + port_type_t port_type, + cnat_gen_icmp_info *info, + cnat_key_t *dest_info) +{ + u16 protocol; + cnat_errno_t rv; + cnat_db_key_bucket_t u_ki, ko; + u32 my_index, free_main, free_user; + u32 current_timestamp; + u16 my_vrfmap_index; + u16 my_vrfmap_entry_found = 0; + cnat_vrfmap_t *my_vrfmap =0; + cnat_portmap_v2_t *pm =0; + cnat_user_db_entry_t *udb = 0; + cnat_main_db_entry_t *db = 0; + pool_header_t *h; + u16 port_limit; + cnat_portmap_v2_t *my_pm = 0; + +#ifndef NO_BULK_LOGGING + int nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED; +#endif + + + /* + * need to try lookup again because + * second pkt may come here before the entry is created + * by receiving first pkt due to high line rate. + */ + info->gen_icmp_msg = CNAT_NO_ICMP_MSG; + info->error = CNAT_SUCCESS; + db = cnat_main_db_lookup_entry(ki); + if (PREDICT_TRUE(db)) { + /* what if the source is talking to a + * new dest now? We will have to handle this case and + * take care of - creating session db and logging + */ + if(PREDICT_FALSE((!dest_info->k.ipv4) && (!dest_info->k.port))) { + return db; /* if dest_info is null don't create session */ + } + if(PREDICT_TRUE((db->dst_ipv4 == dest_info->k.ipv4) && + (db->dst_port == dest_info->k.port))) { + return db; + } + dest_info->k.vrf = db->in2out_key.k.vrf; + /* Src is indeed talking to a different dest */ + cnat_session_entry_t *session_db2 = NULL; + if(PREDICT_TRUE(db->nsessions == 1)) { + session_db2 = cnat_handle_1to2_session(db, dest_info); + if(PREDICT_TRUE(session_db2 != NULL)) { + CNAT_DB_TIMEOUT_RST(session_db2); + return db; + } else { + info->error = CNAT_ERR_NO_SESSION_DB; + return NULL; + } + } else if(PREDICT_FALSE(db->nsessions == 0)) { + /* Should be static entry.. should never happen + */ + if(PREDICT_TRUE(dest_info->k.ipv4 != 0)) { + cnat_add_dest_n_log(db, dest_info); + } + return db; + } else { + /* The src has already created multiple sessions.. very rare + */ + session_db2 = cnat_create_session_db_entry(dest_info, + db, TRUE); + if(PREDICT_TRUE(session_db2 != NULL)) { + CNAT_DB_TIMEOUT_RST(session_db2); + return db; + } else { + info->error = CNAT_ERR_NO_SESSION_DB; + return NULL; + } + } + + } + + /* + * step 1. check if outside vrf is configured or not + * and Find the set of portmaps for the outside vrf + * insider vrf is one to one mappted to outside vrf + * key is vrf and ip only + * ki.k.k.vrf has protocol bits, mask out + */ + protocol = ki->k.k.vrf & CNAT_PRO_MASK; + u_ki.k.k.vrf = ki->k.k.vrf & CNAT_VRF_MASK; + u_ki.k.k.ipv4 = ki->k.k.ipv4; + u_ki.k.k.port = 0; + + my_vrfmap_index = vrf_map_array[u_ki.k.k.vrf]; + my_vrfmap = cnat_map_by_vrf + my_vrfmap_index; + + my_vrfmap_entry_found = ((my_vrfmap_index != VRF_MAP_ENTRY_EMPTY) && + (my_vrfmap->status == S_RUN) && + (my_vrfmap->i_vrf == u_ki.k.k.vrf)); + + if (PREDICT_FALSE(!my_vrfmap_entry_found)) { + u32 arr[] = {ki->k.k.vrf, ki->k.k.ipv4, ki->k.k.port}; + if ((my_vrfmap_index == VRF_MAP_ENTRY_EMPTY) || + (my_vrfmap->i_vrf == u_ki.k.k.vrf)) { + info->error = CNAT_NO_CONFIG; + CNAT_DEBUG_INSIDE_ERR(CNAT_NO_CONFIG) + spp_printf(CNAT_NO_CONFIG_ERROR, 3, arr); + } else { + info->error = CNAT_NO_VRF_RUN; + CNAT_DEBUG_INSIDE_ERR(CNAT_NO_VRF_RUN) + spp_printf(CNAT_NO_VRF_RUN_ERROR, 3, arr); + } + + return (NULL); + } + + pm = my_vrfmap->portmap_list; + + port_limit = my_vrfmap->port_limit; + if(PREDICT_FALSE(!port_limit)) { + port_limit = cnat_main_db_max_ports_per_user; + } + /* + * set o2i key with protocl bits + */ + ko.k.k.vrf = my_vrfmap->o_vrf | protocol; + + /* + * step 2. check if src vrf, src ip addr is alreay + * in the user db + * if yes, use PORT_ALLOC_DIRECTED + * if no, use PORT_ALLOC_ANY since it is first time + */ + udb = cnat_user_db_lookup_entry(&u_ki); + if (PREDICT_TRUE(udb)) { + /* + * not first time allocate port for this user + * check limit + */ + if (PREDICT_FALSE(udb->ntranslations >= + port_limit)) { + /* Check for the port type here. If we are getting + * a STATIC PORT, allow the config. + */ + if (PREDICT_TRUE(port_type != PORT_TYPE_STATIC)) { + info->error = CNAT_OUT_LIMIT; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(CNAT_OUT_LIMIT) + port_exceeded_msg_log(u_ki.k.k.ipv4, u_ki.k.k.vrf); + in2out_drops_port_limit_exceeded ++; + u_ki.k.k.port = ki->k.k.port; + u_ki.k.k.vrf = ki->k.k.vrf; + handle_cnat_port_exceeded_logging(udb, &u_ki.k, my_vrfmap); + return (NULL); + } + } + CHECK_CLEAR_PORT_LIMIT_EXCEED_FLAG(udb, + port_limit) + + /* + * check if main db has space to accomodate new entry + */ + h = pool_header(cnat_main_db); + + free_main = vec_len(h->free_indices) - 1; + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + CNAT_SET_ICMP_MSG_INFO + in2out_drops_system_limit_reached ++; + CNAT_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) + + current_timestamp = spp_trace_log_get_unix_time_in_seconds(); + if (PREDICT_FALSE((current_timestamp - last_log_timestamp) > + 1800)) { + spp_printf(CNAT_SESSION_THRESH_EXCEEDED, 0, NULL); + last_log_timestamp = current_timestamp; + } + +#ifdef UT_TEST_CODE + printf("Limit reached : OLD USER"); +#endif + return NULL; + } + + /* + * allocate port, from existing mapping + */ + my_index = udb->portmap_index; + + if (PREDICT_FALSE(port_type == PORT_TYPE_STATIC)) { + rv = cnat_static_port_alloc_v2_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + ki->k.k.ipv4, + ki->k.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + cnat_static_port_range +#ifndef NO_BULK_LOGGING + , + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), + &nfv9_log_req +#endif + , my_vrfmap->ip_n_to_1 + ); + + } else if (PREDICT_TRUE(port_type != PORT_TYPE_RTSP) ) { + + rv = cnat_dynamic_port_alloc_v2_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + cnat_static_port_range +#ifndef NO_BULK_LOGGING + , + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), + &nfv9_log_req +#endif + , my_vrfmap->ip_n_to_1, + &(my_vrfmap->rseed_ip) + ); + + } else { + /* + * For RTSP, two translation entries are created, + * check if main db has space to accomodate two new entry + */ + free_main = free_main - 1; + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + CNAT_SET_ICMP_MSG_INFO + in2out_drops_system_limit_reached ++; + CNAT_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) + + return NULL; + } else { + rv = cnat_dynamic_port_alloc_rtsp_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + ki->k.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + cnat_static_port_range +#ifndef NO_BULK_LOGGING + , + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), + &nfv9_log_req +#endif + , &(my_vrfmap->rseed_ip) + ); + } + } + + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(rv) + in2out_drops_resource_depletion++; + log_port_alloc_error(rv, &(ki->k)); + return (NULL); + } + /* + * increment port in use for this user + */ + pthread_spin_lock(cnat_db_v2_main.user_db_lockp); + udb->ntranslations += 1; + pthread_spin_unlock(cnat_db_v2_main.user_db_lockp); + + } else { + /* + * first time allocate port for this user + */ + + /* + * Do not create entry if port limit is invalid + */ + + if (PREDICT_FALSE(!port_limit)) { + if (PREDICT_TRUE(port_type != PORT_TYPE_STATIC)) { + info->error = CNAT_OUT_LIMIT; + in2out_drops_port_limit_exceeded ++; + port_exceeded_msg_log(u_ki.k.k.ipv4, u_ki.k.k.vrf); + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(CNAT_OUT_LIMIT) + return (NULL); + } + } + + /* + * Check if main db has space for new entry + * Allowing a user db entry to be created if main db is not free + * will cause a port to be allocated to that user, which results in + * wastage of that port, hence the check is done here. + */ + h = pool_header(cnat_main_db); + free_main = vec_len(h->free_indices) - 1; + h = pool_header(cnat_user_db); + free_user = vec_len(h->free_indices) - 1; + + /* + * If either main_db or user_db does not have entries + * bail out, with appropriate error + */ + if (PREDICT_FALSE(!(free_main && free_user))) { + u32 log_error; + if(free_main) { + info->error = CNAT_USER_DB_LIMIT; + log_error = CNAT_USER_DB_LIMIT_ERROR; + } else { + info->error = CNAT_MAIN_DB_LIMIT; + log_error = CNAT_MAIN_DB_LIMIT_ERROR; + } + in2out_drops_system_limit_reached ++; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(info->error) + spp_printf(log_error, 0, 0); + return NULL; + } + + if (PREDICT_FALSE(port_type == PORT_TYPE_STATIC)) { + rv = cnat_static_port_alloc_v2_bulk(pm, + PORT_ALLOC_ANY, + port_pair_type, + ki->k.k.ipv4, + ki->k.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + cnat_static_port_range +#ifndef NO_BULK_LOGGING + , + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), + &nfv9_log_req +#endif + , my_vrfmap->ip_n_to_1 + ); + + } else if (PREDICT_TRUE(port_type != PORT_TYPE_RTSP)) { + rv = cnat_dynamic_port_alloc_v2_bulk(pm, + PORT_ALLOC_ANY, + port_pair_type, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + cnat_static_port_range +#ifndef NO_BULK_LOGGING + , NULL, BULKSIZE_FROM_VRFMAP(my_vrfmap), + &nfv9_log_req +#endif + , my_vrfmap->ip_n_to_1, + &(my_vrfmap->rseed_ip) + ); + } else { + /* + * For RTSP, two translation entries are created, + * check if main db has space to accomodate two new entry + */ + free_main = free_main - 1; + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + CNAT_SET_ICMP_MSG_INFO + in2out_drops_system_limit_reached ++; + CNAT_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) + + return NULL; + } else { + + rv = cnat_dynamic_port_alloc_rtsp_bulk(pm, + PORT_ALLOC_ANY, + port_pair_type, + ki->k.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + cnat_static_port_range +#ifndef NO_BULK_LOGGING + , NULL, BULKSIZE_FROM_VRFMAP(my_vrfmap), + &nfv9_log_req +#endif + , &(my_vrfmap->rseed_ip) + ); + /* TODO: Add the port pair flag here */ + } + } + + + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + in2out_drops_resource_depletion ++; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(rv) + log_port_alloc_error(rv, &(ki->k)); + return (NULL); + } + /* + * create entry in user db + */ + udb = cnat_user_db_create_entry(&u_ki, my_index); + NAT44_COMMON_STATS.num_subscribers++; + my_pm = pm + my_index; + if(PREDICT_TRUE(my_pm->private_ip_users_count < PORTS_PER_ADDR)) { + my_pm->private_ip_users_count++; +#ifdef DEBUG_PRINTF_IP_N_TO_1_ENABLED + PLATFORM_DEBUG_PRINT("\n cnat_get_main_db_entry_v2 " + "dynamic alloc private_ip_users_count = %d", + my_pm->private_ip_users_count); +#endif + } else { + PLATFORM_DEBUG_PRINT("\n ERROR: private_ip_users_count has " + "reached MAX PORTS_PER_ADDR"); + } +#ifndef NO_BULK_LOGGING + if(PREDICT_TRUE(udb && (BULK_ALLOC_NOT_ATTEMPTED != nfv9_log_req))) { + cnat_update_bulk_range_cache(udb, ko.k.k.port, + BULKSIZE_FROM_VRFMAP(my_vrfmap)); + } +#endif /* #ifndef NO_BULK_LOGGING */ + + } + + /* + * step 3: + * outside port is allocated for this src vrf/src ip addr + * 1)create a new entry in main db + * 2)setup cnat_out2in_hash key + * 3)setup cnat_in2out_hash key + */ + db = cnat_create_main_db_entry_and_hash(ki, &ko, udb); + + translation_create_count ++; +#ifdef DSLITE_DEF + db->dslite_nat44_inst_id = NAT44_RESERVED_INST_ID; +#endif + db->vrfmap_index = my_vrfmap - cnat_map_by_vrf; + + /* + * don't forget logging + * logging API is unconditional, + * logging configuration check is done inside the inline function + */ + + db->dst_ipv4 = dest_info->k.ipv4; + db->dst_port = dest_info->k.port; + if(PREDICT_TRUE(db->dst_ipv4 || db->dst_port)) { + db->nsessions++; + } + + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + if(PREDICT_FALSE(my_vrfmap->nf_logging_policy == SESSION_LOG_ENABLE)) { + /* do not log for static entries.. we will log when traffic flows */ + if(PREDICT_TRUE(db->dst_ipv4 || db->dst_port)) { + cnat_nfv9_nat44_log_session_create(db, 0, my_vrfmap); + } + } else { + cnat_nfv9_log_mapping_create(db, my_vrfmap +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + if(PREDICT_TRUE((my_vrfmap->syslog_logging_policy != SESSION_LOG_ENABLE) || + (db->dst_ipv4 || db->dst_port))) { + cnat_syslog_nat44_mapping_create(db, my_vrfmap, 0 +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + } + if (PREDICT_FALSE(port_pair_type == PORT_PAIR)) { + cnat_main_db_entry_t *db2 = 0; + cnat_db_key_bucket_t new_ki = *ki; + u64 a, b, c; + + new_ki.k.k.port += 1; + ko.k.k.port += 1; + + CNAT_V4_GET_HASH(new_ki.k.key64, new_ki.bucket, + CNAT_MAIN_HASH_MASK); + + db2 = cnat_create_main_db_entry_and_hash(&new_ki, &ko, udb); + + translation_create_count ++; +#ifdef DSLITE_DEF + db2->dslite_nat44_inst_id = NAT44_RESERVED_INST_ID; +#endif + db2->vrfmap_index = my_vrfmap - cnat_map_by_vrf; + db2->entry_expires = cnat_current_time; + db2->flags |= CNAT_DB_FLAG_ALG_ENTRY; + pthread_spin_lock(cnat_db_v2_main.user_db_lockp); + udb->ntranslations += 1; + pthread_spin_unlock(cnat_db_v2_main.user_db_lockp); + db2->dst_ipv4 = dest_info->k.ipv4; + db2->dst_port = dest_info->k.port; + db2->nsessions = 0; /* For ALG db, set sessions to 0 - CSCuf78420 */ + + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + if(PREDICT_FALSE(my_vrfmap->nf_logging_policy == SESSION_LOG_ENABLE)) { + /* do not log for static entries.. we will log when traffic flows */ + if(PREDICT_TRUE(db2->dst_ipv4 || db2->dst_port)) { + cnat_nfv9_nat44_log_session_create(db2, 0, my_vrfmap); + } + } else { + cnat_nfv9_log_mapping_create(db2, my_vrfmap +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + if(PREDICT_TRUE((my_vrfmap->syslog_logging_policy != SESSION_LOG_ENABLE) || + (db2->dst_ipv4 || db2->dst_port))) { + cnat_syslog_nat44_mapping_create(db2, my_vrfmap, 0 +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + } + } + + return db; +} + +cnat_main_db_entry_t* +cnat_get_main_db_entry_v2(cnat_db_key_bucket_t *ki, + port_pair_t port_pair_type, + port_type_t port_type, + cnat_gen_icmp_info *info, + cnat_key_t *dest_info) +{ + + cnat_main_db_entry_t *db; + pthread_spin_lock(cnat_db_v2_main.main_db_lockp); + db = _cnat_get_main_db_entry_v2(ki, port_pair_type, + port_type, info, dest_info); + pthread_spin_unlock(cnat_db_v2_main.main_db_lockp); + return db; +} + +/* + * this function is called from config handler only + * to allocate a static port based db entry + * + * the actual mapped address and port are already specified + */ +cnat_main_db_entry_t* +cnat_create_static_main_db_entry_v2 (cnat_db_key_bucket_t *ki, + cnat_db_key_bucket_t *ko, + cnat_vrfmap_t *my_vrfmap, + cnat_gen_icmp_info *info) +{ + u16 protocol; + u32 head; + cnat_errno_t rv; + cnat_db_key_bucket_t u_ki; + u32 my_index, free_main, free_user; + cnat_portmap_v2_t *pm =0; + cnat_portmap_v2_t *my_pm =0; + cnat_user_db_entry_t *udb = 0; + cnat_main_db_entry_t *db = 0; + pool_header_t *h; +#ifndef NO_BULK_LOGGING + int nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED; +#endif + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + + /* + * need to try lookup again because + * second pkt may come here before the entry is created + * by receiving first pkt due to high line rate. + */ + info->gen_icmp_msg = CNAT_NO_ICMP_MSG; + info->error = CNAT_SUCCESS; + db = cnat_main_db_lookup_entry(ki); + + /* + * If we already have an entry with this inside address, port + * check delete the entry and proceed further. This should + * If yes, something is terribly wrong. Bail out + */ + if (PREDICT_FALSE(db)) { + + if (db->flags & CNAT_DB_FLAG_STATIC_PORT) { + + if ((db->out2in_key.k.ipv4 == ko->k.k.ipv4) && + (db->out2in_key.k.port == ko->k.k.port) && + (db->out2in_key.k.vrf == ko->k.k.vrf)) { + +#ifdef DEBUG_PRINTF_ENABLED + printf("Same Static Port Exists ki 0x%16llx ko 0x%16llx", + ki->k, ko->k); +#endif + /* + * We have already programmed this, return + */ + return (db); + } + + /* + * We already have a static port with different mapping + * Return an error for this case. + */ + info->error = CNAT_ERR_PARSER; + +#ifdef DEBUG_PRINTF_ENABLED + printf("Static Port Existing and Diff ki 0x%16llx ko 0x%16llx", + ki, db->out2in_key); +#endif + { + u32 arr[] = {STAT_PORT_CONFIG_IN_USE, (ki->k.k.vrf & CNAT_VRF_MASK), + ki->k.k.ipv4, ki->k.k.port, (ki->k.k.vrf & CNAT_PRO_MASK) }; + spp_printf(CNAT_CONFIG_ERROR, 5, arr); + } + return (db); + } + +#ifdef DEBUG_PRINTF_ENABLED + printf("Deleting Dynamic entry ki 0x%16llx ko 0x%16llx", + ki, db->out2in_key); +#endif + + /* + * If for some reason we have dynamic entries, just delete them + * and proceed. + */ + cnat_delete_main_db_entry_v2(db); + + db = NULL; + } + + protocol = ki->k.k.vrf & CNAT_PRO_MASK; + u_ki.k.k.vrf = ki->k.k.vrf & CNAT_VRF_MASK; + u_ki.k.k.ipv4 = ki->k.k.ipv4; + u_ki.k.k.port = 0; + + pm = my_vrfmap->portmap_list; + + /* + * check if src vrf, src ip addr is already + * in the user db + * if yes, use PORT_ALLOC_DIRECTED + * if no, use PORT_ALLOC_ANY since it is first time + */ + udb = cnat_user_db_lookup_entry(&u_ki); + if (PREDICT_TRUE(udb)) { + /* + * check if main db has space to accomodate new entry + */ + h = pool_header(cnat_main_db); + + free_main = vec_len(h->free_indices) - 1; + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + CNAT_SET_ICMP_MSG_INFO + in2out_drops_system_limit_reached ++; + CNAT_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) +#ifdef UT_TEST_CODE + printf("Limit reached : OLD USER"); +#endif + spp_printf(CNAT_MAIN_DB_LIMIT_ERROR, 0, 0); + return NULL; + } + + /* + * allocate port, from existing mapping + */ + my_index = udb->portmap_index; + my_pm = pm + my_index; + /* It is quite possible that we hit the scenario of CSCtj17774. + * Delete all the main db entries and add the ipv4 address sent by + * CGN-MA as Static port alloc any + */ + + if (PREDICT_FALSE(my_pm->ipv4_address != ko->k.k.ipv4)) { + if (PREDICT_FALSE(global_debug_flag && CNAT_DEBUG_GLOBAL_ALL)) { + printf("Delete Main db entry and check for" + " ipv4 address sanity pm add = 0x%x ip add = 0x%x\n", + my_pm->ipv4_address, ko->k.k.ipv4); + } + do { + /* udb is not NULL when we begin with for sure */ + head = udb->translation_list_head_index; + db = cnat_main_db + head; + cnat_delete_main_db_entry_v2(db); + } while (!pool_is_free(cnat_user_db, udb)); + + rv = cnat_mapped_static_port_alloc_v2_bulk (pm, + PORT_ALLOC_ANY, &my_index, ko->k.k.ipv4, ko->k.k.port, + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), &nfv9_log_req, + my_vrfmap->ip_n_to_1); + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + in2out_drops_resource_depletion ++; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(rv) + return (NULL); + } + /* + * create entry in user db + */ + udb = cnat_user_db_create_entry(&u_ki, my_index); + my_pm = pm + my_index; + if(PREDICT_TRUE(my_pm->private_ip_users_count < PORTS_PER_ADDR)) { + my_pm->private_ip_users_count++; +#ifdef DEBUG_PRINTF_IP_N_TO_1_ENABLED + PLATFORM_DEBUG_PRINT("\n cnat_create_static_main_db_entry_v2 " + "static del n alloc private_ip_users_count = " + "%d",my_pm->private_ip_users_count); +#endif + } else { + PLATFORM_DEBUG_PRINT("\n ERROR: private_ip_users_count has " + "reached MAX PORTS_PER_ADDR"); + } + NAT44_COMMON_STATS.num_subscribers++; +#ifndef NO_BULK_LOGGING + cnat_update_bulk_range_cache(udb, ko->k.k.port, + BULKSIZE_FROM_VRFMAP(my_vrfmap)); +#endif /* #ifndef NO_BULK_LOGGING */ + } else { + + rv = cnat_mapped_static_port_alloc_v2_bulk (pm, + PORT_ALLOC_DIRECTED, &my_index, ko->k.k.ipv4, ko->k.k.port, + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), &nfv9_log_req, + my_vrfmap->ip_n_to_1); + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(rv) + log_port_alloc_error(rv, &(ki->k)); + return (NULL); + } + + /* + * increment port in use for this user + */ + udb->ntranslations += 1; + } + } else { + if (PREDICT_FALSE(global_debug_flag && CNAT_DEBUG_GLOBAL_ALL)) { + printf ("Static port alloc any\n"); + } + /* + * first time allocate port for this user + */ + + /* + * Check if main db has space for new entry + * Allowing a user db entry to be created if main db is not free + * will cause a port to be allocated to that user, which results in + * wastage of that port, hence the check is done here. + */ + h = pool_header(cnat_main_db); + free_main = vec_len(h->free_indices) - 1; + h = pool_header(cnat_user_db); + free_user = vec_len(h->free_indices) - 1; + + /* + * If either main_db or user_db does not have entries + * bail out, with appropriate error + */ + if (PREDICT_FALSE(!(free_main && free_user))) { + u32 log_error; + if(free_main) { + info->error = CNAT_USER_DB_LIMIT; + log_error = CNAT_USER_DB_LIMIT_ERROR; + } else { + info->error = CNAT_MAIN_DB_LIMIT; + log_error = CNAT_MAIN_DB_LIMIT_ERROR; + } + in2out_drops_system_limit_reached ++; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(info->error) + spp_printf(log_error, 0, 0); + return NULL; + } + + rv = cnat_mapped_static_port_alloc_v2_bulk (pm, + PORT_ALLOC_ANY, &my_index, ko->k.k.ipv4, ko->k.k.port, + udb, BULKSIZE_FROM_VRFMAP(my_vrfmap), &nfv9_log_req, + my_vrfmap->ip_n_to_1); + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + in2out_drops_resource_depletion ++; + CNAT_SET_ICMP_MSG_INFO + CNAT_DEBUG_INSIDE_ERR(rv) + log_port_alloc_error(rv, &(ki->k)); + return (NULL); + } + /* + * create entry in user db + */ + udb = cnat_user_db_create_entry(&u_ki, my_index); + my_pm = pm + my_index; + if(PREDICT_TRUE(my_pm->private_ip_users_count < PORTS_PER_ADDR)) { + my_pm->private_ip_users_count++; +#ifdef DEBUG_PRINTF_IP_N_TO_1_ENABLED + PLATFORM_DEBUG_PRINT("\n cnat_create_static_main_db_entry_v2 " + "static alloc private_ip_users_count = %d", + my_pm->private_ip_users_count); +#endif + } else { + PLATFORM_DEBUG_PRINT("\n ERROR: private_ip_users_count has " + "reached MAX PORTS_PER_ADDR"); + } + NAT44_COMMON_STATS.num_subscribers++; +#ifndef NO_BULK_LOGGING + cnat_update_bulk_range_cache(udb, ko->k.k.port, + BULKSIZE_FROM_VRFMAP(my_vrfmap)); +#endif /* #ifndef NO_BULK_LOGGING */ + } + + /* + * step 3: + * outside port is allocated for this src vrf/src ip addr + * 1)create a new entry in main db + * 2)setup cnat_out2in_hash key + * 3)setup cnat_in2out_hash key + */ + db = cnat_create_main_db_entry_and_hash(ki, ko, udb); + + translation_create_count ++; + db->vrfmap_index = my_vrfmap - cnat_map_by_vrf; + + /* + * don't forget logging + * logging API is unconditional, + * logging configuration check is done inside the inline function + */ + + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + /* if session logging is enabled .. do not log as there is no + * traffic yet + */ + if(PREDICT_FALSE(my_vrfmap->nf_logging_policy != SESSION_LOG_ENABLE)) { + cnat_nfv9_log_mapping_create(db, my_vrfmap +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + if(PREDICT_FALSE(my_vrfmap->syslog_logging_policy != SESSION_LOG_ENABLE)) { + cnat_syslog_nat44_mapping_create(db, my_vrfmap, 0 +#ifndef NO_BULK_LOGGING + , nfv9_log_req +#endif + ); + } + } + + return db; +} + + +cnat_main_db_entry_t* +dslite_main_db_lookup_entry(dslite_db_key_bucket_t *ki); + +cnat_user_db_entry_t* +dslite_user_db_lookup_entry(dslite_db_key_bucket_t *uki); + +cnat_user_db_entry_t* +dslite_user_db_create_entry(dslite_db_key_bucket_t *uki, u32 portmap_index); + +cnat_main_db_entry_t* +dslite_create_main_db_entry_and_hash(dslite_db_key_bucket_t *ki, + cnat_db_key_bucket_t *ko, + cnat_user_db_entry_t *udb); + +#ifdef TOBE_PORTED +/* + * this function is called from config handler only + * to allocate a static port based db entry + * + * the actual mapped address and port are already specified + */ +cnat_main_db_entry_t* +dslite_create_static_main_db_entry_v2 (dslite_db_key_bucket_t *ki, + cnat_db_key_bucket_t *ko, + dslite_table_entry_t *dslite_entry_ptr, + cnat_gen_icmp_info *info) +{ + u16 protocol; + u32 head; + cnat_errno_t rv; + dslite_db_key_bucket_t u_ki; + u32 my_index, free_main, free_user; + cnat_portmap_v2_t *pm =0; + cnat_portmap_v2_t *my_pm =0; + cnat_user_db_entry_t *udb = 0; + cnat_main_db_entry_t *db = 0; + pool_header_t *h; + u16 dslite_id = dslite_entry_ptr->dslite_id; +#ifndef NO_BULK_LOGGING + int nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED; +#endif + cnat_vrfmap_t *my_vrfmap =0; + u16 my_vrfmap_index; + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + /* + * need to try lookup again because + * second pkt may come here before the entry is created + * by receiving first pkt due to high line rate. + */ + info->gen_icmp_msg = CNAT_NO_ICMP_MSG; + info->error = CNAT_SUCCESS; + db = dslite_main_db_lookup_entry(ki); + + /* + * If we already have an entry with this inside address, port + * check delete the entry and proceed further. This should + * If yes, something is terribly wrong. Bail out + */ + if (PREDICT_FALSE(db)) { + + if (db->flags & CNAT_DB_FLAG_STATIC_PORT) { + + if ((db->out2in_key.k.ipv4 == ko->k.k.ipv4) && + (db->out2in_key.k.port == ko->k.k.port) && + (db->out2in_key.k.vrf == ko->k.k.vrf)) { + +#ifdef DEBUG_PRINTF_ENABLED + printf("Same Static Port Exists ki 0x%16llx ko 0x%16llx", + ki->k, ko->k); +#endif + /* + * We have already programmed this, return + */ + return (db); + } + + /* + * We already have a static port with different mapping + * Return an error for this case. + */ + info->error = CNAT_ERR_PARSER; + +#ifdef DEBUG_PRINTF_ENABLED + printf("Static Port Existing and Diff ki 0x%16llx ko 0x%16llx", + ki, db->out2in_key); +#endif + { + u32 arr[] = {STAT_PORT_CONFIG_IN_USE, (ki->dk.ipv4_key.k.vrf & CNAT_VRF_MASK), + ki->dk.ipv4_key.k.ipv4, ki->dk.ipv4_key.k.port, (ki->dk.ipv4_key.k.vrf & CNAT_PRO_MASK) }; + spp_printf(CNAT_CONFIG_ERROR, 5, arr); + } + return (db); + } + +#ifdef DEBUG_PRINTF_ENABLED + printf("Deleting Dynamic entry ki 0x%16llx ko 0x%16llx", + ki, db->out2in_key); +#endif + + /* + * If for some reason we have dynamic entries, just delete them + * and proceed. + */ + cnat_delete_main_db_entry_v2(db); + + db = NULL; + } + + + protocol = ki->dk.ipv4_key.k.vrf & CNAT_PRO_MASK; + u_ki.dk.ipv4_key.k.vrf = ki->dk.ipv4_key.k.vrf & CNAT_VRF_MASK; + u_ki.dk.ipv4_key.k.ipv4 = ki->dk.ipv4_key.k.ipv4; + u_ki.dk.ipv4_key.k.port = 0; + u_ki.dk.ipv6[0] = ki->dk.ipv6[0]; + u_ki.dk.ipv6[1] = ki->dk.ipv6[1]; + u_ki.dk.ipv6[2] = ki->dk.ipv6[2]; + u_ki.dk.ipv6[3] = ki->dk.ipv6[3]; + + my_vrfmap_index = vrf_map_array[u_ki.dk.ipv4_key.k.vrf]; + my_vrfmap = cnat_map_by_vrf + my_vrfmap_index; + + pm = dslite_entry_ptr->portmap_list; + + /* + * check if src vrf, src ip addr is already + * in the user db + * if yes, use PORT_ALLOC_DIRECTED + * if no, use PORT_ALLOC_ANY since it is first time + */ + udb = dslite_user_db_lookup_entry(&u_ki); + if (PREDICT_TRUE(udb)) { + /* + * check if main db has space to accomodate new entry + */ + h = pool_header(cnat_main_db); + + free_main = vec_len(h->free_indices) - 1; + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + nat44_dslite_common_stats[dslite_id].in2out_drops_port_limit_exceeded ++; + DSLITE_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) +#ifdef UT_TEST_CODE + printf("Limit reached : OLD USER"); +#endif + spp_printf(CNAT_MAIN_DB_LIMIT_ERROR, 0, 0); + return NULL; + } + + /* + * allocate port, from existing mapping + */ + my_index = udb->portmap_index; + my_pm = pm + my_index; + /* It is quite possible that we hit the scenario of CSCtj17774. + * Delete all the main db entries and add the ipv4 address sent by + * CGN-MA as Static port alloc any + */ + + if (PREDICT_FALSE(my_pm->ipv4_address != ko->k.k.ipv4)) { + if (PREDICT_FALSE(global_debug_flag && CNAT_DEBUG_GLOBAL_ALL)) { + printf("Delete Main db entry and check for" + " ipv4 address sanity pm add = 0x%x ip add = 0x%x\n", + my_pm->ipv4_address, ko->k.k.ipv4); + } + do { + /* udb is not NULL when we begin with for sure */ + head = udb->translation_list_head_index; + db = cnat_main_db + head; + cnat_delete_main_db_entry_v2(db); + } while (!pool_is_free(cnat_user_db, udb)); + + rv = cnat_mapped_static_port_alloc_v2_bulk (pm, + PORT_ALLOC_ANY, &my_index, ko->k.k.ipv4, ko->k.k.port, + udb, BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), &nfv9_log_req, + my_vrfmap->ip_n_to_1); + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + nat44_dslite_common_stats[dslite_id].in2out_drops_port_limit_exceeded ++; + DSLITE_DEBUG_INSIDE_ERR(rv) + return (NULL); + } + /* + * create entry in user db + */ + udb = dslite_user_db_create_entry(&u_ki, my_index); + nat44_dslite_common_stats[dslite_id].num_subscribers++; +#ifndef NO_BULK_LOGGING + if(PREDICT_FALSE(udb && (BULK_ALLOC_NOT_ATTEMPTED != nfv9_log_req))) { + cnat_update_bulk_range_cache(udb, ko->k.k.port, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr)); + } +#endif /* #ifndef NO_BULK_LOGGING */ + } else { + + rv = cnat_mapped_static_port_alloc_v2_bulk (pm, + PORT_ALLOC_DIRECTED, &my_index, ko->k.k.ipv4, ko->k.k.port, + udb, BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), &nfv9_log_req, + my_vrfmap->ip_n_to_1); + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + DSLITE_DEBUG_INSIDE_ERR(rv) + log_port_alloc_error(rv, &(ki->dk.ipv4_key)); + return (NULL); + } + + /* + * increment port in use for this user + */ + udb->ntranslations += 1; + } + } else { + if (PREDICT_FALSE(global_debug_flag && CNAT_DEBUG_GLOBAL_ALL)) { + printf ("Static port alloc any\n"); + } + /* + * first time allocate port for this user + */ + + /* + * Check if main db has space for new entry + * Allowing a user db entry to be created if main db is not free + * will cause a port to be allocated to that user, which results in + * wastage of that port, hence the check is done here. + */ + h = pool_header(cnat_main_db); + free_main = vec_len(h->free_indices) - 1; + h = pool_header(cnat_user_db); + free_user = vec_len(h->free_indices) - 1; + + /* + * If either main_db or user_db does not have entries + * bail out, with appropriate error + */ + if (PREDICT_FALSE(!(free_main && free_user))) { + u32 log_error; + if(free_main) { + info->error = CNAT_USER_DB_LIMIT; + log_error = CNAT_USER_DB_LIMIT_ERROR; + } else { + info->error = CNAT_MAIN_DB_LIMIT; + log_error = CNAT_MAIN_DB_LIMIT_ERROR; + } + nat44_dslite_common_stats[dslite_id].in2out_drops_port_limit_exceeded ++; + DSLITE_DEBUG_INSIDE_ERR(info->error) + spp_printf(log_error, 0, 0); + return NULL; + } + + rv = cnat_mapped_static_port_alloc_v2_bulk (pm, + PORT_ALLOC_ANY, &my_index, ko->k.k.ipv4, ko->k.k.port, + udb, BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), &nfv9_log_req, + my_vrfmap->ip_n_to_1); + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + info->error = rv; + nat44_dslite_common_stats[dslite_id].in2out_drops_port_limit_exceeded ++; + DSLITE_DEBUG_INSIDE_ERR(rv) + log_port_alloc_error(rv, &(ki->dk.ipv4_key)); + return (NULL); + } + /* + * create entry in user db + */ + udb = dslite_user_db_create_entry(&u_ki, my_index); + nat44_dslite_common_stats[dslite_id].num_subscribers++; +#ifndef NO_BULK_LOGGING + if(PREDICT_FALSE(udb && (BULK_ALLOC_NOT_ATTEMPTED != nfv9_log_req))) { + cnat_update_bulk_range_cache(udb, ko->k.k.port, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr)); + } +#endif /* #ifndef NO_BULK_LOGGING */ + } + + /* + * step 3: + * outside port is allocated for this src vrf/src ip addr + * 1)create a new entry in main db + * 2)setup cnat_out2in_hash key + * 3)setup cnat_in2out_hash key + */ + db = dslite_create_main_db_entry_and_hash(ki, ko, udb); + db->dslite_nat44_inst_id = dslite_id; + nat44_dslite_common_stats[dslite_id].active_translations++; + dslite_translation_create_count++; + + /* + * don't forget logging + * logging API is unconditional, + * logging configuration check is done inside the inline function + */ +#if 0 /* TBD - NEED TO DECIDE ON LOGGING */ + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + /* if session logging is enabled .. do not log as there is no + * traffic yet + */ +#endif /* #if 0 - this has to be removed later */ + + return db; +} +#endif /* TOBE_PORTED */ + + +/* Per port/ip timeout related routines */ +static +u32 cnat_timeout_db_hash_lookup (cnat_key_t t_key) +{ + cnat_key_t key; + u64 a, b, c; + u32 index; + cnat_timeout_db_entry_t *db; + + key.k.ipv4 = t_key.k.ipv4; + key.k.port = t_key.k.port; + key.k.vrf = t_key.k.vrf; + + CNAT_V4_GET_HASH(key.key64, + index, CNAT_TIMEOUT_HASH_MASK) + + + index = cnat_timeout_hash[index].next; + + if (PREDICT_FALSE(index == EMPTY)) + return EMPTY; + + do { + db = cnat_timeout_db + index; + if (PREDICT_TRUE((db->t_key.timeout_key.key64 & CNAT_TIMEOUT_FULL_MASK) + == (key.key64 & CNAT_TIMEOUT_FULL_MASK))) + break; + index = db->t_hash.next; + } while (index != EMPTY); + + return index; +} + +/* Pass db_type as MAIN_DB_TYPE if you are passing + * cnat_main_db_entry_t * casted as void * for db + * else pass db_type as SESSION_DB_TYPE + */ +u16 +query_and_update_db_timeout(void *db, u8 db_type) +{ + cnat_key_t t_search_key; + u32 index; + cnat_timeout_db_entry_t *timeout_db_entry; + pool_header_t *h; + u32 free; + + cnat_main_db_entry_t *mdb = NULL; + cnat_session_entry_t *sdb = NULL; + + if(PREDICT_TRUE(db_type == MAIN_DB_TYPE)) { + mdb = (cnat_main_db_entry_t *)db; + } else if(db_type == SESSION_DB_TYPE) { + sdb = (cnat_session_entry_t *)db; + } else { + return 0; + } + + h = pool_header(cnat_timeout_db); + free = vec_len(h->free_indices) - 1; + + if(free == CNAT_TIMEOUT_HASH_SIZE) { + /* No timeout db configured */ + return 0; + } + + /* First search for ip/port pair */ + if(PREDICT_TRUE(db_type == MAIN_DB_TYPE)) { + t_search_key.k.ipv4 = mdb->dst_ipv4; + t_search_key.k.port = mdb->dst_port; + t_search_key.k.vrf = mdb->in2out_key.k.vrf; + } else { + t_search_key.k.ipv4 = sdb->v4_dest_key.k.ipv4; + t_search_key.k.port = sdb->v4_dest_key.k.port; + t_search_key.k.vrf = sdb->v4_dest_key.k.vrf; + } + + index = cnat_timeout_db_hash_lookup(t_search_key); + + if(index == EMPTY) { + /* Search for port map */ + t_search_key.k.ipv4 = 0; + + index = cnat_timeout_db_hash_lookup(t_search_key); + + if(index == EMPTY) { + /* Search for ip only map */ + if(PREDICT_TRUE(db_type == MAIN_DB_TYPE)) { + t_search_key.k.ipv4 = mdb->dst_ipv4; + } else { + t_search_key.k.ipv4 = sdb->v4_dest_key.k.ipv4; + } + t_search_key.k.port = 0; + + index = cnat_timeout_db_hash_lookup(t_search_key); + if(index != EMPTY) { +#ifdef DEBUG_PRINTF_ENABLED + printf("%s: ip only map sucess\n","query_and_update_db_timeout"); +#endif + } + } else { +#ifdef DEBUG_PRINTF_ENABLED + printf("%s: port only map sucess\n", "query_and_update_db_timeout"); +#endif + } + + } else { +#ifdef DEBUG_PRINTF_ENABLED + printf("%s: ip port map sucess\n","query_and_update_db_timeout"); +#endif + + } + + if(index == EMPTY) { + /* No match found, clear timeout */ + if(PREDICT_TRUE(db_type == MAIN_DB_TYPE)) { + mdb->timeout = 0; + } else { + sdb->timeout = 0; + } +#ifdef DEBUG_PRINTF_ENABLED + printf("%s: No match\n","query_and_update_db_timeout"); +#endif + } else { + /* Match found, update timeout */ + timeout_db_entry = cnat_timeout_db + index; + if(PREDICT_TRUE(db_type == MAIN_DB_TYPE)) { + mdb->timeout = timeout_db_entry->t_key.timeout_value; + } else { + sdb->timeout = timeout_db_entry->t_key.timeout_value; + } + return timeout_db_entry->t_key.timeout_value; + } + return 0; +} + + + +static +void cnat_timeout_db_hash_add (cnat_timeout_db_entry_t *t_entry) +{ + cnat_key_t key; + u64 a, b, c; + u32 index, bucket; + cnat_key_t t_key = t_entry->t_key.timeout_key; + + key.k.ipv4 = t_key.k.ipv4; + key.k.port = t_key.k.port; + key.k.vrf = t_key.k.vrf; + + CNAT_V4_GET_HASH(key.key64, + bucket, CNAT_TIMEOUT_HASH_MASK) + + + index = cnat_timeout_hash[bucket].next; + + /* Add this db entry to the head of the bucket chain */ + t_entry->t_hash.next = index; + cnat_timeout_hash[bucket].next = t_entry - cnat_timeout_db; +} + + + +u16 +cnat_timeout_db_create (cnat_timeout_t t_entry) +{ + cnat_timeout_db_entry_t *db; + cnat_key_t t_key = t_entry.timeout_key; + u32 db_index; + + pool_header_t *h; + u32 free; + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + + db_index = cnat_timeout_db_hash_lookup(t_key); + + if(db_index != EMPTY) { + /* Entry already exists. Check if it is replay or update */ + db = cnat_timeout_db + db_index; + db->t_key.timeout_value = t_entry.timeout_value; + return CNAT_SUCCESS; + } + + h = pool_header(cnat_timeout_db); + free = vec_len(h->free_indices) - 1; + + if(free == 0) { + return CNAT_OUT_LIMIT; + } + + + pool_get(cnat_timeout_db, db); + ASSERT(db); + + memset(db, 0, sizeof(*db)); + + db_index = db - cnat_timeout_db; + + db->t_key.timeout_key.k.ipv4 = t_key.k.ipv4; + db->t_key.timeout_key.k.port = t_key.k.port; + db->t_key.timeout_key.k.vrf = t_key.k.vrf; + db->t_key.timeout_value = t_entry.timeout_value; + + + cnat_timeout_db_hash_add(db); + return CNAT_SUCCESS; +} + +void cnat_timeout_db_delete(cnat_key_t t_key) +{ + cnat_key_t key; + u64 a, b, c; + u32 index, bucket; + cnat_timeout_db_entry_t *this, *prev; + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + + key.k.ipv4 = t_key.k.ipv4; + key.k.port = t_key.k.port; + key.k.vrf = t_key.k.vrf; + + + CNAT_V4_GET_HASH(key.key64, + bucket, CNAT_TIMEOUT_HASH_MASK) + + + index = cnat_timeout_hash[bucket].next; + + if(index == EMPTY) return; + + prev = 0; + do { + this = cnat_timeout_db + index; + if (PREDICT_TRUE( + (this->t_key.timeout_key.key64 & CNAT_TIMEOUT_FULL_MASK) == + (key.key64 & CNAT_TIMEOUT_FULL_MASK))) { + if (prev == 0) { + cnat_timeout_hash[bucket].next = this->t_hash.next; + goto found; + } else { + prev->t_hash.next = this->t_hash.next; + goto found; + } + } + + prev = this; + index = this->t_hash.next; + } while (index != EMPTY); + + if(index == EMPTY) return; + + found: + pool_put(cnat_timeout_db, this); + +} + +void cnat_session_db_hash_delete (cnat_session_entry_t *ep) +{ + u32 a, b, c; + u32 index, bucket; + cnat_session_entry_t *this, *prev; + + CNAT_V4_GET_SESSION_HASH(ep->main_db_index, ep->v4_dest_key.k.ipv4, + ep->v4_dest_key.k.port, ep->v4_dest_key.k.vrf, bucket, + CNAT_SESSION_HASH_MASK) + + + index = cnat_session_hash[bucket].next; + + ASSERT(index != EMPTY); + + prev = 0; + do { + this = cnat_session_db + index; + if (PREDICT_TRUE(this == ep)) { + if (prev == 0) { + cnat_session_hash[bucket].next = + ep->cnat_session_hash.next; + return; + } else { + prev->cnat_session_hash.next = + ep->cnat_session_hash.next; + return; + } + } + prev = this; + index = this->cnat_session_hash.next; + } while (index != EMPTY); + + ASSERT(0); + +} + +cnat_session_entry_t * +cnat_session_db_edm_lookup_entry(cnat_key_t *ko,u32 session_head_index, + u32 main_db_index) +{ + u32 index; + cnat_session_entry_t *db; + + + index = session_head_index; + if (PREDICT_TRUE(index == EMPTY)) { + return (NULL); + } + + do { + db = cnat_session_db + index; + if(PREDICT_TRUE((db->main_db_index == main_db_index) && + (db->v4_dest_key.k.vrf == ko->k.vrf) && + (db->v4_dest_key.k.ipv4 == ko->k.ipv4))) { + + return db; + } + index = db->cnat_session_hash.next; + } while (index != EMPTY); + + return (NULL); +} + + + +cnat_session_entry_t * +cnat_session_db_lookup_entry(cnat_key_t *ko,u32 main_db_index) +{ + u32 a, b, c; + u32 index, bucket; + cnat_session_entry_t *db; + + CNAT_V4_GET_SESSION_HASH(main_db_index, ko->k.ipv4, ko->k.port, + ko->k.vrf, bucket, CNAT_SESSION_HASH_MASK) + + + index = cnat_session_hash[bucket].next; + if (PREDICT_TRUE(index == EMPTY)) { + return (NULL); + } + + do { + db = cnat_session_db + index; + if(PREDICT_TRUE((db->main_db_index == main_db_index) && + (db->v4_dest_key.k.vrf == ko->k.vrf) && + (db->v4_dest_key.k.port == ko->k.port) && + (db->v4_dest_key.k.ipv4 == ko->k.ipv4))) { + + return db; + } + index = db->cnat_session_hash.next; + } while (index != EMPTY); + + return (NULL); +} + +cnat_session_entry_t * +cnat_create_session_db_entry(cnat_key_t *ko, + cnat_main_db_entry_t *bdb, u8 log) +{ + u32 a, b, c; + u32 db_index, bucket_out; + cnat_session_entry_t *db = NULL; + pool_header_t *h; + u32 free_session; + u16 instance; + + db = cnat_session_db_lookup_entry(ko, bdb - cnat_main_db); + if (PREDICT_FALSE(db != NULL)) { + /*printf("Create Session - Entry already Exists\n");*/ + return db; + } + + h = pool_header(cnat_session_db); + free_session = vec_len(h->free_indices) - 1; + + if (bdb->flags & CNAT_DB_DSLITE_FLAG) { + instance = bdb->dslite_nat44_inst_id; + } else { + instance = NAT44_RESERVED_INST_ID; + } + + if (PREDICT_FALSE(!free_session)) { + nat44_dslite_common_stats[instance].drops_sessiondb_limit_exceeded++; + return NULL; + } + + if( PREDICT_FALSE(bdb->nsessions == CNAT_MAX_SESSIONS_PER_BIB)) { + /* printf("Create Session - Max sessions per BIB reached\n"); */ + return NULL; + } + + pthread_spin_lock(cnat_db_v2_main.session_db_lockp); + pool_get(cnat_session_db, db); + memset(db, 0, sizeof(*db)); + + db_index = db - cnat_session_db; + db->v4_dest_key.k.port = ko->k.port; + db->v4_dest_key.k.ipv4 = ko->k.ipv4; + db->v4_dest_key.k.vrf = ko->k.vrf; + + db->main_list.next = db_index; + db->main_list.prev = db_index; + db->main_db_index = bdb - cnat_main_db; + + db->tcp_seq_num = 0; + db->ack_no = 0; + db->window = 0; + + if(PREDICT_FALSE(log)) { + bdb->nsessions++; + query_and_update_db_timeout(db, SESSION_DB_TYPE); + } + + if (PREDICT_FALSE(bdb->nsessions == 1)) { + /* + * first port for this src vrf/src ip addr + */ + bdb->session_head_index = db_index; + } else { + index_dlist_addtail(bdb->session_head_index, + (u8 *)cnat_session_db, sizeof(cnat_session_db[0]), + STRUCT_OFFSET_OF(cnat_session_entry_t, main_list), + db_index); + } + + /* + * setup o2i hash key + */ + CNAT_V4_GET_SESSION_HASH(db->main_db_index, ko->k.ipv4, ko->k.port, + ko->k.vrf, bucket_out, CNAT_SESSION_HASH_MASK) + + + db->cnat_session_hash.next = + cnat_session_hash[bucket_out].next; + cnat_session_hash[bucket_out].next = db_index; + + + if(PREDICT_FALSE(log)) { + if (bdb->flags & CNAT_DB_DSLITE_FLAG) { + cnat_session_log_ds_lite_mapping_create(bdb, + (dslite_table_db_ptr + instance),db); + } else { + cnat_vrfmap_t *my_vrfmap = cnat_map_by_vrf + bdb->vrfmap_index; + cnat_session_log_nat44_mapping_create(bdb, db, my_vrfmap); + } + } + + /* Need to set entry_expires here, as we need to override 0 check for + newly established sessions */ + db->entry_expires = cnat_current_time; + nat44_dslite_common_stats[instance].sessions++; + pthread_spin_unlock(cnat_db_v2_main.session_db_lockp); + return db; +} + +void +cnat_dest_update_main2session(cnat_main_db_entry_t *mdb, + cnat_session_entry_t *sdb) +{ + + sdb->flags = mdb->flags; + sdb->timeout = mdb->timeout; + sdb->entry_expires = mdb->entry_expires; + sdb->alg.delta = mdb->alg.delta; + sdb->tcp_seq_num = mdb->proto_data.seq_pcp.tcp_seq_num; + + /* Reset Main db values to 0 */ + /* Reset only session specific flags */ + mdb->flags &= ~(CNAT_DB_FLAG_TCP_ACTIVE | CNAT_DB_FLAG_UDP_ACTIVE + | CNAT_DB_FLAG_ALG_ENTRY | CNAT_DB_FLAG_ALG_CTRL_FLOW); + mdb->timeout = 0; + mdb->entry_expires = 0; + mdb->alg.delta = 0; + if(PREDICT_FALSE(!((mdb->flags & CNAT_DB_FLAG_PPTP_TUNNEL_ACTIVE) || + (mdb->flags & CNAT_DB_FLAG_PPTP_TUNNEL_INIT)))) { + mdb->proto_data.seq_pcp.tcp_seq_num = 0; + } + + mdb->dst_ipv4 = 0; + mdb->dst_port = 0; +} + + +void +cnat_dest_update_session2main(cnat_main_db_entry_t *mdb, + cnat_session_entry_t *sdb) +{ + + u16 flags = sdb->flags & (CNAT_DB_FLAG_TCP_ACTIVE | + CNAT_DB_FLAG_UDP_ACTIVE | CNAT_DB_FLAG_ALG_ENTRY | + CNAT_DB_FLAG_ALG_CTRL_FLOW); + mdb->flags |= flags; + mdb->timeout = sdb->timeout; + mdb->entry_expires = sdb->entry_expires; + mdb->alg.delta = sdb->alg.delta; + if(PREDICT_FALSE(!((mdb->flags & CNAT_DB_FLAG_PPTP_TUNNEL_ACTIVE) || + (mdb->flags & CNAT_DB_FLAG_PPTP_TUNNEL_INIT)))) { + mdb->proto_data.seq_pcp.tcp_seq_num = sdb->tcp_seq_num; + } + mdb->dst_ipv4 = sdb->v4_dest_key.k.ipv4; + mdb->dst_port = sdb->v4_dest_key.k.port; +} + +static void +_cnat_delete_session_db_entry (cnat_session_entry_t *ep, u8 log) +{ + u32 session_db_index; + u32 bdb_len; + cnat_main_db_entry_t *be =0; + cnat_session_entry_t *sdb_last = NULL; + u16 instance; + + if (PREDICT_FALSE(ep->flags & CNAT_DB_NAT64_FLAG) != 0) { + /* Preventive check - Not a NAT44 entry */ + return; + } + + pool_header_t *h = pool_header(cnat_main_db); + + /* Validate .. just in case we are trying to delete a non existing one */ + bdb_len = vec_len(cnat_main_db); + + /* In case of invalid user just return, deleting only main db + * is not a good idea, since some valid user db entry might be pointing + * to that main db and hence leave the dbs in a inconsistent state + */ + if (PREDICT_FALSE((ep->main_db_index >= bdb_len) || + (clib_bitmap_get(h->free_bitmap, ep->main_db_index)))) { +#ifdef DEBUG_PRINTF_ENABLED + printf("invalid/unused user index in db %d\n", ep->main_db_index); +#endif + spp_printf(CNAT_INV_UNUSED_USR_INDEX, 1, (u32 *) &(ep->main_db_index)); + return; + } + + be = cnat_main_db + ep->main_db_index; + + session_db_index = ep - cnat_session_db; + + be->session_head_index = index_dlist_remelem ( + be->session_head_index, (u8 *)cnat_session_db, + sizeof (cnat_session_db[0]), + STRUCT_OFFSET_OF(cnat_session_entry_t, main_list), + session_db_index); + + if (be->flags & CNAT_DB_DSLITE_FLAG) { + instance = be->dslite_nat44_inst_id; + } else { + instance = NAT44_RESERVED_INST_ID; + } + + if(PREDICT_TRUE(log)) { + if (be->flags & CNAT_DB_DSLITE_FLAG) { + cnat_session_log_ds_lite_mapping_delete(be, + (dslite_table_db_ptr + instance),ep); + } else { + cnat_vrfmap_t *my_vrfmap = cnat_map_by_vrf + be->vrfmap_index; + cnat_session_log_nat44_mapping_delete(be, ep, my_vrfmap); + } + be->nsessions--; + } + + if (PREDICT_FALSE(be->nsessions == 1 && log)) { + /* There is only 1 session left + * Copy the info back to main db and release the last + * existing session + */ + + sdb_last = cnat_session_db + be->session_head_index; + ASSERT(sdb_last != NULL); + + cnat_dest_update_session2main(be, sdb_last); + _cnat_delete_session_db_entry(sdb_last, FALSE); + } + + /* Remove from session DB hashes */ + cnat_session_db_hash_delete(ep); + nat44_dslite_common_stats[instance].sessions--; + + pool_put(cnat_session_db, ep); +} + +void cnat_delete_session_db_entry (cnat_session_entry_t *ep, u8 log) +{ + pthread_spin_lock(cnat_db_v2_main.session_db_lockp); + _cnat_delete_session_db_entry (ep, log); + pthread_spin_unlock(cnat_db_v2_main.session_db_lockp); +} + +cnat_main_db_entry_t* +dslite_main_db_lookup_entry(dslite_db_key_bucket_t *ki) +{ + u64 a, b, c; + u32 index; + cnat_main_db_entry_t *db; + cnat_user_db_entry_t *userdb; + + DSLITE_V6_GET_HASH((&(ki->dk)), + ki->bucket, + CNAT_MAIN_HASH_MASK); + + DSLITE_PRINTF(1,"MDBLU hash..%u\n", ki->bucket); + + index = cnat_in2out_hash[ki->bucket].next; + if (PREDICT_TRUE(index == EMPTY)) { + DSLITE_PRINTF(1,"MDBLU index MT..\n"); + return (NULL); + } + + do { +/* We can add a flag here to indicate if the db entry is for nat44 or + * dslite. If the db entry is for nat44 then we can simply move to the + * one. + */ + db = cnat_main_db + index; + userdb = cnat_user_db + db->user_index; + if (PREDICT_TRUE(db->in2out_key.key64 == ki->dk.ipv4_key.key64) + && userdb->ipv6[0] == ki->dk.ipv6[0] + && userdb->ipv6[1] == ki->dk.ipv6[1] + && userdb->ipv6[2] == ki->dk.ipv6[2] + && userdb->ipv6[3] == ki->dk.ipv6[3]) { + DSLITE_PRINTF(1,"MDBLU success..%u\n", index); + return db; + } + index = db->in2out_hash.next; + } while (index != EMPTY); + + DSLITE_PRINTF(1,"MDBLU Entry does not exist..\n"); + return (NULL); +} + +cnat_user_db_entry_t* +dslite_user_db_lookup_entry(dslite_db_key_bucket_t *uki) +{ + u64 a, b, c; + u32 index; + cnat_user_db_entry_t *udb=NULL; + + DSLITE_V6_GET_HASH((&(uki->dk)), + uki->bucket, + CNAT_USER_HASH_MASK) + + DSLITE_PRINTF(1,"UDBLU hash..%u\n", uki->bucket); + + /* now: index in user vector */ + index = cnat_user_hash[uki->bucket].next; + if (PREDICT_TRUE(index != EMPTY)) { + DSLITE_PRINTF(1,"UDBLU hash table entry not MT..\n"); + do { + udb = cnat_user_db + index; + if (PREDICT_FALSE(udb->key.key64 == uki->dk.ipv4_key.key64) + && udb->ipv6[0] == uki->dk.ipv6[0] + && udb->ipv6[1] == uki->dk.ipv6[1] + && udb->ipv6[2] == uki->dk.ipv6[2] + && udb->ipv6[3] == uki->dk.ipv6[3]) { + DSLITE_PRINTF(1,"UDBLU success..%u\n", index); + return udb; + } + index = udb->user_hash.next; + } while (index != EMPTY); + } + DSLITE_PRINTF(1,"UDBLU Entry doesnt exist..\n"); + return (NULL); +} + +cnat_user_db_entry_t* +dslite_user_db_create_entry(dslite_db_key_bucket_t *uki, + u32 portmap_index) +{ + cnat_user_db_entry_t *udb = NULL; + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + + pool_get(cnat_user_db, udb); + memset(udb, 0, sizeof(*udb)); + + udb->ntranslations = 1; + udb->portmap_index = portmap_index; +// udb->key.key64 = uki->k.key64; + + udb->key.key64 = uki->dk.ipv4_key.key64; + udb->ipv6[0] = uki->dk.ipv6[0]; + udb->ipv6[1] = uki->dk.ipv6[1]; + udb->ipv6[2] = uki->dk.ipv6[2]; + udb->ipv6[3] = uki->dk.ipv6[3]; + + udb->flags |= CNAT_USER_DB_DSLITE_FLAG; + /* Add this user to the head of the bucket chain */ + udb->user_hash.next = + cnat_user_hash[uki->bucket].next; + cnat_user_hash[uki->bucket].next = udb - cnat_user_db; + +#ifndef NO_BULK_LOGGING + INIT_BULK_CACHE(udb) +#endif /* NO_BULK_LOGGING */ + + return udb; +} + +#ifndef TOBE_PORTED +cnat_main_db_entry_t* +dslite_create_main_db_entry_and_hash(dslite_db_key_bucket_t *ki, + cnat_db_key_bucket_t *ko, + cnat_user_db_entry_t *udb) +{ + return 0; +} +#else +cnat_main_db_entry_t* +dslite_create_main_db_entry_and_hash(dslite_db_key_bucket_t *ki, + cnat_db_key_bucket_t *ko, + cnat_user_db_entry_t *udb) +{ + u64 a, b, c; + u32 db_index; + cnat_main_db_entry_t *db = NULL; + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + + pool_get(cnat_main_db, db); + memset(db, 0, sizeof(*db)); + + db_index = db - cnat_main_db; + db->in2out_key.k.ipv4 = ki->dk.ipv4_key.k.ipv4; + db->in2out_key.k.port = ki->dk.ipv4_key.k.port; + db->in2out_key.k.vrf = ki->dk.ipv4_key.k.vrf; + db->out2in_key.k.ipv4 = ko->k.k.ipv4; + db->out2in_key.k.port = ko->k.k.port; + db->out2in_key.k.vrf = ko->k.k.vrf; + + db->user_ports.next = db_index; + db->user_ports.prev = db_index; + db->user_index = udb - cnat_user_db; + //db->portmap_index = udb->portmap_index; + db->flags |= CNAT_DB_DSLITE_FLAG; + + if (PREDICT_FALSE(udb->ntranslations == 1)) { + /* + * first port for this src vrf/src ip addr + */ + udb->translation_list_head_index = db_index; + DSLITE_PRINTF(1,"First translation of this user..\n"); + } else { + index_dlist_addtail(udb->translation_list_head_index, + (u8 *)cnat_main_db, sizeof(cnat_main_db[0]), + STRUCT_OFFSET_OF(cnat_main_db_entry_t, user_ports), + db_index); + } + + /* + * setup o2i hash key + */ + CNAT_V4_GET_HASH(ko->k.key64, + ko->bucket, + CNAT_MAIN_HASH_MASK) + db->out2in_hash.next = cnat_out2in_hash[ko->bucket].next; + cnat_out2in_hash[ko->bucket].next = db_index; + /* + * setup i2o hash key, bucket is already calculate + */ + db->in2out_hash.next = cnat_in2out_hash[ki->bucket].next; + cnat_in2out_hash[ki->bucket].next = db_index; + + DSLITE_PRINTF(1,"Create main db and hash..%u %u %u %u %x\n", + ki->bucket, ko->bucket, + db_index, db->user_index, ko->k.key64); + +#if DEBUG > 1 + printf("\nMy_Instance_Number %d: Bucket %d, Db_Index %d", + my_instance_number, ki->bucket, db_index); + printf("\nInside (VRF 0x%x, IP 0x%x, PORT 0x%x)", + db->in2out_key.k.vrf, db->in2out_key.k.ipv4, db->in2out_key.k.port); + printf("\nOutside (VRF 0x%x, IP 0x%x, PORT 0x%x)", + db->out2in_key.k.vrf, db->out2in_key.k.ipv4, db->out2in_key.k.port); + printf("\nUser Index %d, IP 0x%x", + db->user_index, udb->key.k.ipv4); +#endif + + //nat44_dslite_common_stats[DSLITE_COMMON_STATS].active_translations++; + + return db; +} + +static inline void handle_dslite_port_exceeded_logging( + cnat_user_db_entry_t *udb, + dslite_key_t * key, + dslite_table_entry_t *dslite_entry_ptr) +{ + + if(PREDICT_TRUE(udb->flags & CNAT_USER_DB_PORT_LIMIT_EXCEEDED)) { + /* Already logged ..*/ + return; + } + + /* else, set the flag and call the log API */ + udb->flags = udb->flags | CNAT_USER_DB_PORT_LIMIT_EXCEEDED; + cnat_log_ds_lite_port_limit_exceeded(key, dslite_entry_ptr); + return; +} +#endif + +void handle_cnat_port_exceeded_logging( + cnat_user_db_entry_t *udb, + cnat_key_t * key, + cnat_vrfmap_t *vrfmap) +{ + + if(PREDICT_TRUE(udb->flags & CNAT_USER_DB_PORT_LIMIT_EXCEEDED)) { + /* Already logged ..*/ + return; + } + + /* else, set the flag and call the log API */ + udb->flags = udb->flags | CNAT_USER_DB_PORT_LIMIT_EXCEEDED; + cnat_log_nat44_port_limit_exceeded(key,vrfmap); + return; +} + +#ifndef TOBE_PORTED +cnat_main_db_entry_t* +dslite_get_main_db_entry_v2(dslite_db_key_bucket_t *ki, + port_pair_t port_pair_type, + port_type_t port_type, + cnat_gen_icmp_info *info, + dslite_table_entry_t *dslite_entry_ptr, + cnat_key_t *dest_info) +{ + return 0; +} +#else +/* + * this function is called by exception node + * when lookup is fialed in i2o node + * + * if reash per user port limit, + * set user_db_entry pointer, and error == CNAT_OUT_LIMIT + */ +cnat_main_db_entry_t* +dslite_get_main_db_entry_v2(dslite_db_key_bucket_t *ki, + port_pair_t port_pair_type, + port_type_t port_type, + cnat_gen_icmp_info *info, + dslite_table_entry_t *dslite_entry_ptr, + cnat_key_t *dest_info) +{ + u16 protocol; + cnat_errno_t rv; + dslite_db_key_bucket_t u_ki; + cnat_db_key_bucket_t ko; + u32 my_index, free_main, free_user; + u32 current_timestamp; + cnat_vrfmap_t *my_vrfmap =0; + u16 my_vrfmap_index; + cnat_portmap_v2_t *pm =0; + cnat_user_db_entry_t *udb = 0; + cnat_main_db_entry_t *db = 0; + pool_header_t *h; + u16 dslite_id = dslite_entry_ptr->dslite_id; + +#ifndef NO_BULK_LOGGING + int nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED; +#endif + + /* UNUSED. Therefore not ported to be multi-thread friendly */ + ASSERT(0); + + /* + * need to try lookup again because + * second pkt may come here before the entry is created + * by receiving first pkt due to high line rate. + */ + info->gen_icmp_msg = CNAT_NO_ICMP_MSG; + info->error = CNAT_SUCCESS; + db = dslite_main_db_lookup_entry(ki); + if (PREDICT_TRUE(db)) { + /* what if the source is talking to a + * new dest now? We will have to handle this case and + * take care of - creating session db and logging + */ + if(PREDICT_FALSE((!dest_info->k.ipv4) && (!dest_info->k.port))) { + return db; /* if dest_info is null don't create session */ + } + + if(PREDICT_TRUE((db->dst_ipv4 == dest_info->k.ipv4) && + (db->dst_port == dest_info->k.port))) { + return db; + } + dest_info->k.vrf = db->in2out_key.k.vrf; + /* Src is indeed talking to a different dest */ + cnat_session_entry_t *session_db2 = NULL; + if(PREDICT_TRUE(db->nsessions == 1)) { + session_db2 = cnat_handle_1to2_session(db, dest_info); + if(PREDICT_TRUE(session_db2 != NULL)) { + CNAT_DB_TIMEOUT_RST(session_db2); + return db; + } else { + info->error = CNAT_ERR_NO_SESSION_DB; + return NULL; + } + } else if(PREDICT_FALSE(db->nsessions == 0)) { + /* Should be static entry.. should never happen + */ + if(PREDICT_TRUE(dest_info->k.ipv4 != 0)) { + cnat_add_dest_n_log(db, dest_info); + } + return db; + } else { + /* The src has already created multiple sessions.. very rare + */ + session_db2 = cnat_create_session_db_entry(dest_info, + db, TRUE); + if(PREDICT_TRUE(session_db2 != NULL)) { + CNAT_DB_TIMEOUT_RST(session_db2); + return db; + } else { + info->error = CNAT_ERR_NO_SESSION_DB; + return NULL; + } + } + + } + + /* + * step 1. check if outside vrf is configured or not + * and Find the set of portmaps for the outside vrf + * insider vrf is one to one mappted to outside vrf + * key is vrf and ip only + * ki.k.k.vrf has protocol bits, mask out + */ + protocol = ki->dk.ipv4_key.k.vrf & CNAT_PRO_MASK; + u_ki.dk.ipv4_key.k.vrf = ki->dk.ipv4_key.k.vrf & CNAT_VRF_MASK; +#ifdef DSLITE_USER_IPV4 + u_ki.dk.ipv4_key.k.ipv4 = ki->dk.ipv4_key.k.ipv4; +#else + /* + * Inside ipv4 address should be masked, if port limit + * need to be done at B4 element level. + */ + u_ki.dk.ipv4_key.k.ipv4 = 0; +#endif + u_ki.dk.ipv4_key.k.port = 0; + + u_ki.dk.ipv6[0] = ki->dk.ipv6[0]; + u_ki.dk.ipv6[1] = ki->dk.ipv6[1]; + u_ki.dk.ipv6[2] = ki->dk.ipv6[2]; + u_ki.dk.ipv6[3] = ki->dk.ipv6[3]; + + my_vrfmap_index = vrf_map_array[u_ki.dk.ipv4_key.k.vrf]; + my_vrfmap = cnat_map_by_vrf + my_vrfmap_index; +/* Checking if the inst entry is active or not is done much earlier + */ +#if 0 + my_vrfmap_index = vrf_map_array[u_ki.k.k.vrf]; + my_vrfmap = cnat_map_by_vrf + my_vrfmap_index; + my_vrfmap_entry_found = ((my_vrfmap_index != VRF_MAP_ENTRY_EMPTY) && + (my_vrfmap->status == S_RUN) && + (my_vrfmap->i_vrf == u_ki.k.k.vrf)); + + if (PREDICT_FALSE(!my_vrfmap_entry_found)) { + u32 arr[] = {ki->k.k.vrf, ki->k.k.ipv4, ki->k.k.port}; + if ((my_vrfmap_index == VRF_MAP_ENTRY_EMPTY) || + (my_vrfmap->i_vrf == u_ki.k.k.vrf)) { + info->error = CNAT_NO_CONFIG; + CNAT_DEBUG_INSIDE_ERR(CNAT_NO_CONFIG) + spp_printf(CNAT_NO_CONFIG_ERROR, 3, arr); + } else { + info->error = CNAT_NO_VRF_RUN; + CNAT_DEBUG_INSIDE_ERR(CNAT_NO_VRF_RUN) + spp_printf(CNAT_NO_VRF_RUN_ERROR, 3, arr); + } + + return (NULL); + } +#endif +/* + dslite_inst_ptr = dslite_nat44_config_table[dslite_inst_id]; +*/ + pm = dslite_entry_ptr->portmap_list; + //pm = my_vrfmap->portmap_list; + + /* + * set o2i key with protocl bits + */ + ko.k.k.vrf = dslite_entry_ptr->o_vrf | protocol; + //ko.k.k.vrf = my_vrfmap->o_vrf | protocol; + + /* + * step 2. check if src vrf, src ip addr is alreay + * in the user db + * if yes, use PORT_ALLOC_DIRECTED + * if no, use PORT_ALLOC_ANY since it is first time + */ + udb = dslite_user_db_lookup_entry(&u_ki); + if (PREDICT_TRUE(udb)) { + /* + * not first time allocate port for this user + * check limit + */ + if (PREDICT_FALSE(udb->ntranslations >= + dslite_entry_ptr->cnat_main_db_max_ports_per_user)) { + //cnat_main_db_max_ports_per_user)) + + /* Check for the port type here. If we are getting + * a STATIC PORT, allow the config. + */ + if (PREDICT_TRUE(port_type != PORT_TYPE_STATIC)) { + info->error = CNAT_OUT_LIMIT; + DSLITE_DEBUG_INSIDE_ERR(CNAT_OUT_LIMIT) + port_exceeded_msg_log(u_ki.dk.ipv4_key.k.ipv4, u_ki.dk.ipv4_key.k.vrf); + nat44_dslite_common_stats[dslite_id].in2out_drops_port_limit_exceeded ++; + u_ki.dk.ipv4_key.k.vrf = ki->dk.ipv4_key.k.vrf; + u_ki.dk.ipv4_key.k.port = ki->dk.ipv4_key.k.port; + handle_dslite_port_exceeded_logging(udb, &u_ki.dk, dslite_entry_ptr); + return (NULL); + } + } + + CHECK_CLEAR_PORT_LIMIT_EXCEED_FLAG(udb, + dslite_entry_ptr->cnat_main_db_max_ports_per_user) + + /* + * check if main db has space to accomodate new entry + */ + h = pool_header(cnat_main_db); + + free_main = vec_len(h->free_indices) - 1; + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + nat44_dslite_common_stats[dslite_id].in2out_drops_system_limit_reached ++; + DSLITE_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) + + current_timestamp = spp_trace_log_get_unix_time_in_seconds(); + if (PREDICT_FALSE((current_timestamp - last_log_timestamp) > + 1800)) { + spp_printf(CNAT_SESSION_THRESH_EXCEEDED, 0, NULL); + last_log_timestamp = current_timestamp; + } + +#ifdef UT_TEST_CODE + printf("Limit reached : OLD USER"); +#endif + return NULL; + } + + /* + * allocate port, from existing mapping + */ + my_index = udb->portmap_index; + + if (PREDICT_FALSE(port_type == PORT_TYPE_STATIC)) { + rv = cnat_static_port_alloc_v2_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + ki->dk.ipv4_key.k.ipv4, + ki->dk.ipv4_key.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + STAT_PORT_RANGE_FROM_INST_PTR(dslite_entry_ptr) +#ifndef NO_BULK_LOGGING + , udb, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), + &nfv9_log_req +#endif + , my_vrfmap->ip_n_to_1 + ); + } else if (PREDICT_TRUE(port_type != PORT_TYPE_RTSP) ) { + + rv = cnat_dynamic_port_alloc_v2_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + STAT_PORT_RANGE_FROM_INST_PTR(dslite_entry_ptr) +#ifndef NO_BULK_LOGGING + , udb, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), + &nfv9_log_req +#endif + , 0, + &(dslite_entry_ptr->rseed_ip) + ); + DSLITE_PRINTF(1,"D_PORT_ALLOC %x %u\n", ko.k.k.ipv4, ko.k.k.port); + } else { + /* + * For RTSP, two translation entries are created, + * check if main db has space to accomodate two new entry + */ + free_main = free_main - 1; + + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + nat44_dslite_common_stats[dslite_id].in2out_drops_system_limit_reached ++; + DSLITE_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) + + return NULL; + } else { + + rv = cnat_dynamic_port_alloc_rtsp_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + ki->dk.ipv4_key.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + STAT_PORT_RANGE_FROM_INST_PTR(dslite_entry_ptr) +#ifndef NO_BULK_LOGGING + , udb, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), + &nfv9_log_req +#endif + , &(dslite_entry_ptr->rseed_ip) + ); + } + } + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + DSLITE_PRINTF(1,"D_PORT_ALLOC port alloc error\n"); + info->error = rv; + DSLITE_DEBUG_INSIDE_ERR(rv) + nat44_dslite_common_stats[dslite_id].in2out_drops_resource_depletion ++; + log_port_alloc_error(rv, &(ki->dk.ipv4_key)); + return (NULL); + } + /* + * increment port in use for this user + */ + udb->ntranslations += 1; + } else { + /* + * first time allocate port for this user + */ + + /* + * Do not create entry if port limit is invalid + */ + if (PREDICT_FALSE(!(dslite_entry_ptr->cnat_main_db_max_ports_per_user))) { + if (PREDICT_TRUE(port_type != PORT_TYPE_STATIC)) { + info->error = CNAT_OUT_LIMIT; + nat44_dslite_common_stats[dslite_id].in2out_drops_port_limit_exceeded ++; + port_exceeded_msg_log(u_ki.dk.ipv4_key.k.ipv4, u_ki.dk.ipv4_key.k.vrf); + DSLITE_DEBUG_INSIDE_ERR(CNAT_OUT_LIMIT) + return (NULL); + } + } + + /* + * Check if main db has space for new entry + * Allowing a user db entry to be created if main db is not free + * will cause a port to be allocated to that user, which results in + * wastage of that port, hence the check is done here. + */ + h = pool_header(cnat_main_db); + free_main = vec_len(h->free_indices) - 1; + + h = pool_header(cnat_user_db); + free_user = vec_len(h->free_indices) - 1; + + /* + * If either main_db or user_db does not have entries + * bail out, with appropriate error + */ + if (PREDICT_FALSE(!(free_main && free_user))) { + u32 log_error; + if(free_main) { + info->error = CNAT_USER_DB_LIMIT; + log_error = CNAT_USER_DB_LIMIT_ERROR; + } else { + info->error = CNAT_MAIN_DB_LIMIT; + log_error = CNAT_MAIN_DB_LIMIT_ERROR; + } + nat44_dslite_common_stats[dslite_id].in2out_drops_system_limit_reached ++; + DSLITE_DEBUG_INSIDE_ERR(info->error) + spp_printf(log_error, 0, 0); + return NULL; + } + + if (PREDICT_FALSE(port_type == PORT_TYPE_STATIC)) { + rv = cnat_static_port_alloc_v2_bulk(pm, + PORT_ALLOC_ANY, + port_pair_type, + ki->dk.ipv4_key.k.ipv4, + ki->dk.ipv4_key.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + STAT_PORT_RANGE_FROM_INST_PTR(dslite_entry_ptr) +#ifndef NO_BULK_LOGGING + , NULL, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), + &nfv9_log_req +#endif + , my_vrfmap->ip_n_to_1 + + ); + } else if (PREDICT_TRUE(port_type != PORT_TYPE_RTSP)) { + rv = cnat_dynamic_port_alloc_v2_bulk(pm, + PORT_ALLOC_ANY, + port_pair_type, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + STAT_PORT_RANGE_FROM_INST_PTR(dslite_entry_ptr) +#ifndef NO_BULK_LOGGING + , NULL, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), + &nfv9_log_req +#endif + , 0, + &(dslite_entry_ptr->rseed_ip) + ); + DSLITE_PRINTF(1,"NU:D PORT ALLOC..%x %u\n", ko.k.k.ipv4, + ko.k.k.port); + + } else { + /* + * For RTSP, two translation entries are created, + * check if main db has space to accomodate two new entry + */ + free_main = free_main - 1; + + if (PREDICT_FALSE(!free_main)) { + info->error = CNAT_MAIN_DB_LIMIT; + nat44_dslite_common_stats[dslite_id].in2out_drops_system_limit_reached ++; + DSLITE_DEBUG_INSIDE_ERR(CNAT_MAIN_DB_LIMIT) + + return NULL; + } else { + + rv = cnat_dynamic_port_alloc_rtsp_bulk(pm, + PORT_ALLOC_DIRECTED, + port_pair_type, + ki->dk.ipv4_key.k.port, + &my_index, + &(ko.k.k.ipv4), + &(ko.k.k.port), + STAT_PORT_RANGE_FROM_INST_PTR(dslite_entry_ptr) +#ifndef NO_BULK_LOGGING + , NULL, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr), + &nfv9_log_req +#endif + , &(dslite_entry_ptr->rseed_ip) + ); + /* TODO: Add the port pair flag here */ + } + } + + + + if (PREDICT_FALSE(rv != CNAT_SUCCESS)) { + DSLITE_PRINTF(1,"NU:D_PORT_ALLOC port alloc error\n"); + info->error = rv; + nat44_dslite_common_stats[dslite_id].in2out_drops_resource_depletion ++; + DSLITE_DEBUG_INSIDE_ERR(rv) + log_port_alloc_error(rv, &(ki->dk.ipv4_key)); + return (NULL); + } + /* + * create entry in user db + */ + udb = dslite_user_db_create_entry(&u_ki, my_index); + nat44_dslite_common_stats[dslite_id].num_subscribers++; + DSLITE_PRINTF(1,"UDB crete entry done..\n"); +#ifndef NO_BULK_LOGGING + if(PREDICT_TRUE(udb && (BULK_ALLOC_NOT_ATTEMPTED != nfv9_log_req))) { + cnat_update_bulk_range_cache(udb, ko.k.k.port, + BULKSIZE_FROM_VRFMAP(dslite_entry_ptr)); + } +#endif /* #ifndef NO_BULK_LOGGING */ + } + + /* + * step 3: + * outside port is allocated for this src vrf/src ip addr + * 1)create a new entry in main db + * 2)setup cnat_out2in_hash key + * 3)setup cnat_in2out_hash key + */ + db = dslite_create_main_db_entry_and_hash(ki, &ko, udb); + DSLITE_PRINTF(1,"dslite_create_main_db_entry_and_hash done..\n"); + //db->vrfmap_index = my_vrfmap - cnat_map_by_vrf; + db->dslite_nat44_inst_id = dslite_id; + nat44_dslite_common_stats[dslite_id].active_translations++; + if (PREDICT_FALSE(port_type == PORT_TYPE_STATIC)) { + nat44_dslite_common_stats[dslite_id].num_static_translations++; + } else { + nat44_dslite_common_stats[dslite_id].num_dynamic_translations++; + } + + dslite_translation_create_count++; + + db->dst_ipv4 = dest_info->k.ipv4; + db->dst_port = dest_info->k.port; + if(PREDICT_TRUE(db->dst_ipv4 || db->dst_port)) { + /* for static fwding, let the nsessions remain zero */ + db->nsessions++; + } + + /* + * don't forget logging + * logging API is unconditional, + * logging configuration check is done inside the inline function + */ + if(PREDICT_FALSE(nfv9_log_req != CACHE_ALLOC_NO_LOG_REQUIRED)) { + if(PREDICT_FALSE( dslite_entry_ptr->nf_logging_policy == + SESSION_LOG_ENABLE)) { + if(PREDICT_TRUE(db->dst_ipv4 || db->dst_port)) { + cnat_nfv9_ds_lite_log_session_create(db, + dslite_entry_ptr,NULL); + } + } else { + cnat_nfv9_ds_lite_mapping_create(db,dslite_entry_ptr +#ifndef NO_BULK_LOGGING + ,nfv9_log_req +#endif + ); + } + if(PREDICT_TRUE((dslite_entry_ptr->syslog_logging_policy != SESSION_LOG_ENABLE) || + (db->dst_ipv4 || db->dst_port))) { + cnat_syslog_ds_lite_mapping_create(db,dslite_entry_ptr,NULL +#ifndef NO_BULK_LOGGING + ,nfv9_log_req +#endif + ); + } + } + +#if 0 + if (PREDICT_FALSE(port_pair_type == PORT_PAIR)) { + cnat_main_db_entry_t *db2 = 0; + dslite_db_key_bucket_t new_ki = *ki; + u64 a, b, c; + + new_ki.k.k.port += 1; + ko.k.k.port += 1; + + CNAT_V4_GET_HASH(new_ki.k.key64, new_ki.bucket, + CNAT_MAIN_HASH_MASK); + + db2 = cnat_create_main_db_entry_and_hash(&new_ki, &ko, udb); + + translation_create_count ++; + db2->dslite_nat44_inst_id = dslite_id; + db2->entry_expires = cnat_current_time; + db2->flags |= CNAT_DB_FLAG_ALG_ENTRY; + udb->ntranslations += 1; +#ifndef NO_BULK_LOGGING + if(PREDICT_FALSE(nfv9_log_req == BULK_ALLOC_NOT_ATTEMPTED)) + cnat_nfv9_log_mapping_create(db2, my_vrfmap, nfv9_log_req); +#else + cnat_nfv9_log_mapping_create(db2, my_vrfmap); +#endif + } +#endif + return db; +} +#endif /* TOBE_PORTED */ + +#if 0 +/* TOBE_PORTED */ +uword +cnat_db_v2_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return 0; +} +VLIB_REGISTER_NODE (cnat_db_v2_node) = { + .function = cnat_db_v2_node_fn, + .name = "vcgn-db-v2", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(cnat_db_v2_error_strings), + .error_strings = cnat_db_v2_error_strings, + + .n_next_nodes = CNAT_DB_V2_DROP, + + /* edit / add dispositions here */ + .next_nodes = { + [CNAT_DB_V2_DROP] = "error-drop", + }, +}; +#endif +void cnat_db_v2_init (void) +{ + + u32 i, n; + cnat_timeout_db_entry_t * tdb __attribute__((unused)); + + cgse_nat_db_entry_t *comb_db __attribute__((unused)); + cgse_nat_user_db_entry_t *comb_user __attribute__((unused)); + cgse_nat_session_db_entry_t *comb_session __attribute__((unused)); + + n = CNAT_DB_SIZE*1.15; /* add 15% LB margin */ + + /* + * We also make it multiple of NUM_BITS_IN_UWORD for better + * DB scanning algorithm + */ + if (n % NUM_BITS_IN_UWORD) + n += (NUM_BITS_IN_UWORD - (n % NUM_BITS_IN_UWORD)); + + pool_alloc(cgse_nat_db,n); + for(i=0; i< n; i++) { + pool_get(cgse_nat_db, comb_db); + } + + for(i=0; i< n; i++) { + pool_put(cgse_nat_db, cgse_nat_db + i); + } + + cnat_main_db = &cgse_nat_db->nat44_main_db; + + /* For Sessions */ + if(PLATFORM_DBL_SUPPORT) { + /* create session table for NAT44 and NAT64 itself */ + printf("DBL Support exist %d\n", PLATFORM_DBL_SUPPORT); + n = CNAT_SESSION_DB_SIZE * 1.15; /* add 15% LB margin */ + } else { + /* Create session table for NAT64 only */ + printf("DBL Support Not exist\n"); + n = NAT64_MAIN_DB_SIZE * 1.15; /* add 15% LB margin */ + } + + /* + * We also make it multiple of NUM_BITS_IN_UWORD for better + * DB scanning algorithm + */ + if (n % NUM_BITS_IN_UWORD) + n += (NUM_BITS_IN_UWORD - (n % NUM_BITS_IN_UWORD)); + + pool_alloc(cgse_session_db,n); + for(i=0; i< n; i++) { + pool_get(cgse_session_db, comb_session); + } + + for(i=0; i< n; i++) { + pool_put(cgse_session_db, cgse_session_db + i); + } + + cnat_session_db = &cgse_session_db->nat44_session_db; + + vec_validate(cnat_out2in_hash, CNAT_MAIN_HASH_MASK); + memset(cnat_out2in_hash, 0xff, CNAT_MAIN_HASH_SIZE*sizeof(index_slist_t)); + + vec_validate(cnat_in2out_hash, CNAT_MAIN_HASH_MASK); + memset(cnat_in2out_hash, 0xff, CNAT_MAIN_HASH_SIZE*sizeof(index_slist_t)); + + vec_validate(cnat_session_hash, CNAT_SESSION_HASH_MASK); + memset(cnat_session_hash, 0xff, CNAT_SESSION_HASH_SIZE*sizeof(index_slist_t)); + + n = CNAT_USER_DB_SIZE * 1.15; /* use hash size as db size for LB margin */ + if (n % NUM_BITS_IN_UWORD) + n += (NUM_BITS_IN_UWORD - (n % NUM_BITS_IN_UWORD)); + + pool_alloc(cgse_user_db,n); + for(i=0; i< n; i++) { + pool_get(cgse_user_db, comb_user); + } + + for(i=0; i< n; i++) { + pool_put(cgse_user_db, cgse_user_db + i); + } + + cnat_user_db = &cgse_user_db->nat44_user_db; + + vec_validate(cnat_user_hash, CNAT_USER_HASH_MASK); + memset(cnat_user_hash, 0xff, CNAT_USER_HASH_SIZE*sizeof(index_slist_t)); + + n = CNAT_TIMEOUT_HASH_SIZE; /* use hash size as db size for LB margin */ + for(i=0; i< n; i++) { + pool_get(cnat_timeout_db, tdb); + } + + for(i=0; i< n; i++) { + pool_put(cnat_timeout_db, cnat_timeout_db + i); + } + + vec_validate(cnat_timeout_hash, CNAT_TIMEOUT_HASH_MASK); + memset(cnat_timeout_hash, 0xff, CNAT_TIMEOUT_HASH_SIZE*sizeof(index_slist_t)); + +#ifdef TOBE_PORTED + for (i=0;i<CNAT_MAX_VRFMAP_ENTRIES; i++) { + svi_params_array[i].svi_type = CGSE_SVI_TYPE_INFRA; + } +#endif + + cnat_db_v2_main.main_db_lockp = + clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, + CLIB_CACHE_LINE_BYTES); + + cnat_db_v2_main.user_db_lockp = + clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, + CLIB_CACHE_LINE_BYTES); + + cnat_db_v2_main.session_db_lockp = + clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, + CLIB_CACHE_LINE_BYTES); + + ASSERT (pthread_spin_init(cnat_db_v2_main.main_db_lockp, + PTHREAD_PROCESS_PRIVATE) == 0); + ASSERT (pthread_spin_init(cnat_db_v2_main.user_db_lockp, + PTHREAD_PROCESS_PRIVATE) == 0); + ASSERT (pthread_spin_init(cnat_db_v2_main.session_db_lockp, + PTHREAD_PROCESS_PRIVATE) == 0); + + cnat_db_init_done = 1; + printf("CNAT DB init is successful\n"); + return; + //return 0; +} |