FD.io VPP  v17.01-9-ge7dcee4
Vector Packet Processing
cnat_ipv4_icmp_error_inside_input.c
Go to the documentation of this file.
1 /*
2  *---------------------------------------------------------------------------
3  * cnat_ipv4_icmp_error_inside_input.c - cnat_ipv4_icmp_error_inside_input node pipeline stage functions
4  *
5  * Copyright (c) 2008-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 
25 #include "cnat_ipv4_icmp.h"
26 
27 #define foreach_cnat_ipv4_icmp_e_inside_input_error \
28 _(CNAT_V4_ICMP_E_I2O_T_PKT, "cnat v4 icmp_e i2o packet transmit") \
29 _(CNAT_V4_ICMP_E_I2O_D_PKT, "cnat v4 icmp_e i2o packet drop") \
30 _(CNAT_V4_ICMP_E_I2O_TTL_DROP, "cnat v4 icmp_e i2o ttl drop")
31 
32 typedef enum {
33 #define _(sym,str) sym,
35 #undef _
38 
40 #define _(sym,string) string,
42 #undef _
43 };
44 
45 typedef struct {
47  /* $$$$ add data here */
48 
49  /* convenience variables */
53 
54 typedef enum {
59 
62 
63 #define NSTAGES 5
64 
66  icmp_em_ip_info *icmp_info,
67  cnat_main_db_entry_t *db, u16 vrf)
68 {
69  icmp_v4_t *icmp;
70  ipv4_header *em_ip;
71  u16 *em_port;
72  u32 old_ip;
73  u16 old_port;
74  u16 old_ip_checksum;
75 
76  /*
77  * declear variable
78  */
81 
82  /*
83  * fix inner layer ip & l4 checksum
84  */
85  em_ip = icmp_info->em_ip;
86  em_port = icmp_info->em_port;
87 
89  ((u16)(db->in2out_key.k.ipv4 >> 16)),
90  (clib_net_to_host_u16(em_ip->checksum)),
91  ((u16)(db->out2in_key.k.ipv4)),
92  ((u16)(db->out2in_key.k.ipv4 >> 16)))
93 
94  old_ip = clib_net_to_host_u32(em_ip->dest_addr);
95  old_port = clib_net_to_host_u16(*em_port);
96  old_ip_checksum = clib_net_to_host_u16(em_ip->checksum);
97 
98  em_ip->dest_addr =
99  clib_host_to_net_u32(db->out2in_key.k.ipv4);
100  em_ip->checksum =
101  clib_host_to_net_u16(new_l3_c);
102  *em_port =
103  clib_host_to_net_u16(db->out2in_key.k.port);
104 
105  /*
106  * fix outter layer ip & icmp checksum
107  */
108  icmp = icmp_info->icmp;
109  CNAT_UPDATE_ICMP_ERR_CHECKSUM(((u16)(old_ip & 0xFFFF)),
110  ((u16)(old_ip >> 16)),
111  (old_port),
112  (old_ip_checksum),
113  (clib_net_to_host_u16(icmp->checksum)),
114  ((u16)(db->out2in_key.k.ipv4 & 0xffff)),
115  ((u16)(db->out2in_key.k.ipv4 >> 16)),
116  ((u16)(db->out2in_key.k.port)),
117  ((u16)(new_l3_c)))
118 
119  icmp->checksum =
120  clib_host_to_net_u16(new_icmp_c);
121 
122  old_ip = clib_net_to_host_u32(ip->src_addr);
123 
124  ip->src_addr =
125  clib_host_to_net_u32(db->out2in_key.k.ipv4);
126 
127  CNAT_UPDATE_L3_CHECKSUM(((u16)(old_ip & 0xFFFF)),
128  ((u16)(old_ip >> 16)),
129  (clib_net_to_host_u16(ip->checksum)),
130  ((u16)(db->out2in_key.k.ipv4)),
131  ((u16)(db->out2in_key.k.ipv4 >> 16)))
132  ip->checksum =
133  clib_host_to_net_u16(new_l3_c);
134 
135 #if 0
136  if(is_static_dest_nat_enabled(vrf) == CNAT_SUCCESS) {
137  /*
138  * fix inner layer ip & l4 checksum
139  */
140  em_snat_ip = icmp_info->em_ip;
141  em_snat_port = icmp_info->em_port;
142 
143  old_ip = spp_net_to_host_byte_order_32(&(em_snat_ip->src_addr));
144  old_port = spp_net_to_host_byte_order_16(em_snat_port);
145  old_ip_checksum = spp_net_to_host_byte_order_16(&(em_snat_ip->checksum));
146  direction = 0;
147  if(cnat_static_dest_db_get_translation(em_snat_ip->src_addr, &postmap_ip, vrf, direction) == CNAT_SUCCESS) {
148  old_postmap_ip = spp_net_to_host_byte_order_32(&postmap_ip);
149 
150  CNAT_UPDATE_L3_CHECKSUM(((u16)(old_ip)),
151  ((u16)(old_ip >> 16)),
152  (spp_net_to_host_byte_order_16(&(em_snat_ip->checksum))),
153  ((u16)(old_postmap_ip)),
154  ((u16)(old_postmap_ip >> 16)))
155  em_snat_ip->src_addr = postmap_ip;
156  em_snat_ip->checksum =
157  spp_host_to_net_byte_order_16(new_l3_c);
158 
159  /*
160  * fix outter layer ip & icmp checksum
161  */
162  icmp = icmp_info->icmp;
163  CNAT_UPDATE_ICMP_ERR_CHECKSUM(((u16)(old_ip & 0xFFFF)),
164  ((u16)(old_ip >> 16)),
165  (old_port),
166  (old_ip_checksum),
167  (spp_net_to_host_byte_order_16(&(icmp->checksum))),
168  ((u16)(old_postmap_ip & 0xffff)),
169  ((u16)(old_postmap_ip >> 16)),
170  ((u16)(old_port)),
171  ((u16)(new_l3_c)))
172 
173  icmp->checksum =
174  spp_host_to_net_byte_order_16(new_icmp_c);
175 
176  }
177  }
178 
179  if(is_static_dest_nat_enabled(vrf) == CNAT_SUCCESS) {
180  direction = 0;
181  if(cnat_static_dest_db_get_translation(ip->dest_addr, &postmap_ip, vrf, direction) == CNAT_SUCCESS) {
182 
183  old_ip = spp_net_to_host_byte_order_32(&(ip->dest_addr));
184  old_postmap_ip = spp_net_to_host_byte_order_32(&postmap_ip);
185 
186  CNAT_UPDATE_L3_CHECKSUM(((u16)(old_ip & 0xFFFF)),
187  ((u16)(old_ip >> 16)),
188  (spp_net_to_host_byte_order_16(&(ip->checksum))),
189  ((u16)(old_postmap_ip & 0xFFFF)),
190  ((u16)(old_postmap_ip >> 16)))
191  ip->dest_addr = postmap_ip;
192 
193  ip->checksum =
194  clib_host_to_net_u16(new_l3_c);
195  }
196  }
197 #endif /* if 0 */
198 
199 }
200 
201 /*
202  * Use the generic buffer metadata + first line of packet data prefetch
203  * stage function from <api/pipeline.h>. This is usually a Good Idea.
204  */
205 #define stage0 generic_stage0
206 
207 
208 static inline void
209 stage1(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
210 {
211  u64 a, b, c;
212  u32 bucket;
213  u8 *prefetch_target;
214 
215  vlib_buffer_t * b0 = vlib_get_buffer (vm, buffer_index);
217  u8 ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
218  icmp_v4_t *icmp = (icmp_v4_t *)((u8*)ip + ipv4_hdr_len);
219  ipv4_header *em_ip = (ipv4_header*)((u8*)icmp + 8); /* embedded pkt's v4 hdr */
220  u8 em_ip_hdr_len = (em_ip->version_hdr_len_words & 0xf) << 2;
221 
222  u64 tmp = 0;
223  u32 protocol = CNAT_ICMP;
224 
225  /* Check L4 header for embedded packet */
226  if (em_ip->protocol == TCP_PROT) {
227  tcp_hdr_type *tcp = (tcp_hdr_type*)((u8 *)em_ip + em_ip_hdr_len);
228  vnet_buffer(b0)->vcgn_uii.key.k.port =
229  clib_net_to_host_u16(tcp->dest_port);
230  protocol = CNAT_TCP;
231 
232  } else if (em_ip->protocol == UDP_PROT) {
233  udp_hdr_type_t *udp = (udp_hdr_type_t *)((u8 *)em_ip + em_ip_hdr_len);
234  vnet_buffer(b0)->vcgn_uii.key.k.port =
235  clib_net_to_host_u16(udp->dest_port);
236  protocol = CNAT_UDP;
237 
238  } else {
239  icmp_v4_t *icmp = (icmp_v4_t*)((u8 *)em_ip + em_ip_hdr_len);
240  vnet_buffer(b0)->vcgn_uii.key.k.port =
241  clib_net_to_host_u16(icmp->identifier);
242 
243  if (PREDICT_FALSE((icmp->type != ICMPV4_ECHOREPLY) &&
244  (icmp->type != ICMPV4_ECHO))) {
245  /*
246  * Try to set invalid protocol for these cases, so that
247  * hash lookup does not return valid main_db. This approach
248  * may optimize the regular cases with valid protocols
249  * as it avoids one more check for regular cases in stage3
250  */
251  protocol = CNAT_INVALID_PROTO;
252  }
253  }
254 
255  tmp = vnet_buffer(b0)->vcgn_uii.key.k.ipv4 =
256  clib_net_to_host_u32(em_ip->dest_addr);
257 
258  tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.port) << 32;
259 
260  PLATFORM_CNAT_SET_RX_VRF(vnet_buffer(b0)->sw_if_index[VLIB_RX],
261  vnet_buffer(b0)->vcgn_uii.key.k.vrf,
262  protocol)
263  tmp |= ((u64)vnet_buffer(b0)->vcgn_uii.key.k.vrf) << 48;
264 
266 
267  prefetch_target = (u8 *)(&cnat_in2out_hash[bucket]);
268  vnet_buffer(b0)->vcgn_uii.bucket = bucket;
269 
270  /* Prefetch the hash bucket */
271  CLIB_PREFETCH(prefetch_target, CLIB_CACHE_LINE_BYTES, LOAD);
272 }
273 
274 #define SPP_LOG2_CACHE_LINE_BYTES 6
275 #define SPP_CACHE_LINE_BYTES (1 << SPP_LOG2_CACHE_LINE_BYTES)
276 
277 static inline void
278 stage2(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
279 {
280  vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
281  uword prefetch_target0, prefetch_target1;
282  u32 bucket = vnet_buffer(b0)->vcgn_uii.bucket;
283 
284  /* read the hash bucket */
285  u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket
286  = cnat_in2out_hash[bucket].next;
287 
288  if (PREDICT_TRUE(db_index != EMPTY)) {
289  /*
290  * Prefetch database keys. We save space by not cache-line
291  * aligning the DB entries. We don't want to waste LSU
292  * bandwidth prefetching stuff we won't need.
293  */
294  prefetch_target0 = (uword)(cnat_main_db + db_index);
295  CLIB_PREFETCH((void*)prefetch_target0, CLIB_CACHE_LINE_BYTES, LOAD);
296  /* Just beyond DB key #2 */
297  prefetch_target1 = prefetch_target0 +
299  /* If the targets are in different lines, do the second prefetch */
300  if (PREDICT_FALSE((prefetch_target0 & ~(SPP_CACHE_LINE_BYTES-1)) !=
301  (prefetch_target1 & ~(SPP_CACHE_LINE_BYTES-1)))) {
302  CLIB_PREFETCH((void *)prefetch_target1, CLIB_CACHE_LINE_BYTES, LOAD);
303  }
304  }
305 }
306 
307 static inline void
308 stage3(vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index)
309 {
311  vlib_buffer_t * b0 = vlib_get_buffer(vm, buffer_index);
312  u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
313 
314  /*
315  * Note: if the search already failed (empty bucket),
316  * the answer is already in the pipeline context structure
317  */
318  if (PREDICT_TRUE(db_index != EMPTY)) {
319 
320  /*
321  * Note: hash collisions suck. We can't easily prefetch around them.
322  * The first trip around the track will be fast. After that, maybe
323  * not so much...
324  */
325  do {
326  db = cnat_main_db + db_index;
327  if (PREDICT_TRUE(db->in2out_key.key64 ==
328  vnet_buffer(b0)->vcgn_uii.key.key64)) {
329  break;
330  }
331  db_index = db->in2out_hash.next;
332  } while (db_index != EMPTY);
333 
334  /* Stick the answer back into the pipeline context structure */
335  vnet_buffer(b0)->vcgn_uii.bucket = db_index;
336  }
337 }
338 
339 
340 
341 static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
342  u32 bi)
343 {
344  vlib_buffer_t *b0 = vlib_get_buffer (vm, bi);
345  u32 db_index = vnet_buffer(b0)->vcgn_uii.bucket;
346  int disposition = CNAT_V4_ICMP_E_I2O_T;
347  int counter = CNAT_V4_ICMP_E_I2O_T_PKT;
348 
350  u8 ipv4_hdr_len = (ip->version_hdr_len_words & 0xf) << 2;
351  icmp_v4_t *icmp = (icmp_v4_t *)((u8*)ip + ipv4_hdr_len);
352  ipv4_header *em_ip = (ipv4_header*)((u8*)icmp + 8); /* embedded pkt's v4 hdr */
353  u8 em_ip_hdr_len = (em_ip->version_hdr_len_words & 0xf) << 2;
355  u32 node_counter_base_index = n->error_heap_index;
356  vlib_error_main_t * em = &vm->error_main;
358  icmp_em_ip_info icmp_info;
359 
361  if (PREDICT_FALSE(ip->ttl <= 1)) {
362  /*
363  * As it is ICMP error packet with TTL <= 1,
364  * let's drop the packet (no need to genereate
365  * another ICMP error).
366  */
367 
368  disposition = CNAT_V4_ICMP_E_I2O_D;
369  counter = CNAT_V4_ICMP_E_I2O_TTL_DROP;
370 
371  goto drop_pkt;
372  }
373  }
374 
375  if (PREDICT_TRUE(db_index != EMPTY)) {
376  icmp_info.em_ip = em_ip;
377  icmp_info.icmp = icmp;
378  //icmp_info.em_port = vnet_buffer(b0)->vcgn_uii.key.k.port;
379 
380  /* Note: This could have been done in stage1 itself,
381  * but we need to introduce one u16 * in vnet_buffer_opaque_t
382  * Since this flow is expected to be very rare in actual
383  * deployment scenario, we may afford to do these steps here
384  * as well. Lets confirm during core review. */
385  if (em_ip->protocol == TCP_PROT) {
386  tcp_hdr_type *tcp = (tcp_hdr_type*)((u8 *)em_ip + em_ip_hdr_len);
387  icmp_info.em_port = &(tcp->dest_port);
388  } else if (em_ip->protocol == UDP_PROT) {
389  udp_hdr_type_t *udp = (udp_hdr_type_t *)
390  ((u8 *)em_ip + em_ip_hdr_len);
391  icmp_info.em_port = &(udp->dest_port);
392  } else {
393  icmp_v4_t *icmp_inner = (icmp_v4_t*)((u8 *)em_ip + em_ip_hdr_len);
394  icmp_info.em_port = &(icmp_inner->identifier);
395  }
396 
397  db = cnat_main_db + db_index;
398  /*
399  * 1. update dst addr:dst port of embedded ip pkt
400  * update src addr of icmp pkt
401  * 2. fix udp/tcp/ip checksum of embedded pkt
402  * fix icmp, ip check of icmp pkt
403  * don need to update the timer
404  */
405 
407  printf("\nDUMPING ICMP PKT BEFORE\n");
408  print_icmp_pkt(ip);
409  }
410 
412  /*
413  * Decrement TTL and update IPv4 checksum
414  */
416  }
417 
418  swap_ip_src_emip_dst(ip, &icmp_info,
419  db, db->in2out_key.k.vrf);
420 
422  printf("\nDUMPING ICMP PKT AFTER\n");
423  print_icmp_pkt(ip);
424  }
426 
427  } else {
428  disposition = CNAT_V4_ICMP_E_I2O_D;
429  counter = CNAT_V4_ICMP_E_I2O_D_PKT;
430  }
431 
432 drop_pkt:
433 
434  em->counters[node_counter_base_index + counter] += 1;
435  return disposition;
436 }
437 
438 #include <vnet/pipeline.h>
439 
441  vlib_node_runtime_t * node,
442  vlib_frame_t * frame)
443 {
444  return dispatch_pipeline (vm, node, frame);
445 }
446 
447 
450  .name = "vcgn-v4-icmp-e-i2o",
451  .vector_size = sizeof (u32),
453 
456 
457  .n_next_nodes = CNAT_V4_ICMP_E_I2O_NEXT,
458 
459  /* edit / add dispositions here */
460  .next_nodes = {
461  [CNAT_V4_ICMP_E_I2O_T] = "ip4-input",
462  [CNAT_V4_ICMP_E_I2O_D] = "error-drop",
463  },
464 };
465 
467 {
469 
470  mp->vlib_main = vm;
471  mp->vnet_main = vnet_get_main();
472 
473  return 0;
474 }
475 
static void stage1(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
clib_error_t * cnat_ipv4_icmp_e_inside_input_init(vlib_main_t *vm)
u32 error_heap_index
Definition: node.h:278
cnat_main_db_entry_t * cnat_main_db
Definition: cnat_db_v2.c:201
#define SPP_CACHE_LINE_BYTES
a
Definition: bitmap.h:516
bad routing header type(not 4)") sr_error (NO_MORE_SEGMENTS
#define PREDICT_TRUE(x)
Definition: clib.h:98
void ipv4_decr_ttl_n_calc_csum(ipv4_header *ipv4)
#define UDP_PROT
#define NULL
Definition: clib.h:55
u64 key64
Definition: cnat_db.h:109
Definition: cnat_db.h:153
u32 icmp_debug_flag
struct _vlib_node_registration vlib_node_registration_t
#define ICMPV4_ECHOREPLY
#define STRUCT_OFFSET_OF(t, f)
Definition: clib.h:62
index_slist_t in2out_hash
Definition: cnat_db.h:156
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
static void stage2(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
index_slist_t * cnat_in2out_hash
Definition: cnat_db_v2.c:196
ipv4_header * em_ip
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
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
#define CNAT_UPDATE_ICMP_ERR_CHECKSUM(old_ip_1, old_ip_2, old_ip_port, old_ip_c, old_icmp_c, new_ip_1, new_ip_2, new_ip_port, new_ip_c)
#define PLATFORM_CNAT_SET_RX_VRF(ctx, rx_vrf, proto)
vlib_node_registration_t cnat_ipv4_icmp_e_inside_input_node
(constructor) VLIB_REGISTER_NODE (cnat_ipv4_icmp_e_inside_input_node)
vlib_error_main_t error_main
Definition: main.h:124
cnat_ipv4_icmp_e_inside_input_main_t cnat_ipv4_icmp_e_inside_input_main
static void stage3(vlib_main_t *vm, vlib_node_runtime_t *node, u32 buffer_index)
void swap_ip_src_emip_dst(ipv4_header *ip, icmp_em_ip_info *icmp_info, cnat_main_db_entry_t *db, u16 vrf)
#define PREDICT_FALSE(x)
Definition: clib.h:97
#define CNAT_UDP
Definition: cnat_db.h:93
u64 * counters
Definition: error.h:78
#define CNAT_ICMP
Definition: cnat_db.h:95
svmdb_client_t * c
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:82
static u32 last_stage(vlib_main_t *vm, vlib_node_runtime_t *node, u32 bi)
#define CNAT_UPDATE_ICMP_ERR_CHECKSUM_DECLARE
#define ARRAY_LEN(x)
Definition: clib.h:59
cnat_db_key_t k
Definition: cnat_db.h:108
static uword cnat_ipv4_icmp_e_inside_input_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
unsigned int u32
Definition: types.h:88
#define CNAT_UPDATE_L3_CHECKSUM_DECLARE
#define vnet_buffer(b)
Definition: buffer.h:361
#define TCP_PROT
cnat_key_t out2in_key
Definition: cnat_db.h:198
#define ICMPV4_ECHO
#define CNAT_TCP
Definition: cnat_db.h:94
u64 uword
Definition: types.h:112
unsigned short u16
Definition: types.h:57
#define CNAT_MAIN_HASH_MASK
Definition: cnat_db.h:56
unsigned char u8
Definition: types.h:56
static char * cnat_ipv4_icmp_e_inside_input_error_strings[]
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 foreach_cnat_ipv4_icmp_e_inside_input_error
#define EMPTY
Definition: index_list.h:24
icmp_v4_t * icmp
#define PLATFORM_HANDLE_TTL_DECREMENT
#define CNAT_V4_GET_HASH(key64, hash, mask)
Definition: cnat_db.h:536
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
void print_icmp_pkt(ipv4_header *ip)
#define CNAT_INVALID_PROTO
Definition: cnat_db.h:91
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
Definition: defs.h:46
cnat_key_t in2out_key
Definition: cnat_db.h:201
#define CNAT_UPDATE_L3_CHECKSUM(old_l3_1, old_l3_2, old_l3_c,new_l3_1, new_l3_2)