Initial commit of vpp code.
[vpp.git] / vnet / vnet / vcgn / cnat_ipv4_icmp_query_outside_input.c
1 /* 
2  *---------------------------------------------------------------------------
3  * cnat_ipv4_icmp_query_outside_input.c - cnat_ipv4_icmp_query_outside_input 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 #include <vlib/vlib.h>
21 #include <vnet/vnet.h>
22 #include <vppinfra/error.h>
23 #include <vnet/buffer.h>
24
25 #include "cnat_ipv4_icmp.h"
26
27 #define foreach_cnat_ipv4_icmp_q_outside_input_error            \
28 _(CNAT_V4_ICMP_Q_O2I_T_PKT, "cnat v4 icmp_q o2i packet transmit")                       \
29 _(CNAT_V4_ICMP_Q_O2I_MISS_PKT, "cnat v4 icmp_q o2i drop")                       \
30 _(CNAT_V4_ICMP_Q_O2I_TTL_GEN, "cnat v4 icmp_q o2i ttl generate")                        \
31 _(CNAT_V4_ICMP_Q_O2I_TTL_DROP, "cnat v4 icmp_q o2i ttl drop")                   \
32 _(CNAT_V4_ICMP_Q_O2I_NO_SESSION_DROP, "cnat v4 icmp_q o2i no session drop")
33
34 typedef enum {
35 #define _(sym,str) sym,
36   foreach_cnat_ipv4_icmp_q_outside_input_error 
37 #undef _
38   CNAT_IPV4_ICMP_Q_OUTSIDE_INPUT_N_ERROR,
39 } cnat_ipv4_icmp_q_outside_input_t;
40
41 static char * cnat_ipv4_icmp_q_outside_input_error_strings[] = {
42 #define _(sym,string) string,
43   foreach_cnat_ipv4_icmp_q_outside_input_error
44 #undef _
45 };
46
47 typedef struct {
48   u32 cached_next_index;
49   /* $$$$ add data here */
50
51   /* convenience variables */
52   vlib_main_t * vlib_main;
53   vnet_main_t * vnet_main;
54 } cnat_ipv4_icmp_q_outside_input_main_t;
55
56 typedef enum {
57     CNAT_V4_ICMP_Q_O2I_T,
58     CNAT_V4_ICMP_Q_O2I_D,
59     CNAT_V4_ICMP_Q_O2I_NEXT,
60 } cnat_ipv4_icmp_q_outside_input_next_t;
61
62 cnat_ipv4_icmp_q_outside_input_main_t cnat_ipv4_icmp_q_outside_input_main;
63 vlib_node_registration_t cnat_ipv4_icmp_q_outside_input_node;
64
65 #define NSTAGES 5
66
67 inline void swap_ip_dst_icmp_id(ipv4_header *ip,
68                                 icmp_v4_t *icmp,
69                          cnat_main_db_entry_t *db, u16 vrf)
70 {
71 #if 0
72     u32 postmap_ip;
73     u8 direction;
74     u32 old_ip;
75     u32 old_postmap_ip;
76
77     if(is_static_dest_nat_enabled(vrf) == CNAT_SUCCESS) {
78         direction = 1;
79         if(cnat_static_dest_db_get_translation(ip->src_addr, &postmap_ip, vrf, direction) ==  CNAT_SUCCESS) {
80              CNAT_UPDATE_L3_CHECKSUM_DECLARE 
81                 
82             old_ip = spp_net_to_host_byte_order_32(&(ip->src_addr));
83             old_postmap_ip = spp_net_to_host_byte_order_32(&postmap_ip);
84
85             CNAT_UPDATE_L3_CHECKSUM(((u16)(old_ip & 0xFFFF)),
86                                     ((u16)(old_ip >> 16)),
87                                     (spp_net_to_host_byte_order_16(&(ip->checksum))),
88                                     ((u16)(old_postmap_ip & 0xFFFF)),
89                                     ((u16)(old_postmap_ip >> 16)))          
90             ip->checksum =
91                 spp_host_to_net_byte_order_16(new_l3_c);
92             ip->src_addr = postmap_ip;
93         }
94     }
95 #endif /* if 0 */
96     /*
97      * declare variable
98      */
99     CNAT_UPDATE_L3_L4_CHECKSUM_DECLARE
100     /*
101      * calculate checksum
102      */
103     CNAT_UPDATE_L3_ICMP_CHECKSUM(((u16)(db->out2in_key.k.ipv4)),
104                                ((u16)(db->out2in_key.k.ipv4 >> 16)),
105                                (db->out2in_key.k.port),
106                                (clib_net_to_host_u16(ip->checksum)),
107                                (clib_net_to_host_u16(icmp->checksum)),
108                                ((u16)(db->in2out_key.k.ipv4)),
109                                ((u16)(db->in2out_key.k.ipv4 >> 16)),
110                                (db->in2out_key.k.port))
111     //set ip header
112     ip->dest_addr =
113         clib_host_to_net_u32(db->in2out_key.k.ipv4);
114     ip->checksum =
115         clib_host_to_net_u16(new_l3_c);
116
117     //set icmp header
118     icmp->identifier =
119         clib_host_to_net_u16(db->in2out_key.k.port);
120     icmp->checksum =
121         clib_host_to_net_u16(new_l4_c);
122 }
123
124 /*
125  * Use the generic buffer metadata + first line of packet data prefetch
126  * stage function from <api/pipeline.h>. This is usually a Good Idea.
127  */
128 #define stage0 generic_stage0
129
130 static inline void
131 stage1(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
132 {
133     u64 a, b, c;
134     u32 bucket;
135     u8 *prefetch_target;
136
137     vlib_buffer_t * b0 = vlib_get_buffer (vm, buffer_index);
138     ipv4_header *ip = vlib_buffer_get_current (b0);
139     u8   ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
140     icmp_v4_t *icmp = (icmp_v4_t *)((u8*)ip + ipv4_hdr_len);
141   
142     u64 tmp = 0;
143     tmp = vnet_buffer(b0)->vcgn_uii.key.k.ipv4 =
144             clib_net_to_host_u32(ip->dest_addr);
145     vnet_buffer(b0)->vcgn_uii.key.k.port =
146             clib_net_to_host_u16 (icmp->identifier);
147
148     tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.port) << 32;
149
150     PLATFORM_CNAT_SET_RX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_RX],
151                              vnet_buffer(b0)->vcgn_uii.key.k.vrf,
152                              CNAT_ICMP)
153     tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.vrf) << 48;
154
155     CNAT_V4_GET_HASH(tmp, bucket, CNAT_MAIN_HASH_MASK)
156
157     prefetch_target = (u8 *)(&cnat_out2in_hash[bucket]);
158     vnet_buffer(b0)->vcgn_uii.bucket = bucket;
159      
160     /* Prefetch the hash bucket */
161     CLIB_PREFETCH(prefetch_target, CLIB_CACHE_LINE_BYTES, LOAD);
162 }
163
164 #define SPP_LOG2_CACHE_LINE_BYTES 6
165 #define SPP_CACHE_LINE_BYTES (1 << SPP_LOG2_CACHE_LINE_BYTES)
166
167 static inline void
168 stage2(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
169 {
170     vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
171     uword prefetch_target0, prefetch_target1;
172     u32 bucket = vnet_buffer(b0)->vcgn_uii.bucket;
173   
174     /* read the hash bucket */
175     u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket
176                  = cnat_out2in_hash[bucket].next;
177
178     if (PREDICT_TRUE(db_index != EMPTY)) {
179         /*
180          * Prefetch database keys. We save space by not cache-line
181          * aligning the DB entries. We don't want to waste LSU
182          * bandwidth prefetching stuff we won't need.
183          */
184         prefetch_target0 = (uword)(cnat_main_db + db_index);
185         CLIB_PREFETCH((void*)prefetch_target0, CLIB_CACHE_LINE_BYTES, LOAD);
186         /* Just beyond DB key #2 */
187         prefetch_target1 = prefetch_target0 +
188         STRUCT_OFFSET_OF(cnat_main_db_entry_t, user_ports);
189         /* If the targets are in different lines, do the second prefetch */
190         if (PREDICT_FALSE((prefetch_target0 & ~(SPP_CACHE_LINE_BYTES-1)) !=
191                       (prefetch_target1 & ~(SPP_CACHE_LINE_BYTES-1)))) {
192             CLIB_PREFETCH((void *)prefetch_target1, CLIB_CACHE_LINE_BYTES, LOAD);
193         }
194     }
195 }
196
197 static inline void
198 stage3(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
199 {
200   cnat_main_db_entry_t *db;
201   vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
202   u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
203
204   /*
205    * Note: if the search already failed (empty bucket),
206    * the answer is already in the pipeline context structure
207    */
208   if (PREDICT_TRUE(db_index != EMPTY)) {
209
210     /*
211      * Note: hash collisions suck. We can't easily prefetch around them.
212      * The first trip around the track will be fast. After that, maybe
213      * not so much...
214      */
215     do {
216       db = cnat_main_db + db_index;
217       if (PREDICT_TRUE(db->out2in_key.key64 ==
218                   vnet_buffer(b0)->vcgn_uii.key.key64)) {
219         break;
220       }
221       db_index = db->out2in_hash.next;
222     } while (db_index != EMPTY);
223
224     /* Stick the answer back into the pipeline context structure */
225     vnet_buffer(b0)->vcgn_uii.bucket = db_index;
226   }
227 }
228
229
230 static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
231                               u32 bi)
232 {
233
234     vlib_buffer_t *b0 = vlib_get_buffer (vm, bi);
235     u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
236     int disposition = CNAT_V4_ICMP_Q_O2I_T;
237     int counter = CNAT_V4_ICMP_Q_O2I_T_PKT;
238
239     ipv4_header *ip = (ipv4_header *)vlib_buffer_get_current(b0);
240     u8   ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
241     icmp_v4_t *icmp = (icmp_v4_t *)((u8*)ip + ipv4_hdr_len);
242     vlib_node_t *n = vlib_get_node (vm, cnat_ipv4_icmp_q_outside_input_node.index);
243     u32 node_counter_base_index = n->error_heap_index;
244     vlib_error_main_t * em = &vm->error_main;
245     cnat_session_entry_t *session_db = NULL;
246     cnat_main_db_entry_t *db = NULL;
247     cnat_key_t dest_info;
248     cnat_vrfmap_t * vrf_map_p __attribute__((unused)) = NULL;
249     u32 vrf_index __attribute__((unused)) = 0;
250
251     if (PREDICT_TRUE(db_index != EMPTY)) {
252
253         db = cnat_main_db + db_index;
254         dest_info.k.port = 0;
255         dest_info.k.ipv4 = clib_net_to_host_u32(ip->src_addr);
256
257         if (PREDICT_FALSE(icmp_debug_flag)) {
258             printf("\nDUMPING ICMP PKT BEFORE\n");
259             print_icmp_pkt(ip);
260         }
261
262        vrf_map_p     = cnat_map_by_vrf + db->vrfmap_index;
263        vrf_index     = (db->in2out_key.k.vrf & CNAT_VRF_MASK);
264
265        if(PREDICT_TRUE(!PLATFORM_DBL_SUPPORT)) {
266
267             /* No DBL support, so just update the destn and proceed */
268             db->dst_ipv4 = dest_info.k.ipv4;
269             db->dst_port = dest_info.k.port;
270             goto update_pkt;
271         }
272
273         if(PREDICT_FALSE(db->dst_ipv4 != dest_info.k.ipv4)) {
274
275             if(PREDICT_TRUE(db->nsessions == 1)) {
276                 /* Handle one to 2 dest scenarion */
277                 dest_info.k.vrf = db->in2out_key.k.vrf;
278                 session_db = cnat_handle_1to2_session(db, &dest_info);
279
280                 if(PREDICT_FALSE(session_db == NULL)) {
281                     disposition = CNAT_V4_ICMP_Q_O2I_D;
282                     counter = CNAT_V4_ICMP_Q_O2I_NO_SESSION_DROP;
283                     goto drop_pkt;
284                 }
285             } else if (PREDICT_FALSE(db->nsessions == 0)) {
286                 /* Should be a static entry
287                  * Note this session as the first session and log
288                  */
289                 cnat_add_dest_n_log(db, &dest_info);
290             } else { /* Many translations exist already */
291                 dest_info.k.vrf = db->in2out_key.k.vrf;
292                 /* If session already exists,
293                  * cnat_create_session_db_entry will return the existing db
294                  * else create a new db
295                  * If could not create, return NULL
296                  */
297                 session_db = cnat_create_session_db_entry(&dest_info,
298                     db, TRUE);
299
300                 if(PREDICT_FALSE(session_db == NULL)) {
301                     disposition = CNAT_V4_ICMP_Q_O2I_D;
302                     counter = CNAT_V4_ICMP_Q_O2I_NO_SESSION_DROP;
303                     goto drop_pkt;
304                 }
305             }
306         }
307
308 update_pkt:
309
310         if (PLATFORM_HANDLE_TTL_DECREMENT) {
311             /*
312              * Decrement TTL and update IPv4 checksum
313              */
314             ipv4_decr_ttl_n_calc_csum(ip);
315         }
316
317         /*
318          * 1. update dest ipv4 addr and icmp id
319          * 2. update ipv4 checksum and icmp checksum
320          */
321         swap_ip_dst_icmp_id(ip, icmp, db, db->in2out_key.k.vrf);
322
323         if (PREDICT_FALSE(icmp_debug_flag)) {
324             printf("\nDUMPING ICMP PKT AFTER\n");
325             print_icmp_pkt(ip);
326         }
327
328         db->out2in_pkts++;
329
330         //nat44_dslite_global_stats[dslite_flag].out2in_forwarding_count++;
331
332     } else {
333         disposition = CNAT_V4_ICMP_Q_O2I_D;
334         counter = CNAT_V4_ICMP_Q_O2I_MISS_PKT;
335     }
336
337 drop_pkt:
338     em->counters[node_counter_base_index + counter] += 1;
339     return  disposition;
340
341 }
342
343 #include <vnet/pipeline.h>
344
345 static uword cnat_ipv4_icmp_q_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_icmp_q_outside_input_node) = {
354   .function = cnat_ipv4_icmp_q_outside_input_node_fn,
355   .name = "vcgn-v4-icmp-q-o2i",
356   .vector_size = sizeof (u32),
357   .type = VLIB_NODE_TYPE_INTERNAL,
358
359   .n_errors = ARRAY_LEN(cnat_ipv4_icmp_q_outside_input_error_strings),
360   .error_strings = cnat_ipv4_icmp_q_outside_input_error_strings,
361
362   .n_next_nodes = CNAT_V4_ICMP_Q_O2I_NEXT,
363
364   /* edit / add dispositions here */
365   .next_nodes = {
366       [CNAT_V4_ICMP_Q_O2I_T] = "ip4-input",
367       [CNAT_V4_ICMP_Q_O2I_D] = "error-drop",
368   },
369 };
370
371 clib_error_t *cnat_ipv4_icmp_q_outside_input_init (vlib_main_t *vm)
372 {
373   cnat_ipv4_icmp_q_outside_input_main_t * mp = &cnat_ipv4_icmp_q_outside_input_main;
374
375   mp->vlib_main = vm;
376   mp->vnet_main = vnet_get_main();
377
378   return 0;
379 }
380
381 VLIB_INIT_FUNCTION (cnat_ipv4_icmp_q_outside_input_init);