3 *---------------------------------------------------------------------------
4 * cnat_ipv4_udp_outside_input_stages.c - cnat_ipv4_udp_outside_input node pipeline stage functions
7 * Copyright (c) 2008-2014 Cisco and/or its affiliates.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at:
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *---------------------------------------------------------------------------
22 #include <vlib/vlib.h>
23 #include <vnet/vnet.h>
24 #include <vppinfra/error.h>
25 #include <vnet/buffer.h>
27 #include "cnat_ipv4_udp.h"
28 #include "dslite_db.h"
30 #include "cnat_v4_functions.h"
32 //#include <dslite_v6_functions.h>
34 //#include "cnat_va_db.h"
36 #define foreach_cnat_ipv4_udp_outside_input_error \
37 _(CNAT_V4_UDP_O2I_T_PKT, "v4 udp o2i transmit") \
38 _(CNAT_V4_DSLITE_ENCAP_CTR, "to dslite encap") \
39 _(CNAT_V4_UDP_O2I_MISS_PKT, "v4 udp o2i db miss drop") \
40 _(CNAT_V4_UDP_O2I_TTL_GEN, "v4 udp o2i TTL gen") \
41 _(CNAT_V4_UDP_O2I_TTL_DROP, "v4 udp o2i TTL drop") \
42 _(CNAT_V4_UDP_O2I_PTB_GEN, "v4 ptb gen") \
43 _(CNAT_V4_UDP_O2I_PTB_DROP, "v4 ptb throttle drop") \
44 _(CNAT_V4_UDP_O2I_SESSION_DROP, "v4 udp o2i session drop") \
45 _(CNAT_V4_UDP_O2I_FILTER_DROP, "v4 udp o2i drop: end point filtering") \
46 _(CNAT_V4_UDP_O2I_SUB_FRAG_NO_DB_DROP, "v4 udp o2i subsequent frag no DB drop") \
47 _(CNAT_V4_UDP_O2I_1ST_FRAG_FILTER_DROP, "v4 udp i2o 1st frag filter drop")
50 #define _(sym,str) sym,
51 foreach_cnat_ipv4_udp_outside_input_error
53 CNAT_IPV4_UDP_OUTSIDE_INPUT_N_ERROR,
54 } cnat_ipv4_udp_outside_input_t;
56 static char * cnat_ipv4_udp_outside_input_error_strings[] = {
57 #define _(sym,string) string,
58 foreach_cnat_ipv4_udp_outside_input_error
63 u32 cached_next_index;
64 /* $$$$ add data here */
66 /* convenience variables */
67 vlib_main_t * vlib_main;
68 vnet_main_t * vnet_main;
69 } cnat_ipv4_udp_outside_input_main_t;
76 } cnat_ipv4_udp_outside_input_next_t;
78 //#define CNAT_V4_DSLITE_ENCAP CNAT_V4_O2I_FIXME
79 //#define CNAT_V4_UDP_O2I_E CNAT_V4_O2I_FIXME
81 cnat_ipv4_udp_outside_input_main_t cnat_ipv4_udp_outside_input_main;
82 vlib_node_registration_t cnat_ipv4_udp_outside_input_node;
87 * Use the generic buffer metadata + first line of packet data prefetch
88 * stage function from <api/pipeline.h>. This is usually a Good Idea.
90 #define stage0 generic_stage0
94 typedef struct cnat_ipv4_udp_outside_input_pipeline_data_ {
95 //spp_node_main_vector_t *nmv;
96 dslite_common_pipeline_data_t common_data;
97 /* Add additional pipeline stage data here... */
101 dslite_v4_to_v6_udp_counter_t *udp_counter;
102 dslite_icmp_gen_counter_t *icmp_gen_counter;
108 } cnat_ipv4_udp_outside_input_pipeline_data_t;
112 #define CNAT_UDP_OUTSIDE_UPDATE_FLAG_TIMER(db,dslite_nat44_inst_id) \
113 if (PREDICT_FALSE(!(db->flags & CNAT_DB_FLAG_UDP_ACTIVE))) { \
114 db->flags |= CNAT_DB_FLAG_UDP_ACTIVE; \
115 CNAT_DB_TIMEOUT_RST(db); \
116 } else if (PREDICT_FALSE(db->flags & CNAT_DB_DSLITE_FLAG)) { \
117 if (PREDICT_TRUE(dslite_table_db_ptr[dslite_nat44_inst_id].mapping_refresh_both_direction)) { \
118 CNAT_DB_TIMEOUT_RST(db); \
120 } else if (PREDICT_TRUE(mapping_refresh_both_direction)) { \
121 CNAT_DB_TIMEOUT_RST(db); \
125 static cnat_ipv4_udp_outside_input_pipeline_data_t pctx_data[SPP_MAXDISPATCH];
126 #define EXTRA_PIPELINE_ARGS_PROTO , cnat_ipv4_udp_outside_input_pipeline_data_t *pctx
127 #define EXTRA_PIPELINE_ARGS , pctx
132 is_static_dest_nat_enabled(u16 vrf)
134 if(static_dest_vrf_map_array[vrf] == 1) {
137 return CNAT_NO_CONFIG;
140 static inline void __attribute__((unused))
141 swap_ip_dst(ipv4_header *ip, cnat_main_db_entry_t *db, u16 vrf)
144 CNAT_UPDATE_L3_CHECKSUM_DECLARE
148 CNAT_UPDATE_L3_CHECKSUM(((u16)(db->out2in_key.k.ipv4)),
149 ((u16)(db->out2in_key.k.ipv4 >> 16)),
150 (clib_host_to_net_u16(ip->checksum)),
151 ((u16)(db->in2out_key.k.ipv4)),
152 ((u16)(db->in2out_key.k.ipv4 >> 16)))
155 clib_host_to_net_u32(db->in2out_key.k.ipv4);
157 clib_host_to_net_u16(new_l3_c);
161 if(is_static_dest_nat_enabled(vrf) == CNAT_SUCCESS) {
163 if(cnat_static_dest_db_get_translation(ip->src_addr, &postmap_ip, vrf, direction) == CNAT_SUCCESS) {
164 old_ip = spp_net_to_host_byte_order_32(&(ip->src_addr));
165 old_postmap_ip = spp_net_to_host_byte_order_32(&postmap_ip);
167 CNAT_UPDATE_L3_CHECKSUM(((u16)(old_ip & 0xFFFF)),
168 ((u16)(old_ip >> 16)),
169 (spp_net_to_host_byte_order_16(&(ip->checksum))),
170 ((u16)(old_postmap_ip & 0xFFFF)),
171 ((u16)(old_postmap_ip >> 16)))
173 clib_host_to_net_u16(new_l3_c);
174 ip->src_addr = postmap_ip;
180 inline void swap_ip_dst_udp_port(ipv4_header *ip,
182 cnat_main_db_entry_t *db, u16 vrf)
185 #define UDP_PACKET_DEBUG 1
187 // Temporary debugs which will be suppressed later
188 #ifdef UDP_PACKET_DEBUG
189 if (PREDICT_FALSE(udp_outside_packet_dump_enable)) {
190 printf("\nOut2In UDP packet before translation");
196 if(is_static_dest_nat_enabled(vrf) == CNAT_SUCCESS) {
198 if(cnat_static_dest_db_get_translation(ip->src_addr, &postmap_ip, vrf, direction) == CNAT_SUCCESS) {
200 CNAT_UPDATE_L3_L4_CHECKSUM_DECLARE
202 old_ip = spp_net_to_host_byte_order_32(&(ip->src_addr));
203 old_postmap_ip = spp_net_to_host_byte_order_32(&postmap_ip);
205 CNAT_UPDATE_L3_L4_CHECKSUM(((u16)(old_ip & 0xFFFF)),
206 ((u16)(old_ip >> 16)),
207 (spp_net_to_host_byte_order_16(&(udp->src_port))),
208 (spp_net_to_host_byte_order_16(&(ip->checksum))),
209 (spp_net_to_host_byte_order_16(&(udp->udp_checksum))),
210 ((u16)(old_postmap_ip & 0xFFFF)),
211 ((u16)(old_postmap_ip >> 16)),
212 (spp_net_to_host_byte_order_16(&(udp->src_port))))
215 clib_host_to_net_u16(new_l3_c);
216 ip->src_addr = postmap_ip;
217 if (PREDICT_TRUE(udp->udp_checksum)) {
218 udp->udp_checksum = clib_host_to_net_u16(new_l4_c);
226 CNAT_UPDATE_L3_L4_CHECKSUM_DECLARE
230 CNAT_UPDATE_L3_L4_CHECKSUM(((u16)(db->out2in_key.k.ipv4)),
231 ((u16)(db->out2in_key.k.ipv4 >> 16)),
232 (db->out2in_key.k.port),
233 (clib_net_to_host_u16(ip->checksum)),
234 (clib_net_to_host_u16(udp->udp_checksum)),
235 ((u16)(db->in2out_key.k.ipv4)),
236 ((u16)(db->in2out_key.k.ipv4 >> 16)),
237 (db->in2out_key.k.port))
244 clib_host_to_net_u32(db->in2out_key.k.ipv4);
246 clib_host_to_net_u16(new_l3_c);
250 clib_host_to_net_u16(db->in2out_key.k.port);
253 * No easy way to avoid this if check except by using
254 * complex logic - may not be worth it.
256 if (PREDICT_TRUE(udp->udp_checksum)) {
257 udp->udp_checksum = clib_host_to_net_u16(new_l4_c);
262 // Temporary debugs which will be suppressed later
263 #ifdef UDP_PACKET_DEBUG
264 if (PREDICT_FALSE(udp_outside_checksum_disable)) {
265 printf("\nOut2In UDP checksum 0x%x disabled by force", new_l4_c);
266 udp->udp_checksum = 0;
268 if (PREDICT_FALSE(udp_outside_packet_dump_enable)) {
269 printf("\nOut2In UDP packet after translation");
276 stage1(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
282 vlib_buffer_t * b0 = vlib_get_buffer (vm, buffer_index);
283 ipv4_header *ip = vlib_buffer_get_current (b0);
284 u8 ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
285 udp_hdr_type_t *udp = (udp_hdr_type_t *)((u8*)ip + ipv4_hdr_len);
288 tmp = vnet_buffer(b0)->vcgn_uii.key.k.ipv4 =
289 clib_net_to_host_u32(ip->dest_addr);
290 vnet_buffer(b0)->vcgn_uii.key.k.port =
291 clib_net_to_host_u16 (udp->dest_port);
293 tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.port) << 32;
295 PLATFORM_CNAT_SET_RX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_RX],
296 vnet_buffer(b0)->vcgn_uii.key.k.vrf,
298 tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.vrf) << 48;
300 CNAT_V4_GET_HASH(tmp, bucket, CNAT_MAIN_HASH_MASK)
302 prefetch_target = (u8 *)(&cnat_out2in_hash[bucket]);
303 vnet_buffer(b0)->vcgn_uii.bucket = bucket;
305 /* Prefetch the hash bucket */
306 CLIB_PREFETCH(prefetch_target, CLIB_CACHE_LINE_BYTES, LOAD);
310 stage2(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
313 #define SPP_LOG2_CACHE_LINE_BYTES 6
314 #define SPP_CACHE_LINE_BYTES (1 << SPP_LOG2_CACHE_LINE_BYTES)
317 stage3(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
319 vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
320 uword prefetch_target0, prefetch_target1;
321 u32 bucket = vnet_buffer(b0)->vcgn_uii.bucket;
323 /* read the hash bucket */
324 u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket
325 = cnat_out2in_hash[bucket].next;
327 if (PREDICT_TRUE(db_index != EMPTY)) {
329 * Prefetch database keys. We save space by not cache-line
330 * aligning the DB entries. We don't want to waste LSU
331 * bandwidth prefetching stuff we won't need.
333 prefetch_target0 = (uword)(cnat_main_db + db_index);
334 CLIB_PREFETCH((void*)prefetch_target0, CLIB_CACHE_LINE_BYTES, STORE);
335 /* Just beyond DB key #2 */
336 prefetch_target1 = prefetch_target0 +
337 STRUCT_OFFSET_OF(cnat_main_db_entry_t, user_ports);
338 /* If the targets are in different lines, do the second prefetch */
339 if (PREDICT_FALSE((prefetch_target0 & ~(SPP_CACHE_LINE_BYTES-1)) !=
340 (prefetch_target1 & ~(SPP_CACHE_LINE_BYTES-1)))) {
341 CLIB_PREFETCH((void *)prefetch_target1, CLIB_CACHE_LINE_BYTES, STORE);
347 stage4(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
349 cnat_main_db_entry_t *db;
350 vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
351 u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
354 * Note: if the search already failed (empty bucket),
355 * the answer is already in the pipeline context structure
357 if (PREDICT_TRUE(db_index != EMPTY)) {
360 * Note: hash collisions suck. We can't easily prefetch around them.
361 * The first trip around the track will be fast. After that, maybe
365 db = cnat_main_db + db_index;
366 if (PREDICT_TRUE(db->out2in_key.key64 ==
367 vnet_buffer(b0)->vcgn_uii.key.key64)) {
370 db_index = db->out2in_hash.next;
371 } while (db_index != EMPTY);
373 /* Stick the answer back into the pipeline context structure */
374 vnet_buffer(b0)->vcgn_uii.bucket = db_index;
382 stage5(spp_ctx_t **ctxs, int index, spp_node_t *np,
383 u8 *disp_used EXTRA_PIPELINE_ARGS_PROTO))
385 spp_ctx_t *ctx = ctxs[index];
386 u32 db_index = pctx[index].bucket;
387 /* for nat44, dslite_id will be 1 */
388 u16 dslite_id = *(pctx[index].common_data.dslite_id_ptr);
390 DSLITE_PREFETCH_COUNTER(pctx[index].udp_counter,
391 &dslite_all_counters[dslite_id].v46_udp_counters,
392 dslite_v4_to_v6_udp_counter_t,
393 v4_to_v6_udp_output_count,
396 DSLITE_PREFETCH_COUNTER(pctx[index].icmp_gen_counter,
397 &dslite_all_counters[dslite_id].dslite_icmp_gen_counters,
398 dslite_icmp_gen_counter_t,
402 if (PREDICT_TRUE(db_index != EMPTY)) {
403 cnat_main_db_entry_t *db = cnat_main_db + db_index;
405 u32 user_db_index = db->user_index;
406 DSLITE_PRINTF(1, "UDP o2i, db entry found %u %u %u\n",
407 db_index, user_db_index,
408 db->dslite_nat44_inst_id);
409 uword prefetch_target0 = (uword)(cnat_user_db + user_db_index);
410 SPP_PREFETCH(prefetch_target0, 0, LOAD);
411 pctx[index].user_bucket = user_db_index;
412 DSLITE_PRINTF(1, "UDP: Done with prefetch..\n");
414 DSLITE_PRINTF(1, "UDP: Stage 5, db_index empty...\n");
421 static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
425 vlib_buffer_t *b0 = vlib_get_buffer (vm, bi);
426 u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
427 //spp_ctx_t *ctx = (spp_ctx_t *) &vnet_buffer(b0)->vcgn_uii;
428 int disposition = CNAT_V4_UDP_O2I_T;
429 int counter = CNAT_V4_UDP_O2I_T_PKT;
430 ipv4_header *ip = (ipv4_header *)vlib_buffer_get_current(b0);
431 u8 ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
432 udp_hdr_type_t *udp = (udp_hdr_type_t *)((u8*)ip + ipv4_hdr_len);
433 vlib_node_t *n = vlib_get_node (vm, cnat_ipv4_udp_outside_input_node.index);
434 u32 node_counter_base_index = n->error_heap_index;
435 vlib_error_main_t * em = &vm->error_main;
436 cnat_session_entry_t *session_db = NULL;
437 cnat_main_db_entry_t *db = NULL;
438 cnat_key_t dest_info;
439 u16 dslite_nat44_inst_id __attribute__((unused)) = 0;
441 dest_info.k.port = clib_net_to_host_u16(udp->src_port);
442 dest_info.k.ipv4 = clib_net_to_host_u32(ip->src_addr);
444 if (PREDICT_TRUE(db_index != EMPTY)) {
445 /* TTL gen was disabled for nat44 earlier
446 * But since dslite has got integrated in this
450 db = cnat_main_db + db_index;
451 if (PLATFORM_HANDLE_TTL_DECREMENT) {
453 * Decrement TTL and update IPv4 checksum
455 ipv4_decr_ttl_n_calc_csum(ip);
457 if(PREDICT_TRUE(!PLATFORM_DBL_SUPPORT)) {
459 /* No DBL support, so just update the destn and proceed */
460 db->dst_ipv4 = dest_info.k.ipv4;
461 db->dst_port = dest_info.k.port;
462 CNAT_UDP_OUTSIDE_UPDATE_FLAG_TIMER(db, 0)
467 if(PREDICT_TRUE((db->dst_ipv4 == dest_info.k.ipv4) &&
468 (db->dst_port == dest_info.k.port))) {
470 CNAT_UDP_OUTSIDE_UPDATE_FLAG_TIMER(db, 0)
473 /* The session entries belonging to this entry are checked to find
474 * if an entry exist whose destination IP and port match with the
475 * source IP and port of the packet being processed
477 dest_info.k.vrf = db->in2out_key.k.vrf;
479 if (PREDICT_FALSE(db->nsessions == 0)) {
480 /* Should be a static entry
481 * Note this session as the first session and log
483 cnat_add_dest_n_log(db, &dest_info);
484 CNAT_UDP_OUTSIDE_UPDATE_FLAG_TIMER(db, 0)
486 } else if(PREDICT_TRUE(db->nsessions == 1)) {
488 /* Destn is not same as in main db. Multiple session
491 dest_info.k.vrf = db->in2out_key.k.vrf;
492 session_db = cnat_handle_1to2_session(db, &dest_info);
494 if(PREDICT_FALSE(session_db == NULL)) {
495 disposition = CNAT_V4_UDP_O2I_E;
496 counter = CNAT_V4_UDP_O2I_SESSION_DROP;
500 /* update session_db(cur packet) timer */
501 CNAT_UDP_OUTSIDE_UPDATE_FLAG_TIMER(session_db, 0)
503 /* More 2 sessions exists */
505 dest_info.k.vrf = db->in2out_key.k.vrf;
507 /* If session already exists,
508 * cnat_create_session_db_entry will return the existing db
509 * else create a new db
510 * If could not create, return NULL
512 session_db = cnat_create_session_db_entry(&dest_info,
515 if(PREDICT_FALSE(session_db != NULL)) {
517 CNAT_UDP_OUTSIDE_UPDATE_FLAG_TIMER(session_db, 0)
519 /* could not create session db - drop packet */
520 disposition = CNAT_V4_UDP_O2I_E;
521 counter = CNAT_V4_UDP_O2I_SESSION_DROP;
530 * 1. update dest ipv4 addr and dest udp port
531 * 2. update ipv4 checksum and udp checksum
533 //swap_ip_dst(ip, db, db->in2out_key.k.vrf);
534 swap_ip_dst_udp_port(ip, udp, db, db->in2out_key.k.vrf);
535 //DSLITE_PRINTF(1, "Done with swap_ip_dst_udp_port..\n");
539 nat44_dslite_global_stats[0].out2in_forwarding_count++;
541 /* #### Temporarily COMMENTED FOR IP ROUTE LOOKUP ISSUE #### */
543 //PLATFORM_CNAT_SET_TX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_TX],
544 // db->in2out_key.k.vrf)
546 disposition = CNAT_V4_UDP_O2I_E;
547 counter = CNAT_V4_UDP_O2I_MISS_PKT;
548 /* for NAT44 dslite_id would be 1 */
549 nat44_dslite_common_stats[0].no_translation_entry_drops ++;
554 em->counters[node_counter_base_index + counter] += 1;
558 #include <vnet/pipeline.h>
560 static uword cnat_ipv4_udp_outside_input_node_fn (vlib_main_t * vm,
561 vlib_node_runtime_t * node,
562 vlib_frame_t * frame)
564 return dispatch_pipeline (vm, node, frame);
568 VLIB_REGISTER_NODE (cnat_ipv4_udp_outside_input_node) = {
569 .function = cnat_ipv4_udp_outside_input_node_fn,
570 .name = "vcgn-v4-udp-o2i",
571 .vector_size = sizeof (u32),
572 .type = VLIB_NODE_TYPE_INTERNAL,
574 .n_errors = ARRAY_LEN(cnat_ipv4_udp_outside_input_error_strings),
575 .error_strings = cnat_ipv4_udp_outside_input_error_strings,
577 .n_next_nodes = CNAT_V4_UDP_O2I_NEXT,
579 /* edit / add dispositions here */
582 //[CNAT_V4_O2I_FIXME] = "error-drop",
583 //[CNAT_V4_UDP_O2I_E] = "vcgn-v4-udp-o2i-e",
584 [CNAT_V4_UDP_O2I_E] = "vcgn-v4-udp-o2i-e",
585 [CNAT_V4_UDP_O2I_T] = "ip4-input",
589 [CNAT_V4_UDP_O2I_E] = "error-drop",
590 [CNAT_V4_UDP_O2I_T] = "ip4-input",
595 clib_error_t *cnat_ipv4_udp_outside_input_init (vlib_main_t *vm)
597 cnat_ipv4_udp_outside_input_main_t * mp = &cnat_ipv4_udp_outside_input_main;
600 mp->vnet_main = vnet_get_main();
605 VLIB_INIT_FUNCTION (cnat_ipv4_udp_outside_input_init);