Plugins: Clean up the plugin directory so that each plugin has its own
[vpp.git] / plugins / vcgn-plugin / vcgn / cnat_bulk_port.c
1 /* 
2  *------------------------------------------------------------------
3  * cnat_bulk_ports.c - wrappers for bulk port allocation
4  *
5  * Copyright (c) 2011-2013 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20
21 #include <vlib/vlib.h>
22 #include <vnet/vnet.h>
23 #include <vppinfra/error.h>
24 #include <vnet/buffer.h>
25 #include <vppinfra/vec.h>
26 #include <vppinfra/hash.h>
27 #include <vppinfra/pool.h>
28 #include <vppinfra/bitmap.h>
29
30 #include "cnat_db.h"
31 #include "cnat_config.h"
32 #include "cnat_global.h"
33 #include "cnat_logging.h"
34 #include "spp_timers.h"
35 #include "platform_common.h"
36 #include "cgn_bitmap.h"
37 #include "spp_platform_trace_log.h"
38 #include "cnat_ports.h"
39
40 #ifndef NO_BULK_LOGGING
41
42 #define PORT_TO_CACHE(y, z)   ((y)/(z))
43 /* The last bit (MSB) is used to indicate whether the cache entry is full */
44 #define CACHE_TO_PORT(x, z)   (((x)& 0x7FFF) * (z))
45 #define IS_CACHE_ENTRY_FULL(x) ((x) & 0x8000)
46 #define MARK_CACHE_ENTRY_AS_FULL(x) ((x) = ((x) | 0x8000))
47 #define UNMARK_CACHE_ENTRY_AS_FULL(x) ((x) = ((x) & 0x7FFF))
48 #define CACHE_ENTRY_WITHOUT_FULL_STAT(x) ((x) & 0x7FFF) 
49
50
51 #define NUM_BULK_CHECK  128 /* max number of previous chache to check.
52     * somewhat orbirtrary.. assume 64 as bulk size.. can handle up 
53     * to 128*64 ports allocated by a single subscriber */
54
55 /* #define DEBUG_BULK_PORT 1 */
56 /* #define DEBUG_BULK_PORT_DETAIL   1   */
57 #define HAVE_BULK_PORT_STATS    1 
58
59 #ifdef HAVE_BULK_PORT_STATS
60 static uword bulk_cache_hit_count;
61 static uword bulk_port_use_count;
62 static uword bulk_port_alloc_count;
63 static uword mapped_port_alloc_count;
64 #endif /* HAVE_BULK_PORT_STATS */
65
66 static u32 bulk_port_rand_across;
67
68 void show_bulk_port_allocation(u16 in_vrfid, u32 inside_ip)
69 {
70     cnat_db_key_bucket_t u_ki;
71     cnat_user_db_entry_t *udb;
72     int i;
73     u32 head;
74     cnat_main_db_entry_t *db = NULL;
75     i16 printed_so_far = 0; /* entries printed so far */
76     u16 prev_bulks[NUM_BULK_CHECK]; 
77     cnat_vrfmap_t *my_vrfmap = 0;
78     cnat_vrfmap_t *vrfmap = 0;
79     bulk_alloc_size_t bulk_size;
80
81     u_ki.k.k.vrf = in_vrfid;
82     u_ki.k.k.ipv4 = inside_ip; 
83     u_ki.k.k.port = 0;
84
85     PLATFORM_DEBUG_PRINT("Searching for user %x in invrf %d\n",
86         inside_ip, in_vrfid);
87     udb = cnat_user_db_lookup_entry(&u_ki);
88     if(!udb) {
89         PLATFORM_DEBUG_PRINT("No such user\n"); return; 
90     }
91
92     pool_foreach (vrfmap, cnat_map_by_vrf, ({
93         if(vrfmap->i_vrf == in_vrfid) {
94             my_vrfmap = vrfmap;
95             break;
96         }}));
97
98     if(!my_vrfmap) {
99         PLATFORM_DEBUG_PRINT("Vrf map not found\n");
100         return;
101     }
102     bulk_size = BULKSIZE_FROM_VRFMAP(my_vrfmap);
103
104     if(bulk_size == BULK_ALLOC_SIZE_NONE) {  
105         PLATFORM_DEBUG_PRINT("Bulk allocation not enabled\n"); 
106         return;
107     }
108     
109     PLATFORM_DEBUG_PRINT("\nBulk cache for subscriber 0x%x: ", inside_ip);
110     for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
111         PLATFORM_DEBUG_PRINT("%d , ", 
112             CACHE_TO_PORT(udb->bulk_port_range_cache[i], bulk_size));
113     }
114     PLATFORM_DEBUG_PRINT("\nNon cached bulk allocation for subscriber 0x%x:\n", 
115             inside_ip);
116     ASSERT(udb);
117     memset(prev_bulks, 0,sizeof(prev_bulks));
118
119     head = udb->translation_list_head_index;
120     if(PREDICT_FALSE(head == EMPTY)) {
121         return;
122     }
123     db = cnat_main_db + head;
124     while (1) { 
125         /* skip static ports - static ports may not belong to bulk pool*/
126         if(db->out2in_key.k.port < cnat_static_port_range) goto next_entry;
127
128         u16 bm_index = PORT_TO_CACHE(db->out2in_key.k.port, bulk_size);
129
130         /*Check if we have already tested this bulk */
131         for(i=0; i < printed_so_far; i++) {
132             if(prev_bulks[i] == bm_index) goto next_entry;
133         }
134
135         /*Check if this base port is already part of cache */
136         for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
137             if(CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i])
138                 == bm_index) {
139                 goto next_entry;
140             }
141         }
142         /* this is not in chache already */
143         PLATFORM_DEBUG_PRINT("%d ", CACHE_TO_PORT(bm_index, bulk_size));
144         if(printed_so_far <  NUM_BULK_CHECK) {
145             prev_bulks[printed_so_far] = bm_index;
146             printed_so_far++;
147         }
148
149 next_entry:
150         db = cnat_main_db + db->user_ports.next;
151         /*
152          * its a circular list, so if we have reached the head again
153          * all the entries for that user have been read
154          */
155         if (db == (cnat_main_db + head)) {
156             break;
157         }
158     } /* while loop for db entries */
159
160     PLATFORM_DEBUG_PRINT("\n");
161     return;
162 }
163
164 void show_bulk_port_stats()
165 {
166
167     cnat_vrfmap_t *my_vrfmap = 0;
168     PLATFORM_DEBUG_PRINT("Bulk size settings of each inside vrf ...\n");
169     pool_foreach (my_vrfmap, cnat_map_by_vrf, ({
170         PLATFORM_DEBUG_PRINT("vrf id %d, bulk size %d\n", my_vrfmap->i_vrf,
171                 BULKSIZE_FROM_VRFMAP(my_vrfmap));
172         }));
173
174 #ifdef HAVE_BULK_PORT_STATS
175     PLATFORM_DEBUG_PRINT("\nBulk port allocation, use and cache hit statistics\n");
176     PLATFORM_DEBUG_PRINT("Number of times bulk ports allocated %lld\n", 
177             bulk_port_alloc_count);
178     PLATFORM_DEBUG_PRINT("Number of times pre-allocated ports used %lld\n",
179             bulk_port_use_count);
180     PLATFORM_DEBUG_PRINT(
181         "Number of times pre-allocated bulk port found from cache %lld\n",
182         bulk_cache_hit_count);
183     PLATFORM_DEBUG_PRINT(
184         "Number of times mapped port (static) allocations made %lld\n", 
185          mapped_port_alloc_count);
186 #else 
187     PLATFORM_DEBUG_PRINT("\nNat44 bulk port statistics not turned on\n");
188 #endif /* HAVE_BULK_PORT_STATS */
189 }
190
191 void clear_bulk_port_stats()
192 {
193 #ifdef HAVE_BULK_PORT_STATS
194     bulk_port_alloc_count = 0;
195     bulk_port_use_count = 0;
196     bulk_cache_hit_count = 0;
197     mapped_port_alloc_count = 0;
198 #endif /* HAVE_BULK_PORT_STATS */
199     return;
200 }
201
202 void cnat_update_bulk_range_cache(cnat_user_db_entry_t *udb, u16 o_port, 
203         bulk_alloc_size_t bulk_size)
204 {
205     i16 i;
206     if(!udb) {
207 #ifdef DEBUG_BULK_PORT    
208         PLATFORM_DEBUG_PRINT("%s, null udb!\n", __func__);
209 #endif
210         return;        
211      }
212     if(BULK_ALLOC_SIZE_NONE == bulk_size) { /* no bulk logging */
213         return;
214     }
215
216     /* Take care of caching */
217     if(o_port & 0x1) {
218         o_port--;
219     }
220     if(PREDICT_FALSE(o_port <= 0)) {
221 #ifdef DEBUG_BULK_PORT
222         PLATFORM_DEBUG_PRINT("%s invalid port: %d\n", __func__, o_port);
223 #endif 
224         return;
225     }
226
227     /* First preference is for the cache entry's that are not used yet */
228     for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
229         if(PREDICT_FALSE(
230             udb->bulk_port_range_cache[i] == (i16)BULK_RANGE_INVALID)) {
231             udb->bulk_port_range_cache[i] = PORT_TO_CACHE(o_port, bulk_size); 
232             return;
233         }
234     }
235
236     /* Now check if any cache entry is full and if it can be replaced */
237     for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
238         if(PREDICT_FALSE(IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i]))) {
239             udb->bulk_port_range_cache[i] = PORT_TO_CACHE(o_port, bulk_size); 
240             return;
241         }
242     }
243
244     return;
245 }
246
247
248 void cnat_port_free_v2_bulk (
249             cnat_portmap_v2_t    *pm,
250             int                index,
251             port_pair_t        ptype,
252             u16                base_port,
253             cnat_user_db_entry_t *udb,
254             u16               static_port_range,
255             bulk_alloc_size_t    bulk_size,
256             int                *nfv9_log_req)
257 {
258     cnat_portmap_v2_t *my_pm;
259     i16 bm_index;
260     i16 i;
261     int unmark_full_status = 0;
262
263     *nfv9_log_req = BULK_ALLOC_NOT_ATTEMPTED;
264     
265     /* First free up the port */
266     cnat_port_free_v2(pm, index, ptype, base_port, static_port_range);
267     if(BULK_ALLOC_SIZE_NONE == bulk_size) /* no bulk logging */
268         return;
269     if(PREDICT_FALSE(!udb)) {
270 #ifdef DEBUG_BULK_PORT
271         PLATFORM_DEBUG_PRINT("%s udb is null\n", __func__);
272 #endif
273     }
274
275     if(PREDICT_FALSE(base_port < static_port_range)) {
276         return;
277     }
278     /* Now check if cache needs to be removed */
279     my_pm = pm + index;
280     base_port = base_port/bulk_size;
281     base_port = base_port * bulk_size; /*Align it to multiples of bulk_size */
282     if(PREDICT_TRUE(!cgn_clib_bitmap_check_if_all(
283         my_pm->bm, base_port, bulk_size))) {
284         *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
285         unmark_full_status = 1;
286         /* One or more ports are still in use */
287     } else {
288         *nfv9_log_req = base_port; /* logging required now. indicate base port*/
289     }
290     bm_index = PORT_TO_CACHE(base_port, bulk_size);
291     /* Now check if this is in the cache */
292     for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
293         if(PREDICT_FALSE(
294             CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i]))
295             == bm_index) {
296             if(unmark_full_status) {
297                 /* Unmark full stat.. if it was marked so..*/
298                 UNMARK_CACHE_ENTRY_AS_FULL(udb->bulk_port_range_cache[i]);
299             } else {
300                 udb->bulk_port_range_cache[i] = (i16)BULK_RANGE_INVALID; 
301 #ifdef DEBUG_BULK_PORT
302                 PLATFORM_DEBUG_PRINT(
303                     "Clearing cache for client 0x%x, bulk port %d\n", 
304                     my_pm->ipv4_address, base_port);
305 #endif
306             }
307             break; 
308         }
309     }
310     return;
311 }
312
313
314 /* Get suitable port from range */
315 static i16 get_suiting_port_pos_from_range(cnat_portmap_v2_t *my_pm, 
316     u16 bulk_start, i16 bulk_size, port_pair_t pair_type)
317 {
318     i16 num_pos = 0, num_bits, iterations;
319     uword bulk_ports;
320     i16 inc = 0;
321     i16 num_uwords = bulk_size/BITS(my_pm->bm[0]);
322
323     if(PREDICT_FALSE(!num_uwords)) {
324         iterations = 0;
325         num_bits = bulk_size;
326         bulk_size = 0;
327     } else {
328         bulk_port_rand_across = randq1(bulk_port_rand_across);    
329         iterations = bulk_port_rand_across % num_uwords;
330         num_bits = BITS(my_pm->bm[0]);
331     }
332
333     do {
334         bulk_ports = cgn_clib_bitmap_get_bits(my_pm->bm, 
335             (bulk_start + iterations * BITS(my_pm->bm[0])), num_bits);
336 #ifdef DEBUG_BULK_PORT_DETAIL
337         PLATFORM_DEBUG_PRINT("%s %d, bulk start %d, num_bits %d, ports %lld \n",
338             __func__, __LINE__, bulk_start, num_bits, bulk_ports);
339 #endif /* DEBUG_BULK_PORT_DETAIL */
340         if(PREDICT_FALSE(!bulk_ports)) goto next_uword;
341         if(PREDICT_TRUE((pair_type == PORT_SINGLE)
342             || (pair_type == PORT_PAIR))) {
343             num_pos =0;
344             inc = 1;
345         } else if(pair_type == PORT_S_ODD) {
346             num_pos = 1;
347             inc = 2;
348         } else if(pair_type == PORT_S_EVEN) {
349             num_pos =0;
350             inc = 2;
351         }    
352
353         for(; num_pos < num_bits; num_pos = num_pos + inc) {
354             if(!((bulk_ports >> num_pos) & 1))
355                 continue; /* In use */
356             /* Check if the available port meets our
357              * criteria such as add, even, pair etc */
358             else if(PREDICT_FALSE(
359                 (pair_type == PORT_PAIR) && ((num_pos & 0x1) ||
360                 (!((bulk_ports >> (num_pos + 1)) & 1)))))
361                     continue;
362             else break; /* Found one that meets the criteria */
363         }
364         if(num_pos < num_bits) 
365             return (num_pos + iterations * BITS(my_pm->bm[0]));
366 next_uword:        
367         num_bits = BITS(my_pm->bm[0]);
368         bulk_size -= BITS(my_pm->bm[0]);
369         iterations++;
370         if(iterations >= num_uwords) iterations = 0;
371     } while (bulk_size >  0);
372
373     return -2; /* nothing found */
374 }
375
376 static cnat_errno_t try_bulk_port_from_non_cache(
377             cnat_user_db_entry_t *udb,
378             cnat_portmap_v2_t *my_pm,
379             port_pair_t pair_type,
380             bulk_alloc_size_t bulk_size,
381             u16 *port_available,
382             u16  static_port_range
383     )
384 {
385     /****
386     1. user should have existing translations.. otherwise, we wouldn't get here.
387     2. For each, get the outside port. get the base port. 
388        check if it is already in cache
389     3. if not, we stand chance.
390     4. Check for availability from this non cached pool.
391     5. if found, repalce this with one of the cache that is invalid or full??
392     6. if we are replacing the cache.. it has to be governed by user 
393         preference on prefer oldest pool or prefer newest pool
394     ********/
395     u32 head;
396     cnat_main_db_entry_t *db = NULL;
397     u16 bulk_start; /* start point in 64 bitmap array to search for port */
398     i16 port_pos; /* indicates the position of available port in bulk */
399     i16 i; /* just a counter */
400     i16 attempts_so_far = 0; /* (futile-;) attemps so far..*/
401     u16 prev_bulks[NUM_BULK_CHECK]; 
402     ASSERT(udb);
403     memset(prev_bulks, 0,sizeof(prev_bulks));
404
405     head = udb->translation_list_head_index;
406     if(PREDICT_FALSE(head == EMPTY)) return CNAT_NO_PRE_ALLOCATED_BULK_PORTS;
407
408     db = cnat_main_db + head;
409     while (1) { //what should be the limit??
410
411         /* skip static ports - static ports may not belong to bulk pool*/
412         if(db->out2in_key.k.port < static_port_range) goto next_entry;
413
414         u16 bm_index = PORT_TO_CACHE(db->out2in_key.k.port, bulk_size);
415
416         /*Check if we have already tested this bulk */
417         for(i=0; i < attempts_so_far; i++) {
418             if(prev_bulks[i] == bm_index) { 
419                 goto next_entry;
420             }
421         }
422
423         /*Check if this base port is already part of cache */
424         for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
425             if(CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i])
426                 == bm_index)
427                 goto next_entry;
428         }
429
430         /* this is not in chache already */
431         bulk_start = CACHE_TO_PORT(bm_index, bulk_size);
432         port_pos = get_suiting_port_pos_from_range(my_pm, 
433                 bulk_start, bulk_size, pair_type);
434
435         if(port_pos < 0) { /* no port available in this range */
436             /* Mark this bulk so that we don't have to try this again */
437             if(attempts_so_far <  NUM_BULK_CHECK) {
438                 prev_bulks[attempts_so_far] = bm_index;
439                 attempts_so_far++;
440             }
441             goto next_entry;
442         }
443
444         /* Got one...Get the port number */
445         *port_available = bulk_start + port_pos;
446
447         /* Check to see if we shoud replace one of the cache */
448         for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
449             if(PREDICT_FALSE((udb->bulk_port_range_cache[i] 
450                 == (i16)BULK_RANGE_INVALID) || (
451                 IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i])))) {
452                 udb->bulk_port_range_cache[i] = bm_index;
453                 return CNAT_SUCCESS;
454             }
455         }
456         /* Check to replace an existing (in use) entry */
457         /* TODO: enforce policy */
458         /* order of looping should depend on policy */
459
460         return CNAT_SUCCESS;
461
462 next_entry:
463         db = cnat_main_db + db->user_ports.next;
464         /*
465          * its a circular list, so if we have reached the head again
466          * all the entries for that user have been read
467          */
468         if (db == (cnat_main_db + head)) {
469             break;
470         }
471     } /* while loop for db entries */
472     /* no ports available from pre allocated bulk pool */
473     return CNAT_NO_PORT_FROM_BULK;
474 }
475
476 cnat_errno_t
477 cnat_dynamic_port_alloc_v2_bulk (
478                  cnat_portmap_v2_t    *pm,
479                  port_alloc_t         atype,
480                  port_pair_t          pair_type,
481                  u32                  *index,
482                  u32                  *o_ipv4_address,
483                  u16                  *o_port,
484                  u16                  static_port_range,
485                  cnat_user_db_entry_t *udb,
486                  bulk_alloc_size_t    bulk_size,
487                  int                  *nfv9_log_req,
488                  u16                  ip_n_to_1,
489                  u32                  *rseed_ip
490                  )
491 {
492
493     cnat_errno_t rv;
494     u16 port_available = 0;
495     i16  i;
496     cnat_portmap_v2_t *my_pm;
497
498     if((BULK_ALLOC_SIZE_NONE != bulk_size) /* bulk logging enabled */
499         &&  (udb)) {  /* This user does have translations already */
500         u16 bulk_start;
501         i16 port_pos;
502
503         my_pm = pm + *index;
504         /* We have a case to check if bulk allocated ports can be used */
505         /* TODO: order of looping to be based on policy
506          * like prefer older or prefer newer ?? 
507          * For now, start with most recent cache entry
508          * so that we stand a better chance of 
509          * finding a port
510          */
511         for(i= 0; i < BULK_RANGE_CACHE_SIZE; i++) {
512             if(PREDICT_TRUE((udb->bulk_port_range_cache[i] == 
513                 (i16)BULK_RANGE_INVALID) || 
514                 IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i]))) {
515                 continue; /* This range is not initialized yet or it is full */
516             }
517             bulk_start = CACHE_TO_PORT(udb->bulk_port_range_cache[i], 
518                     bulk_size);
519             port_pos = get_suiting_port_pos_from_range(my_pm, 
520                 bulk_start, bulk_size, pair_type);
521             if(PREDICT_FALSE(port_pos < 0)) { 
522                 /* Mark this cache entry as full so that we do not 
523                  * waste time on this entry again */
524                 MARK_CACHE_ENTRY_AS_FULL(udb->bulk_port_range_cache[i]);
525 #ifdef DEBUG_BULK_PORT 
526                 PLATFORM_DEBUG_PRINT("Marked bulk cache entry %d as full for %x \n",
527                 i, my_pm->ipv4_address);
528 #endif /*  #ifdef DEBUG_BULK_PORT */
529                 continue;
530             }
531             /* Get the port number */
532             port_available = bulk_start+ port_pos;
533 #ifdef DEBUG_BULK_PORT
534             PLATFORM_DEBUG_PRINT(
535                 "Found port from cache : IP 0x%x, port %d %d iterations\n",
536                 my_pm->ipv4_address, port_available, i)
537 #endif 
538 #ifdef HAVE_BULK_PORT_STATS
539             bulk_cache_hit_count++;
540 #endif /* HAVE_BULK_PORT_STATS */
541             break;
542         } /* end of for loop for cache check */
543         /* If we have not found a port yet, check if we can have 
544          *   pre allocated bulk port from non-cache */
545         if(PREDICT_FALSE(i == BULK_RANGE_CACHE_SIZE)) { 
546             if( try_bulk_port_from_non_cache(udb, my_pm, pair_type,
547                 bulk_size, &port_available, 
548                 static_port_range) != CNAT_SUCCESS ) {
549                 goto ALLCOATE_NEW_BULK;
550             }
551 #ifdef DEBUG_BULK_PORT
552             PLATFORM_DEBUG_PRINT("Found port from non-cache : IP 0x%x, port %d\n",
553                 my_pm->ipv4_address, port_available);
554 #endif 
555         }
556         /* Assign the port, mark it as in use */
557         cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available);
558         (my_pm->inuse)++;
559         if(PREDICT_FALSE(pair_type == PORT_PAIR)) {/* Mark the next one too */
560             cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available + 1);
561             (my_pm->inuse)++;
562         }
563         *o_ipv4_address = my_pm->ipv4_address;
564         *o_port = port_available;
565         *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
566 #ifdef HAVE_BULK_PORT_STATS
567         bulk_port_use_count++;
568 #endif /* HAVE_BULK_PORT_STATS */
569         return (CNAT_SUCCESS);
570     } 
571 ALLCOATE_NEW_BULK:
572 #ifdef DEBUG_BULK_PORT
573     if(BULK_ALLOC_SIZE_NONE != bulk_size) {
574         PLATFORM_DEBUG_PRINT(
575             "No port available from bulk cache, bulk size %d\n", bulk_size);
576     }
577 #endif 
578     /* For whatever reason, we have not got a port yet */
579     rv = cnat_dynamic_port_alloc_v2(pm, atype, pair_type, index,
580             o_ipv4_address, o_port, static_port_range, bulk_size, nfv9_log_req,
581             ip_n_to_1, rseed_ip);
582     if (PREDICT_FALSE(rv != CNAT_SUCCESS)) {
583         return rv;
584     }
585     /* Take care of caching */
586     if(PREDICT_FALSE(udb != NULL)) { 
587         /* Predict false because, we usually allocate for new users */
588         cnat_update_bulk_range_cache(udb, *o_port, bulk_size);
589     }
590 #ifdef HAVE_BULK_PORT_STATS
591         bulk_port_alloc_count++;
592 #endif /* HAVE_BULK_PORT_STATS */
593     return (CNAT_SUCCESS);
594 }
595
596
597 cnat_errno_t
598 cnat_static_port_alloc_v2_bulk (
599                  cnat_portmap_v2_t    *pm,
600                  port_alloc_t         atype,
601                  port_pair_t          pair_type,
602                  u32                  i_ipv4_address,
603                  u16                  i_port,
604                  u32                  *index,
605                  u32                  *o_ipv4_address,
606                  u16                  *o_port,
607                  u16                  static_port_range,
608                  cnat_user_db_entry_t *udb,
609                  bulk_alloc_size_t    bulk_size,
610                  int                  *nfv9_log_req,
611                  u16                  ip_n_to_1
612                  )
613 {
614
615     /***
616      * Requirements - 
617      * 1. If the port allocated is below dyn start, it should be individual 
618      * port (not bulk)
619      * 2.  If NOT, it should be bulk allocated
620      * 3.  Try and keep the inside port same as outside port in both the 
621      * cases (best effort)
622
623      * Algorithm
624      * 1.  Check if it is below stat port start or user is new or bulk is 
625      * disabled. If yes, call existing function
626      * 2.  If not, see if we can pick from bulk and yet try to keep the port 
627      * same - difficult thing - check if the port is free - then check if the 
628      * entire bulk is free - if not check if bulk is owned by the user already.
629      * If all of these fail, call existing function to allocate a new bulk
630      * 3.  Update cache, etc return log requirements
631      *****/
632
633     cnat_errno_t rv;
634     i16  i;
635     u32 head;
636     cnat_portmap_v2_t *my_pm;
637     uword bit_test_result, start_bit;
638     cnat_main_db_entry_t *db = NULL;
639
640     if((BULK_ALLOC_SIZE_NONE != bulk_size) /* bulk logging enabled */
641         &&  (udb) && /* This user does have translations already */
642         i_port >= static_port_range ) { /* It is outside stat port range*/
643
644         my_pm = pm + *index;
645         /* We have a case to check if bulk allocated ports can be used */
646
647         /* First check if the required port is available. */
648         if(PREDICT_FALSE(clib_bitmap_get_no_check(my_pm->bm, i_port) == 0)) {
649             goto ALLOCATE_NEW_BULK_STATIC;
650         }
651
652         /* Port is free.. check if the bulk is also free */
653         start_bit= ((i_port/bulk_size) * bulk_size);
654         bit_test_result = cgn_clib_bitmap_check_if_all(my_pm->bm,
655                             start_bit, bulk_size);
656         if(PREDICT_TRUE(bit_test_result)) { /* bulk is available, grab it */
657             goto ALLOCATE_NEW_BULK_STATIC;
658         }
659
660         /* else, bulk is taken by someone. check if it is me */
661         /* Check if we own the bulk by any chance */
662         for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
663             if(udb->bulk_port_range_cache[i] == start_bit) break;
664         }
665         if(i == BULK_RANGE_CACHE_SIZE) { /* no luck with cache */        
666             head = udb->translation_list_head_index;
667             if(PREDICT_FALSE(head == EMPTY)) 
668                 goto ALLOCATE_NEW_BULK_STATIC;
669             db = cnat_main_db + head;
670             i = 0;
671             while(1) {
672                 if((db->out2in_key.k.port/bulk_size) * bulk_size ==                                             start_bit) {    
673                         i = 1; /* Just to indicate it is found */
674                         break;
675                 }
676                 db = cnat_main_db + db->user_ports.next;
677                 /*
678                  * its a circular list, so if we have reached the head again
679                  * all the entries for that user have been read
680                  */
681                 if (db == (cnat_main_db + head)) break;
682             } /* while loop for db entries */
683             if(!i) {
684                 goto ALLOCATE_NEW_BULK_STATIC;    
685             }
686         }        
687         /* Assign the port, mark it as in use */
688         cgn_clib_bitmap_clear_no_check(my_pm->bm, i_port);
689         (my_pm->inuse)++;
690         *o_ipv4_address = my_pm->ipv4_address;
691         *o_port = i_port;
692         *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
693 #ifdef HAVE_BULK_PORT_STATS
694         bulk_port_use_count++;
695 #endif /* HAVE_BULK_PORT_STATS */
696
697 #ifdef DEBUG_BULK_PORT
698         PLATFORM_DEBUG_PRINT("%s, %d, found stat port from bulk: %x, %d\n", 
699             __func__, 
700             __LINE__, *o_ipv4_address, *o_port);
701 #endif /* DEBUG_BULK_PORT */
702         return (CNAT_SUCCESS);
703     } 
704
705 ALLOCATE_NEW_BULK_STATIC:
706 #ifdef DEBUG_BULK_PORT
707     PLATFORM_DEBUG_PRINT("%s No port available from bulk cache, bulk size %d\n", 
708             __func__,bulk_size);
709 #endif 
710     /* For whatever reason, we have not got a port yet */
711     rv = cnat_static_port_alloc_v2(pm, atype, pair_type, i_ipv4_address,
712             i_port, index, o_ipv4_address, o_port, static_port_range,
713             bulk_size, nfv9_log_req,ip_n_to_1);
714     if (PREDICT_FALSE(rv != CNAT_SUCCESS)) {
715         return rv;
716     }
717     /* Take care of caching only if it was a bulk alloc */
718     if(PREDICT_FALSE(udb && (BULK_ALLOC_NOT_ATTEMPTED != *nfv9_log_req))) {
719         cnat_update_bulk_range_cache(udb, *o_port, bulk_size);
720     }
721 #ifdef HAVE_BULK_PORT_STATS
722     bulk_port_alloc_count++;
723 #endif /* HAVE_BULK_PORT_STATS */
724     return (CNAT_SUCCESS);
725
726 }
727
728 cnat_errno_t
729 cnat_mapped_static_port_alloc_v2_bulk (
730             cnat_portmap_v2_t    *pm,
731             port_alloc_t         atype,
732             u32                  *index,
733             u32                   ipv4_address,
734             u16                   port,
735             cnat_user_db_entry_t *udb,
736             bulk_alloc_size_t    bulk_size,
737             int                  *nfv9_log_req,
738             u16                  ip_n_to_1
739             )
740 {
741     /* Requirements : 
742      * 1. Check if bulk allocation is required.
743      * 2. Call cnat_mapped_static_port_alloc_v2 to allocate
744      * 3. Decide if alloc has to be cached
745      * 4. Update nfv9_log_req
746      */
747     cnat_errno_t rv; 
748     rv = cnat_mapped_static_port_alloc_v2 (pm,
749         atype, index, ipv4_address, port, nfv9_log_req, bulk_size, ip_n_to_1);
750     if (PREDICT_FALSE(rv != CNAT_SUCCESS)) {
751         return rv;
752     }
753     /* Take care of caching only if it was a bulk alloc */
754     if(PREDICT_FALSE(udb && (BULK_ALLOC_NOT_ATTEMPTED != *nfv9_log_req))) {
755         int i;
756         port = port*bulk_size;
757         port = port/bulk_size; /* align it to bulk size boundary */
758         for(i=0; i < BULK_RANGE_CACHE_SIZE; i++) {
759             if(CACHE_ENTRY_WITHOUT_FULL_STAT(udb->bulk_port_range_cache[i])
760                 == PORT_TO_CACHE(port, bulk_size))
761                 break; 
762         }
763         if( i == BULK_RANGE_CACHE_SIZE) { /* else, it is alredy in cache */
764             cnat_update_bulk_range_cache(udb, port, bulk_size);
765         }
766     }
767 #ifdef HAVE_BULK_PORT_STATS
768     mapped_port_alloc_count++;
769 #endif /* HAVE_BULK_PORT_STATS */
770     return (CNAT_SUCCESS);
771 }
772
773
774 cnat_errno_t
775 cnat_dynamic_port_alloc_rtsp_bulk (
776                  cnat_portmap_v2_t    *pm,
777                  port_alloc_t         atype,
778                  port_pair_t          pair_type,
779                  u16                  i_port,
780                  u32                  *index,
781                  u32                  *o_ipv4_address,
782                  u16                  *o_port,
783                  u16                  static_port_range,
784                  cnat_user_db_entry_t *udb,
785                  bulk_alloc_size_t    bulk_size,
786                  int                  *nfv9_log_req,
787                  u32                  *rseed_ip)
788 {
789
790     /***
791      * Algorithm
792      * 1. Compute the range of ports required based on the number of digits
793      * in the port request made by the client.
794      * 2. Check if bulk logging is enabled. If not, use the existing method.
795      * 3. Check if there are 2 adjacent ports available that meet the above
796      * criteria in any of the bulk allocations made already.
797      * 4. If yes, mark them in use and return.
798      * 5. If not allocate a new bulk and pick 2 ports in it
799      ***/
800
801     i16  i;
802     cnat_portmap_v2_t *my_pm = 0;
803     u32 start_port1, end_port1, start_port2, end_port2;
804     int range_loop;
805     u16 bulk_start;
806     i16 port_pos;
807     u16 port_available = 0;
808     
809     ASSERT(index);
810     ASSERT(o_ipv4_address);
811     ASSERT(o_port);
812     
813     /*
814      * Check if the port is 4 digit or 5 digit.   I am assuming we are
815      * not getting 3 (or 2 or 1) digit ports, which we cannot anyway
816      * allocate same sized outside ports - as outside ports start from 1024
817      *
818      * Static Port has its own reserved range.  Ensure that the range is
819      * such that atleast few 4 digit ports are available for RTSP.  If
820      * not it does not make sense to do special allocation for RTSP.
821      */
822     if (PREDICT_TRUE(static_port_range < MIN_STATIC_PORT_RANGE_FOR_RTSP)) {
823         /*
824          * 4 digit port or less
825          */
826         if (i_port <= 9999) {
827             start_port1  = static_port_range;
828             end_port1    = 9999;
829             
830             start_port2 = 10000;
831             end_port2   = PORTS_PER_ADDR - 1;
832         } else { /* 5 digit port */
833             start_port1 = 10000;
834             end_port1   = PORTS_PER_ADDR - 1;
835             
836             start_port2 = static_port_range;
837             end_port2   = 9999;
838         }
839     } else { /* Static port range is too big */
840         start_port1 = static_port_range;
841         end_port1   = PORTS_PER_ADDR - 1;
842         
843         /*
844          * PORTS_PER_ADDR is just a placeholder for
845          * INVALID_PORT, valid ports are b/w 1 and PORTS_PER_ADDR
846          */
847         start_port2 = PORTS_PER_ADDR;
848         end_port2   = PORTS_PER_ADDR;
849     }
850
851
852     if(PREDICT_TRUE(udb != NULL)) {
853         my_pm = pm + *index;
854     }
855
856     /* Now check if this user already owns a bulk range that is 
857      * within start range 1 
858      */
859
860     u32 start_range = start_port1;
861     u32 end_range = end_port1;
862     for(range_loop = 0; range_loop < 2; range_loop++) {
863         if((BULK_ALLOC_SIZE_NONE == bulk_size) || (!udb)) {
864             goto ALLOCATE_NEW_RTSP_PORTS;
865         }
866         for(i= 0; i < BULK_RANGE_CACHE_SIZE; i++) {
867             if(PREDICT_TRUE((udb->bulk_port_range_cache[i] == 
868                 (i16)BULK_RANGE_INVALID) || 
869                 IS_CACHE_ENTRY_FULL(udb->bulk_port_range_cache[i]))) {
870                 continue; /* This range is not initialized yet or it is full */ 
871             }
872
873             bulk_start = CACHE_TO_PORT(udb->bulk_port_range_cache[i], 
874                         bulk_size);
875             if(bulk_start < start_port1 || bulk_start >= end_port1) {
876                 continue; /* Not in the range */
877             }
878
879             port_pos = get_suiting_port_pos_from_range(my_pm, 
880                 bulk_start, bulk_size, pair_type);
881             if(PREDICT_FALSE(port_pos < 0)) { 
882                 /*  Not Marking  this cache entry as full as it failed 
883                  * for pair type. It might have individual entries
884                  */
885                 continue;
886             }
887             /* Get the port number */
888             port_available = bulk_start+ port_pos;
889 #ifdef DEBUG_BULK_PORT
890             PLATFORM_DEBUG_PRINT(
891                 "Found port from cache : IP 0x%x, port %d %d iterations\n",
892                 my_pm->ipv4_address, port_available, i)
893 #endif 
894 #ifdef HAVE_BULK_PORT_STATS
895             bulk_cache_hit_count += 2;
896 #endif /* HAVE_BULK_PORT_STATS */
897             break;
898         } /* end of for loop for cache check */
899
900         if(PREDICT_FALSE(i == BULK_RANGE_CACHE_SIZE)) { 
901             /* we have not found a port yet, but to do not want to try 
902              * non-cache bulks.. because, it is a very low probability and 
903              * do not want to tweak that code for this special case
904              * The impact of non checking the non-cache is, we give this 
905              * user few  extra ports .. which is OK
906              */
907             goto ALLOCATE_NEW_RTSP_PORTS;
908         }
909 #ifdef DEBUG_BULK_PORT
910         PLATFORM_DEBUG_PRINT("RTSP: Found port from non-cache : IP 0x%x, port %d\n",
911                 my_pm->ipv4_address, port_available);
912 #endif 
913
914         /* Assign the port, mark it as in use */
915         cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available);
916         (my_pm->inuse)++;
917         cgn_clib_bitmap_clear_no_check(my_pm->bm, port_available + 1);
918         (my_pm->inuse)++;
919
920         *o_ipv4_address = my_pm->ipv4_address;
921         *o_port = port_available;
922         *nfv9_log_req = CACHE_ALLOC_NO_LOG_REQUIRED;
923 #ifdef HAVE_BULK_PORT_STATS
924         bulk_port_use_count += 2;
925 #endif /* HAVE_BULK_PORT_STATS */
926         return (CNAT_SUCCESS);
927
928 ALLOCATE_NEW_RTSP_PORTS:
929         /* No luck. Let's try allocating new bulk.. */
930         if(PREDICT_TRUE(CNAT_SUCCESS == cnat_dynamic_port_alloc_rtsp 
931             (pm, atype, pair_type,
932                 start_range, end_range,index, o_ipv4_address, 
933                 o_port, bulk_size, nfv9_log_req,rseed_ip))) {
934             if(PREDICT_FALSE(udb && 
935                 (BULK_ALLOC_NOT_ATTEMPTED != *nfv9_log_req))) {
936                 cnat_update_bulk_range_cache(udb, *o_port, bulk_size);
937             }
938 #ifdef HAVE_BULK_PORT_STATS
939             bulk_port_alloc_count++;
940 #endif /* HAVE_BULK_PORT_STATS */
941             return CNAT_SUCCESS; 
942         }
943
944         /* Could not allocate in range 1.. so move to range 2. */
945         start_range = start_port2;
946         end_range = end_port2;
947
948     }
949
950     return (CNAT_NOT_FOUND_DIRECT); /* if we are here, we could not get any ports */
951
952 }
953
954 #else /* Dummy definitions */
955 void show_bulk_port_stats()
956 {
957     PLATFORM_DEBUG_PRINT("\nBulk logging feature not included\n");
958 }
959
960  void clear_bulk_port_stats()
961 {
962     PLATFORM_DEBUG_PRINT("\nBulk logging feature not included\n");
963 }
964 #endif /* NO_BULK_LOGGING */