FD.io VPP  v18.10-32-g1161dda
Vector Packet Processing
nat44_classify.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief Classify for one armed NAT44 (in+out interface)
18  */
19 
20 #include <vlib/vlib.h>
21 #include <vnet/vnet.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <nat/nat.h>
24 #include <nat/nat_reass.h>
25 #include <nat/nat_inlines.h>
26 
31 
32 #define foreach_nat44_classify_error \
33 _(MAX_REASS, "Maximum reassemblies exceeded") \
34 _(MAX_FRAG, "Maximum fragments per reassembly exceeded")
35 
36 typedef enum
37 {
38 #define _(sym,str) NAT44_CLASSIFY_ERROR_##sym,
40 #undef _
43 
44 static char *nat44_classify_error_strings[] = {
45 #define _(sym,string) string,
47 #undef _
48 };
49 
50 typedef enum
51 {
57 
58 typedef struct
59 {
63 
64 static u8 *
65 format_nat44_classify_trace (u8 * s, va_list * args)
66 {
67  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
68  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
69  nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *);
70  char *next;
71 
72  if (t->cached)
73  s = format (s, "nat44-classify: fragment cached");
74  else
75  {
76  next = t->next_in2out ? "nat44-in2out" : "nat44-out2in";
77  s = format (s, "nat44-classify: next %s", next);
78  }
79 
80  return s;
81 }
82 
83 static inline uword
85  vlib_node_runtime_t * node,
86  vlib_frame_t * frame, int is_ed)
87 {
88  u32 n_left_from, *from, *to_next;
89  nat44_classify_next_t next_index;
90  snat_main_t *sm = &snat_main;
92  u32 thread_index = vm->thread_index;
93  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
94  u32 *fragments_to_drop = 0;
95  u32 *fragments_to_loopback = 0;
96 
97  from = vlib_frame_vector_args (frame);
98  n_left_from = frame->n_vectors;
99  next_index = node->cached_next_index;
100 
101  while (n_left_from > 0)
102  {
103  u32 n_left_to_next;
104 
105  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
106 
107  while (n_left_from > 0 && n_left_to_next > 0)
108  {
109  u32 bi0;
110  vlib_buffer_t *b0;
111  u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT, sw_if_index0, rx_fib_index0;
112  ip4_header_t *ip0;
113  snat_address_t *ap;
114  snat_session_key_t m_key0;
115  clib_bihash_kv_8_8_t kv0, value0;
116  clib_bihash_kv_16_8_t ed_kv0, ed_value0;
117  udp_header_t *udp0;
118  nat_reass_ip4_t *reass0;
119  u8 cached0 = 0;
120 
121  /* speculatively enqueue b0 to the current next frame */
122  bi0 = from[0];
123  to_next[0] = bi0;
124  from += 1;
125  to_next += 1;
126  n_left_from -= 1;
127  n_left_to_next -= 1;
128 
129  b0 = vlib_get_buffer (vm, bi0);
130  ip0 = vlib_buffer_get_current (b0);
131  udp0 = ip4_next_header (ip0);
132 
133  if (is_ed && ip0->protocol != IP_PROTOCOL_ICMP)
134  {
135  if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
136  {
137  /* process leading fragment/whole packet (with L4 header) */
138  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
139  rx_fib_index0 =
141  sw_if_index0);
142  make_ed_kv (&ed_kv0, &ip0->src_address, &ip0->dst_address,
143  ip0->protocol, rx_fib_index0, udp0->src_port,
144  udp0->dst_port);
145  if (ip4_is_fragment (ip0))
146  {
148  ip0->dst_address,
149  ip0->fragment_id,
150  ip0->protocol,
151  1,
152  &fragments_to_drop);
153  if (PREDICT_FALSE (!reass0))
154  {
155  next0 = NAT44_CLASSIFY_NEXT_DROP;
156  b0->error =
157  node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
158  nat_log_notice ("maximum reassemblies exceeded");
159  goto enqueue0;
160  }
161  if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &ed_kv0,
162  &ed_value0))
163  {
164  /* session exists so classify as IN2OUT,
165  * save this information for future fragments and set
166  * past fragments to be looped over and reprocessed */
167  reass0->sess_index = ed_value0.value;
168  reass0->classify_next =
170  nat_ip4_reass_get_frags (reass0,
171  &fragments_to_loopback);
172  goto enqueue0;
173  }
174  else
175  {
176  /* session doesn't exist so continue in the code,
177  * save this information for future fragments and set
178  * past fragments to be looped over and reprocessed */
179  reass0->flags |=
181  nat_ip4_reass_get_frags (reass0,
182  &fragments_to_loopback);
183  }
184  }
185  else
186  {
187  /* process whole packet */
188  if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &ed_kv0,
189  &ed_value0))
190  goto enqueue0;
191  /* session doesn't exist so continue in code */
192  }
193  }
194  else
195  {
196  /* process non-first fragment */
198  ip0->dst_address,
199  ip0->fragment_id,
200  ip0->protocol,
201  1,
202  &fragments_to_drop);
203  if (PREDICT_FALSE (!reass0))
204  {
205  next0 = NAT44_CLASSIFY_NEXT_DROP;
206  b0->error =
207  node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
208  nat_log_notice ("maximum reassemblies exceeded");
209  goto enqueue0;
210  }
211  /* check if first fragment has arrived */
212  if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE &&
213  !(reass0->flags & NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE))
214  {
215  /* first fragment still hasn't arrived, cache this fragment */
216  if (nat_ip4_reass_add_fragment (reass0, bi0,
217  &fragments_to_drop))
218  {
219  b0->error =
220  node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
222  ("maximum fragments per reassembly exceeded");
223  next0 = NAT44_CLASSIFY_NEXT_DROP;
224  goto enqueue0;
225  }
226  cached0 = 1;
227  goto enqueue0;
228  }
229  if (reass0->classify_next ==
231  goto enqueue0;
232  /* flag NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE is set
233  * so keep the default next0 and continue in code to
234  * potentially find other classification for this packet */
235  }
236  }
237 
238  /* *INDENT-OFF* */
239  vec_foreach (ap, sm->addresses)
240  {
241  if (ip0->dst_address.as_u32 == ap->addr.as_u32)
242  {
244  goto enqueue0;
245  }
246  }
247  /* *INDENT-ON* */
248 
250  {
251  m_key0.addr = ip0->dst_address;
252  m_key0.port = 0;
253  m_key0.protocol = 0;
254  m_key0.fib_index = 0;
255  kv0.key = m_key0.as_u64;
256  /* try to classify the fragment based on IP header alone */
257  if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external,
258  &kv0, &value0))
259  {
260  m = pool_elt_at_index (sm->static_mappings, value0.value);
261  if (m->local_addr.as_u32 != m->external_addr.as_u32)
263  goto enqueue0;
264  }
265  if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
266  {
267  /* process leading fragment/whole packet (with L4 header) */
268  m_key0.port = clib_net_to_host_u16 (udp0->dst_port);
269  m_key0.protocol = ip_proto_to_snat_proto (ip0->protocol);
270  kv0.key = m_key0.as_u64;
271  if (!clib_bihash_search_8_8
272  (&sm->static_mapping_by_external, &kv0, &value0))
273  {
274  m =
276  if (m->local_addr.as_u32 != m->external_addr.as_u32)
278  }
279  if (ip4_is_fragment (ip0))
280  {
282  ip0->dst_address,
283  ip0->fragment_id,
284  ip0->protocol,
285  1,
286  &fragments_to_drop);
287  if (PREDICT_FALSE (!reass0))
288  {
289  next0 = NAT44_CLASSIFY_NEXT_DROP;
290  b0->error =
291  node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
292  nat_log_notice ("maximum reassemblies exceeded");
293  goto enqueue0;
294  }
295  /* save classification for future fragments and set past
296  * fragments to be looped over and reprocessed */
297  if (next0 == NAT44_CLASSIFY_NEXT_OUT2IN)
298  reass0->classify_next =
300  else
301  reass0->classify_next =
303  nat_ip4_reass_get_frags (reass0,
304  &fragments_to_loopback);
305  }
306  }
307  else
308  {
309  /* process non-first fragment */
311  ip0->dst_address,
312  ip0->fragment_id,
313  ip0->protocol,
314  1,
315  &fragments_to_drop);
316  if (PREDICT_FALSE (!reass0))
317  {
318  next0 = NAT44_CLASSIFY_NEXT_DROP;
319  b0->error =
320  node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
321  nat_log_notice ("maximum reassemblies exceeded");
322  goto enqueue0;
323  }
324  if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE)
325  /* first fragment still hasn't arrived */
326  {
327  if (nat_ip4_reass_add_fragment (reass0, bi0,
328  &fragments_to_drop))
329  {
330  b0->error =
331  node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
333  ("maximum fragments per reassembly exceeded");
334  next0 = NAT44_CLASSIFY_NEXT_DROP;
335  goto enqueue0;
336  }
337  cached0 = 1;
338  goto enqueue0;
339  }
340  else if (reass0->classify_next ==
343  else if (reass0->classify_next ==
346  }
347  }
348 
349  enqueue0:
351  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
352  {
354  vlib_add_trace (vm, node, b0, sizeof (*t));
355  t->cached = cached0;
356  if (!cached0)
357  t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0;
358  }
359 
360  if (cached0)
361  {
362  n_left_to_next++;
363  to_next--;
364  }
365  else
366  /* verify speculative enqueue, maybe switch current next frame */
367  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
368  to_next, n_left_to_next,
369  bi0, next0);
370 
371  if (n_left_from == 0 && vec_len (fragments_to_loopback))
372  {
373  from = vlib_frame_vector_args (frame);
374  u32 len = vec_len (fragments_to_loopback);
375  if (len <= VLIB_FRAME_SIZE)
376  {
377  clib_memcpy (from, fragments_to_loopback,
378  sizeof (u32) * len);
379  n_left_from = len;
380  vec_reset_length (fragments_to_loopback);
381  }
382  else
383  {
384  clib_memcpy (from,
385  fragments_to_loopback + (len -
387  sizeof (u32) * VLIB_FRAME_SIZE);
388  n_left_from = VLIB_FRAME_SIZE;
389  _vec_len (fragments_to_loopback) = len - VLIB_FRAME_SIZE;
390  }
391  }
392  }
393 
394  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
395  }
396 
397  nat_send_all_to_node (vm, fragments_to_drop, node, 0,
399 
400  vec_free (fragments_to_drop);
401 
402  return frame->n_vectors;
403 }
404 
405 static uword
407  vlib_node_runtime_t * node, vlib_frame_t * frame)
408 {
409  return nat44_classify_node_fn_inline (vm, node, frame, 0);
410 }
411 
412 /* *INDENT-OFF* */
414  .function = nat44_classify_node_fn,
415  .name = "nat44-classify",
416  .vector_size = sizeof (u32),
417  .format_trace = format_nat44_classify_trace,
418  .type = VLIB_NODE_TYPE_INTERNAL,
420  .error_strings = nat44_classify_error_strings,
421  .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
422  .next_nodes = {
423  [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out",
424  [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in",
425  [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
426  },
427 };
428 /* *INDENT-ON* */
429 
431 static uword
433  vlib_node_runtime_t * node, vlib_frame_t * frame)
434 {
435  return nat44_classify_node_fn_inline (vm, node, frame, 1);
436 }
437 
438 /* *INDENT-OFF* */
440  .function = nat44_ed_classify_node_fn,
441  .name = "nat44-ed-classify",
442  .vector_size = sizeof (u32),
443  .format_trace = format_nat44_classify_trace,
444  .type = VLIB_NODE_TYPE_INTERNAL,
445  .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
446  .next_nodes = {
447  [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-ed-in2out",
448  [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-ed-out2in",
449  [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
450  },
451 };
452 /* *INDENT-ON* */
453 
456 
457 static uword
459  vlib_node_runtime_t * node, vlib_frame_t * frame)
460 {
461  return nat44_classify_node_fn_inline (vm, node, frame, 0);
462 }
463 
464 /* *INDENT-OFF* */
466  .function = nat44_det_classify_node_fn,
467  .name = "nat44-det-classify",
468  .vector_size = sizeof (u32),
469  .format_trace = format_nat44_classify_trace,
470  .type = VLIB_NODE_TYPE_INTERNAL,
471  .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
472  .next_nodes = {
473  [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-det-in2out",
474  [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-det-out2in",
475  [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
476  },
477 };
478 /* *INDENT-ON* */
479 
482 
483 static uword
485  vlib_node_runtime_t * node,
486  vlib_frame_t * frame)
487 {
488  return nat44_classify_node_fn_inline (vm, node, frame, 0);
489 }
490 
491 /* *INDENT-OFF* */
493  .function = nat44_handoff_classify_node_fn,
494  .name = "nat44-handoff-classify",
495  .vector_size = sizeof (u32),
496  .format_trace = format_nat44_classify_trace,
497  .type = VLIB_NODE_TYPE_INTERNAL,
498  .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
499  .next_nodes = {
500  [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out-worker-handoff",
501  [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in-worker-handoff",
502  [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
503  },
504 };
505 
508 /* *INDENT-ON* */
509 
510 /*
511  * fd.io coding-style-patch-verification: ON
512  *
513  * Local Variables:
514  * eval: (c-set-style "gnu")
515  * End:
516  */
ip4_address_t external_addr
Definition: nat.h:327
nat44_classify_next_t
#define CLIB_UNUSED(x)
Definition: clib.h:81
ip4_address_t src_address
Definition: ip4_packet.h:169
vlib_node_registration_t nat44_ed_classify_node
(constructor) VLIB_REGISTER_NODE (nat44_ed_classify_node)
u32 fib_table_get_index_for_sw_if_index(fib_protocol_t proto, u32 sw_if_index)
Get the index of the FIB bound to the interface.
Definition: fib_table.c:956
u32 thread_index
Definition: main.h:179
static uword nat44_classify_node_fn_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, int is_ed)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
static u8 * format_nat44_classify_trace(u8 *s, va_list *args)
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:472
unsigned char u8
Definition: types.h:56
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
static int ip4_is_fragment(const ip4_header_t *i)
Definition: ip4_packet.h:212
ip4_address_t dst_address
Definition: ip4_packet.h:169
static uword nat44_handoff_classify_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
static void * ip4_next_header(ip4_header_t *i)
Definition: ip4_packet.h:240
unsigned int u32
Definition: types.h:88
#define NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE
Definition: nat_reass.h:34
ip4_address_t local_addr
Definition: nat.h:325
#define VLIB_FRAME_SIZE
Definition: node.h:382
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:464
VLIB_NODE_FUNCTION_MULTIARCH(nat44_classify_node, nat44_classify_node_fn)
u64 key
the key
Definition: bihash_8_8.h:33
u16 protocol
Definition: nat.h:54
snat_static_mapping_t * static_mappings
Definition: nat.h:452
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:205
#define PREDICT_FALSE(x)
Definition: clib.h:107
clib_bihash_8_8_t static_mapping_by_external
Definition: nat.h:449
vlib_node_registration_t nat44_handoff_classify_node
(constructor) VLIB_REGISTER_NODE (nat44_handoff_classify_node)
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
Finish enqueueing one buffer forward in the graph.
Definition: buffer_node.h:218
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Get pointer to next frame vector data by (vlib_node_runtime_t, next_index).
Definition: node_funcs.h:364
#define nat_log_notice(...)
Definition: nat.h:689
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:138
snat_main_t snat_main
Definition: nat.c:37
u64 value
the value
Definition: bihash_8_8.h:34
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:155
static uword nat44_ed_classify_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
u16 n_vectors
Definition: node.h:401
vlib_main_t * vm
Definition: buffer.c:294
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:339
#define clib_memcpy(a, b, c)
Definition: string.h:75
8 octet key, 8 octet key value pair
Definition: bihash_8_8.h:31
#define ARRAY_LEN(x)
Definition: clib.h:61
ip4_address_t addr
Definition: nat.h:52
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Release pointer to next frame vector data.
Definition: main.c:455
vlib_node_registration_t nat44_classify_node
(constructor) VLIB_REGISTER_NODE (nat44_classify_node)
static void make_ed_kv(clib_bihash_kv_16_8_t *kv, ip4_address_t *l_addr, ip4_address_t *r_addr, u8 proto, u32 fib_index, u16 l_port, u16 r_port)
Definition: nat_inlines.h:311
static char * nat44_classify_error_strings[]
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:513
ip4_address_t addr
Definition: nat.h:240
static void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:57
struct _vlib_node_registration vlib_node_registration_t
static int ip4_is_first_fragment(const ip4_header_t *i)
Definition: ip4_packet.h:219
static u32 ip_proto_to_snat_proto(u8 ip_proto)
The NAT inline functions.
Definition: nat_inlines.h:26
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
u64 uword
Definition: types.h:112
snat_main_per_thread_data_t * per_thread_data
Definition: nat.h:443
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:267
static uword nat44_classify_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
snat_address_t * addresses
Definition: nat.h:459
nat_reass_ip4_t * nat_ip4_reass_find_or_create(ip4_address_t src, ip4_address_t dst, u16 frag_id, u8 proto, u8 reset_timeout, u32 **bi_to_drop)
Find or create reassembly.
Definition: nat_reass.c:220
vlib_node_registration_t nat44_det_classify_node
(constructor) VLIB_REGISTER_NODE (nat44_det_classify_node)
nat44_classify_error_t
#define vnet_buffer(b)
Definition: buffer.h:344
int nat_ip4_reass_add_fragment(nat_reass_ip4_t *reass, u32 bi, u32 **bi_to_drop)
Cache fragment.
Definition: nat_reass.c:338
#define vec_foreach(var, vec)
Vector iterator.
u16 flags
Copy of main node flags.
Definition: node.h:507
static void nat_send_all_to_node(vlib_main_t *vm, u32 *bi_vector, vlib_node_runtime_t *node, vlib_error_t *error, u32 next)
Definition: nat_inlines.h:104
static uword nat44_det_classify_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
clib_bihash_16_8_t in2out_ed
Definition: nat.h:384
void nat_ip4_reass_get_frags(nat_reass_ip4_t *reass, u32 **bi)
Get cached fragments.
Definition: nat_reass.c:370
NAT plugin virtual fragmentation reassembly.
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:310
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index, VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:116
#define foreach_nat44_classify_error
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:58
Definition: defs.h:46
u16 fib_index
Definition: nat.h:54
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:128