vpp/plugins/vcgn-plugin/vcgn/cnat_db_v2.c
Michael Qiu c11ce3ee25 plugins/vcgn-plugin: Fix vpp hang with pthread spinlock
Currently, pthread spinlock is initialized in macro ASSERT, thus
the spinlock only will be initialized when CLIB_ASSERT_ENABLE is true.
But CLIB_ASSERT_ENABLE depends on CLIB_DEBUG been set.

Mostly, CLIB_DEBUG is zero, so spinlock will never been initialized,
which will lead vpp hang when try to hold the lock.

This patch fix this issue.

Change-Id: If01982b5df849834ccbc705f8e9291f2b6c32d0a
Signed-off-by: Michael Qiu <qiudayu@chinac.com>
2016-07-29 13:01:38 +00:00

3807 lines
120 KiB
C

/*
*------------------------------------------------------------------
* 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, lockinit;
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);
lockinit = pthread_spin_init(cnat_db_v2_main.main_db_lockp,
PTHREAD_PROCESS_PRIVATE);
ASSERT (lockinit == 0);
lockinit = pthread_spin_init(cnat_db_v2_main.user_db_lockp,
PTHREAD_PROCESS_PRIVATE);
ASSERT (lockinit == 0);
lockinit = pthread_spin_init(cnat_db_v2_main.session_db_lockp,
PTHREAD_PROCESS_PRIVATE);
ASSERT (lockinit == 0);
cnat_db_init_done = 1;
printf("CNAT DB init is successful\n");
return;
}