aboutsummaryrefslogtreecommitdiffstats
path: root/vnet/vnet/vcgn/cnat_bulk_port.c
diff options
context:
space:
mode:
Diffstat (limited to 'vnet/vnet/vcgn/cnat_bulk_port.c')
-rw-r--r--vnet/vnet/vcgn/cnat_bulk_port.c964
1 files changed, 964 insertions, 0 deletions
diff --git a/vnet/vnet/vcgn/cnat_bulk_port.c b/vnet/vnet/vcgn/cnat_bulk_port.c
new file mode 100644
index 00000000000..67ddd255e0f
--- /dev/null
+++ b/vnet/vnet/vcgn/cnat_bulk_port.c
@@ -0,0 +1,964 @@
+/*
+ *------------------------------------------------------------------
+ * cnat_bulk_ports.c - wrappers for bulk port allocation
+ *
+ * Copyright (c) 2011-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/error.h>
+#include <vnet/buffer.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/pool.h>
+#include <vppinfra/bitmap.h>
+
+#include "cnat_db.h"
+#include "cnat_config.h"
+#include "cnat_global.h"
+#include "cnat_logging.h"
+#include "spp_timers.h"
+#include "platform_common.h"
+#include "cgn_bitmap.h"
+#include "spp_platform_trace_log.h"
+#include "cnat_ports.h"
+
+#ifndef NO_BULK_LOGGING
+
+#define PORT_TO_CACHE(y, z) ((y)/(z))
+/* The last bit (MSB) is used to indicate whether the cache entry is full */
+#define CACHE_TO_PORT(x, z) (((x)& 0x7FFF) * (z))
+#define IS_CACHE_ENTRY_FULL(x) ((x) & 0x8000)
+#define MARK_CACHE_ENTRY_AS_FULL(x) ((x) = ((x) | 0x8000))
+#define UNMARK_CACHE_ENTRY_AS_FULL(x) ((x) = ((x) & 0x7FFF))
+#define CACHE_ENTRY_WITHOUT_FULL_STAT(x) ((x) & 0x7FFF)
+
+
+#define NUM_BULK_CHECK 128 /* max number of previous chache to check.
+ * somewhat orbirtrary.. assume 64 as bulk size.. can handle up
+ * to 128*64 ports allocated by a single subscriber */
+
+/* #define DEBUG_BULK_PORT 1 */
+/* #define DEBUG_BULK_PORT_DETAIL 1 */
+#define HAVE_BULK_PORT_STATS 1
+
+#ifdef HAVE_BULK_PORT_STATS
+static uword bulk_cache_hit_count;
+static uword bulk_port_use_count;
+static uword bulk_port_alloc_count;
+static uword mapped_port_alloc_count;
+#endif /* HAVE_BULK_PORT_STATS */
+
+static u32 bulk_port_rand_across;
+
+void show_bulk_port_allocation(u16 in_vrfid, u32 inside_ip)
+{
+ cnat_db_key_bucket_t u_ki;
+ cnat_user_db_entry_t *udb;
+ int i;
+ u32 head;
+ cnat_main_db_entry_t *db = NULL;
+ i16 printed_so_far = 0; /* entries printed so far */
+ u16 prev_bulks[NUM_BULK_CHECK];
+ cnat_vrfmap_t *my_vrfmap = 0;
+ cnat_vrfmap_t *vrfmap = 0;
+ bulk_alloc_size_t bulk_size;
+
+ u_ki.k.k.vrf = in_vrfid;
+ u_ki.k.k.ipv4 = inside_ip;
+ u_ki.k.k.port = 0;
+
+ PLATFORM_DEBUG_PRINT("Searching for user %x in invrf %d\n",
+ inside_ip, in_vrfid);
+ udb = cnat_user_db_lookup_entry(&u_ki);
+ if(!udb) {
+ PLATFORM_DEBUG_PRINT("No such user\n"); return;
+ }
+
+ pool_foreach (vrfmap, cnat_map_by_vrf, ({
+ if(vrfmap->i_vrf == in_vrfid) {
+ my_vrfmap = vrfmap;
+ break;
+ }}));
+
+ if(!my_vrfmap) {
+ PLATFORM_DEBUG_PRINT("Vrf map not found\n");
+ return;
+ }
+ bulk_size = BULKSIZE_FROM_VRFMAP(my_vrfmap);
+
+ if(bulk_size == BULK_ALLOC_SIZE_NONE) {
+ PLATFORM_DEBUG_PRINT("Bulk allocation not enabled\n");
+ return;
+ }
+
+ PLATFORM_DEBUG_PRINT("\nBulk cache for subscriber 0x%x: ", inside_ip);
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ PLATFORM_DEBUG_PRINT("%d , ",
+ CACHE_TO_PORT(udb->bulk_port_range_cache[i], bulk_size));
+ }
+ PLATFORM_DEBUG_PRINT("\nNon cached bulk allocation for subscriber 0x%x:\n",
+ inside_ip);
+ ASSERT(udb);
+ memset(prev_bulks, 0,sizeof(prev_bulks));
+
+ head = udb->translation_list_head_index;
+ if(PREDICT_FALSE(head == EMPTY)) {
+ return;
+ }
+ db = cnat_main_db + head;
+ while (1) {
+ /* skip static ports - static ports may not belong to bulk pool*/
+ if(db->out2in_key.k.port < cnat_static_port_range) goto next_entry;
+
+ u16 bm_index = PORT_TO_CACHE(db->out2in_key.k.port, bulk_size);
+
+ /*Check if we have already tested this bulk */
+ for(i=0; i < printed_so_far; i++) {
+ if(prev_bulks[i] == bm_index) goto next_entry;
+ }
+
+ /*Check if this base port is already part of cache */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i])
+ == bm_index) {
+ goto next_entry;
+ }
+ }
+ /* this is not in chache already */
+ PLATFORM_DEBUG_PRINT("%d ", CACHE_TO_PORT(bm_index, bulk_size));
+ if(printed_so_far < NUM_BULK_CHECK) {
+ prev_bulks[printed_so_far] = bm_index;
+ printed_so_far++;
+ }
+
+next_entry:
+ db = cnat_main_db + db->user_ports.next;
+ /*
+ * its a circular list, so if we have reached the head again
+ * all the entries for that user have been read
+ */
+ if (db == (cnat_main_db + head)) {
+ break;
+ }
+ } /* while loop for db entries */
+
+ PLATFORM_DEBUG_PRINT("\n");
+ return;
+}
+
+void show_bulk_port_stats()
+{
+
+ cnat_vrfmap_t *my_vrfmap = 0;
+ PLATFORM_DEBUG_PRINT("Bulk size settings of each inside vrf ...\n");
+ pool_foreach (my_vrfmap, cnat_map_by_vrf, ({
+ PLATFORM_DEBUG_PRINT("vrf id %d, bulk size %d\n", my_vrfmap->i_vrf,
+ BULKSIZE_FROM_VRFMAP(my_vrfmap));
+ }));
+
+#ifdef HAVE_BULK_PORT_STATS
+ PLATFORM_DEBUG_PRINT("\nBulk port allocation, use and cache hit statistics\n");
+ PLATFORM_DEBUG_PRINT("Number of times bulk ports allocated %lld\n",
+ bulk_port_alloc_count);
+ PLATFORM_DEBUG_PRINT("Number of times pre-allocated ports used %lld\n",
+ bulk_port_use_count);
+ PLATFORM_DEBUG_PRINT(
+ "Number of times pre-allocated bulk port found from cache %lld\n",
+ bulk_cache_hit_count);
+ PLATFORM_DEBUG_PRINT(
+ "Number of times mapped port (static) allocations made %lld\n",
+ mapped_port_alloc_count);
+#else
+ PLATFORM_DEBUG_PRINT("\nNat44 bulk port statistics not turned on\n");
+#endif /* HAVE_BULK_PORT_STATS */
+}
+
+void clear_bulk_port_stats()
+{
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_alloc_count = 0;
+ bulk_port_use_count = 0;
+ bulk_cache_hit_count = 0;
+ mapped_port_alloc_count = 0;
+#endif /* HAVE_BULK_PORT_STATS */
+ return;
+}
+
+void cnat_update_bulk_range_cache(cnat_user_db_entry_t *udb, u16 o_port,
+ bulk_alloc_size_t bulk_size)
+{
+ i16 i;
+ if(!udb) {
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("%s, null udb!\n", __func__);
+#endif
+ return;
+ }
+ if(BULK_ALLOC_SIZE_NONE == bulk_size) { /* no bulk logging */
+ return;
+ }
+
+ /* Take care of caching */
+ if(o_port & 0x1) {
+ o_port--;
+ }
+ if(PREDICT_FALSE(o_port <= 0)) {
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("%s invalid port: %d\n", __func__, o_port);
+#endif
+ return;
+ }
+
+ /* First preference is for the cache entry's that are not used yet */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(PREDICT_FALSE(
+ udb->bulk_port_range_cache[i] == (i16)BULK_RANGE_INVALID)) {
+ udb->bulk_port_range_cache[i] = PORT_TO_CACHE(o_port, bulk_size);
+ return;
+ }
+ }
+
+ /* Now check if any cache entry is full and if it can be replaced */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(PREDICT_FALSE(IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i]))) {
+ udb->bulk_port_range_cache[i] = PORT_TO_CACHE(o_port, bulk_size);
+ return;
+ }
+ }
+
+ return;
+}
+
+
+void cnat_port_free_v2_bulk (
+ cnat_portmap_v2_t *pm,
+ int index,
+ port_pair_t ptype,
+ u16 base_port,
+ cnat_user_db_entry_t *udb,
+ u16 static_port_range,
+ bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req)
+{
+ cnat_portmap_v2_t *my_pm;
+ i16 bm_index;
+ i16 i;
+ int unmark_full_status = 0;
+
+ *nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED;
+
+ /* First free up the port */
+ cnat_port_free_v2(pm, index, ptype, base_port, static_port_range);
+ if(BULK_ALLOC_SIZE_NONE == bulk_size) /* no bulk logging */
+ return;
+ if(PREDICT_FALSE(!udb)) {
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("%s udb is null\n", __func__);
+#endif
+ }
+
+ if(PREDICT_FALSE(base_port < static_port_range)) {
+ return;
+ }
+ /* Now check if cache needs to be removed */
+ my_pm = pm + index;
+ base_port = base_port/bulk_size;
+ base_port = base_port * bulk_size; /*Align it to multiples of bulk_size */
+ if(PREDICT_TRUE(!cgn_clib_bitmap_check_if_all(
+ my_pm->bm, base_port, bulk_size))) {
+ *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
+ unmark_full_status = 1;
+ /* One or more ports are still in use */
+ } else {
+ *nfv9_log_req = base_port; /* logging required now. indicate base port*/
+ }
+ bm_index = PORT_TO_CACHE(base_port, bulk_size);
+ /* Now check if this is in the cache */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(PREDICT_FALSE(
+ CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i]))
+ == bm_index) {
+ if(unmark_full_status) {
+ /* Unmark full stat.. if it was marked so..*/
+ UNMARK_CACHE_ENTRY_AS_FULL(udb->bulk_port_range_cache[i]);
+ } else {
+ udb->bulk_port_range_cache[i] = (i16)BULK_RANGE_INVALID;
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT(
+ "Clearing cache for client 0x%x, bulk port %d\n",
+ my_pm->ipv4_address, base_port);
+#endif
+ }
+ break;
+ }
+ }
+ return;
+}
+
+
+/* Get suitable port from range */
+static i16 get_suiting_port_pos_from_range(cnat_portmap_v2_t *my_pm,
+ u16 bulk_start, i16 bulk_size, port_pair_t pair_type)
+{
+ i16 num_pos, num_bits, iterations;
+ uword bulk_ports;
+ i16 inc;
+ i16 num_uwords = bulk_size/BITS(my_pm->bm[0]);
+
+ if(PREDICT_FALSE(!num_uwords)) {
+ iterations = 0;
+ num_bits = bulk_size;
+ bulk_size = 0;
+ } else {
+ bulk_port_rand_across = randq1(bulk_port_rand_across);
+ iterations = bulk_port_rand_across % num_uwords;
+ num_bits = BITS(my_pm->bm[0]);
+ }
+
+ do {
+ bulk_ports = cgn_clib_bitmap_get_bits(my_pm->bm,
+ (bulk_start + iterations * BITS(my_pm->bm[0])), num_bits);
+#ifdef DEBUG_BULK_PORT_DETAIL
+ PLATFORM_DEBUG_PRINT("%s %d, bulk start %d, num_bits %d, ports %lld \n",
+ __func__, __LINE__, bulk_start, num_bits, bulk_ports);
+#endif /* DEBUG_BULK_PORT_DETAIL */
+ if(PREDICT_FALSE(!bulk_ports)) goto next_uword;
+ if(PREDICT_TRUE((pair_type == PORT_SINGLE)
+ || (pair_type == PORT_PAIR))) {
+ num_pos =0;
+ inc = 1;
+ } else if(pair_type == PORT_S_ODD) {
+ num_pos = 1;
+ inc = 2;
+ } else if(pair_type == PORT_S_EVEN) {
+ num_pos =0;
+ inc = 2;
+ }
+
+ for(; num_pos < num_bits; num_pos = num_pos + inc) {
+ if(!((bulk_ports >> num_pos) & 1))
+ continue; /* In use */
+ /* Check if the available port meets our
+ * criteria such as add, even, pair etc */
+ else if(PREDICT_FALSE(
+ (pair_type == PORT_PAIR) && ((num_pos & 0x1) ||
+ (!((bulk_ports >> (num_pos + 1)) & 1)))))
+ continue;
+ else break; /* Found one that meets the criteria */
+ }
+ if(num_pos < num_bits)
+ return (num_pos + iterations * BITS(my_pm->bm[0]));
+next_uword:
+ num_bits = BITS(my_pm->bm[0]);
+ bulk_size -= BITS(my_pm->bm[0]);
+ iterations++;
+ if(iterations >= num_uwords) iterations = 0;
+ } while (bulk_size > 0);
+
+ return -2; /* nothing found */
+}
+
+static cnat_errno_t try_bulk_port_from_non_cache(
+ cnat_user_db_entry_t *udb,
+ cnat_portmap_v2_t *my_pm,
+ port_pair_t pair_type,
+ bulk_alloc_size_t bulk_size,
+ u16 *port_available,
+ u16 static_port_range
+ )
+{
+ /****
+ 1. user should have existing translations.. otherwise, we wouldn't get here.
+ 2. For each, get the outside port. get the base port.
+ check if it is already in cache
+ 3. if not, we stand chance.
+ 4. Check for availability from this non cached pool.
+ 5. if found, repalce this with one of the cache that is invalid or full??
+ 6. if we are replacing the cache.. it has to be governed by user
+ preference on prefer oldest pool or prefer newest pool
+ ********/
+ u32 head;
+ cnat_main_db_entry_t *db = NULL;
+ u16 bulk_start; /* start point in 64 bitmap array to search for port */
+ i16 port_pos; /* indicates the position of available port in bulk */
+ i16 i; /* just a counter */
+ i16 attempts_so_far = 0; /* (futile-;) attemps so far..*/
+ u16 prev_bulks[NUM_BULK_CHECK];
+ ASSERT(udb);
+ memset(prev_bulks, 0,sizeof(prev_bulks));
+
+ head = udb->translation_list_head_index;
+ if(PREDICT_FALSE(head == EMPTY)) return CNAT_NO_PRE_ALLOCATED_BULK_PORTS;
+
+ db = cnat_main_db + head;
+ while (1) { //what should be the limit??
+
+ /* skip static ports - static ports may not belong to bulk pool*/
+ if(db->out2in_key.k.port < static_port_range) goto next_entry;
+
+ u16 bm_index = PORT_TO_CACHE(db->out2in_key.k.port, bulk_size);
+
+ /*Check if we have already tested this bulk */
+ for(i=0; i < attempts_so_far; i++) {
+ if(prev_bulks[i] == bm_index) {
+ goto next_entry;
+ }
+ }
+
+ /*Check if this base port is already part of cache */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i])
+ == bm_index)
+ goto next_entry;
+ }
+
+ /* this is not in chache already */
+ bulk_start = CACHE_TO_PORT(bm_index, bulk_size);
+ port_pos = get_suiting_port_pos_from_range(my_pm,
+ bulk_start, bulk_size, pair_type);
+
+ if(port_pos < 0) { /* no port available in this range */
+ /* Mark this bulk so that we don't have to try this again */
+ if(attempts_so_far < NUM_BULK_CHECK) {
+ prev_bulks[attempts_so_far] = bm_index;
+ attempts_so_far++;
+ }
+ goto next_entry;
+ }
+
+ /* Got one...Get the port number */
+ *port_available = bulk_start + port_pos;
+
+ /* Check to see if we shoud replace one of the cache */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(PREDICT_FALSE((udb->bulk_port_range_cache[i]
+ == (i16)BULK_RANGE_INVALID) || (
+ IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i])))) {
+ udb->bulk_port_range_cache[i] = bm_index;
+ return CNAT_SUCCESS;
+ }
+ }
+ /* Check to replace an existing (in use) entry */
+ /* TODO: enforce policy */
+ /* order of looping should depend on policy */
+
+ return CNAT_SUCCESS;
+
+next_entry:
+ db = cnat_main_db + db->user_ports.next;
+ /*
+ * its a circular list, so if we have reached the head again
+ * all the entries for that user have been read
+ */
+ if (db == (cnat_main_db + head)) {
+ break;
+ }
+ } /* while loop for db entries */
+ /* no ports available from pre allocated bulk pool */
+ return CNAT_NO_PORT_FROM_BULK;
+}
+
+cnat_errno_t
+cnat_dynamic_port_alloc_v2_bulk (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ port_pair_t pair_type,
+ u32 *index,
+ u32 *o_ipv4_address,
+ u16 *o_port,
+ u16 static_port_range,
+ cnat_user_db_entry_t *udb,
+ bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req,
+ u16 ip_n_to_1,
+ u32 *rseed_ip
+ )
+{
+
+ cnat_errno_t rv;
+ u16 port_available = 0;
+ i16 i;
+ cnat_portmap_v2_t *my_pm;
+
+ if((BULK_ALLOC_SIZE_NONE != bulk_size) /* bulk logging enabled */
+ && (udb)) { /* This user does have translations already */
+ u16 bulk_start;
+ i16 port_pos;
+
+ my_pm = pm + *index;
+ /* We have a case to check if bulk allocated ports can be used */
+ /* TODO: order of looping to be based on policy
+ * like prefer older or prefer newer ??
+ * For now, start with most recent cache entry
+ * so that we stand a better chance of
+ * finding a port
+ */
+ for(i= 0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(PREDICT_TRUE((udb->bulk_port_range_cache[i] ==
+ (i16)BULK_RANGE_INVALID) ||
+ IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i]))) {
+ continue; /* This range is not initialized yet or it is full */
+ }
+ bulk_start = CACHE_TO_PORT(udb->bulk_port_range_cache[i],
+ bulk_size);
+ port_pos = get_suiting_port_pos_from_range(my_pm,
+ bulk_start, bulk_size, pair_type);
+ if(PREDICT_FALSE(port_pos < 0)) {
+ /* Mark this cache entry as full so that we do not
+ * waste time on this entry again */
+ MARK_CACHE_ENTRY_AS_FULL(udb->bulk_port_range_cache[i]);
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("Marked bulk cache entry %d as full for %x \n",
+ i, my_pm->ipv4_address);
+#endif /* #ifdef DEBUG_BULK_PORT */
+ continue;
+ }
+ /* Get the port number */
+ port_available = bulk_start+ port_pos;
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT(
+ "Found port from cache : IP 0x%x, port %d %d iterations\n",
+ my_pm->ipv4_address, port_available, i)
+#endif
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_cache_hit_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+ break;
+ } /* end of for loop for cache check */
+ /* If we have not found a port yet, check if we can have
+ * pre allocated bulk port from non-cache */
+ if(PREDICT_FALSE(i == BULK_RANGE_CACHE_SIZE)) {
+ if( try_bulk_port_from_non_cache(udb, my_pm, pair_type,
+ bulk_size, &port_available,
+ static_port_range) != CNAT_SUCCESS ) {
+ goto ALLCOATE_NEW_BULK;
+ }
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("Found port from non-cache : IP 0x%x, port %d\n",
+ my_pm->ipv4_address, port_available);
+#endif
+ }
+ /* Assign the port, mark it as in use */
+ cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available);
+ (my_pm->inuse)++;
+ if(PREDICT_FALSE(pair_type == PORT_PAIR)) {/* Mark the next one too */
+ cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available + 1);
+ (my_pm->inuse)++;
+ }
+ *o_ipv4_address = my_pm->ipv4_address;
+ *o_port = port_available;
+ *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_use_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+ return (CNAT_SUCCESS);
+ }
+ALLCOATE_NEW_BULK:
+#ifdef DEBUG_BULK_PORT
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ PLATFORM_DEBUG_PRINT(
+ "No port available from bulk cache, bulk size %d\n", bulk_size);
+ }
+#endif
+ /* For whatever reason, we have not got a port yet */
+ rv = cnat_dynamic_port_alloc_v2(pm, atype, pair_type, index,
+ o_ipv4_address, o_port, static_port_range, bulk_size, nfv9_log_req,
+ ip_n_to_1, rseed_ip);
+ if (PREDICT_FALSE(rv != CNAT_SUCCESS)) {
+ return rv;
+ }
+ /* Take care of caching */
+ if(PREDICT_FALSE(udb != NULL)) {
+ /* Predict false because, we usually allocate for new users */
+ cnat_update_bulk_range_cache(udb, *o_port, bulk_size);
+ }
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_alloc_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+ return (CNAT_SUCCESS);
+}
+
+
+cnat_errno_t
+cnat_static_port_alloc_v2_bulk (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ port_pair_t pair_type,
+ u32 i_ipv4_address,
+ u16 i_port,
+ u32 *index,
+ u32 *o_ipv4_address,
+ u16 *o_port,
+ u16 static_port_range,
+ cnat_user_db_entry_t *udb,
+ bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req,
+ u16 ip_n_to_1
+ )
+{
+
+ /***
+ * Requirements -
+ * 1. If the port allocated is below dyn start, it should be individual
+ * port (not bulk)
+ * 2. If NOT, it should be bulk allocated
+ * 3. Try and keep the inside port same as outside port in both the
+ * cases (best effort)
+
+ * Algorithm
+ * 1. Check if it is below stat port start or user is new or bulk is
+ * disabled. If yes, call existing function
+ * 2. If not, see if we can pick from bulk and yet try to keep the port
+ * same - difficult thing - check if the port is free - then check if the
+ * entire bulk is free - if not check if bulk is owned by the user already.
+ * If all of these fail, call existing function to allocate a new bulk
+ * 3. Update cache, etc return log requirements
+ *****/
+
+ cnat_errno_t rv;
+ i16 i;
+ u32 head;
+ cnat_portmap_v2_t *my_pm;
+ uword bit_test_result, start_bit;
+ cnat_main_db_entry_t *db = NULL;
+
+ if((BULK_ALLOC_SIZE_NONE != bulk_size) /* bulk logging enabled */
+ && (udb) && /* This user does have translations already */
+ i_port >= static_port_range ) { /* It is outside stat port range*/
+
+ my_pm = pm + *index;
+ /* We have a case to check if bulk allocated ports can be used */
+
+ /* First check if the required port is available. */
+ if(PREDICT_FALSE(clib_bitmap_get_no_check(my_pm->bm, i_port) == 0)) {
+ goto ALLOCATE_NEW_BULK_STATIC;
+ }
+
+ /* Port is free.. check if the bulk is also free */
+ start_bit= ((i_port/bulk_size) * bulk_size);
+ bit_test_result = cgn_clib_bitmap_check_if_all(my_pm->bm,
+ start_bit, bulk_size);
+ if(PREDICT_TRUE(bit_test_result)) { /* bulk is available, grab it */
+ goto ALLOCATE_NEW_BULK_STATIC;
+ }
+
+ /* else, bulk is taken by someone. check if it is me */
+ /* Check if we own the bulk by any chance */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(udb->bulk_port_range_cache[i] == start_bit) break;
+ }
+ if(i == BULK_RANGE_CACHE_SIZE) { /* no luck with cache */
+ head = udb->translation_list_head_index;
+ if(PREDICT_FALSE(head == EMPTY))
+ goto ALLOCATE_NEW_BULK_STATIC;
+ db = cnat_main_db + head;
+ i = 0;
+ while(1) {
+ if((db->out2in_key.k.port/bulk_size) * bulk_size == start_bit) {
+ i = 1; /* Just to indicate it is found */
+ break;
+ }
+ db = cnat_main_db + db->user_ports.next;
+ /*
+ * its a circular list, so if we have reached the head again
+ * all the entries for that user have been read
+ */
+ if (db == (cnat_main_db + head)) break;
+ } /* while loop for db entries */
+ if(!i) {
+ goto ALLOCATE_NEW_BULK_STATIC;
+ }
+ }
+ /* Assign the port, mark it as in use */
+ cgn_clib_bitmap_clear_no_check(my_pm->bm, i_port);
+ (my_pm->inuse)++;
+ *o_ipv4_address = my_pm->ipv4_address;
+ *o_port = i_port;
+ *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_use_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("%s, %d, found stat port from bulk: %x, %d\n",
+ __func__,
+ __LINE__, *o_ipv4_address, *o_port);
+#endif /* DEBUG_BULK_PORT */
+ return (CNAT_SUCCESS);
+ }
+
+ALLOCATE_NEW_BULK_STATIC:
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("%s No port available from bulk cache, bulk size %d\n",
+ __func__,bulk_size);
+#endif
+ /* For whatever reason, we have not got a port yet */
+ rv = cnat_static_port_alloc_v2(pm, atype, pair_type, i_ipv4_address,
+ i_port, index, o_ipv4_address, o_port, static_port_range,
+ bulk_size, nfv9_log_req,ip_n_to_1);
+ if (PREDICT_FALSE(rv != CNAT_SUCCESS)) {
+ return rv;
+ }
+ /* Take care of caching only if it was a bulk alloc */
+ if(PREDICT_FALSE(udb && (BULK_ALLOC_NOT_ATTEMPTED != *nfv9_log_req))) {
+ cnat_update_bulk_range_cache(udb, *o_port, bulk_size);
+ }
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_alloc_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+ return (CNAT_SUCCESS);
+
+}
+
+cnat_errno_t
+cnat_mapped_static_port_alloc_v2_bulk (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ u32 *index,
+ u32 ipv4_address,
+ u16 port,
+ cnat_user_db_entry_t *udb,
+ bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req,
+ u16 ip_n_to_1
+ )
+{
+ /* Requirements :
+ * 1. Check if bulk allocation is required.
+ * 2. Call cnat_mapped_static_port_alloc_v2 to allocate
+ * 3. Decide if alloc has to be cached
+ * 4. Update nfv9_log_req
+ */
+ cnat_errno_t rv;
+ rv = cnat_mapped_static_port_alloc_v2 (pm,
+ atype, index, ipv4_address, port, nfv9_log_req, bulk_size, ip_n_to_1);
+ if (PREDICT_FALSE(rv != CNAT_SUCCESS)) {
+ return rv;
+ }
+ /* Take care of caching only if it was a bulk alloc */
+ if(PREDICT_FALSE(udb && (BULK_ALLOC_NOT_ATTEMPTED != *nfv9_log_req))) {
+ int i;
+ port = port*bulk_size;
+ port = port/bulk_size; /* align it to bulk size boundary */
+ for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i])
+ == PORT_TO_CACHE(port, bulk_size))
+ break;
+ }
+ if( i == BULK_RANGE_CACHE_SIZE) { /* else, it is alredy in cache */
+ cnat_update_bulk_range_cache(udb, port, bulk_size);
+ }
+ }
+#ifdef HAVE_BULK_PORT_STATS
+ mapped_port_alloc_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+ return (CNAT_SUCCESS);
+}
+
+
+cnat_errno_t
+cnat_dynamic_port_alloc_rtsp_bulk (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ port_pair_t pair_type,
+ u16 i_port,
+ u32 *index,
+ u32 *o_ipv4_address,
+ u16 *o_port,
+ u16 static_port_range,
+ cnat_user_db_entry_t *udb,
+ bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req,
+ u32 *rseed_ip)
+{
+
+ /***
+ * Algorithm
+ * 1. Compute the range of ports required based on the number of digits
+ * in the port request made by the client.
+ * 2. Check if bulk logging is enabled. If not, use the existing method.
+ * 3. Check if there are 2 adjacent ports available that meet the above
+ * criteria in any of the bulk allocations made already.
+ * 4. If yes, mark them in use and return.
+ * 5. If not allocate a new bulk and pick 2 ports in it
+ ***/
+
+ i16 i;
+ cnat_portmap_v2_t *my_pm = 0;
+ u32 start_port1, end_port1, start_port2, end_port2;
+ int range_loop;
+ u16 bulk_start;
+ i16 port_pos;
+ u16 port_available = 0;
+
+ ASSERT(index);
+ ASSERT(o_ipv4_address);
+ ASSERT(o_port);
+
+ /*
+ * Check if the port is 4 digit or 5 digit. I am assuming we are
+ * not getting 3 (or 2 or 1) digit ports, which we cannot anyway
+ * allocate same sized outside ports - as outside ports start from 1024
+ *
+ * Static Port has its own reserved range. Ensure that the range is
+ * such that atleast few 4 digit ports are available for RTSP. If
+ * not it does not make sense to do special allocation for RTSP.
+ */
+ if (PREDICT_TRUE(static_port_range < MIN_STATIC_PORT_RANGE_FOR_RTSP)) {
+ /*
+ * 4 digit port or less
+ */
+ if (i_port <= 9999) {
+ start_port1 = static_port_range;
+ end_port1 = 9999;
+
+ start_port2 = 10000;
+ end_port2 = PORTS_PER_ADDR - 1;
+ } else { /* 5 digit port */
+ start_port1 = 10000;
+ end_port1 = PORTS_PER_ADDR - 1;
+
+ start_port2 = static_port_range;
+ end_port2 = 9999;
+ }
+ } else { /* Static port range is too big */
+ start_port1 = static_port_range;
+ end_port1 = PORTS_PER_ADDR - 1;
+
+ /*
+ * PORTS_PER_ADDR is just a placeholder for
+ * INVALID_PORT, valid ports are b/w 1 and PORTS_PER_ADDR
+ */
+ start_port2 = PORTS_PER_ADDR;
+ end_port2 = PORTS_PER_ADDR;
+ }
+
+
+ if(PREDICT_TRUE(udb != NULL)) {
+ my_pm = pm + *index;
+ }
+
+ /* Now check if this user already owns a bulk range that is
+ * within start range 1
+ */
+
+ u32 start_range = start_port1;
+ u32 end_range = end_port1;
+ for(range_loop = 0; range_loop < 2; range_loop++) {
+ if((BULK_ALLOC_SIZE_NONE == bulk_size) || (!udb)) {
+ goto ALLOCATE_NEW_RTSP_PORTS;
+ }
+ for(i= 0; i < BULK_RANGE_CACHE_SIZE; i++) {
+ if(PREDICT_TRUE((udb->bulk_port_range_cache[i] ==
+ (i16)BULK_RANGE_INVALID) ||
+ IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i]))) {
+ continue; /* This range is not initialized yet or it is full */
+ }
+
+ bulk_start = CACHE_TO_PORT(udb->bulk_port_range_cache[i],
+ bulk_size);
+ if(bulk_start < start_port1 || bulk_start >= end_port1) {
+ continue; /* Not in the range */
+ }
+
+ port_pos = get_suiting_port_pos_from_range(my_pm,
+ bulk_start, bulk_size, pair_type);
+ if(PREDICT_FALSE(port_pos < 0)) {
+ /* Not Marking this cache entry as full as it failed
+ * for pair type. It might have individual entries
+ */
+ continue;
+ }
+ /* Get the port number */
+ port_available = bulk_start+ port_pos;
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT(
+ "Found port from cache : IP 0x%x, port %d %d iterations\n",
+ my_pm->ipv4_address, port_available, i)
+#endif
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_cache_hit_count += 2;
+#endif /* HAVE_BULK_PORT_STATS */
+ break;
+ } /* end of for loop for cache check */
+
+ if(PREDICT_FALSE(i == BULK_RANGE_CACHE_SIZE)) {
+ /* we have not found a port yet, but to do not want to try
+ * non-cache bulks.. because, it is a very low probability and
+ * do not want to tweak that code for this special case
+ * The impact of non checking the non-cache is, we give this
+ * user few extra ports .. which is OK
+ */
+ goto ALLOCATE_NEW_RTSP_PORTS;
+ }
+#ifdef DEBUG_BULK_PORT
+ PLATFORM_DEBUG_PRINT("RTSP: Found port from non-cache : IP 0x%x, port %d\n",
+ my_pm->ipv4_address, port_available);
+#endif
+
+ /* Assign the port, mark it as in use */
+ cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available);
+ (my_pm->inuse)++;
+ cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available + 1);
+ (my_pm->inuse)++;
+
+ *o_ipv4_address = my_pm->ipv4_address;
+ *o_port = port_available;
+ *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_use_count += 2;
+#endif /* HAVE_BULK_PORT_STATS */
+ return (CNAT_SUCCESS);
+
+ALLOCATE_NEW_RTSP_PORTS:
+ /* No luck. Let's try allocating new bulk.. */
+ if(PREDICT_TRUE(CNAT_SUCCESS == cnat_dynamic_port_alloc_rtsp
+ (pm, atype, pair_type,
+ start_range, end_range,index, o_ipv4_address,
+ o_port, bulk_size, nfv9_log_req,rseed_ip))) {
+ if(PREDICT_FALSE(udb &&
+ (BULK_ALLOC_NOT_ATTEMPTED != *nfv9_log_req))) {
+ cnat_update_bulk_range_cache(udb, *o_port, bulk_size);
+ }
+#ifdef HAVE_BULK_PORT_STATS
+ bulk_port_alloc_count++;
+#endif /* HAVE_BULK_PORT_STATS */
+ return CNAT_SUCCESS;
+ }
+
+ /* Could not allocate in range 1.. so move to range 2. */
+ start_range = start_port2;
+ end_range = end_port2;
+
+ }
+
+ return (CNAT_NOT_FOUND_DIRECT); /* if we are here, we could not get any ports */
+
+}
+
+#else /* Dummy definitions */
+void show_bulk_port_stats()
+{
+ PLATFORM_DEBUG_PRINT("\nBulk logging feature not included\n");
+}
+
+ void clear_bulk_port_stats()
+{
+ PLATFORM_DEBUG_PRINT("\nBulk logging feature not included\n");
+}
+#endif /* NO_BULK_LOGGING */