/* *------------------------------------------------------------------ * 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 #include #include #include #include #include #include #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) || (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);