Initial commit of vpp code.
[vpp.git] / vnet / vnet / vcgn / cnat_ipv4_tcp_outside_input.c
1 /* 
2  *---------------------------------------------------------------------------
3  * cnat_ipv4_tcp_outside_input.c - cnat_v4_tcp_out2in node pipeline stage functions
4  *
5  *
6  * Copyright (c) 2008-2014 Cisco and/or its affiliates.
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at:
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *---------------------------------------------------------------------------
19  */
20
21 #include <vlib/vlib.h>
22 #include <vnet/vnet.h>
23 #include <vppinfra/error.h>
24 #include <vnet/buffer.h>
25
26 #include "cnat_db.h"
27 #include "tcp_header_definitions.h"
28 #include "cnat_config.h"
29 #include "cnat_global.h"
30 #include "cnat_ipv4_udp.h"
31 #include "cnat_v4_functions.h"
32
33
34 #define foreach_cnat_ipv4_tcp_outside_input_error               \
35 _(CNAT_V4_TCP_O2I_R_PKT, "v4 tcp o2i pkt received")                     \
36 _(CNAT_V4_TCP_O2I_T_PKT, "v4 tcp o2i pkt natted & transmitted")                 \
37 _(CNAT_V4_TCP_O2I_LOOKUP_FAILED, "v4 tcp o2i lookup failed")                    \
38 _(CNAT_V4_TCP_O2I_TTL_GEN, "v4 tcp o2i generated TTL Expiry ICMP packet")               \
39 _(CNAT_V4_TCP_O2I_TTL_DROP, "v4 tcp o2i drop due to failure in creating TTL expiry ICMP msg")           \
40 _(CNAT_V4_TCP_O2I_PTB_GEN, "v4 tcp o2i PTB ICMP pkt generation")                \
41 _(CNAT_V4_UDP_O2I_PTB_DROP, "v4 tcp o2i drop due to failure in creating PTB ICMP pkt")          \
42 _(CNAT_V4_TCP_O2I_SESSION_DROP, "v4 tcp o2i drop due to failure in creating session db")                \
43 _(CNAT_V4_TCP_O2I_SEQ_MISMATCH_DROP, "v4 tcp o2i drop due to TCP sequence mismatch")            \
44 _(CNAT_V4_TCP_O2I_FILTER_DROP, "v4 tcp o2i drop due to endpoint filtering")             \
45 _(CNAT_V4_TCP_O2I_NON_SYN_RST_DROP, "v4 tcp o2i drop due no syn/rst flag")  \
46 _(CNAT_V4_TCP_O2I_FIRST_FRAG_DROP, "v4 tcp o2i first fragment drop")  \
47 _(CNAT_V4_TCP_O2I_SUB_FRAG_NO_DB_DROP, "v4 tcp o2i subsequest frag no DB drop")
48
49 typedef enum {
50 #define _(sym,str) sym,
51   foreach_cnat_ipv4_tcp_outside_input_error 
52 #undef _
53   CNAT_IPV4_TCP_OUTSIDE_INPUT_N_ERROR,
54 } cnat_ipv4_tcp_outside_input_t;
55
56 static char * cnat_ipv4_tcp_outside_input_error_strings[] = {
57 #define _(sym,string) string,
58   foreach_cnat_ipv4_tcp_outside_input_error
59 #undef _
60 };
61
62 typedef struct {
63   u32 cached_next_index;
64   /* $$$$ add data here */
65
66   /* convenience variables */
67   vlib_main_t * vlib_main;
68   vnet_main_t * vnet_main;
69 } cnat_ipv4_tcp_outside_input_main_t;
70
71 typedef enum {
72     //CNAT_V4_TCP_O2I_E,
73     CNAT_V4_TCP_O2I_T,
74     CNAT_V4_TCP_O2I_D,
75     CNAT_V4_TCP_O2I_NEXT,
76 } cnat_ipv4_tcp_outside_input_next_t;
77
78 cnat_ipv4_tcp_outside_input_main_t cnat_ipv4_tcp_outside_input_main;
79 vlib_node_registration_t cnat_ipv4_tcp_outside_input_node;
80
81 #define NSTAGES 6
82
83 /*
84  * Use the generic buffer metadata + first line of packet data prefetch
85  * stage function from <api/pipeline.h>. This is usually a Good Idea.
86  */
87 #define stage0 generic_stage0
88
89
90 static inline void
91 stage1(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
92 {
93     u64 a, b, c;
94     u32 bucket;
95     u8 *prefetch_target;
96
97
98     vlib_buffer_t * b0 = vlib_get_buffer (vm, buffer_index);
99     ipv4_header *ip = vlib_buffer_get_current (b0);
100     u8   ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
101     tcp_hdr_type *tcp = (tcp_hdr_type *)((u8*)ip + ipv4_hdr_len);
102   
103     u64 tmp = 0;
104     tmp = vnet_buffer(b0)->vcgn_uii.key.k.ipv4 =
105             clib_net_to_host_u32(ip->dest_addr);
106     vnet_buffer(b0)->vcgn_uii.key.k.port =
107             clib_net_to_host_u16 (tcp->dest_port);
108
109     tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.port) << 32;
110
111     PLATFORM_CNAT_SET_RX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_RX],
112                              vnet_buffer(b0)->vcgn_uii.key.k.vrf,
113                              CNAT_TCP)
114     tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.vrf) << 48;
115
116     CNAT_V4_GET_HASH(tmp, bucket, CNAT_MAIN_HASH_MASK)
117
118     prefetch_target = (u8 *)(&cnat_out2in_hash[bucket]);
119     vnet_buffer(b0)->vcgn_uii.bucket = bucket;
120
121     /* Prefetch the hash bucket */
122     CLIB_PREFETCH(prefetch_target, CLIB_CACHE_LINE_BYTES, LOAD);
123 }
124
125 static inline void
126 stage2(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
127 { /* nothing */ }
128
129 #define SPP_LOG2_CACHE_LINE_BYTES 6
130 #define SPP_CACHE_LINE_BYTES (1 << SPP_LOG2_CACHE_LINE_BYTES)
131
132 static inline void
133 stage3(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
134 {
135     vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
136     uword prefetch_target0, prefetch_target1;
137     u32 bucket = vnet_buffer(b0)->vcgn_uii.bucket;
138   
139     /* read the hash bucket */
140     u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket
141                  = cnat_out2in_hash[bucket].next;
142
143     if (PREDICT_TRUE(db_index != EMPTY)) {
144         /*
145          * Prefetch database keys. We save space by not cache-line
146          * aligning the DB entries. We don't want to waste LSU
147          * bandwidth prefetching stuff we won't need.
148          */
149         prefetch_target0 = (uword)(cnat_main_db + db_index);
150         CLIB_PREFETCH((void*)prefetch_target0, CLIB_CACHE_LINE_BYTES, STORE);
151         /* Just beyond DB key #2 */
152         prefetch_target1 = prefetch_target0 +
153         STRUCT_OFFSET_OF(cnat_main_db_entry_t, user_ports);
154         /* If the targets are in different lines, do the second prefetch */
155         if (PREDICT_FALSE((prefetch_target0 & ~(SPP_CACHE_LINE_BYTES-1)) !=
156                       (prefetch_target1 & ~(SPP_CACHE_LINE_BYTES-1)))) {
157             CLIB_PREFETCH((void *)prefetch_target1, CLIB_CACHE_LINE_BYTES, STORE);
158         }
159     }
160 }
161
162 static inline void
163 stage4(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
164 {
165   cnat_main_db_entry_t *db;
166   vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
167   u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
168
169   /*
170    * Note: if the search already failed (empty bucket),
171    * the answer is already in the pipeline context structure
172    */
173   if (PREDICT_TRUE(db_index != EMPTY)) {
174
175     /*
176      * Note: hash collisions suck. We can't easily prefetch around them.
177      * The first trip around the track will be fast. After that, maybe
178      * not so much...
179      */
180     do {
181       db = cnat_main_db + db_index;
182       if (PREDICT_TRUE(db->out2in_key.key64 ==
183                   vnet_buffer(b0)->vcgn_uii.key.key64)) {
184         break;
185       }
186       db_index = db->out2in_hash.next;
187     } while (db_index != EMPTY);
188
189     /* Stick the answer back into the pipeline context structure */
190     vnet_buffer(b0)->vcgn_uii.bucket = db_index;
191   }
192 }
193
194 static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
195                               u32 bi)
196 {
197     vlib_buffer_t *b0 = vlib_get_buffer (vm, bi);
198     u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
199     spp_ctx_t *ctx = (spp_ctx_t *) &vnet_buffer(b0)->vcgn_uii;
200     int disposition = CNAT_V4_TCP_O2I_T;
201     int counter = CNAT_V4_TCP_O2I_T_PKT;
202
203     ipv4_header *ip = (ipv4_header *)vlib_buffer_get_current(b0);
204     u8   ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
205     tcp_hdr_type *tcp = (tcp_hdr_type *)((u8*)ip + ipv4_hdr_len);
206     vlib_node_t *n = vlib_get_node (vm, cnat_ipv4_tcp_outside_input_node.index);
207     u32 node_counter_base_index = n->error_heap_index;
208     vlib_error_main_t * em = &vm->error_main;
209     cnat_session_entry_t *session_db = NULL;
210     cnat_main_db_entry_t *db = NULL;
211     cnat_key_t dest_info;
212
213     INCREMENT_NODE_COUNTER(CNAT_V4_TCP_O2I_R_PKT);
214
215     if (PREDICT_FALSE(db_index == EMPTY)) {
216         nat44_dslite_common_stats[0].no_translation_entry_drops ++; 
217         counter = CNAT_V4_TCP_O2I_LOOKUP_FAILED;
218         disposition = CNAT_V4_TCP_O2I_D;
219     } else {
220         if (PLATFORM_HANDLE_TTL_DECREMENT) {
221             if (PREDICT_FALSE(ip->ttl <= 1)) {
222                 /* Try to generate ICMP error msg, as TTL is <= 1 */
223                 if (icmpv4_generate_with_throttling(ctx, 
224                         ip, ctx->ru.rx.uidb_index)) {
225                     /* Generated ICMP */
226                     disposition = CNAT_V4_TCP_O2I_T_PKT; //CNAT_REWRITE_OUTPUT;
227                     counter = CNAT_V4_TCP_O2I_TTL_GEN;
228                 } else {
229                 /* Could not generated ICMP - drop the packet */
230                     disposition = CNAT_V4_TCP_O2I_D;
231                     counter = CNAT_V4_TCP_O2I_TTL_DROP;
232                 } 
233                 goto drop_pkt;
234             } 
235         }
236         db = cnat_main_db + db_index;
237 #if 0
238         window        = db->diff_window;
239         stored_seq_no = db->proto_data.tcp_seq_chk.seq_no;
240         stored_ack_no = db->proto_data.tcp_seq_chk.ack_no;
241         vrf_map_p     = cnat_map_by_vrf + db->vrfmap_index;
242         vrf_index     = (db->in2out_key.k.vrf & CNAT_VRF_MASK);
243 #endif
244         /* For Out2In packet, the dest info is src address and port */
245         dest_info.k.port = clib_net_to_host_u16(tcp->src_port);
246         dest_info.k.ipv4 = clib_net_to_host_u32(ip->src_addr);
247
248         if(PREDICT_TRUE(!PLATFORM_DBL_SUPPORT)) {
249
250             /* No DBL support, so just update the destn and proceed */
251             db->dst_ipv4 = dest_info.k.ipv4;
252             db->dst_port = dest_info.k.port;
253             goto update_pkt;
254         }
255
256
257         if(PREDICT_FALSE(db->dst_ipv4 != dest_info.k.ipv4 ||
258                 db->dst_port != dest_info.k.port)) {
259                     
260             if(PREDICT_TRUE(db->nsessions == 0)) {
261                 /* Should be a static entry
262                  * Note this session as the first session and log
263                  */
264                 cnat_add_dest_n_log(db, &dest_info);
265                 //goto packet_upd;
266             } else if(PREDICT_FALSE(db->nsessions == 1)) {
267                 /* Destn is not same as in main db. Multiple session
268                  * scenario
269                  */
270                 dest_info.k.vrf = db->in2out_key.k.vrf;
271                 session_db = cnat_handle_1to2_session(db, &dest_info);
272                 if(PREDICT_FALSE(session_db == NULL)) {
273                     disposition = CNAT_V4_TCP_O2I_D;
274                     counter = CNAT_V4_TCP_O2I_SESSION_DROP;
275                     goto drop_pkt;
276                 }
277             } else { /* There are already multiple destinations */
278                 dest_info.k.vrf = db->in2out_key.k.vrf;
279                 /* If session already exists,
280                  * cnat_create_session_db_entry will return the existing db
281                  * else create a new db
282                  * If could not create, return NULL
283                  */
284                 session_db = cnat_create_session_db_entry(&dest_info, db, TRUE);
285                 if(PREDICT_FALSE(session_db == NULL)) {
286                     disposition = CNAT_V4_TCP_O2I_D;
287                     counter = CNAT_V4_TCP_O2I_SESSION_DROP;
288                     goto drop_pkt;
289                 }
290             }
291             /* useful for ALG only */
292             #if 0
293             if(PREDICT_TRUE(session_db)) {
294                 stored_seq_no = session_db->tcp_seq_num;
295                 stored_ack_no = session_db->ack_no;
296                 window        = session_db->window;
297             }   
298             #endif      
299         }
300
301         
302 update_pkt:
303         
304         counter = CNAT_V4_TCP_O2I_T_PKT;
305
306         if (PLATFORM_HANDLE_TTL_DECREMENT) {
307             /*
308              * Decrement TTL and update IPv4 checksum
309              */
310             ipv4_decr_ttl_n_calc_csum(ip);
311         }
312
313         /* update ip checksum, newchecksum = ~(~oldchecksum + ~old + new) */
314         cnat_v4_recalculate_tcp_checksum(ip, tcp,
315                                          &(ip->dest_addr),
316                                          &(tcp->dest_port),
317                                          db->in2out_key.k.ipv4,
318                                          db->in2out_key.k.port);
319
320         /* CNAT_PPTP_ALG_SUPPORT */
321         db->out2in_pkts++;
322
323         nat44_dslite_global_stats[0].out2in_forwarding_count++;;
324
325         V4_TCP_UPDATE_SESSION_FLAG(db, tcp);
326
327         
328         if(PREDICT_FALSE(session_db != NULL)) {
329             V4_TCP_UPDATE_SESSION_DB_FLAG(session_db, tcp);
330                     CNAT_DB_TIMEOUT_RST(session_db);
331         } else {
332             V4_TCP_UPDATE_SESSION_FLAG(db, tcp);
333                     CNAT_DB_TIMEOUT_RST(db);
334         }
335
336     }
337
338 drop_pkt:
339     em->counters[node_counter_base_index + counter] += 1;
340     return  disposition;
341 }
342
343 #include <vnet/pipeline.h>
344
345 static uword cnat_ipv4_tcp_outside_input_node_fn (vlib_main_t * vm,
346                               vlib_node_runtime_t * node,
347                               vlib_frame_t * frame)
348 {
349     return dispatch_pipeline (vm, node, frame);
350 }
351
352
353 VLIB_REGISTER_NODE (cnat_ipv4_tcp_outside_input_node) = {
354   .function = cnat_ipv4_tcp_outside_input_node_fn,
355   .name = "vcgn-v4-tcp-o2i",
356   .vector_size = sizeof (u32),
357   .type = VLIB_NODE_TYPE_INTERNAL,
358
359   .n_errors = ARRAY_LEN(cnat_ipv4_tcp_outside_input_error_strings),
360   .error_strings = cnat_ipv4_tcp_outside_input_error_strings,
361
362   .n_next_nodes = CNAT_V4_TCP_O2I_NEXT,
363
364   /* edit / add dispositions here */
365   .next_nodes = {
366       //[CNAT_V4_TCP_O2I_E] = "vcgn-v4-tcp-o2i-e",
367       [CNAT_V4_TCP_O2I_T] = "ip4-input",
368       [CNAT_V4_TCP_O2I_D] = "error-drop",
369   },
370 };
371
372 clib_error_t *cnat_ipv4_tcp_outside_input_init (vlib_main_t *vm)
373 {
374   cnat_ipv4_tcp_outside_input_main_t * mp = &cnat_ipv4_tcp_outside_input_main;
375
376   mp->vlib_main = vm;
377   mp->vnet_main = vnet_get_main();
378
379   return 0;
380 }
381
382 VLIB_INIT_FUNCTION (cnat_ipv4_tcp_outside_input_init);