summaryrefslogtreecommitdiffstats
path: root/plugins/vcgn-plugin/vcgn/cnat_ports.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/vcgn-plugin/vcgn/cnat_ports.c')
-rw-r--r--plugins/vcgn-plugin/vcgn/cnat_ports.c1113
1 files changed, 1113 insertions, 0 deletions
diff --git a/plugins/vcgn-plugin/vcgn/cnat_ports.c b/plugins/vcgn-plugin/vcgn/cnat_ports.c
new file mode 100644
index 00000000000..943fb3ed38c
--- /dev/null
+++ b/plugins/vcgn-plugin/vcgn/cnat_ports.c
@@ -0,0 +1,1113 @@
+/*
+ *------------------------------------------------------------------
+ * cnat_ports.c - port allocator
+ *
+ * Copyright (c) 2008-2014 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/hash.h>
+#include <vppinfra/pool.h>
+#include <vppinfra/clib.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"
+
+#if 1 /* TOBE_PORTED */
+/* Following is defined elsewhere. */
+#define msg_spp_err(s) \
+do { \
+ fprintf(stderr,(i8 *)s); \
+ fputs("\n", stderr); \
+} while(0);
+#endif
+
+
+#define PM_90_PERCENT_USE 58980
+/*
+ * instance number provisioned from HW
+ */
+u8 my_instance_number = 0;
+
+typedef struct {
+ u32 cached_next_index;
+ /* $$$$ add data here */
+
+ /* convenience variables */
+ vlib_main_t * vlib_main;
+ vnet_main_t * vnet_main;
+} cnat_ports_main_t;
+
+cnat_ports_main_t cnat_ports_main;
+
+static u32 rseed_port; /* random number generator seed */
+
+void
+cnat_db_dump_portmap_for_vrf (u32 vrfmap_index)
+{
+ u32 i, pm_len;
+ cnat_vrfmap_t *my_vrfmap = cnat_map_by_vrf + vrfmap_index;
+ cnat_portmap_v2_t *pm, *my_pm __attribute__((unused));
+
+ pm = my_vrfmap->portmap_list;
+ pm_len = vec_len(pm);
+
+ for (i = 0; i < pm_len; i++) {
+ my_pm = pm + i;
+
+ PLATFORM_DEBUG_PRINT("pm %d: IPv4 Addr 0x%x - in use %d private_ip_users_count %d\n",
+ i, my_pm->ipv4_address, my_pm->inuse,
+ my_pm->private_ip_users_count);
+
+ PLATFORM_DEBUG_PRINT("pm %d: IPv4 Addr 0x%x - in use %d "
+ "private_ip_users_count %d\n",
+ i, my_pm->ipv4_address, my_pm->inuse,
+ my_pm->private_ip_users_count);
+ }
+}
+
+void
+cnat_db_dump_portmaps ()
+{
+ u32 i, vrfmap_index;
+
+ for (i = 0; i < CNAT_MAX_VRFMAP_ENTRIES; i++) {
+ vrfmap_index = vrf_map_array[i];
+
+ if (vrfmap_index == VRF_MAP_ENTRY_EMPTY) {
+ continue;
+ }
+
+ PLATFORM_DEBUG_PRINT("\n\nDumping the port map for uidb_index %d\n", i);
+ cnat_db_dump_portmap_for_vrf(vrfmap_index);
+ }
+}
+
+#ifndef NO_BULK_LOGGING
+static int check_if_stat_alloc_ok_for_bulk(cnat_portmap_v2_t *pm,
+ u16 i_port, bulk_alloc_size_t bulk_size,
+ u16 static_port_range)
+{
+ uword bit_test_result;
+ if(BULK_ALLOC_SIZE_NONE == bulk_size) return 1; /* No issues */
+
+ if(i_port < static_port_range) return 1; /* we don't want bulk */
+
+ i_port = (i_port/bulk_size) * bulk_size;
+ bit_test_result = cgn_clib_bitmap_check_if_all(pm->bm, i_port, bulk_size);
+ return(bit_test_result);
+}
+#else /* dummy */
+inline static int check_if_stat_alloc_ok_for_bulk(cnat_portmap_v2_t *pm,
+ u16 i_port, bulk_alloc_size_t bulk_size,
+ u16 static_port_range)
+{
+ return 1;
+}
+#endif /* NO_BULK_LOGGING */
+/*
+ * cnat_port_alloc_static_v2
+ * public ipv4 address/port allocator for Static Port commands
+ * tries to allocate same outside port as inside port
+ */
+cnat_errno_t
+cnat_static_port_alloc_v2 (
+ 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
+#ifndef NO_BULK_LOGGING
+ , bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req
+#endif
+ , u16 ip_n_to_1
+ )
+{
+ u32 i, hash_value, my_index, found, max_attempts;
+ u16 start_bit, new_port;
+ cnat_portmap_v2_t *my_pm = 0;
+ u32 pm_len = vec_len(pm);
+ uword bit_test_result;
+
+#ifndef NO_BULK_LOGGING
+ *nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED;
+#endif
+
+ if (PREDICT_FALSE(pm_len == 0)) {
+ return (CNAT_NO_POOL_ANY);
+ }
+
+ switch (atype) {
+
+ case PORT_ALLOC_ANY:
+
+ found = 0;
+
+ /*
+ * Try to hash the IPv4 address to get an index value to select the pm
+ */
+ hash_value = (i_ipv4_address & 0xffff) ^
+ ((i_ipv4_address > 16) & 0xffff);
+
+ /*
+ * If pm_len <= 256, compact the hash to 8 bits
+ */
+ if (PREDICT_TRUE(pm_len <= 256)) {
+ hash_value = (hash_value & 0xff) ^ ((hash_value > 8) & 0xff);
+ }
+
+ /*
+ * Ensure that the hash value is in the range 0 .. (pm_len-1)
+ */
+ my_index = hash_value % pm_len;
+
+ for (i = 0; i < PORT_PROBE_LIMIT; i++) {
+ my_pm = pm + my_index;
+ if(PREDICT_TRUE(ip_n_to_1)) {
+ if(PREDICT_TRUE(my_pm->private_ip_users_count < ip_n_to_1)) {
+ /*
+ * Try to find a PM with atlest 33% free and my_port free
+ */
+ if (PREDICT_TRUE((my_pm->inuse < ((BITS_PER_INST*2)/3)) &&
+ clib_bitmap_get_no_check(my_pm->bm,
+ i_port) == 1)
+#ifndef NO_BULK_LOGGING
+ && check_if_stat_alloc_ok_for_bulk(my_pm, i_port,
+ bulk_size,
+ static_port_range)
+#endif
+ ) {
+ found = 1;
+ break;
+ }
+ }
+
+ } else {
+ /*
+ * Try to find a PM with atlest 33% free and my_port free
+ */
+ if (PREDICT_TRUE((my_pm->inuse < ((BITS_PER_INST*2)/3)) &&
+ clib_bitmap_get_no_check(my_pm->bm,
+ i_port) == 1)
+#ifndef NO_BULK_LOGGING
+ && check_if_stat_alloc_ok_for_bulk(my_pm, i_port,
+ bulk_size,
+ static_port_range)
+#endif
+ ) {
+ found = 1;
+ break;
+ }
+ }
+ my_index = (my_index + 1) % pm_len;
+ }
+
+ /*
+ * If not found do it the hard way .
+ * "hard" way, best-fit.
+ */
+ if (!found) {
+ u32 min_inuse_any, min_inuse_myport;
+ u32 min_index_any, min_index_myport;
+
+ min_inuse_any = min_inuse_myport = PORTS_PER_ADDR + 1;
+ min_index_any = min_index_myport = ~0;
+ for (i = 0; i < pm_len; i++) {
+ my_pm = pm + i;
+ if(PREDICT_TRUE(ip_n_to_1)) {
+ if(PREDICT_TRUE(my_pm->private_ip_users_count < ip_n_to_1)) {
+ if (PREDICT_FALSE(my_pm->inuse < min_inuse_any)) {
+ min_inuse_any = my_pm->inuse;
+ min_index_any = my_pm - pm;
+ }
+ if (PREDICT_FALSE(my_pm->inuse < min_inuse_myport)) {
+ if (PREDICT_TRUE(clib_bitmap_get_no_check(
+ my_pm->bm,i_port) == 1)
+#ifndef NO_BULK_LOGGING
+ && check_if_stat_alloc_ok_for_bulk(my_pm,
+ i_port,bulk_size,static_port_range)
+#endif
+ ) {
+ min_inuse_myport = my_pm->inuse;
+ min_index_myport = my_pm - pm;
+ }
+ }
+
+ }
+
+ } else {
+ if (PREDICT_FALSE(my_pm->inuse < min_inuse_any)) {
+ min_inuse_any = my_pm->inuse;
+ min_index_any = my_pm - pm;
+ }
+ if (PREDICT_FALSE(my_pm->inuse < min_inuse_myport)) {
+ if (PREDICT_TRUE(clib_bitmap_get_no_check(
+ my_pm->bm, i_port) == 1)
+#ifndef NO_BULK_LOGGING
+ && check_if_stat_alloc_ok_for_bulk(my_pm, i_port,
+ bulk_size, static_port_range)
+#endif
+ ) {
+ min_inuse_myport = my_pm->inuse;
+ min_index_myport = my_pm - pm;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check if we have an exactly matching PM that has
+ * myport free. If so use it. If no such PM is
+ * available, use any PM
+ */
+ if (PREDICT_TRUE(min_inuse_myport < PORTS_PER_ADDR)) {
+ my_pm = pm + min_index_myport;
+ my_index = min_index_myport;
+ found = 1;
+ } else if (PREDICT_TRUE(min_inuse_any < PORTS_PER_ADDR)) {
+ my_pm = pm + min_index_any;
+ my_index = min_index_any;
+ found = 1;
+ }
+ }
+
+ if (!found) {
+ return (CNAT_NO_PORT_ANY);
+ }
+ break;
+
+ case PORT_ALLOC_DIRECTED:
+ my_index = *index;
+ if (PREDICT_FALSE(my_index > pm_len)) {
+ return (CNAT_INV_PORT_DIRECT);
+ }
+ my_pm = pm + my_index;
+ break;
+
+ default:
+ return (CNAT_ERR_PARSER);
+ }
+
+ /* Allocate a matching port if possible */
+ start_bit = i_port;
+ found = 0;
+ max_attempts = BITS_PER_INST;
+#ifndef NO_BULK_LOGGING
+ if((BULK_ALLOC_SIZE_NONE != bulk_size) &&
+ (i_port >= static_port_range)) {
+ start_bit = (start_bit/bulk_size) * bulk_size;
+ max_attempts = BITS_PER_INST/bulk_size;
+ }
+#endif /* NO_BULK_LOGGING */
+
+ for (i = 0; i < max_attempts; i++) {
+#ifndef NO_BULK_LOGGING
+ if((BULK_ALLOC_SIZE_NONE != bulk_size) &&
+ (i_port >= static_port_range)) {
+ bit_test_result = cgn_clib_bitmap_check_if_all(my_pm->bm,
+ start_bit, bulk_size);
+ }
+ else
+#endif /* #ifndef NO_BULK_LOGGING */
+ bit_test_result = clib_bitmap_get_no_check(my_pm->bm, start_bit);
+
+ if (PREDICT_TRUE(bit_test_result)) {
+#ifndef NO_BULK_LOGGING
+ if((BULK_ALLOC_SIZE_NONE != bulk_size) &&
+ (i_port >= static_port_range)) {
+ *nfv9_log_req = start_bit;
+ if(i==0) new_port = i_port; /* First go */
+ else {
+ new_port = bit2port(start_bit);
+ if (pair_type == PORT_S_ODD && (new_port & 0x1) == 0)
+ new_port++;
+ }
+ found = 1;
+ break;
+ }
+ else {
+#endif /* NO_BULK_LOGGING */
+ new_port = bit2port(start_bit);
+ if (pair_type == PORT_S_ODD) {
+ if ((new_port & 0x1) == 1) {
+ found = 1;
+ break;
+ }
+ } else if (pair_type == PORT_S_EVEN) {
+ if ((new_port & 0x1) == 0) {
+ found = 1;
+ break;
+ }
+ } else {
+ found = 1;
+ break;
+ }
+#ifndef NO_BULK_LOGGING
+ }
+#endif
+ }
+#ifndef NO_BULK_LOGGING
+ if((BULK_ALLOC_SIZE_NONE != bulk_size) &&
+ (i_port >= static_port_range))
+ start_bit = (start_bit + bulk_size) % BITS_PER_INST;
+ else {
+#endif /* NO_BULK_LOGGING */
+ start_bit = (start_bit + 1) % BITS_PER_INST;
+ if(PREDICT_FALSE(start_bit == 0)) {
+ start_bit = 1; /* Port 0 is invalid, so start from 1 */
+ }
+#ifndef NO_BULK_LOGGING
+ }
+#endif
+ } /* End of for loop */
+
+ if (!found) {
+ /* Port allocation failure */
+ if (atype == PORT_ALLOC_DIRECTED) {
+ return (CNAT_NOT_FOUND_DIRECT);
+ } else {
+ return (CNAT_NOT_FOUND_ANY);
+ }
+ }
+
+ /* Accounting */
+ cgn_clib_bitmap_clear_no_check(my_pm->bm, new_port);
+ (my_pm->inuse)++;
+
+ *index = my_pm - pm;
+ *o_ipv4_address = my_pm->ipv4_address;
+
+ *o_port = new_port;
+
+ return (CNAT_SUCCESS);
+}
+
+/*
+ * Try to allocate a portmap structure based on atype field
+ */
+cnat_portmap_v2_t *
+cnat_dynamic_addr_alloc_from_pm (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ u32 *index,
+ cnat_errno_t *err,
+ u16 ip_n_to_1,
+ u32 *rseed_ip)
+{
+ u32 i, pm_len;
+ int my_index;
+ int min_inuse, min_index;
+
+ cnat_portmap_v2_t *my_pm = 0;
+ *err = CNAT_NO_POOL_ANY;
+
+ pm_len = vec_len(pm);
+
+ switch(atype) {
+ case PORT_ALLOC_ANY:
+ if (PREDICT_FALSE(pm_len == 0)) {
+ my_pm = 0;
+ *err = CNAT_NO_POOL_ANY;
+ goto done;
+ }
+
+ /* "Easy" way, first address with at least 200 free ports */
+ for (i = 0; i < PORT_PROBE_LIMIT; i++) {
+ *rseed_ip = randq1(*rseed_ip);
+ my_index = (*rseed_ip) % pm_len;
+ my_pm = pm + my_index;
+ if (PREDICT_FALSE(ip_n_to_1)) {
+ if(PREDICT_TRUE(ip_n_to_1 == 1)) {
+ if (PREDICT_FALSE(0 == my_pm->inuse)) {
+ goto done;
+ }
+ } else {
+ if(PREDICT_TRUE(my_pm->private_ip_users_count < ip_n_to_1)) {
+ if (PREDICT_FALSE(my_pm->inuse < ((BITS_PER_INST*2)/3))) {
+ goto done;
+ }
+ }
+ }
+ } else {
+ if (PREDICT_FALSE(my_pm->inuse < ((BITS_PER_INST*2)/3))) {
+ goto done;
+ }
+ }
+ }
+
+ /* "hard" way, best-fit. $$$$ Throttle complaint */
+ min_inuse = PORTS_PER_ADDR + 1;
+ min_index = ~0;
+ for (i = 0; i < pm_len; i++) {
+ my_pm = pm + i;
+ if (PREDICT_FALSE(ip_n_to_1)) {
+ if(PREDICT_TRUE(ip_n_to_1 == 1)) {
+ if (PREDICT_FALSE(!my_pm->inuse)) {
+ min_inuse = my_pm->inuse;
+ min_index = my_pm - pm;
+ }
+ } else {
+ if(PREDICT_TRUE(my_pm->private_ip_users_count < ip_n_to_1)) {
+ if (PREDICT_TRUE(my_pm->inuse < min_inuse)) {
+ min_inuse = my_pm->inuse;
+ min_index = my_pm - pm;
+ }
+
+ }
+ }
+
+ } else {
+ if (PREDICT_TRUE(my_pm->inuse < min_inuse)) {
+ min_inuse = my_pm->inuse;
+ min_index = my_pm - pm;
+ }
+ }
+ }
+
+ if (PREDICT_TRUE(min_inuse < PORTS_PER_ADDR)) {
+ my_pm = pm + min_index;
+ my_index = min_index;
+ goto done;
+ }
+
+ /* Completely out of ports */
+#ifdef DEBUG_PRINTF_ENABLED
+ PLATFORM_DEBUG_PRINT("%s out of ports\n", __FUNCTION__);
+#endif
+
+ my_pm = 0;
+ *err = CNAT_NO_PORT_ANY;
+ break;
+
+
+ case PORT_ALLOC_DIRECTED:
+ //ASSERT(*index < pm_len);
+ if (PREDICT_FALSE(*index > pm_len)) {
+ my_pm = 0;
+ *err = CNAT_INV_PORT_DIRECT;
+ goto done;
+ }
+ my_pm = pm + *index;
+ my_index = *index;
+ break;
+
+ default:
+ msg_spp_err("bad allocation type in cnat_port_alloc");
+ my_pm = 0;
+ *err = CNAT_ERR_PARSER;
+ break;
+ }
+
+ done:
+ if (PREDICT_FALSE(my_pm == NULL)) {
+ return (my_pm);
+ }
+
+ if (PREDICT_FALSE(my_pm->inuse >= BITS_PER_INST)) {
+ my_pm = 0;
+ if (atype == PORT_ALLOC_DIRECTED) {
+ *err = CNAT_BAD_INUSE_DIRECT;
+ } else {
+ *err = CNAT_BAD_INUSE_ANY;
+ }
+ }
+
+ return (my_pm);
+}
+
+
+/*
+ * cnat_port_alloc_v2
+ * public ipv4 address/port allocator for dynamic ports
+ *
+ * 200K users / 20M translations means vec_len(cnat_portmap) will be
+ * around 300.
+ *
+ */
+cnat_errno_t
+cnat_dynamic_port_alloc_v2 (
+ 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
+#ifndef NO_BULK_LOGGING
+ , bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req
+#endif
+ , u16 ip_n_to_1,
+ u32 *rseed_ip
+ )
+{
+ int i;
+ cnat_errno_t my_err = CNAT_NO_POOL_ANY;
+ cnat_portmap_v2_t *my_pm = 0;
+ u16 start_bit;
+ u16 new_port;
+ uword bit_test_result;
+ uword max_trys_to_find_port;
+
+ ASSERT(index);
+ ASSERT(o_ipv4_address);
+ ASSERT(o_port);
+
+ my_pm = cnat_dynamic_addr_alloc_from_pm(pm, atype, index, &my_err, ip_n_to_1,
+ rseed_ip);
+
+ if (PREDICT_FALSE(my_pm == NULL)) {
+ return (my_err);
+ }
+ if(PREDICT_FALSE(my_pm->dyn_full == 1)) {
+ if (atype == PORT_ALLOC_DIRECTED) {
+ return (CNAT_NOT_FOUND_DIRECT);
+ } else {
+ return (CNAT_NOT_FOUND_ANY);
+ }
+ }
+
+#if DEBUG > 1
+ PLATFORM_DEBUG_PRINT("ALLOC_PORT_V2: My_Instance_Number %d: IP addr 0x%x, Inuse %d\n",
+ my_instance_number, my_pm->ipv4_address, my_pm->inuse);
+#endif
+
+ rseed_port = randq1(rseed_port);
+
+ /*
+ * Exclude the static port range for allocating dynamic ports
+ */
+ start_bit = (rseed_port) % (BITS_PER_INST - static_port_range);
+ start_bit = start_bit + static_port_range;
+
+#ifndef NO_BULK_LOGGING
+ *nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED;
+ if(BULK_ALLOC_SIZE_NONE != bulk_size)
+ {
+ /* We need the start port of the range to be alined on integer multiple
+ * of bulk_size */
+ max_trys_to_find_port = BITS_PER_INST/bulk_size;
+ start_bit= ((start_bit + bulk_size -1)/bulk_size) * bulk_size;
+ }
+ else
+#endif /* #ifndef NO_BULK_LOGGING */
+ max_trys_to_find_port = BITS_PER_INST;
+
+ /* Allocate a random port / port-pair */
+ for (i = 0; i < max_trys_to_find_port; i++) {
+
+ /* start_bit is only a u16.. so it can rollover and become zero */
+ if (PREDICT_FALSE( /* (start_bit >= BITS_PER_INST) || FIXME u16 cannot be >= 65536 */
+ (start_bit < static_port_range))) {
+ start_bit = static_port_range;
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ start_bit= ((start_bit + bulk_size -1)/bulk_size) * bulk_size;
+ }
+#endif /* #ifndef NO_BULK_LOGGING */
+ }
+ /* Scan forward from random position */
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ bit_test_result = cgn_clib_bitmap_check_if_all(my_pm->bm,
+ start_bit, bulk_size);
+ }
+ else
+#endif /* #ifndef NO_BULK_LOGGING */
+ bit_test_result = clib_bitmap_get_no_check(my_pm->bm, start_bit);
+
+ if (PREDICT_TRUE(bit_test_result)) {
+ new_port = bit2port(start_bit);
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size)
+ *nfv9_log_req = new_port;
+#endif
+ if ((pair_type == PORT_S_ODD) &&
+ (!(new_port & 0x1))) {
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ start_bit++; /* Just use the next one in the bulk range */
+ new_port++;
+ goto found2;
+ }
+#endif /* #ifndef NO_BULK_LOGGING */
+ goto notfound;
+ } else if ((pair_type == PORT_S_EVEN) &&
+ (new_port & 0x1)) {
+ goto notfound;
+ }
+
+ /* OK we got one or two suitable ports */
+ goto found2;
+ }
+
+ notfound:
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size)
+ start_bit += bulk_size;
+ else
+#endif /* #ifndef NO_BULK_LOGGING */
+ start_bit++;
+
+ } /* end of for loop */
+
+ /* Completely out of ports */
+
+ /* Port allocation failure */
+ /* set dyn_full flag. This would be used to verify
+ * for further dyn session before searching for port
+ */
+ if (atype == PORT_ALLOC_DIRECTED) {
+ my_pm->dyn_full = 1;
+ return (CNAT_NOT_FOUND_DIRECT);
+ } else {
+ my_pm->dyn_full = 1;
+ return (CNAT_NOT_FOUND_ANY);
+ }
+
+
+ found2:
+
+ /* Accounting */
+ cgn_clib_bitmap_clear_no_check (my_pm->bm, start_bit);
+ (my_pm->inuse)++;
+
+ *index = my_pm - pm;
+ *o_ipv4_address = my_pm->ipv4_address;
+
+ *o_port = new_port;
+ return (CNAT_SUCCESS);
+}
+
+#ifdef TOBE_PORTED
+/*
+ * cnat_alloc_port_from_pm
+ * Given a portmap structure find port/port_pair that are free
+ *
+ * The assumption in this function is that bit in bm corresponds
+ * to a port number. This is TRUE and hence there is no call
+ * to the function bit2port here, though it is done in other
+ * places in this file.
+ *
+ */
+static u32
+cnat_alloc_port_from_pm (
+ u32 start_port,
+ u32 end_port,
+ cnat_portmap_v2_t *my_pm,
+ port_pair_t pair_type
+#ifndef NO_BULK_LOGGING
+ , bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req
+#endif /* #ifnded NO_BULK_ALLOCATION */
+ )
+{
+ u32 i;
+ u32 start_bit;
+ u32 total_ports = end_port - start_port + 1;
+ uword bit_test_result;
+ uword max_trys_to_find_port;
+
+ rseed_port = randq1(rseed_port);
+
+ start_bit = rseed_port % total_ports;
+ start_bit = start_bit + start_port;
+#ifndef NO_BULK_LOGGING
+ *nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED;
+ if(BULK_ALLOC_SIZE_NONE != bulk_size)
+ {
+ /* We need the start port of the range to be alined on integer multiple
+ * of bulk_size */
+ max_trys_to_find_port = total_ports/bulk_size;
+ start_bit= ((start_bit + bulk_size -1)/bulk_size) * bulk_size;
+ }
+ else
+#endif /* #ifndef NO_BULK_LOGGING */
+ max_trys_to_find_port = total_ports;
+
+ /* Allocate a random port / port-pair */
+ for (i = 0; i < max_trys_to_find_port; i++) {
+ /* start_bit is only a u16.. so it can rollover and become zero */
+ if (PREDICT_FALSE((start_bit >= end_port) ||
+ (start_bit < start_port))) {
+ start_bit = start_port;
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ start_bit= ((start_bit + bulk_size -1)/bulk_size) * bulk_size;
+ }
+#endif /* #ifndef NO_BULK_LOGGING */
+ }
+
+ /* Scan forward from random position */
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ bit_test_result = cgn_clib_bitmap_check_if_all(my_pm->bm,
+ start_bit, bulk_size);
+ }
+ else
+#endif /* #ifndef NO_BULK_LOGGING */
+ bit_test_result = clib_bitmap_get_no_check(my_pm->bm, start_bit);
+
+ if (PREDICT_TRUE(bit_test_result)) {
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ /* Got the entire bulk range */
+ *nfv9_log_req = bit2port(start_bit);
+ return start_bit;
+ } else {
+#endif /* #ifndef NO_BULK_LOGGING */
+ /*
+ * For PORT_PAIR, first port has to be Even
+ * subsequent port <= end_port
+ * subsequent port should be unallocated
+ */
+ if ((start_bit & 0x1) ||
+ ((start_bit + 1) > end_port) ||
+ (clib_bitmap_get_no_check(my_pm->bm,
+ (start_bit + 1)) == 0)) {
+ goto notfound;
+ }
+ return (start_bit);
+#ifndef NO_BULK_LOGGING
+ }
+#endif /* #ifndef NO_BULK_LOGGING */
+ } /* if( free port found ) */
+
+notfound:
+#ifndef NO_BULK_LOGGING
+ if(BULK_ALLOC_SIZE_NONE != bulk_size) {
+ start_bit += bulk_size;
+ } else
+#endif /* #ifndef NO_BULK_LOGGING */
+ start_bit++;
+
+ }
+ return (BITS_PER_INST);
+}
+
+/*
+ * cnat_dynamic_port_alloc_rtsp
+ * public ipv4 address/port allocator for dynamic ports
+ *
+ * 200K users / 20M translations means vec_len(cnat_portmap) will be
+ * around 300.
+ *
+ */
+
+cnat_errno_t
+cnat_dynamic_port_alloc_rtsp (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ port_pair_t pair_type,
+ u16 start_range,
+ u16 end_range,
+ u32 *index,
+ u32 *o_ipv4_address,
+ u16 *o_port
+#ifndef NO_BULK_LOGGING
+ , bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req
+#endif
+ , u32 *rseed_ip
+ )
+{
+
+ u32 current_timestamp;
+ cnat_errno_t my_err = CNAT_NO_POOL_ANY;
+ cnat_portmap_v2_t *my_pm = 0;
+ u32 alloc_bit;
+
+ ASSERT(index);
+ ASSERT(o_ipv4_address);
+ ASSERT(o_port);
+
+ my_pm = cnat_dynamic_addr_alloc_from_pm(pm, atype, index, &my_err, 0,rseed_ip);
+
+ if (PREDICT_FALSE(my_pm == NULL)) {
+ return (my_err);
+ }
+
+#if DEBUG > 1
+ PLATFORM_DEBUG_PRINT("ALLOC_PORT_V2: My_Instance_Number %d: IP addr 0x%x, Inuse %d\n",
+ my_instance_number, my_pm->ipv4_address, my_pm->inuse);
+#endif
+
+ alloc_bit =
+ cnat_alloc_port_from_pm(start_range, end_range, my_pm, pair_type
+#ifndef NO_BULK_LOGGING
+ , bulk_size, nfv9_log_req
+#endif /* #ifndef NO_BULK_LOGGING */
+ );
+
+ if (alloc_bit < BITS_PER_INST) {
+ if (pair_type == PORT_PAIR) {
+ /* Accounting */
+ cgn_clib_bitmap_clear_no_check (my_pm->bm, alloc_bit);
+ cgn_clib_bitmap_clear_no_check (my_pm->bm, alloc_bit+1);
+ (my_pm->inuse) += 2;
+ } else {
+ /* Accounting */
+ cgn_clib_bitmap_clear_no_check (my_pm->bm, alloc_bit);
+ (my_pm->inuse)++;
+ }
+
+ *index = my_pm - pm;
+ *o_ipv4_address = my_pm->ipv4_address;
+
+ *o_port = bit2port(alloc_bit);;
+
+ return (CNAT_SUCCESS);
+ }
+
+ /* Completely out of ports */
+ current_timestamp = spp_trace_log_get_unix_time_in_seconds();
+ if (PREDICT_FALSE((current_timestamp - my_pm->last_sent_timestamp) >
+ 1000)) {
+ spp_printf(CNAT_NO_EXT_PORT_AVAILABLE, 0, NULL);
+ my_pm->last_sent_timestamp = current_timestamp;
+ }
+
+
+ /* Port allocation failure */
+ if (atype == PORT_ALLOC_DIRECTED) {
+ return (CNAT_NOT_FOUND_DIRECT);
+ } else {
+ return (CNAT_NOT_FOUND_ANY);
+ }
+}
+#else
+cnat_errno_t
+cnat_dynamic_port_alloc_rtsp (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ port_pair_t pair_type,
+ u16 start_range,
+ u16 end_range,
+ u32 *index,
+ u32 *o_ipv4_address,
+ u16 *o_port
+#ifndef NO_BULK_LOGGING
+ , bulk_alloc_size_t bulk_size,
+ int *nfv9_log_req
+#endif
+ , u32 *rseed_ip
+ )
+{
+ return (CNAT_NOT_FOUND_ANY);
+}
+#endif
+
+
+/*
+ * cnat_mapped_static_port_alloc_v2
+ * /
+ */
+cnat_errno_t
+cnat_mapped_static_port_alloc_v2 (
+ cnat_portmap_v2_t *pm,
+ port_alloc_t atype,
+ u32 *index,
+ u32 ipv4_address,
+ u16 port
+#ifndef NO_BULK_LOGGING
+ , int *nfv9_log_req,
+ bulk_alloc_size_t bulk_size
+#endif
+ , u16 ip_n_to_1
+ )
+{
+ int i;
+ u32 pm_len;
+ u16 bm_bit;
+ cnat_portmap_v2_t *my_pm = 0;
+ u32 my_index;
+
+ ASSERT(index);
+
+ /*
+ * Map the port to the bit in the pm bitmap structure.
+ * Note that we use ports from 1024..65535, so
+ * port number x corresponds to (x-1024) position in bitmap
+ */
+ bm_bit = port2bit(port);
+
+ pm_len = vec_len(pm);
+
+ switch(atype) {
+ case PORT_ALLOC_ANY:
+ if (PREDICT_FALSE(pm_len == 0)) {
+ return (CNAT_NO_POOL_ANY);
+ }
+
+ /*
+ * Find the pm that is allocated for this translated IP address
+ */
+ my_index = pm_len;
+
+ for (i = 0; i < pm_len; i++) {
+ my_pm = pm + i;
+ if (PREDICT_FALSE(my_pm->ipv4_address == ipv4_address)) {
+ my_index = i;
+ break;
+ }
+ }
+
+ if ((PREDICT_FALSE(my_index >= pm_len)) ||
+ ((PREDICT_FALSE(ip_n_to_1)) && (PREDICT_TRUE(my_pm->private_ip_users_count >= ip_n_to_1)))) {
+ return (CNAT_NO_POOL_ANY);
+ }
+
+ break;
+
+ case PORT_ALLOC_DIRECTED:
+ if (PREDICT_FALSE(*index > pm_len)) {
+ return (CNAT_INV_PORT_DIRECT);
+ }
+
+ my_index = *index;
+ my_pm = pm + my_index;
+ if (PREDICT_FALSE(my_pm->ipv4_address != ipv4_address)) {
+ if (PREDICT_FALSE(global_debug_flag && CNAT_DEBUG_GLOBAL_ALL)) {
+ PLATFORM_DEBUG_PRINT("Delete all main db entry for that particular in ipv4 address\n");
+ }
+ return (CNAT_INV_PORT_DIRECT);
+ }
+
+ break;
+
+ default:
+ msg_spp_err("bad allocation type in cnat_port_alloc");
+ return (CNAT_ERR_PARSER);
+ }
+
+
+ if (PREDICT_FALSE(my_pm == NULL)) {
+ return (CNAT_NO_POOL_ANY);
+ }
+
+ /*
+ * Check if the port is already allocated to some other mapping
+ */
+ if (PREDICT_FALSE(clib_bitmap_get_no_check (my_pm->bm, bm_bit) == 0)) {
+ return (CNAT_NO_POOL_ANY);
+ }
+
+#if DEBUG > 1
+ PLATFORM_DEBUG_PRINT("ALLOC_PORT_V2: My_Instance_Number %d: IP addr 0x%x, Inuse %d\n",
+ my_instance_number, my_pm->ipv4_address, my_pm->inuse);
+#endif
+
+ /*
+ * Indicate that the port is already allocated
+ */
+ cgn_clib_bitmap_clear_no_check (my_pm->bm, bm_bit);
+ (my_pm->inuse)++;
+
+ *index = my_index;
+
+ return (CNAT_SUCCESS);
+}
+
+void cnat_port_free_v2 (
+ cnat_portmap_v2_t *pm,
+ int index,
+ port_pair_t pair_type,
+ u16 base_port,
+ u16 static_port_range)
+{
+ cnat_portmap_v2_t *my_pm;
+ uword bit;
+
+ /* check for valid portmap */
+ if (PREDICT_FALSE(index > vec_len(pm))) {
+ spp_printf(CNAT_INVALID_INDEX_TO_FREE_PORT, 0, 0);
+ return;
+ }
+
+ my_pm = pm + index;
+ bit = port2bit(base_port);
+
+#if DEBUG > 0
+ if(clib_bitmap_get_no_check(my_pm->bm, bit))
+ ASSERT(clib_bitmap_get_no_check(my_pm->bm, bit) == 0);
+#endif
+
+ cgn_clib_bitmap_set_no_check(my_pm->bm, bit);
+
+ my_pm->inuse -= 1;
+ if(base_port >= static_port_range) {
+ /* Clear the full flag. we can have a new dynamic session now */
+ my_pm->dyn_full = 0;
+ }
+
+ return;
+}
+
+void cnat_portmap_dump_v2 (cnat_portmap_v2_t *pm, u16 print_limit)
+{
+ int i;
+ u32 inuse =0;
+
+ ASSERT(pm);
+
+ for (i = 0; i < BITS_PER_INST; i++) {
+ if (PREDICT_FALSE(clib_bitmap_get_no_check (pm->bm, i) == 0)) {
+ if (PREDICT_TRUE(inuse++ < print_limit))
+ PLATFORM_DEBUG_PRINT(" %d", bit2port(i));
+ }
+ }
+ if (PREDICT_FALSE(inuse >= print_limit)) {
+ PLATFORM_DEBUG_PRINT("%d printed, print limit is %d\n",
+ inuse, print_limit);
+ }
+ PLATFORM_DEBUG_PRINT("\n");
+}
+
+
+/*
+ * cnat_ports_init
+ */
+clib_error_t *cnat_ports_init(vlib_main_t *vm)
+{
+ cnat_ports_main_t *mp = &cnat_ports_main;
+
+ mp->vlib_main = vm;
+ mp->vnet_main = vnet_get_main();
+
+ /* suppress crypto-random port numbering */
+#ifdef SOON
+ if (spp_get_int_prop("no_crypto_random_ports") == 0)
+ crypto_random32(&seed);
+#endif
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION(cnat_ports_init);
+