2 *---------------------------------------------------------------------------
3 * cnat_db_scanner.c - cnat_db_scanner dispatch function and initialization
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:
10 * http://www.apache.org/licenses/LICENSE-2.0
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 *---------------------------------------------------------------------------
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>
32 #include "cnat_logging.h"
33 #include "cnat_global.h"
34 #include "cnat_ipv4_udp.h"
35 #include "cnat_common_api.h"
37 u32 translation_create_count, translation_delete_count;
38 u32 translation_create_rate, translation_delete_rate;
40 u32 in2out_forwarding_count, out2in_forwarding_count;
41 u32 in2out_forwarding_rate, out2in_forwarding_rate;
43 u32 nat44_active_translations;
45 uword check_these_pool_indices[2*MAX_DB_ENTRY_SELECTED_PER_SCAN];
47 #define CNAT_DB_SCANNER_TURN_ON 5 /* just an arbitary number for easier debugging */
49 //extern u32 pcp_throttle_count;
52 u32 cached_next_index;
53 /* $$$$ add data here */
55 /* convenience variables */
56 vlib_main_t * vlib_main;
57 vnet_main_t * vnet_main;
58 } cnat_db_scanner_main_t;
60 cnat_db_scanner_main_t cnat_db_scanner_main;
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*/)
67 void cnat_delete_session_db_entry (cnat_session_entry_t *ep, u8 log);
69 * 1. Check for expiry for this entry
70 * 2. Delete if expired
74 switch(sdb->v4_dest_key.k.vrf & CNAT_PRO_MASK) {
76 if (sdb->flags & CNAT_DB_FLAG_TCP_ACTIVE) {
77 timeout = sdb->timeout;
78 if(PREDICT_FALSE(timeout_dirty)) {
79 timeout = query_and_update_db_timeout(
80 (void *)sdb, SESSION_DB_TYPE);
82 if(PREDICT_TRUE(timeout == 0)) {
83 timeout = tcp_active_timeout;
84 //dslite_entry_ptr->timeout_info.tcp_active_timeout;
87 timeout = tcp_initial_setup_timeout;
88 //dslite_entry_ptr->timeout_info.tcp_initial_setup_timeout;
92 if (sdb->flags & CNAT_DB_FLAG_UDP_ACTIVE) {
93 timeout = sdb->timeout;
94 if(PREDICT_FALSE(timeout_dirty)) {
95 timeout = query_and_update_db_timeout(
96 (void *)sdb, SESSION_DB_TYPE);
99 if(PREDICT_TRUE(timeout == 0)) {
100 timeout = udp_act_session_timeout;
101 //dslite_entry_ptr->timeout_info.udp_act_session_timeout;
104 timeout = udp_init_session_timeout;
105 //dslite_entry_ptr->timeout_info.udp_init_session_timeout;
109 timeout = icmp_session_timeout;
110 //dslite_entry_ptr->timeout_info.icmp_session_timeout;
113 timeout = pptp_cfg.timeout;
118 /* Changes required for clearing sessions */
119 if (PREDICT_FALSE((sdb->entry_expires == 0) ||
120 (sdb->entry_expires + timeout < cnat_current_time))) {
121 cnat_delete_session_db_entry(sdb, TRUE);
125 static u8 handle_db_scan_for_sessions(
126 cnat_main_db_entry_t *db, int *dirty_index, uword db_index
127 /* ,dslite_table_entry_t *dslite_entry_ptr */)
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.
138 u32 nsessions, session_index_head, session_index;
139 cnat_session_entry_t *sdb;
140 u8 timeout_dirty = FALSE;
142 if(PREDICT_FALSE(*dirty_index == db_index)) {
145 if(PREDICT_FALSE(timeout_dirty_flag == 1)) {
146 timeout_dirty_flag = 0;
147 *dirty_index = db_index;
148 timeout_dirty = TRUE;
151 session_index_head = session_index = db->session_head_index;
152 nsessions = db->nsessions;
155 sdb = cnat_session_db + session_index;
156 if(PREDICT_FALSE(!sdb)) {
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 &&
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
171 if(PREDICT_FALSE((!db->nsessions) &&
172 (!(db->flags & CNAT_DB_FLAG_STATIC_PORT)))) {
173 cnat_delete_main_db_entry_v2(db);
174 return TRUE; /* to indicate that main db was deleted */
179 static void cnat_db_scanner(void)
181 cnat_main_db_entry_t * db;
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;
189 //pcp_throttle_count = 0;
191 for (i = 0; i < num_entries; i++) {
192 db_index = check_these_pool_indices[i];
193 db = cnat_main_db + db_index;
198 if(PREDICT_FALSE(db->flags & CNAT_PCP_FLAG)) {
200 if(db->proto_data.seq_pcp.pcp_lifetime < cnat_current_time) {
201 /* mark as implicit */
202 db->flags &= ~CNAT_PCP_FLAG;
208 if(PREDICT_FALSE(db->nsessions > 1)) {
210 handle_db_scan_for_sessions(db, &dirty_index, db_index /*,dslite_entry_ptr */))) {
212 } else if(PREDICT_TRUE(db->nsessions > 1)) {
215 /* if there is exactly one dest left.. let it fall through
216 * and check if that needs to be deleted as well
221 if (PREDICT_FALSE(db->flags & CNAT_DB_FLAG_STATIC_PORT)) {
222 if (PREDICT_FALSE(db->flags & CNAT_DB_DSLITE_FLAG)) {
224 ((dslite_entry_ptr->nf_logging_policy != SESSION_LOG_ENABLE) &&
225 (dslite_entry_ptr->syslog_logging_policy != SESSION_LOG_ENABLE))
226 || (db->nsessions !=1))) {
230 my_vrfmap = cnat_map_by_vrf + db->vrfmap_index;
232 ((my_vrfmap->nf_logging_policy != SESSION_LOG_ENABLE) &&
233 (my_vrfmap->syslog_logging_policy != SESSION_LOG_ENABLE)) ||
234 (db->nsessions !=1))) {
241 switch(db->in2out_key.k.vrf & CNAT_PRO_MASK) {
243 if (db->flags & CNAT_DB_FLAG_TCP_ACTIVE) {
244 timeout = db->timeout;
245 if(PREDICT_FALSE(dirty_index == db_index)) {
248 if(PREDICT_FALSE(timeout_dirty_flag == 1)) {
249 timeout_dirty_flag = 0;
250 dirty_index = db_index;
252 if(PREDICT_FALSE(dirty_index != -1)) {
253 timeout = query_and_update_db_timeout(
254 (void *)db, MAIN_DB_TYPE);
256 if(PREDICT_TRUE(timeout == 0)) {
257 timeout = tcp_active_timeout;
260 timeout = tcp_initial_setup_timeout;
264 if (db->flags & CNAT_DB_FLAG_UDP_ACTIVE) {
265 timeout = db->timeout;
266 if(PREDICT_FALSE(dirty_index == db_index)) {
269 if(PREDICT_FALSE(timeout_dirty_flag == 1)) {
270 timeout_dirty_flag = 0;
271 dirty_index = db_index;
273 if(PREDICT_FALSE(dirty_index != -1)) {
274 timeout = query_and_update_db_timeout(
275 (void *)db, MAIN_DB_TYPE);
277 if(PREDICT_TRUE(timeout == 0)) {
278 timeout = udp_act_session_timeout;
281 timeout = udp_init_session_timeout;
285 timeout = icmp_session_timeout;
288 timeout = pptp_cfg.timeout;
295 /* Ref: CSCtu97536 */
296 if (PREDICT_FALSE((db->entry_expires == 0) ||
297 (db->entry_expires + timeout < cnat_current_time))) {
299 if (PREDICT_FALSE(db->flags & CNAT_DB_FLAG_STATIC_PORT)) {
300 if (PREDICT_FALSE(db->flags & CNAT_DB_DSLITE_FLAG)) {
301 instance = db->dslite_nat44_inst_id;
303 instance = NAT44_RESERVED_INST_ID;
304 cnat_session_log_nat44_mapping_delete(db, 0, my_vrfmap);
307 /* Reset the session details */
311 db->flags &= ~(CNAT_DB_FLAG_TCP_ACTIVE | CNAT_DB_FLAG_UDP_ACTIVE
312 | CNAT_DB_FLAG_ALG_ENTRY);
314 db->entry_expires = 0;
316 db->proto_data.seq_pcp.tcp_seq_num = 0;
320 //printf("DELETING DB ENTRY FOR 0x%x\n", db->in2out_key.k.ipv4);
321 cnat_delete_main_db_entry_v2(db);
323 //free(check_these_pool_indices[i]);
327 static void walk_the_db (void)
329 pool_header_t *h = pool_header(cnat_main_db);
331 static u32 base_index = 0, free_bitmap_index = 0;
332 int bits_scanned = 0, i;
337 /* Across all db entries... */
338 db_uword_len = vec_len(cnat_main_db) / NUM_BITS_IN_UWORD;
339 if (PREDICT_FALSE(vec_len(cnat_main_db) % NUM_BITS_IN_UWORD)) {
341 * It should not come here as in cnat_db_init_v2()
342 * it is made multiple of NUM_BITS_IN_UWORD
348 if (PREDICT_FALSE(! db_uword_len))
351 while (bits_scanned < MAX_DB_ENTRY_PER_SCAN) {
353 if (PREDICT_FALSE(free_bitmap_index < vec_len(h->free_bitmap))) {
355 /* free_bitmap exists and it is not all 0 */
357 inuse_bitmap = ~(h->free_bitmap[free_bitmap_index]);
359 while (inuse_bitmap) {
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);
368 } // while (inuse_bitmap)
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
376 for (i=0; i<NUM_BITS_IN_UWORD; i++) {
378 check_these_pool_indices[num_entries] = base_index + i;
381 } // if (free_bitmap_index < vec_len(h->free_bitmap))
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;
389 free_bitmap_index ++;
390 base_index += NUM_BITS_IN_UWORD;
393 /* increment # of bits scanned */
394 bits_scanned += NUM_BITS_IN_UWORD;
396 /* Found enough entries to check ? */
397 if (PREDICT_FALSE(num_entries >= MAX_DB_ENTRY_SELECTED_PER_SCAN))
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() */
403 if (num_entries >= MAX_COMBINED_DB_ENTRIES_PER_SCAN) {
404 num_entries = MAX_COMBINED_DB_ENTRIES_PER_SCAN;
409 } // while (bits_scanned < MAX_DB_ENTRY_PER_SCAN)
411 if (PREDICT_FALSE(num_entries > 0)) {
412 //printf("%s: num_entries [%d]\n", __func__, num_entries);
418 static uword cnat_db_scanner_fn (vlib_main_t * vm,
419 vlib_node_runtime_t * node,
420 vlib_frame_t * frame)
422 f64 timeout = 0.01; /* timeout value in sec (10 ms) */
423 static u8 timeout_count = 0;
426 uword * event_data = 0;
427 /* Wait until vCGN is configured */
429 /* Assigning a huge timeout value, vCGN may or
430 * may not get configured within this timeout */
431 vlib_process_wait_for_event_or_clock (vm, 1e9);
432 event_type = vlib_process_get_events (vm, &event_data);
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) {
442 vlib_process_suspend(vm, timeout);
444 /* Above suspend API should serve the purpose, no need to invoke wait API */
445 /* vlib_process_wait_for_event_or_clock (vm, timeout); */
447 /* Lets make use of this timeout for netflow packet sent */
448 if (timeout_count < 100) { /* 100*10 ms = 1 sec */
451 if (nfv9_configured) {
452 handle_pending_nfv9_pkts();
456 /* Do we need this ? */
457 //event_type = vlib_process_get_events (vm, &event_data);
458 cnat_current_time = (u32)vlib_time_now (vm);
459 if (cnat_db_init_done) {
468 VLIB_REGISTER_NODE (cnat_db_scanner_node) = {
469 .function = cnat_db_scanner_fn,
470 .type = VLIB_NODE_TYPE_PROCESS,
471 .name = "cnat-db-scanner",
472 .process_log2_n_stack_bytes = 18,
475 clib_error_t *cnat_db_scanner_init (vlib_main_t *vm)
477 cnat_db_scanner_main_t *mp = &cnat_db_scanner_main;
480 mp->vnet_main = vnet_get_main();
485 void cnat_scanner_db_process_turn_on(vlib_main_t *vm)
487 vlib_process_signal_event (vm, cnat_db_scanner_node.index,
488 CNAT_DB_SCANNER_TURN_ON, 0);
492 VLIB_INIT_FUNCTION (cnat_db_scanner_init);