FD.io VPP  v16.06
Vector Packet Processing
cnat_db_scanner.c
Go to the documentation of this file.
1 /*
2  *---------------------------------------------------------------------------
3  * cnat_db_scanner.c - cnat_db_scanner dispatch function and initialization
4  *
5  * Copyright (c) 2009-2014 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 #include <vlib/vlib.h>
21 #include <vnet/vnet.h>
22 #include <vppinfra/error.h>
23 #include <vnet/buffer.h>
24 #include <vppinfra/string.h>
25 #include <vppinfra/random.h>
26 #include <vppinfra/fifo.h>
27 #include <vppinfra/hash.h>
28 #include <vppinfra/format.h>
29 
30 
31 #include "cnat_db.h"
32 #include "cnat_logging.h"
33 #include "cnat_global.h"
34 #include "cnat_ipv4_udp.h"
35 #include "cnat_common_api.h"
36 
39 
42 
46 
47 #define CNAT_DB_SCANNER_TURN_ON 5 /* just an arbitary number for easier debugging */
48 
49 //extern u32 pcp_throttle_count;
50 
51 typedef struct {
53  /* $$$$ add data here */
54 
55  /* convenience variables */
59 
61 
62 
63 static inline void check_session_for_expiry(
64  cnat_session_entry_t * sdb, u8 timeout_dirty
65  /*,dslite_table_entry_t *dslite_entry_ptr*/)
66 {
68  /* Tasks -
69  * 1. Check for expiry for this entry
70  * 2. Delete if expired
71  */
72  u32 timeout = 0;
73 
74  switch(sdb->v4_dest_key.k.vrf & CNAT_PRO_MASK) {
75  case CNAT_TCP:
76  if (sdb->flags & CNAT_DB_FLAG_TCP_ACTIVE) {
77  timeout = sdb->timeout;
78  if(PREDICT_FALSE(timeout_dirty)) {
80  (void *)sdb, SESSION_DB_TYPE);
81  }
82  if(PREDICT_TRUE(timeout == 0)) {
83  timeout = tcp_active_timeout;
84  //dslite_entry_ptr->timeout_info.tcp_active_timeout;
85  }
86  } else {
87  timeout = tcp_initial_setup_timeout;
88  //dslite_entry_ptr->timeout_info.tcp_initial_setup_timeout;
89  }
90  break;
91  case CNAT_UDP:
92  if (sdb->flags & CNAT_DB_FLAG_UDP_ACTIVE) {
93  timeout = sdb->timeout;
94  if(PREDICT_FALSE(timeout_dirty)) {
96  (void *)sdb, SESSION_DB_TYPE);
97  }
98 
99  if(PREDICT_TRUE(timeout == 0)) {
100  timeout = udp_act_session_timeout;
101  //dslite_entry_ptr->timeout_info.udp_act_session_timeout;
102  }
103  } else {
104  timeout = udp_init_session_timeout;
105  //dslite_entry_ptr->timeout_info.udp_init_session_timeout;
106  }
107  break;
108  case CNAT_ICMP:
109  timeout = icmp_session_timeout;
110  //dslite_entry_ptr->timeout_info.icmp_session_timeout;
111  break;
112  case CNAT_PPTP:
113  timeout = pptp_cfg.timeout;
114  break;
115  default:
116  return;
117  }
118  /* Changes required for clearing sessions */
119  if (PREDICT_FALSE((sdb->entry_expires == 0) ||
120  (sdb->entry_expires + timeout < cnat_current_time))) {
122  }
123 }
124 
126  cnat_main_db_entry_t *db, int *dirty_index, uword db_index
127  /* ,dslite_table_entry_t *dslite_entry_ptr */)
128 {
129  /* Tasks -
130  * 1. Traverse through the sessions and check for timeouts
131  * 2. Delete sessions that have exipred
132  * 3. Check if the db has only one session remaining.. if so,
133  * the details of the session has to be moved to main db
134  * and session db entry needs to be freed
135  * 4. If db does not have any sessions left, the db itself
136  * needs to be deleted.
137  */
138  u32 nsessions, session_index_head, session_index;
140  u8 timeout_dirty = FALSE;
141 
142  if(PREDICT_FALSE(*dirty_index == db_index)) {
143  *dirty_index = -1;
144  }
146  timeout_dirty_flag = 0;
147  *dirty_index = db_index;
148  timeout_dirty = TRUE;
149  }
150 
151  session_index_head = session_index = db->session_head_index;
152  nsessions = db->nsessions;
153 
154  do {
155  sdb = cnat_session_db + session_index;
156  if(PREDICT_FALSE(!sdb)) {
157  //TO DO: Debug msg?
158  return FALSE;
159  }
160  session_index = sdb->main_list.next;
161  check_session_for_expiry(sdb, timeout_dirty /*,dslite_entry_ptr*/);
162  nsessions--; /* To ensure that we do not get in to an infinite loop */
163  } while(session_index != session_index_head
164  && db->session_head_index != EMPTY &&
165  nsessions);
166 
167  /* Note.. the code below assumes that while deleting the
168  * sessions, we do not delete the main db entry if it does
169  * not have any sessions anymore
170  */
171  if(PREDICT_FALSE((!db->nsessions) &&
172  (!(db->flags & CNAT_DB_FLAG_STATIC_PORT)))) {
174  return TRUE; /* to indicate that main db was deleted */
175  }
176  return FALSE;
177 }
178 
179 static void cnat_db_scanner(void)
180 {
182  u32 timeout;
183  cnat_vrfmap_t *my_vrfmap __attribute__((unused)) = 0;
184  static int dirty_index = -1;
185  u16 instance __attribute__((unused));
186  //dslite_table_entry_t *dslite_entry_ptr;
187  u32 i;
188  uword db_index;
189  //pcp_throttle_count = 0;
190 
191  for (i = 0; i < num_entries; i++) {
192  db_index = check_these_pool_indices[i];
193  db = cnat_main_db + db_index;
194  timeout=0;
195  my_vrfmap = 0;
196 
197 #if 0
198  if(PREDICT_FALSE(db->flags & CNAT_PCP_FLAG)) {
199 
200  if(db->proto_data.seq_pcp.pcp_lifetime < cnat_current_time) {
201  /* mark as implicit */
202  db->flags &= ~CNAT_PCP_FLAG;
203  }
204  continue;
205  }
206 
207 #endif
208  if(PREDICT_FALSE(db->nsessions > 1)) {
209  if(PREDICT_FALSE(
210  handle_db_scan_for_sessions(db, &dirty_index, db_index /*,dslite_entry_ptr */))) {
211  continue;
212  } else if(PREDICT_TRUE(db->nsessions > 1)) {
213  continue;
214  }
215  /* if there is exactly one dest left.. let it fall through
216  * and check if that needs to be deleted as well
217  */
218  }
219 
220 #if 0
223  if(PREDICT_FALSE(
224  ((dslite_entry_ptr->nf_logging_policy != SESSION_LOG_ENABLE) &&
225  (dslite_entry_ptr->syslog_logging_policy != SESSION_LOG_ENABLE))
226  || (db->nsessions !=1))) {
227  continue;
228  }
229  } else {
230  my_vrfmap = cnat_map_by_vrf + db->vrfmap_index;
231  if(PREDICT_FALSE(
232  ((my_vrfmap->nf_logging_policy != SESSION_LOG_ENABLE) &&
233  (my_vrfmap->syslog_logging_policy != SESSION_LOG_ENABLE)) ||
234  (db->nsessions !=1))) {
235  continue;
236  }
237  }
238  }
239 #endif
240 
241  switch(db->in2out_key.k.vrf & CNAT_PRO_MASK) {
242  case CNAT_TCP:
243  if (db->flags & CNAT_DB_FLAG_TCP_ACTIVE) {
244  timeout = db->timeout;
245  if(PREDICT_FALSE(dirty_index == db_index)) {
246  dirty_index = -1;
247  }
249  timeout_dirty_flag = 0;
250  dirty_index = db_index;
251  }
252  if(PREDICT_FALSE(dirty_index != -1)) {
253  timeout = query_and_update_db_timeout(
254  (void *)db, MAIN_DB_TYPE);
255  }
256  if(PREDICT_TRUE(timeout == 0)) {
257  timeout = tcp_active_timeout;
258  }
259  } else {
260  timeout = tcp_initial_setup_timeout;
261  }
262  break;
263  case CNAT_UDP:
264  if (db->flags & CNAT_DB_FLAG_UDP_ACTIVE) {
265  timeout = db->timeout;
266  if(PREDICT_FALSE(dirty_index == db_index)) {
267  dirty_index = -1;
268  }
270  timeout_dirty_flag = 0;
271  dirty_index = db_index;
272  }
273  if(PREDICT_FALSE(dirty_index != -1)) {
274  timeout = query_and_update_db_timeout(
275  (void *)db, MAIN_DB_TYPE);
276  }
277  if(PREDICT_TRUE(timeout == 0)) {
278  timeout = udp_act_session_timeout;
279  }
280  } else {
281  timeout = udp_init_session_timeout;
282  }
283  break;
284  case CNAT_ICMP:
285  timeout = icmp_session_timeout;
286  break;
287  case CNAT_PPTP:
288  timeout = pptp_cfg.timeout;
289  break;
290  default:
291  continue;
292  }
293 
294 
295  /* Ref: CSCtu97536 */
296  if (PREDICT_FALSE((db->entry_expires == 0) ||
297  (db->entry_expires + timeout < cnat_current_time))) {
298 #if 0
301  instance = db->dslite_nat44_inst_id;
302  } else {
303  instance = NAT44_RESERVED_INST_ID;
304  cnat_session_log_nat44_mapping_delete(db, 0, my_vrfmap);
305  }
306 
307  /* Reset the session details */
308  db->nsessions = 0;
309  db->dst_ipv4 = 0;
310  db->dst_port = 0;
313  db->timeout = 0;
314  db->entry_expires = 0;
315  db->alg.delta = 0;
316  db->proto_data.seq_pcp.tcp_seq_num = 0;
317  continue;
318  }
319 #endif
320  //printf("DELETING DB ENTRY FOR 0x%x\n", db->in2out_key.k.ipv4);
322  }
323  //free(check_these_pool_indices[i]);
324  }
325 }
326 
327 static void walk_the_db (void)
328 {
330  u32 db_uword_len;
331  static u32 base_index = 0, free_bitmap_index = 0;
332  int bits_scanned = 0, i;
333  uword inuse_bitmap;
334 
335  num_entries=0;
336 
337  /* Across all db entries... */
338  db_uword_len = vec_len(cnat_main_db) / NUM_BITS_IN_UWORD;
340  /*
341  * It should not come here as in cnat_db_init_v2()
342  * it is made multiple of NUM_BITS_IN_UWORD
343  */
344  ASSERT(0);
345  return ;
346  }
347 
348  if (PREDICT_FALSE(! db_uword_len))
349  return ;
350 
351  while (bits_scanned < MAX_DB_ENTRY_PER_SCAN) {
352 
353  if (PREDICT_FALSE(free_bitmap_index < vec_len(h->free_bitmap))) {
354 
355  /* free_bitmap exists and it is not all 0 */
356 
357  inuse_bitmap = ~(h->free_bitmap[free_bitmap_index]);
358  i = 0;
359  while (inuse_bitmap) {
360 
361  /* Check to see if the index is in use */
362  if (PREDICT_FALSE((inuse_bitmap >> i) & 1)) {
363  check_these_pool_indices[num_entries] = base_index + i;
364  inuse_bitmap &= ~((uword) 1 << i);
365  num_entries++;
366  }
367  i++;
368  } // while (inuse_bitmap)
369  } else {
370 
371  /*
372  * 64-bit entry is 0, means all 64 entries are allocated.
373  * So, simply add all 64 entries here.
374  * No need to form inuse_bitmap, check and reset bits
375  */
376  for (i=0; i<NUM_BITS_IN_UWORD; i++) {
377 
378  check_these_pool_indices[num_entries] = base_index + i;
379  num_entries++;
380  }
381  } // if (free_bitmap_index < vec_len(h->free_bitmap))
382 
383  /* Update free_bitmap_index and base_index for next run */
384  if (PREDICT_FALSE(free_bitmap_index == db_uword_len - 1)) {
385  /* wrap-around for next run */
386  free_bitmap_index = 0;
387  base_index = 0;
388  } else {
389  free_bitmap_index ++;
390  base_index += NUM_BITS_IN_UWORD;
391  }
392 
393  /* increment # of bits scanned */
394  bits_scanned += NUM_BITS_IN_UWORD;
395 
396  /* Found enough entries to check ? */
398  {
399  /* This check is introduced to keep fixed MAX scan entry value */
400  /* This is very much required when we do scanning for NAT64 */
401  /* please check comments in cnat_db_scanner() &
402  * handler_nat64_db_scanner() */
405  }
406  break;
407  }
408 
409  } // while (bits_scanned < MAX_DB_ENTRY_PER_SCAN)
410 
411  if (PREDICT_FALSE(num_entries > 0)) {
412  //printf("%s: num_entries [%d]\n", __func__, num_entries);
413  cnat_db_scanner();
414  }
415  return ;
416 }
417 
419  vlib_node_runtime_t * node,
420  vlib_frame_t * frame)
421 {
422  f64 timeout = 0.01; /* timeout value in sec (10 ms) */
423  static u8 timeout_count = 0;
424 
425  uword event_type;
426  uword * event_data = 0;
427  /* Wait until vCGN is configured */
428  while (1) {
429  /* Assigning a huge timeout value, vCGN may or
430  * may not get configured within this timeout */
432  event_type = vlib_process_get_events (vm, &event_data);
433 
434  /* check whether the process is waken up by correct guy,
435  * otherwise continue waiting for the vCGN config */
436  if (event_type == CNAT_DB_SCANNER_TURN_ON) {
437  break;
438  }
439  }
440 
441  while(1) {
442  vlib_process_suspend(vm, timeout);
443 
444  /* Above suspend API should serve the purpose, no need to invoke wait API */
445  /* vlib_process_wait_for_event_or_clock (vm, timeout); */
446 
447  /* Lets make use of this timeout for netflow packet sent */
448  if (timeout_count < 100) { /* 100*10 ms = 1 sec */
449  timeout_count++;
450  } else {
451  if (nfv9_configured) {
453  }
454  timeout_count = 0;
455  }
456  /* Do we need this ? */
457  //event_type = vlib_process_get_events (vm, &event_data);
459  if (cnat_db_init_done) {
460  walk_the_db();
461  }
462  }
463 
464  return 0;
465 }
466 
467 
469  .function = cnat_db_scanner_fn,
470  .type = VLIB_NODE_TYPE_PROCESS,
471  .name = "cnat-db-scanner",
472  .process_log2_n_stack_bytes = 18,
473 };
474 
476 {
478 
479  mp->vlib_main = vm;
480  mp->vnet_main = vnet_get_main();
481 
482  return 0;
483 }
484 
486 {
489  return;
490 }
491 
493 
#define MAX_DB_ENTRY_SELECTED_PER_SCAN
Definition: cnat_config.h:63
static void check_session_for_expiry(cnat_session_entry_t *sdb, u8 timeout_dirty)
u16 flags
Definition: cnat_db.h:159
cnat_main_db_entry_t * cnat_main_db
Definition: cnat_db_v2.c:195
u16 query_and_update_db_timeout(void *db, u8 db_type)
Definition: cnat_db_v2.c:2285
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:267
u8 nfv9_configured
Definition: cnat_global.c:30
static void cnat_db_scanner(void)
always_inline uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Definition: node_funcs.h:410
u32 out2in_forwarding_count
static u8 handle_db_scan_for_sessions(cnat_main_db_entry_t *db, int *dirty_index, uword db_index)
union cnat_main_db_entry_t::@164 alg
#define NAT44_RESERVED_INST_ID
Definition: cnat_config.h:40
#define PREDICT_TRUE(x)
Definition: clib.h:98
void cnat_scanner_db_process_turn_on(vlib_main_t *vm)
u32 cnat_current_time
Definition: cnat_global.c:29
u32 session_head_index
Definition: cnat_db.h:263
cnat_session_entry_t * cnat_session_db
Definition: cnat_db_v2.c:197
static uword cnat_db_scanner_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: cnat_db.h:153
always_inline void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:789
#define CNAT_DB_FLAG_STATIC_PORT
Definition: cnat_db.h:164
#define MAIN_DB_TYPE
Definition: cnat_db.h:626
always_inline uword vlib_process_suspend(vlib_main_t *vm, f64 dt)
Definition: node_funcs.h:326
vlib_node_registration_t cnat_db_scanner_node
(constructor) VLIB_REGISTER_NODE (cnat_db_scanner_node)
void handle_pending_nfv9_pkts()
u16 udp_init_session_timeout
Definition: cnat_config.c:28
#define MAX_COMBINED_DB_ENTRIES_PER_SCAN
cnat_key_t v4_dest_key
Definition: cnat_db.h:345
u8 cnat_db_init_done
Definition: cnat_db_v2.c:43
u32 translation_delete_rate
void cnat_delete_session_db_entry(cnat_session_entry_t *ep, u8 log)
Definition: cnat_db_v2.c:2742
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:109
index_dlist_t main_list
Definition: cnat_db.h:356
clib_error_t * cnat_db_scanner_init(vlib_main_t *vm)
#define CNAT_PRO_MASK
Definition: cnat_db.h:97
uword check_these_pool_indices[2 *MAX_DB_ENTRY_SELECTED_PER_SCAN]
vhost_user_log_t log
Definition: vhost-user.h:80
u16 dst_port
Definition: cnat_db.h:257
u16 timeout
Definition: cnat_db.h:224
#define CNAT_DB_FLAG_ALG_ENTRY
Definition: cnat_db.h:166
static void walk_the_db(void)
u32 in2out_forwarding_rate
always_inline f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
Definition: node_funcs.h:551
struct cnat_main_db_entry_t::@165::seq_pcp_t seq_pcp
#define PREDICT_FALSE(x)
Definition: clib.h:97
u16 icmp_session_timeout
Definition: cnat_config.c:30
#define CNAT_UDP
Definition: cnat_db.h:93
u32 out2in_forwarding_rate
u16 timeout
Definition: cnat_db.h:351
u32 entry_expires
Definition: cnat_db.h:354
u16 flags
Definition: cnat_db.h:348
#define CNAT_ICMP
Definition: cnat_db.h:95
#define CNAT_DB_SCANNER_TURN_ON
u8 timeout_dirty_flag
Definition: cnat_config.c:41
#define SESSION_LOG_ENABLE
Definition: cnat_db.h:693
#define CNAT_DB_DSLITE_FLAG
Definition: cnat_db.h:188
cnat_db_key_t k
Definition: cnat_db.h:108
#define CNAT_PPTP
Definition: cnat_db.h:92
#define CNAT_DB_FLAG_TCP_ACTIVE
Definition: cnat_db.h:161
#define CNAT_PCP_FLAG
Definition: cnat_db.h:179
u16 tcp_initial_setup_timeout
Definition: cnat_config.c:26
#define MAX_DB_ENTRY_PER_SCAN
Definition: cnat_config.h:61
u32 translation_create_rate
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
u32 entry_expires
Definition: cnat_db.h:213
u16 tcp_active_timeout
Definition: cnat_config.c:27
u32 nat44_active_translations
#define TRUE
Definition: cnat_db.h:78
#define CNAT_TCP
Definition: cnat_db.h:94
cnat_pptp_config_t pptp_cfg
Definition: cnat_config.c:32
always_inline pool_header_t * pool_header(void *v)
Definition: pool.h:62
u64 uword
Definition: types.h:112
u16 nsessions
Definition: cnat_db.h:266
void cnat_delete_main_db_entry_v2(cnat_main_db_entry_t *ep)
Definition: cnat_db_v2.c:753
uword * free_bitmap
Definition: pool.h:51
Definition: cnat_db.h:336
unsigned short u16
Definition: types.h:57
cnat_vrfmap_t * cnat_map_by_vrf
Definition: cnat_db_v2.c:212
u16 dslite_nat44_inst_id
Definition: cnat_db.h:260
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:140
unsigned char u8
Definition: types.h:56
u16 delta
Definition: cnat_db.h:217
#define NUM_BITS_IN_UWORD
Definition: cnat_db.h:69
u16 udp_act_session_timeout
Definition: cnat_config.c:29
u32 dst_ipv4
Definition: cnat_db.h:254
u16 vrfmap_index
Definition: cnat_db.h:192
Linear Congruential Random Number Generator.
u32 num_entries
u32 in2out_forwarding_count
#define SESSION_DB_TYPE
Definition: cnat_db.h:627
#define FALSE
Definition: cnat_db.h:79
u32 translation_create_count
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:140
u32 translation_delete_count
#define EMPTY
Definition: index_list.h:24
cnat_db_scanner_main_t cnat_db_scanner_main
always_inline f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:182
#define CNAT_DB_FLAG_UDP_ACTIVE
Definition: cnat_db.h:163
union cnat_main_db_entry_t::@165 proto_data
static void cnat_session_log_nat44_mapping_delete(cnat_main_db_entry_t *db, cnat_session_entry_t *sdb, cnat_vrfmap_t *vrfmap)
Definition: cnat_log_api.h:88
cnat_key_t in2out_key
Definition: cnat_db.h:201