Initial commit of vpp code.
[vpp.git] / vnet / vnet / vcgn / cnat_db_scanner.c
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
37 u32 translation_create_count, translation_delete_count;
38 u32 translation_create_rate, translation_delete_rate;
39
40 u32 in2out_forwarding_count, out2in_forwarding_count;
41 u32 in2out_forwarding_rate,  out2in_forwarding_rate;
42
43 u32 nat44_active_translations;
44 u32 num_entries;
45 uword check_these_pool_indices[2*MAX_DB_ENTRY_SELECTED_PER_SCAN];
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 {
52   u32 cached_next_index;
53   /* $$$$ add data here */
54
55   /* convenience variables */
56   vlib_main_t * vlib_main;
57   vnet_main_t * vnet_main;
58 } cnat_db_scanner_main_t;
59
60 cnat_db_scanner_main_t cnat_db_scanner_main;
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 {
67     void cnat_delete_session_db_entry (cnat_session_entry_t *ep, u8 log);
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)) {
79                     timeout = query_and_update_db_timeout(
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)) {
95                     timeout = query_and_update_db_timeout(
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))) {
121         cnat_delete_session_db_entry(sdb, TRUE);
122     }
123 }
124
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 */)
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;
139     cnat_session_entry_t *sdb;
140     u8 timeout_dirty = FALSE;
141
142     if(PREDICT_FALSE(*dirty_index == db_index)) {
143         *dirty_index = -1;
144     }
145     if(PREDICT_FALSE(timeout_dirty_flag == 1)) {
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)))) {
173          cnat_delete_main_db_entry_v2(db);
174          return TRUE; /* to indicate that main db was deleted */
175     }
176     return FALSE;
177 }
178
179 static void cnat_db_scanner(void)
180 {
181     cnat_main_db_entry_t * db;
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 
221         if (PREDICT_FALSE(db->flags & CNAT_DB_FLAG_STATIC_PORT)) {
222             if (PREDICT_FALSE(db->flags & CNAT_DB_DSLITE_FLAG)) {
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                     }
248                     if(PREDICT_FALSE(timeout_dirty_flag == 1)) {
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                     }
269                     if(PREDICT_FALSE(timeout_dirty_flag == 1)) {
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
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;
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;
311                 db->flags &= ~(CNAT_DB_FLAG_TCP_ACTIVE | CNAT_DB_FLAG_UDP_ACTIVE
312                         | CNAT_DB_FLAG_ALG_ENTRY);
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);
321             cnat_delete_main_db_entry_v2(db);
322         }
323         //free(check_these_pool_indices[i]);
324     }
325 }
326
327 static void walk_the_db (void)
328 {
329     pool_header_t *h = pool_header(cnat_main_db);
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;
339     if (PREDICT_FALSE(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 ? */
397         if (PREDICT_FALSE(num_entries >= MAX_DB_ENTRY_SELECTED_PER_SCAN))
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() */
403             if (num_entries >= MAX_COMBINED_DB_ENTRIES_PER_SCAN) {
404                 num_entries = MAX_COMBINED_DB_ENTRIES_PER_SCAN;
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
418 static uword cnat_db_scanner_fn (vlib_main_t * vm,
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  */
431       vlib_process_wait_for_event_or_clock (vm, 1e9);  
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) {
452              handle_pending_nfv9_pkts();
453          }
454          timeout_count = 0;
455      }
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) {
460         walk_the_db();  
461      }
462   }
463
464   return 0;
465 }
466
467
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,
473 };
474
475 clib_error_t *cnat_db_scanner_init (vlib_main_t *vm)
476 {
477   cnat_db_scanner_main_t *mp = &cnat_db_scanner_main;
478
479   mp->vlib_main = vm;
480   mp->vnet_main = vnet_get_main();
481
482   return 0;
483 }
484
485 void cnat_scanner_db_process_turn_on(vlib_main_t *vm)
486 {
487     vlib_process_signal_event (vm, cnat_db_scanner_node.index,
488                              CNAT_DB_SCANNER_TURN_ON, 0);
489     return; 
490 }
491
492 VLIB_INIT_FUNCTION (cnat_db_scanner_init);
493