FD.io VPP  v16.09
Vector Packet Processing
cnat_bulk_port.c
Go to the documentation of this file.
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
64 #endif /* HAVE_BULK_PORT_STATS */
65 
67 
68 void show_bulk_port_allocation(u16 in_vrfid, u32 inside_ip)
69 {
72  int i;
73  u32 head;
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++) {
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 
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",
178  PLATFORM_DEBUG_PRINT("Number of times pre-allocated ports used %lld\n",
181  "Number of times pre-allocated bulk port found from cache %lld\n",
184  "Number of times mapped port (static) allocations made %lld\n",
186 #else
187  PLATFORM_DEBUG_PRINT("\nNat44 bulk port statistics not turned on\n");
188 #endif /* HAVE_BULK_PORT_STATS */
189 }
190 
192 {
193 #ifdef HAVE_BULK_PORT_STATS
198 #endif /* HAVE_BULK_PORT_STATS */
199  return;
200 }
201 
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(
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++) {
239  udb->bulk_port_range_cache[i] = PORT_TO_CACHE(o_port, bulk_size);
240  return;
241  }
242  }
243 
244  return;
245 }
246 
247 
249  cnat_portmap_v2_t *pm,
250  int index,
251  port_pair_t ptype,
252  u16 base_port,
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 */
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(
295  == bm_index) {
296  if(unmark_full_status) {
297  /* Unmark full stat.. if it was marked so..*/
299  } else {
301 #ifdef DEBUG_BULK_PORT
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 */
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 {
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 
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;
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;
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++) {
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++) {
450  == (i16)BULK_RANGE_INVALID) || (
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 
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,
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) ||
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 */
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
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
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
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) {
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
592 #endif /* HAVE_BULK_PORT_STATS */
593  return (CNAT_SUCCESS);
594 }
595 
596 
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,
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;
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
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
723 #endif /* HAVE_BULK_PORT_STATS */
724  return (CNAT_SUCCESS);
725 
726 }
727 
730  cnat_portmap_v2_t *pm,
731  port_alloc_t atype,
732  u32 *index,
733  u32 ipv4_address,
734  u16 port,
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;
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++) {
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
769 #endif /* HAVE_BULK_PORT_STATS */
770  return (CNAT_SUCCESS);
771 }
772 
773 
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,
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) ||
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
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
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.. */
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
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 */
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 */
cnat_main_db_entry_t * cnat_main_db
Definition: cnat_db_v2.c:201
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:343
#define PREDICT_TRUE(x)
Definition: clib.h:98
static u32 randq1(u32 prev)
Definition: cnat_ports.h:111
Fixed length block allocator.
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)
#define NULL
Definition: clib.h:55
static uword cgn_clib_bitmap_get_bits(uword *ai, u16 start, unsigned char num_bits)
Definition: cgn_bitmap.h:97
u16 cnat_static_port_range
Definition: cnat_config.c:53
Definition: cnat_db.h:153
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)
cnat_errno_t
Definition: cnat_cli.h:26
static uword clib_bitmap_get_no_check(uword *ai, uword i)
Gets the ith bit value from a bitmap Does not sanity-check the bit position.
Definition: bitmap.h:212
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)
void show_bulk_port_stats()
#define PLATFORM_DEBUG_PRINT(...)
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:348
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, bulk_alloc_size_t bulk_size, int *nfv9_log_req, u16 ip_n_to_1)
Definition: cnat_ports.c:135
void cnat_port_free_v2(cnat_portmap_v2_t *pm, int index, port_pair_t pair_type, u16 base_port, u16 static_port_range)
Definition: cnat_ports.c:1037
#define BULK_RANGE_CACHE_SIZE
#define BULK_RANGE_INVALID
cnat_key_t k
Definition: cnat_db.h:113
#define CACHE_TO_PORT(x, z)
#define PORTS_PER_ADDR
Definition: cnat_ports.h:26
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)
#define MIN_STATIC_PORT_RANGE_FOR_RTSP
Definition: cnat_ports.h:36
#define MARK_CACHE_ENTRY_AS_FULL(x)
#define CACHE_ENTRY_WITHOUT_FULL_STAT(x)
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)
void cnat_update_bulk_range_cache(cnat_user_db_entry_t *udb, u16 o_port, bulk_alloc_size_t bulk_size)
#define PREDICT_FALSE(x)
Definition: clib.h:97
#define CACHE_ALLOC_NO_LOG_REQUIRED
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)
#define IS_CACHE_ENTRY_FULL(x)
u32 translation_list_head_index
Definition: cnat_db.h:303
#define PORT_TO_CACHE(y, z)
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, bulk_alloc_size_t bulk_size, int *nfv9_log_req, u16 ip_n_to_1, u32 *rseed_ip)
Definition: cnat_ports.c:554
#define UNMARK_CACHE_ENTRY_AS_FULL(x)
static uword cgn_clib_bitmap_clear_no_check(uword *a, uword i)
Definition: cgn_bitmap.h:81
cnat_db_key_t k
Definition: cnat_db.h:108
void show_bulk_port_allocation(u16 in_vrfid, u32 inside_ip)
#define BULKSIZE_FROM_VRFMAP(vrfmap)
#define BULK_ALLOC_NOT_ATTEMPTED
Definition: cnat_db.h:285
static uword bulk_cache_hit_count
#define ASSERT(truth)
static u32 bulk_port_rand_across
unsigned int u32
Definition: types.h:88
index_dlist_t user_ports
Definition: cnat_db.h:204
port_alloc_t
Definition: cnat_ports.h:96
static uword bulk_port_alloc_count
cnat_key_t out2in_key
Definition: cnat_db.h:198
Bitmaps built as vectors of machine words.
port_pair_t
Definition: cnat_ports.h:82
i16 bulk_port_range_cache[BULK_RANGE_CACHE_SIZE]
Definition: cnat_db.h:325
u64 uword
Definition: types.h:112
static uword bulk_port_use_count
unsigned short u16
Definition: types.h:57
cnat_vrfmap_t * cnat_map_by_vrf
Definition: cnat_db_v2.c:218
uword bm[(BITS_PER_INST+BITS(uword)-1)/BITS(uword)]
Definition: cnat_ports.h:75
cnat_user_db_entry_t * cnat_user_db_lookup_entry(cnat_db_key_bucket_t *uki)
Definition: cnat_db_v2.c:603
static uword mapped_port_alloc_count
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)
bulk_alloc_size_t
#define NUM_BULK_CHECK
short i16
Definition: types.h:46
#define EMPTY
Definition: index_list.h:24
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, int *nfv9_log_req, bulk_alloc_size_t bulk_size, u16 ip_n_to_1)
Definition: cnat_ports.c:932
static uword cgn_clib_bitmap_check_if_all(uword *ai, u16 start, i16 num_bits)
Definition: cgn_bitmap.h:110
#define BITS(x)
Definition: clib.h:58
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, bulk_alloc_size_t bulk_size, int *nfv9_log_req, u32 *rseed_ip)
Definition: cnat_ports.c:906
void clear_bulk_port_stats()
CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers".