FD.io VPP  v17.01-9-ge7dcee4
Vector Packet Processing
cnat_ipv4_udp_inside_input.c
Go to the documentation of this file.
1 /*
2  *---------------------------------------------------------------------------
3  * cnat_ipv4_udp_inside_input.c - cnat_ipv4_udp_inside_input node 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_global.h"
27 #include "cnat_db.h"
28 #include "cnat_ipv4_udp.h"
29 #include "cnat_pcp_server.h"
30 
31 
32 #define foreach_cnat_ipv4_udp_inside_input_error \
33 _(CNAT_V4_UDP_I2O_T_PKT, "v4 udp i2o transmit") \
34 _(CNAT_V4_UDP_I2O_MISS_PKT, "v4 udp i2o db miss") \
35 _(CNAT_V4_UDP_I2O_TTL_GEN, "v4 udp i2o TTL gen") \
36 _(CNAT_V4_UDP_I2O_TTL_DROP, "v4 udp i2o TTL drop") \
37 _(CNAT_V4_PCP_PKT, "v4 pcp pkt") \
38 _(CNAT_V4_UDP_I2O_SESSION_DROP, "v4 udp i2o session drop")
39 
40 typedef enum {
41 #define _(sym,str) sym,
43 #undef _
46 
48 #define _(sym,string) string,
50 #undef _
51 };
52 
53 typedef struct {
55  /* $$$$ add data here */
56 
57  /* convenience variables */
61 
62 typedef enum {
69 
70 #define CNAT_V4_UDP_I2O_D CNAT_V4_I2O_FIXME
71 #define CNAT_V4_PCP_T CNAT_V4_I2O_FIXME
72 
75 
76 #define NSTAGES 6
77 
78 /*
79  * Use the generic buffer metadata + first line of packet data prefetch
80  * stage function from <api/pipeline.h>. This is usually a Good Idea.
81  */
82 #define stage0 generic_stage0
83 
84 #ifndef TOBE_PORTED
85 static inline u32
87 {
88  return CNAT_NO_CONFIG;
89 }
90 #else
91 static inline u32
92 is_pcp_pkt(spp_ctx_t *ctx, u32 addr, u16 port)
93 {
94  cnat_vrfmap_t *my_vrfmap = NULL;
95  u16 my_vrfmap_index;
96 
97  my_vrfmap_index = vrf_map_array[ctx->ru.rx.uidb_index];
98 
99  if (PREDICT_TRUE(my_vrfmap_index != VRF_MAP_ENTRY_EMPTY)) {
100 
101  my_vrfmap = cnat_map_by_vrf + my_vrfmap_index;
102 
103  if (PREDICT_FALSE( port == my_vrfmap->pcp_server_port)) {
104  if(PREDICT_TRUE(addr == my_vrfmap->pcp_server_addr)) {
105  return CNAT_SUCCESS;
106  }
107  }
108  }
109 
110  return CNAT_NO_CONFIG;
111 }
112 #endif
113 
115  udp_hdr_type_t *udp,
117 {
118  /*
119  * declare varibale
120  */
122  /*
123  * calculate checksum
124  */
126  ((u16)(db->in2out_key.k.ipv4 >> 16)),
127  (db->in2out_key.k.port),
128  (clib_net_to_host_u16(ip->checksum)),
129  (clib_net_to_host_u16(udp->udp_checksum)),
130  ((u16)(db->out2in_key.k.ipv4)),
131  ((u16)(db->out2in_key.k.ipv4 >> 16)),
132  (db->out2in_key.k.port))
133 
134 /* #define UDP_PACKET_DEBUG 1 */
135 
136 // Temporary debugs which will be suppressed later
137 #ifdef UDP_PACKET_DEBUG
139  printf("\nIn2Out UDP packet before translation");
140  print_udp_pkt(ip);
141  }
142 #endif
143 
144  //set ip header
145  ip->src_addr =
146  clib_host_to_net_u32(db->out2in_key.k.ipv4);
147  ip->checksum =
148  clib_host_to_net_u16(new_l3_c);
149 
150  u16 frag_offset =
151  clib_net_to_host_u16(ip->frag_flags_offset);
152 
153  if(PREDICT_FALSE(frag_offset & IP_FRAG_OFFSET_MASK)) {
154  return; /* No need to update UDP fields */
155  }
156  //set udp header
157  udp->src_port =
158  clib_host_to_net_u16(db->out2in_key.k.port);
159 
160  /*
161  * No easy way to avoid this if check except by using
162  * complex logic - may not be worth it.
163  */
164  if (PREDICT_TRUE(udp->udp_checksum)) {
165  udp->udp_checksum =
166  clib_host_to_net_u16(new_l4_c);
167  }
168 
169 // Temporary debugs which will be suppressed later
170 #ifdef UDP_PACKET_DEBUG
172  printf("\nIn2Out UDP checksum 0x%x disabled by force", new_l4_c);
173  udp->udp_checksum = 0;
174  }
176  printf("\nIn2Out UDP packet after translation");
177  print_udp_pkt(ip);
178  }
179 #endif
180 }
181 
182 static inline void
183 stage1(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
184 {
185  u64 a, b, c;
186  u32 bucket;
187  u8 *prefetch_target;
188 
189  vlib_buffer_t * b0 = vlib_get_buffer (vm, buffer_index);
191  u8 ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
192  udp_hdr_type_t *udp = (udp_hdr_type_t *)((u8*)ip + ipv4_hdr_len);
193 
194  u64 tmp = 0;
195  tmp = vnet_buffer(b0)->vcgn_uii.key.k.ipv4 =
196  clib_net_to_host_u32(ip->src_addr);
197  vnet_buffer(b0)->vcgn_uii.key.k.port =
198  clib_net_to_host_u16 (udp->src_port);
199 
200  tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.port) << 32;
201 
202  PLATFORM_CNAT_SET_RX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_RX],
203  vnet_buffer(b0)->vcgn_uii.key.k.vrf,
204  CNAT_UDP)
205  tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.vrf) << 48;
206 
208 
209  prefetch_target = (u8 *)(&cnat_in2out_hash[bucket]);
210  vnet_buffer(b0)->vcgn_uii.bucket = bucket;
211 
212  /* Prefetch the hash bucket */
213  CLIB_PREFETCH(prefetch_target, CLIB_CACHE_LINE_BYTES, LOAD);
214 }
215 
216 static inline void
217 stage2(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
218 { /* nothing */ }
219 
220 #define SPP_LOG2_CACHE_LINE_BYTES 6
221 #define SPP_CACHE_LINE_BYTES (1 << SPP_LOG2_CACHE_LINE_BYTES)
222 
223 static inline void
224 stage3(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
225 {
226  vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
227  uword prefetch_target0, prefetch_target1;
228  u32 bucket = vnet_buffer(b0)->vcgn_uii.bucket;
229 
230  /* read the hash bucket */
231  u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket
232  = cnat_in2out_hash[bucket].next;
233 
234  if (PREDICT_TRUE(db_index != EMPTY)) {
235  /*
236  * Prefetch database keys. We save space by not cache-line
237  * aligning the DB entries. We don't want to waste LSU
238  * bandwidth prefetching stuff we won't need.
239  */
240  prefetch_target0 = (uword)(cnat_main_db + db_index);
241  CLIB_PREFETCH((void*)prefetch_target0, CLIB_CACHE_LINE_BYTES, LOAD);
242  /* Just beyond DB key #2 */
243  prefetch_target1 = prefetch_target0 +
245  /* If the targets are in different lines, do the second prefetch */
246  if (PREDICT_FALSE((prefetch_target0 & ~(SPP_CACHE_LINE_BYTES-1)) !=
247  (prefetch_target1 & ~(SPP_CACHE_LINE_BYTES-1)))) {
248  CLIB_PREFETCH((void *)prefetch_target1, CLIB_CACHE_LINE_BYTES, LOAD);
249  }
250  }
251 }
252 
253 static inline void
254 stage4(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
255 {
257  vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
258  u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
259 
260  /*
261  * Note: if the search already failed (empty bucket),
262  * the answer is already in the pipeline context structure
263  */
264  if (PREDICT_TRUE(db_index != EMPTY)) {
265 
266  /*
267  * Note: hash collisions suck. We can't easily prefetch around them.
268  * The first trip around the track will be fast. After that, maybe
269  * not so much...
270  */
271  do {
272  db = cnat_main_db + db_index;
273  if (PREDICT_TRUE(db->in2out_key.key64 ==
274  vnet_buffer(b0)->vcgn_uii.key.key64)) {
275  break;
276  }
277  db_index = db->in2out_hash.next;
278  } while (db_index != EMPTY);
279 
280  /* Stick the answer back into the pipeline context structure */
281  vnet_buffer(b0)->vcgn_uii.bucket = db_index;
282  }
283 }
284 
285 static u64 pkt_num = 0;
286 static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
287  u32 bi)
288 {
289  vlib_buffer_t *b0 = vlib_get_buffer (vm, bi);
290  u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
291  spp_ctx_t *ctx = (spp_ctx_t *) &vnet_buffer(b0)->vcgn_uii;
292  int disposition = CNAT_V4_UDP_I2O_T;
293  int counter = CNAT_V4_UDP_I2O_T_PKT;
295  u8 ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
296  udp_hdr_type_t *udp = (udp_hdr_type_t *)((u8*)ip + ipv4_hdr_len);
298  u32 node_counter_base_index = n->error_heap_index;
299  vlib_error_main_t * em = &vm->error_main;
300  cnat_session_entry_t *session_db = NULL;
301  cnat_key_t dest_info;
302 
303  pkt_num++;
304 
305  if(PREDICT_FALSE(is_pcp_pkt(ip->dest_addr, udp->dest_port) ==
306  CNAT_SUCCESS))
307  {
308  PCP_INCR(input);
309  disposition = CNAT_V4_PCP_T;
310  counter = CNAT_V4_PCP_PKT;
311 
312  goto pcp_pkt;
313  }
314 
316  if (PREDICT_FALSE(ip->ttl <= 1)) {
317  /* Try to generate ICMP error msg, as TTL is <= 1 */
318 
320  (ctx, ip, ctx->ru.rx.uidb_index)) {
321  /* Generated ICMP */
322  disposition = CNAT_REWRITE_OUTPUT;
323  counter = CNAT_V4_UDP_I2O_TTL_GEN;
324  } else {
325  /* Could not generated ICMP - drop the packet */
326  disposition = CNAT_V4_UDP_I2O_D;
327  counter = CNAT_V4_UDP_I2O_TTL_DROP;
328  }
329  goto drop_pkt;
330  }
331  }
332  if (PREDICT_TRUE(db_index != EMPTY)) {
333  cnat_main_db_entry_t *db = cnat_main_db + db_index;
334 
335  dest_info.k.ipv4 = clib_net_to_host_u32(ip->dest_addr);
336 
337  /* MUST revisit: it seems farg is set to 1 for few packets & because of
338  * this the port is not updated & it becomes 0. Commenting teporarily
339  * this fargment check & setting dst port with udp dst port value */
340  dest_info.k.port = clib_net_to_host_u16(udp->dest_port);
341  #if 0 // DONOT REMOVE THIS if 0
342  if(PREDICT_FALSE(ctx->ru.rx.frag)) {
343 #ifdef TOBE_PORTED
344  /* Must have routed through cnat_v4_frag_in2out node */
345  u16 *feature_data_ports = (u16 *)&ctx->feature_data[4];
346  dest_info.k.port = *feature_data_ports;
347 #endif
348  } else {
349  dest_info.k.port = clib_net_to_host_u16(udp->dest_port);
350  }
351  #endif
352 
353 
355  /*
356  * Decrement TTL and update IPv4 checksum
357  */
359  }
360 
362 
363  /* No DBL support, so just update the destn and proceed */
364  db->dst_ipv4 = dest_info.k.ipv4;
365  db->dst_port = dest_info.k.port;
367  goto update_pkt;
368  }
369 
370  if(PREDICT_TRUE((db->dst_ipv4 == dest_info.k.ipv4) &&
371  (db->dst_port == dest_info.k.port))) {
372 
374  goto update_pkt;
375  } else {
376  if (PREDICT_FALSE(db->nsessions == 0)) {
377  /* Should be a static entry
378  * Note this session as the first session and log
379  */
380  cnat_add_dest_n_log(db, &dest_info);
381  /*
382  * update db counter, timer
383  */
384 
386 
387  } else if(PREDICT_TRUE(db->nsessions == 1)) {
388  /* Destn is not same as in main db. Multiple session
389  * scenario
390  */
391  //printf(">>> [pkt# %lu] src_ip: 0x%x, db ip: 0x%x, db port: %u; dest ip: 0x%x, dest port: %u\n",
392  // pkt_num, ntohl(ip->src_addr), db->dst_ipv4, db->dst_port, dest_info.k.ipv4, dest_info.k.port);
393 
394  dest_info.k.vrf = db->in2out_key.k.vrf;
395  session_db = cnat_handle_1to2_session(db, &dest_info);
396 
397  if(PREDICT_TRUE(session_db != NULL)) {
398  /* session exists */
399  CNAT_DB_TIMEOUT_RST(session_db);
400  } else {
401  /* could not create session db - drop packet */
402  disposition = CNAT_V4_UDP_I2O_D;
403  counter = CNAT_V4_UDP_I2O_SESSION_DROP;
404  goto drop_pkt;
405  }
406 
407  } else {
408  /* More than 2 sessions exists */
409 
410  dest_info.k.vrf = db->in2out_key.k.vrf;
411 
412  /* If session already exists,
413  * cnat_create_session_db_entry will return the existing db
414  * else create a new db
415  * If could not create, return NULL
416  */
417  session_db = cnat_create_session_db_entry(&dest_info,
418  db, TRUE);
419 
420  if(PREDICT_FALSE(session_db != NULL)) {
421  /* session exists */
422  CNAT_DB_TIMEOUT_RST(session_db);
423  } else {
424  /* could not create session db - drop packet */
425  disposition = CNAT_V4_UDP_I2O_D;
426  counter = CNAT_V4_UDP_I2O_SESSION_DROP;
427  goto drop_pkt;
428  }
429  }
430  }
431 
432 update_pkt:
433  /*
434  * 1. update src ipv4 addr and src udp port
435  * 2. update ipv4 checksum and udp checksum
436  */
437  swap_ip_src_udp_port(ip, udp, db);
438  /*
439  * update db counter, timer
440  */
441 
442  db->in2out_pkts++;
443 
444  /*
445  * need to set outside vrf
446  * from db->out2in_key.k.vrf
447  */
448 
449  /* Temporarily keeping this commented */
450  //PLATFORM_CNAT_SET_TX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_TX],
451  // db->out2in_key.k.vrf)
452 
454 
455  } else {
456  disposition = CNAT_V4_UDP_I2O_E;
457  counter = CNAT_V4_UDP_I2O_MISS_PKT;
458  }
459 
460 drop_pkt:
461 pcp_pkt:
462 
463  em->counters[node_counter_base_index + counter] += 1;
464 
465  return disposition;
466 }
467 
468 #include <vnet/pipeline.h>
469 
471  vlib_node_runtime_t * node,
472  vlib_frame_t * frame)
473 {
474  return dispatch_pipeline (vm, node, frame);
475 }
476 
477 
480  .name = "vcgn-v4-udp-i2o",
481  .vector_size = sizeof (u32),
483 
486 
487  .n_next_nodes = CNAT_N_NEXT,
488 
489  /* edit / add dispositions here */
490  .next_nodes = {
491  [CNAT_V4_I2O_FIXME] = "error-drop",
492  // [CNAT_V4_UDP_I2O_T] = "ip4-input",
493  [CNAT_V4_UDP_I2O_E] = "vcgn-v4-udp-i2o-e",
494  [CNAT_REWRITE_OUTPUT] = "ip4-input",
495  },
496 };
497 
499 {
501 
502  mp->vlib_main = vm;
503  mp->vnet_main = vnet_get_main();
504 
505  return 0;
506 }
507 
u32 error_heap_index
Definition: node.h:278
cnat_main_db_entry_t * cnat_main_db
Definition: cnat_db_v2.c:201
u32 udp_inside_checksum_disable
cnat_session_entry_t * cnat_create_session_db_entry(cnat_key_t *ko, cnat_main_db_entry_t *bdb, u8 log)
Definition: cnat_db_v2.c:2643
a
Definition: bitmap.h:516
#define CNAT_V4_UDP_I2O_D
bad routing header type(not 4)") sr_error (NO_MORE_SEGMENTS
static void stage2(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
#define PREDICT_TRUE(x)
Definition: clib.h:98
void ipv4_decr_ttl_n_calc_csum(ipv4_header *ipv4)
#define NULL
Definition: clib.h:55
static uword cnat_ipv4_udp_inside_input_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
u64 key64
Definition: cnat_db.h:109
u32 pcp_server_addr
Definition: cnat_db.h:437
static u64 pkt_num
Definition: cnat_db.h:153
struct _vlib_node_registration vlib_node_registration_t
#define STRUCT_OFFSET_OF(t, f)
Definition: clib.h:62
index_slist_t in2out_hash
Definition: cnat_db.h:156
cnat_session_entry_t * cnat_handle_1to2_session(cnat_main_db_entry_t *mdb, cnat_key_t *dest_info)
Definition: cnat_db_v2.c:1027
static char * cnat_ipv4_udp_inside_input_error_strings[]
clib_error_t * cnat_ipv4_udp_inside_input_init(vlib_main_t *vm)
#define CNAT_UPDATE_L3_L4_CHECKSUM(old_l3_1, old_l3_2, old_l4, old_l3_c, old_l4_c, new_l3_1, new_l3_2, new_l4)
vlib_node_registration_t cnat_ipv4_udp_inside_input_node
(constructor) VLIB_REGISTER_NODE (cnat_ipv4_udp_inside_input_node)
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
index_slist_t * cnat_in2out_hash
Definition: cnat_db_v2.c:196
#define CNAT_DB_TIMEOUT_RST(db)
Definition: cnat_db.h:561
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
#define CNAT_V4_PCP_T
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:194
unsigned long u64
Definition: types.h:89
static void stage3(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
u16 dst_port
Definition: cnat_db.h:257
#define PLATFORM_CNAT_SET_RX_VRF(ctx, rx_vrf, proto)
vlib_error_main_t error_main
Definition: main.h:124
cnat_ipv4_udp_inside_input_main_t cnat_ipv4_udp_inside_input_main
u32 pcp_server_port
Definition: cnat_db.h:438
#define PREDICT_FALSE(x)
Definition: clib.h:97
u16 vrf_map_array[CNAT_MAX_VRFMAP_ENTRIES]
Definition: cnat_db_v2.c:225
#define PCP_INCR(counter)
#define CNAT_UDP
Definition: cnat_db.h:93
static u32 is_pcp_pkt(u32 addr, u16 port)
int icmpv4_generate_with_throttling(spp_ctx_t *ctx, ipv4_header *ipv4, u16 rx_uidb_index)
void print_udp_pkt(ipv4_header *ip)
u64 * counters
Definition: error.h:78
static void stage1(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
svmdb_client_t * c
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:82
#define ARRAY_LEN(x)
Definition: clib.h:59
cnat_db_key_t k
Definition: cnat_db.h:108
static u32 last_stage(vlib_main_t *vm, vlib_node_runtime_t *node, u32 bi)
void cnat_add_dest_n_log(cnat_main_db_entry_t *mdb, cnat_key_t *dest_info)
Definition: cnat_db_v2.c:1083
unsigned int u32
Definition: types.h:88
#define vnet_buffer(b)
Definition: buffer.h:361
#define TRUE
Definition: cnat_db.h:78
#define PLATFORM_DBL_SUPPORT
static void stage4(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
cnat_key_t out2in_key
Definition: cnat_db.h:198
#define SPP_CACHE_LINE_BYTES
#define VRF_MAP_ENTRY_EMPTY
Definition: cnat_db.h:506
u64 uword
Definition: types.h:112
u16 nsessions
Definition: cnat_db.h:266
Definition: cnat_db.h:336
unsigned short u16
Definition: types.h:57
#define IP_FRAG_OFFSET_MASK
cnat_vrfmap_t * cnat_map_by_vrf
Definition: cnat_db_v2.c:218
#define CNAT_MAIN_HASH_MASK
Definition: cnat_db.h:56
unsigned char u8
Definition: types.h:56
u32 dst_ipv4
Definition: cnat_db.h:254
struct _spp_ctx spp_ctx_t
u32 in2out_forwarding_count
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:143
static vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Get vlib node by index.
Definition: node_funcs.h:58
#define EMPTY
Definition: index_list.h:24
cnat_ipv4_udp_inside_input_next_t
#define PLATFORM_HANDLE_TTL_DECREMENT
#define CNAT_UPDATE_L3_L4_CHECKSUM_DECLARE
vhost_vring_addr_t addr
Definition: vhost-user.h:81
#define CNAT_V4_GET_HASH(key64, hash, mask)
Definition: cnat_db.h:536
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:57
void swap_ip_src_udp_port(ipv4_header *ip, udp_hdr_type_t *udp, cnat_main_db_entry_t *db)
u32 udp_inside_packet_dump_enable
Definition: defs.h:46
cnat_key_t in2out_key
Definition: cnat_db.h:201
cnat_ipv4_udp_inside_input_t
u32 in2out_pkts
Definition: cnat_db.h:210
#define foreach_cnat_ipv4_udp_inside_input_error