FD.io VPP  v21.01.1
Vector Packet Processing
ip4_full_reass.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 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 /**
17  * @file
18  * @brief IPv4 Full Reassembly.
19  *
20  * This file contains the source code for IPv4 full reassembly.
21  */
22 
23 #include <vppinfra/vec.h>
24 #include <vnet/vnet.h>
25 #include <vnet/ip/ip.h>
26 #include <vppinfra/fifo.h>
27 #include <vppinfra/bihash_16_8.h>
29 #include <stddef.h>
30 
31 #define MSEC_PER_SEC 1000
32 #define IP4_REASS_TIMEOUT_DEFAULT_MS 100
33 #define IP4_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS 10000 // 10 seconds default
34 #define IP4_REASS_MAX_REASSEMBLIES_DEFAULT 1024
35 #define IP4_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT 3
36 #define IP4_REASS_HT_LOAD_FACTOR (0.75)
37 
38 #define IP4_REASS_DEBUG_BUFFERS 0
39 #if IP4_REASS_DEBUG_BUFFERS
40 #define IP4_REASS_DEBUG_BUFFER(bi, what) \
41  do \
42  { \
43  u32 _bi = bi; \
44  printf (#what "buffer %u", _bi); \
45  vlib_buffer_t *_b = vlib_get_buffer (vm, _bi); \
46  while (_b->flags & VLIB_BUFFER_NEXT_PRESENT) \
47  { \
48  _bi = _b->next_buffer; \
49  printf ("[%u]", _bi); \
50  _b = vlib_get_buffer (vm, _bi); \
51  } \
52  printf ("\n"); \
53  fflush (stdout); \
54  } \
55  while (0)
56 #else
57 #define IP4_REASS_DEBUG_BUFFER(...)
58 #endif
59 
60 typedef enum
61 {
68 
69 typedef struct
70 {
71  union
72  {
73  struct
74  {
81  };
82  u64 as_u64[2];
83  };
85 
86 typedef union
87 {
88  struct
89  {
92  };
95 
96 typedef union
97 {
98  struct
99  {
102  };
105 
108 {
110  return vnb->ip.reass.range_first - vnb->ip.reass.fragment_first;
111 }
112 
115 {
117  return clib_min (vnb->ip.reass.range_last, vnb->ip.reass.fragment_last) -
118  (vnb->ip.reass.fragment_first +
120 }
121 
122 typedef struct
123 {
124  // hash table key
126  // time when last packet was received
128  // internal id of this reassembly
130  // buffer index of first buffer in this reassembly context
132  // last octet of packet, ~0 until fragment without more_fragments arrives
134  // length of data collected so far
136  // trace operation counter
138  // next index - used by non-feature node
140  // error next index - used by custom apps (~0 if not used)
142  // minimum fragment length for this reassembly - used to estimate MTU
144  // number of fragments in this reassembly
146  // thread owning memory for this context (whose pool contains this ctx)
148  // thread which received fragment with offset 0 and which sends out the
149  // completed reassembly
152 
153 typedef struct
154 {
160 
161 typedef struct
162 {
163  // IPv4 config
167  // maximum number of fragments in one reassembly
169  // maximum number of reassemblies
171 
172  // IPv4 runtime
173  clib_bihash_16_8_t hash;
174  // per-thread data
176 
177  // convenience
179 
180  // node index of ip4-drop node
183 
184  /** Worker handoff */
188 
189  // reference count for enabling/disabling feature - per interface
192 
194 
195 #ifndef CLIB_MARCH_VARIANT
197 #endif /* CLIB_MARCH_VARIANT */
198 
199 typedef enum
200 {
206 
207 typedef enum
208 {
213 
214 typedef enum
215 {
223 
224 typedef struct
225 {
233 
234 typedef struct
235 {
236  ip4_full_reass_trace_operation_e action;
249 
253 
254 static void
257 {
258  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
260  trace->range_first = vnb->ip.reass.range_first;
261  trace->range_last = vnb->ip.reass.range_last;
264  trace->range_bi = bi;
265 }
266 
267 static u8 *
269 {
271  va_arg (*args, ip4_full_reass_range_trace_t *);
272  s =
273  format (s, "range: [%u, %u], off %d, len %u, bi %u", trace->range_first,
274  trace->range_last, trace->data_offset, trace->data_len,
275  trace->range_bi);
276  return s;
277 }
278 
279 static u8 *
280 format_ip4_full_reass_trace (u8 * s, va_list * args)
281 {
282  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
283  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
284  ip4_full_reass_trace_t *t = va_arg (*args, ip4_full_reass_trace_t *);
285  u32 indent = 0;
286  if (~0 != t->reass_id)
287  {
288  if (t->is_after_handoff)
289  {
290  s =
291  format (s, "%U\n", format_ip4_header, &t->ip4_header,
292  sizeof (t->ip4_header));
293  indent = 2;
294  }
295  s =
296  format (s, "%Ureass id: %u, op id: %u, ", format_white_space, indent,
297  t->reass_id, t->op_id);
298  indent = format_get_indent (s);
299  s =
300  format (s,
301  "first bi: %u, data len: %u, ip/fragment[%u, %u]",
303  t->fragment_last);
304  }
305  switch (t->action)
306  {
307  case RANGE_SHRINK:
308  s = format (s, "\n%Ushrink %U by %u", format_white_space, indent,
310  t->size_diff);
311  break;
312  case RANGE_DISCARD:
313  s = format (s, "\n%Udiscard %U", format_white_space, indent,
315  break;
316  case RANGE_NEW:
317  s = format (s, "\n%Unew %U", format_white_space, indent,
319  break;
320  case RANGE_OVERLAP:
321  s = format (s, "\n%Uoverlapping/ignored %U", format_white_space, indent,
323  break;
324  case FINALIZE:
325  s = format (s, "\n%Ufinalize reassembly", format_white_space, indent);
326  break;
327  case HANDOFF:
328  s =
329  format (s, "handoff from thread #%u to thread #%u", t->thread_id,
330  t->thread_id_to);
331  break;
332  }
333  return s;
334 }
335 
336 static void
339  ip4_full_reass_t * reass, u32 bi,
340  ip4_full_reass_trace_operation_e action,
341  u32 size_diff, u32 thread_id_to)
342 {
343  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
347  {
348  // this buffer's trace is gone
349  b->flags &= ~VLIB_BUFFER_IS_TRACED;
350  return;
351  }
352  bool is_after_handoff = false;
354  {
355  is_after_handoff = true;
356  }
357  ip4_full_reass_trace_t *t = vlib_add_trace (vm, node, b, sizeof (t[0]));
358  t->is_after_handoff = is_after_handoff;
359  if (t->is_after_handoff)
360  {
362  clib_min (sizeof (t->ip4_header), b->current_length));
363  }
364  if (reass)
365  {
366  t->reass_id = reass->id;
367  t->op_id = reass->trace_op_counter;
368  t->trace_range.first_bi = reass->first_bi;
369  t->total_data_len = reass->data_len;
370  ++reass->trace_op_counter;
371  }
372  else
373  {
374  t->reass_id = ~0;
375  t->op_id = 0;
376  t->trace_range.first_bi = 0;
377  t->total_data_len = 0;
378  }
379  t->action = action;
381  t->size_diff = size_diff;
382  t->thread_id = vm->thread_index;
383  t->thread_id_to = thread_id_to;
384  t->fragment_first = vnb->ip.reass.fragment_first;
385  t->fragment_last = vnb->ip.reass.fragment_last;
386 #if 0
387  static u8 *s = NULL;
388  s = format (s, "%U", format_ip4_full_reass_trace, NULL, NULL, t);
389  printf ("%.*s\n", vec_len (s), s);
390  fflush (stdout);
391  vec_reset_length (s);
392 #endif
393 }
394 
395 always_inline void
397  ip4_full_reass_t * reass)
398 {
399  pool_put (rt->pool, reass);
400  --rt->reass_n;
401 }
402 
403 always_inline void
406  ip4_full_reass_t * reass)
407 {
409  kv.key[0] = reass->key.as_u64[0];
410  kv.key[1] = reass->key.as_u64[1];
411  clib_bihash_add_del_16_8 (&rm->hash, &kv, 0);
412  return ip4_full_reass_free_ctx (rt, reass);
413 }
414 
415 always_inline void
418 {
419  u32 range_bi = reass->first_bi;
420  vlib_buffer_t *range_b;
421  vnet_buffer_opaque_t *range_vnb;
422  u32 *to_free = NULL;
423  while (~0 != range_bi)
424  {
425  range_b = vlib_get_buffer (vm, range_bi);
426  range_vnb = vnet_buffer (range_b);
427  u32 bi = range_bi;
428  while (~0 != bi)
429  {
430  vec_add1 (to_free, bi);
431  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
432  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
433  {
434  bi = b->next_buffer;
435  b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
436  }
437  else
438  {
439  bi = ~0;
440  }
441  }
442  range_bi = range_vnb->ip.reass.next_range_bi;
443  }
444  /* send to next_error_index */
445  if (~0 != reass->error_next_index)
446  {
447  u32 n_left_to_next, *to_next, next_index;
448 
449  next_index = reass->error_next_index;
450  u32 bi = ~0;
451 
452  while (vec_len (to_free) > 0)
453  {
454  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
455 
456  while (vec_len (to_free) > 0 && n_left_to_next > 0)
457  {
458  bi = vec_pop (to_free);
459 
460  if (~0 != bi)
461  {
462  to_next[0] = bi;
463  to_next += 1;
464  n_left_to_next -= 1;
465  }
466  }
467  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
468  }
469  }
470  else
471  {
472  vlib_buffer_free (vm, to_free, vec_len (to_free));
473  }
474  vec_free (to_free);
475 }
476 
477 always_inline void
479 {
480  reass->first_bi = ~0;
481  reass->last_packet_octet = ~0;
482  reass->data_len = 0;
483  reass->next_index = ~0;
484  reass->error_next_index = ~0;
485 }
486 
491  ip4_full_reass_kv_t * kv, u8 * do_handoff)
492 {
493  ip4_full_reass_t *reass;
494  f64 now;
495 
496 again:
497 
498  reass = NULL;
499  now = vlib_time_now (vm);
500  if (!clib_bihash_search_16_8 (&rm->hash, &kv->kv, &kv->kv))
501  {
503  {
504  *do_handoff = 1;
505  return NULL;
506  }
507  reass =
510  kv->v.reass_index);
511 
512  if (now > reass->last_heard + rm->timeout)
513  {
514  ip4_full_reass_drop_all (vm, node, rm, reass);
515  ip4_full_reass_free (rm, rt, reass);
516  reass = NULL;
517  }
518  }
519 
520  if (reass)
521  {
522  reass->last_heard = now;
523  return reass;
524  }
525 
526  if (rt->reass_n >= rm->max_reass_n)
527  {
528  reass = NULL;
529  return reass;
530  }
531  else
532  {
533  pool_get (rt->pool, reass);
534  clib_memset (reass, 0, sizeof (*reass));
535  reass->id = ((u64) vm->thread_index * 1000000000) + rt->id_counter;
537  ++rt->id_counter;
538  ip4_full_reass_init (reass);
539  ++rt->reass_n;
540  }
541 
542  reass->key.as_u64[0] = kv->kv.key[0];
543  reass->key.as_u64[1] = kv->kv.key[1];
544  kv->v.reass_index = (reass - rt->pool);
546  reass->last_heard = now;
547 
548  int rv = clib_bihash_add_del_16_8 (&rm->hash, &kv->kv, 2);
549  if (rv)
550  {
551  ip4_full_reass_free_ctx (rt, reass);
552  reass = NULL;
553  // if other worker created a context already work with the other copy
554  if (-2 == rv)
555  goto again;
556  }
557 
558  return reass;
559 }
560 
561 always_inline ip4_full_reass_rc_t
565  ip4_full_reass_t * reass, u32 * bi0,
566  u32 * next0, u32 * error0, bool is_custom)
567 {
568  vlib_buffer_t *first_b = vlib_get_buffer (vm, reass->first_bi);
569  vlib_buffer_t *last_b = NULL;
570  u32 sub_chain_bi = reass->first_bi;
571  u32 total_length = 0;
572  u32 buf_cnt = 0;
573  do
574  {
575  u32 tmp_bi = sub_chain_bi;
576  vlib_buffer_t *tmp = vlib_get_buffer (vm, tmp_bi);
578  vnet_buffer_opaque_t *vnb = vnet_buffer (tmp);
579  if (!(vnb->ip.reass.range_first >= vnb->ip.reass.fragment_first) &&
580  !(vnb->ip.reass.range_last > vnb->ip.reass.fragment_first))
581  {
583  }
584 
586  u32 trim_front =
588  u32 trim_end =
589  vlib_buffer_length_in_chain (vm, tmp) - trim_front - data_len;
590  if (tmp_bi == reass->first_bi)
591  {
592  /* first buffer - keep ip4 header */
594  {
596  }
597  trim_front = 0;
598  trim_end = vlib_buffer_length_in_chain (vm, tmp) - data_len -
599  ip4_header_bytes (ip);
600  if (!(vlib_buffer_length_in_chain (vm, tmp) - trim_end > 0))
601  {
603  }
604  }
605  u32 keep_data =
606  vlib_buffer_length_in_chain (vm, tmp) - trim_front - trim_end;
607  while (1)
608  {
609  ++buf_cnt;
610  if (trim_front)
611  {
612  if (trim_front > tmp->current_length)
613  {
614  /* drop whole buffer */
615  u32 to_be_freed_bi = tmp_bi;
616  trim_front -= tmp->current_length;
617  if (!(tmp->flags & VLIB_BUFFER_NEXT_PRESENT))
618  {
620  }
621  tmp->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
622  tmp_bi = tmp->next_buffer;
623  tmp->next_buffer = 0;
624  tmp = vlib_get_buffer (vm, tmp_bi);
625  vlib_buffer_free_one (vm, to_be_freed_bi);
626  continue;
627  }
628  else
629  {
630  vlib_buffer_advance (tmp, trim_front);
631  trim_front = 0;
632  }
633  }
634  if (keep_data)
635  {
636  if (last_b)
637  {
638  last_b->flags |= VLIB_BUFFER_NEXT_PRESENT;
639  last_b->next_buffer = tmp_bi;
640  }
641  last_b = tmp;
642  if (keep_data <= tmp->current_length)
643  {
644  tmp->current_length = keep_data;
645  keep_data = 0;
646  }
647  else
648  {
649  keep_data -= tmp->current_length;
650  if (!(tmp->flags & VLIB_BUFFER_NEXT_PRESENT))
651  {
653  }
654  }
655  total_length += tmp->current_length;
656  if (tmp->flags & VLIB_BUFFER_NEXT_PRESENT)
657  {
658  tmp_bi = tmp->next_buffer;
659  tmp = vlib_get_buffer (vm, tmp->next_buffer);
660  }
661  else
662  {
663  break;
664  }
665  }
666  else
667  {
668  u32 to_be_freed_bi = tmp_bi;
669  if (reass->first_bi == tmp_bi)
670  {
672  }
673  if (tmp->flags & VLIB_BUFFER_NEXT_PRESENT)
674  {
675  tmp->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
676  tmp_bi = tmp->next_buffer;
677  tmp->next_buffer = 0;
678  tmp = vlib_get_buffer (vm, tmp_bi);
679  vlib_buffer_free_one (vm, to_be_freed_bi);
680  }
681  else
682  {
683  tmp->next_buffer = 0;
684  vlib_buffer_free_one (vm, to_be_freed_bi);
685  break;
686  }
687  }
688  }
689  sub_chain_bi =
690  vnet_buffer (vlib_get_buffer (vm, sub_chain_bi))->ip.
691  reass.next_range_bi;
692  }
693  while (~0 != sub_chain_bi);
694 
695  if (!last_b)
696  {
698  }
699  last_b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
700 
701  if (total_length < first_b->current_length)
702  {
704  }
705  total_length -= first_b->current_length;
706  first_b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
707  first_b->total_length_not_including_first_buffer = total_length;
710  ip->length = clib_host_to_net_u16 (first_b->current_length + total_length);
711  ip->checksum = ip4_header_checksum (ip);
712  if (!vlib_buffer_chain_linearize (vm, first_b))
713  {
714  return IP4_REASS_RC_NO_BUF;
715  }
716  // reset to reconstruct the mbuf linking
717  first_b->flags &= ~VLIB_BUFFER_EXT_HDR_VALID;
718  if (PREDICT_FALSE (first_b->flags & VLIB_BUFFER_IS_TRACED))
719  {
720  ip4_full_reass_add_trace (vm, node, rm, reass, reass->first_bi,
721  FINALIZE, 0, ~0);
722 #if 0
723  // following code does a hexdump of packet fragments to stdout ...
724  do
725  {
726  u32 bi = reass->first_bi;
727  u8 *s = NULL;
728  while (~0 != bi)
729  {
730  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
731  s = format (s, "%u: %U\n", bi, format_hexdump,
733  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
734  {
735  bi = b->next_buffer;
736  }
737  else
738  {
739  break;
740  }
741  }
742  printf ("%.*s\n", vec_len (s), s);
743  fflush (stdout);
744  vec_free (s);
745  }
746  while (0);
747 #endif
748  }
749  *bi0 = reass->first_bi;
750  if (!is_custom)
751  {
752  *next0 = IP4_FULL_REASS_NEXT_INPUT;
753  }
754  else
755  {
756  *next0 = reass->next_index;
757  }
758  vnet_buffer (first_b)->ip.reass.estimated_mtu = reass->min_fragment_length;
759  *error0 = IP4_ERROR_NONE;
760  ip4_full_reass_free (rm, rt, reass);
761  reass = NULL;
762  return IP4_REASS_RC_OK;
763 }
764 
765 always_inline ip4_full_reass_rc_t
769  ip4_full_reass_t * reass,
770  u32 prev_range_bi, u32 new_next_bi)
771 {
772  vlib_buffer_t *new_next_b = vlib_get_buffer (vm, new_next_bi);
773  vnet_buffer_opaque_t *new_next_vnb = vnet_buffer (new_next_b);
774  if (~0 != prev_range_bi)
775  {
776  vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_range_bi);
777  vnet_buffer_opaque_t *prev_vnb = vnet_buffer (prev_b);
778  new_next_vnb->ip.reass.next_range_bi = prev_vnb->ip.reass.next_range_bi;
779  prev_vnb->ip.reass.next_range_bi = new_next_bi;
780  }
781  else
782  {
783  if (~0 != reass->first_bi)
784  {
785  new_next_vnb->ip.reass.next_range_bi = reass->first_bi;
786  }
787  reass->first_bi = new_next_bi;
788  }
789  vnet_buffer_opaque_t *vnb = vnet_buffer (new_next_b);
790  if (!(vnb->ip.reass.range_first >= vnb->ip.reass.fragment_first) &&
791  !(vnb->ip.reass.range_last > vnb->ip.reass.fragment_first))
792  {
794  }
795  reass->data_len += ip4_full_reass_buffer_get_data_len (new_next_b);
796  return IP4_REASS_RC_OK;
797 }
798 
799 always_inline ip4_full_reass_rc_t
801  vlib_node_runtime_t * node,
803  ip4_full_reass_t * reass,
804  u32 prev_range_bi, u32 discard_bi)
805 {
806  vlib_buffer_t *discard_b = vlib_get_buffer (vm, discard_bi);
807  vnet_buffer_opaque_t *discard_vnb = vnet_buffer (discard_b);
808  if (~0 != prev_range_bi)
809  {
810  vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_range_bi);
811  vnet_buffer_opaque_t *prev_vnb = vnet_buffer (prev_b);
812  if (!(prev_vnb->ip.reass.next_range_bi == discard_bi))
813  {
815  }
816  prev_vnb->ip.reass.next_range_bi = discard_vnb->ip.reass.next_range_bi;
817  }
818  else
819  {
820  reass->first_bi = discard_vnb->ip.reass.next_range_bi;
821  }
822  vnet_buffer_opaque_t *vnb = vnet_buffer (discard_b);
823  if (!(vnb->ip.reass.range_first >= vnb->ip.reass.fragment_first) &&
824  !(vnb->ip.reass.range_last > vnb->ip.reass.fragment_first))
825  {
827  }
828  reass->data_len -= ip4_full_reass_buffer_get_data_len (discard_b);
829  while (1)
830  {
831  u32 to_be_freed_bi = discard_bi;
832  if (PREDICT_FALSE (discard_b->flags & VLIB_BUFFER_IS_TRACED))
833  {
834  ip4_full_reass_add_trace (vm, node, rm, reass, discard_bi,
835  RANGE_DISCARD, 0, ~0);
836  }
837  if (discard_b->flags & VLIB_BUFFER_NEXT_PRESENT)
838  {
839  discard_b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
840  discard_bi = discard_b->next_buffer;
841  discard_b->next_buffer = 0;
842  discard_b = vlib_get_buffer (vm, discard_bi);
843  vlib_buffer_free_one (vm, to_be_freed_bi);
844  }
845  else
846  {
847  discard_b->next_buffer = 0;
848  vlib_buffer_free_one (vm, to_be_freed_bi);
849  break;
850  }
851  }
852  return IP4_REASS_RC_OK;
853 }
854 
855 always_inline ip4_full_reass_rc_t
859  ip4_full_reass_t * reass, u32 * bi0, u32 * next0,
860  u32 * error0, bool is_custom, u32 * handoff_thread_idx)
861 {
862  vlib_buffer_t *fb = vlib_get_buffer (vm, *bi0);
863  vnet_buffer_opaque_t *fvnb = vnet_buffer (fb);
864  if (is_custom)
865  {
866  // store (error_)next_index before it's overwritten
867  reass->next_index = fvnb->ip.reass.next_index;
868  reass->error_next_index = fvnb->ip.reass.error_next_index;
869  }
870  ip4_full_reass_rc_t rc = IP4_REASS_RC_OK;
871  int consumed = 0;
873  const u32 fragment_first = ip4_get_fragment_offset_bytes (fip);
874  const u32 fragment_length =
875  clib_net_to_host_u16 (fip->length) - ip4_header_bytes (fip);
876  const u32 fragment_last = fragment_first + fragment_length - 1;
877  fvnb->ip.reass.fragment_first = fragment_first;
878  fvnb->ip.reass.fragment_last = fragment_last;
879  int more_fragments = ip4_get_fragment_more (fip);
880  u32 candidate_range_bi = reass->first_bi;
881  u32 prev_range_bi = ~0;
882  fvnb->ip.reass.range_first = fragment_first;
883  fvnb->ip.reass.range_last = fragment_last;
884  fvnb->ip.reass.next_range_bi = ~0;
885  if (!more_fragments)
886  {
887  reass->last_packet_octet = fragment_last;
888  }
889  if (~0 == reass->first_bi)
890  {
891  // starting a new reassembly
892  rc =
893  ip4_full_reass_insert_range_in_chain (vm, rm, rt, reass,
894  prev_range_bi, *bi0);
895  if (IP4_REASS_RC_OK != rc)
896  {
897  return rc;
898  }
899  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
900  {
901  ip4_full_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_NEW, 0,
902  ~0);
903  }
904  *bi0 = ~0;
905  reass->min_fragment_length = clib_net_to_host_u16 (fip->length);
906  reass->fragments_n = 1;
907  return IP4_REASS_RC_OK;
908  }
909  reass->min_fragment_length =
910  clib_min (clib_net_to_host_u16 (fip->length),
911  fvnb->ip.reass.estimated_mtu);
912  while (~0 != candidate_range_bi)
913  {
914  vlib_buffer_t *candidate_b = vlib_get_buffer (vm, candidate_range_bi);
915  vnet_buffer_opaque_t *candidate_vnb = vnet_buffer (candidate_b);
916  if (fragment_first > candidate_vnb->ip.reass.range_last)
917  {
918  // this fragments starts after candidate range
919  prev_range_bi = candidate_range_bi;
920  candidate_range_bi = candidate_vnb->ip.reass.next_range_bi;
921  if (candidate_vnb->ip.reass.range_last < fragment_last &&
922  ~0 == candidate_range_bi)
923  {
924  // special case - this fragment falls beyond all known ranges
925  rc =
926  ip4_full_reass_insert_range_in_chain (vm, rm, rt, reass,
927  prev_range_bi, *bi0);
928  if (IP4_REASS_RC_OK != rc)
929  {
930  return rc;
931  }
932  consumed = 1;
933  break;
934  }
935  continue;
936  }
937  if (fragment_last < candidate_vnb->ip.reass.range_first)
938  {
939  // this fragment ends before candidate range without any overlap
940  rc =
941  ip4_full_reass_insert_range_in_chain (vm, rm, rt, reass,
942  prev_range_bi, *bi0);
943  if (IP4_REASS_RC_OK != rc)
944  {
945  return rc;
946  }
947  consumed = 1;
948  }
949  else
950  {
951  if (fragment_first >= candidate_vnb->ip.reass.range_first &&
952  fragment_last <= candidate_vnb->ip.reass.range_last)
953  {
954  // this fragment is a (sub)part of existing range, ignore it
955  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
956  {
957  ip4_full_reass_add_trace (vm, node, rm, reass, *bi0,
958  RANGE_OVERLAP, 0, ~0);
959  }
960  break;
961  }
962  int discard_candidate = 0;
963  if (fragment_first < candidate_vnb->ip.reass.range_first)
964  {
965  u32 overlap =
966  fragment_last - candidate_vnb->ip.reass.range_first + 1;
967  if (overlap < ip4_full_reass_buffer_get_data_len (candidate_b))
968  {
969  candidate_vnb->ip.reass.range_first += overlap;
970  if (reass->data_len < overlap)
971  {
973  }
974  reass->data_len -= overlap;
975  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
976  {
977  ip4_full_reass_add_trace (vm, node, rm, reass,
978  candidate_range_bi,
979  RANGE_SHRINK, 0, ~0);
980  }
981  rc =
982  ip4_full_reass_insert_range_in_chain (vm, rm, rt, reass,
983  prev_range_bi,
984  *bi0);
985  if (IP4_REASS_RC_OK != rc)
986  {
987  return rc;
988  }
989  consumed = 1;
990  }
991  else
992  {
993  discard_candidate = 1;
994  }
995  }
996  else if (fragment_last > candidate_vnb->ip.reass.range_last)
997  {
998  u32 overlap =
999  candidate_vnb->ip.reass.range_last - fragment_first + 1;
1000  if (overlap < ip4_full_reass_buffer_get_data_len (candidate_b))
1001  {
1002  fvnb->ip.reass.range_first += overlap;
1003  if (~0 != candidate_vnb->ip.reass.next_range_bi)
1004  {
1005  prev_range_bi = candidate_range_bi;
1006  candidate_range_bi =
1007  candidate_vnb->ip.reass.next_range_bi;
1008  continue;
1009  }
1010  else
1011  {
1012  // special case - last range discarded
1013  rc =
1015  reass,
1016  candidate_range_bi,
1017  *bi0);
1018  if (IP4_REASS_RC_OK != rc)
1019  {
1020  return rc;
1021  }
1022  consumed = 1;
1023  }
1024  }
1025  else
1026  {
1027  discard_candidate = 1;
1028  }
1029  }
1030  else
1031  {
1032  discard_candidate = 1;
1033  }
1034  if (discard_candidate)
1035  {
1036  u32 next_range_bi = candidate_vnb->ip.reass.next_range_bi;
1037  // discard candidate range, probe next range
1038  rc =
1039  ip4_full_reass_remove_range_from_chain (vm, node, rm, reass,
1040  prev_range_bi,
1041  candidate_range_bi);
1042  if (IP4_REASS_RC_OK != rc)
1043  {
1044  return rc;
1045  }
1046  if (~0 != next_range_bi)
1047  {
1048  candidate_range_bi = next_range_bi;
1049  continue;
1050  }
1051  else
1052  {
1053  // special case - last range discarded
1054  rc =
1055  ip4_full_reass_insert_range_in_chain (vm, rm, rt, reass,
1056  prev_range_bi,
1057  *bi0);
1058  if (IP4_REASS_RC_OK != rc)
1059  {
1060  return rc;
1061  }
1062  consumed = 1;
1063  }
1064  }
1065  }
1066  break;
1067  }
1068  ++reass->fragments_n;
1069  if (consumed)
1070  {
1071  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
1072  {
1073  ip4_full_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_NEW, 0,
1074  ~0);
1075  }
1076  }
1077  if (~0 != reass->last_packet_octet &&
1078  reass->data_len == reass->last_packet_octet + 1)
1079  {
1080  *handoff_thread_idx = reass->sendout_thread_index;
1081  int handoff =
1083  rc =
1084  ip4_full_reass_finalize (vm, node, rm, rt, reass, bi0, next0, error0,
1085  is_custom);
1086  if (IP4_REASS_RC_OK == rc && handoff)
1087  {
1088  rc = IP4_REASS_RC_HANDOFF;
1089  }
1090  }
1091  else
1092  {
1093  if (consumed)
1094  {
1095  *bi0 = ~0;
1096  if (reass->fragments_n > rm->max_reass_len)
1097  {
1099  }
1100  }
1101  else
1102  {
1103  *next0 = IP4_FULL_REASS_NEXT_DROP;
1104  *error0 = IP4_ERROR_REASS_DUPLICATE_FRAGMENT;
1105  }
1106  }
1107  return rc;
1108 }
1109 
1112  vlib_frame_t * frame, ip4_full_reass_node_type_t type)
1113 {
1114  u32 *from = vlib_frame_vector_args (frame);
1115  u32 n_left_from, n_left_to_next, *to_next, next_index;
1118  clib_spinlock_lock (&rt->lock);
1119 
1120  n_left_from = frame->n_vectors;
1121  next_index = node->cached_next_index;
1122  while (n_left_from > 0)
1123  {
1124  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1125 
1126  while (n_left_from > 0 && n_left_to_next > 0)
1127  {
1128  u32 bi0;
1129  vlib_buffer_t *b0;
1130  u32 next0;
1131  u32 error0 = IP4_ERROR_NONE;
1132 
1133  bi0 = from[0];
1134  b0 = vlib_get_buffer (vm, bi0);
1135 
1137  if (!ip4_get_fragment_more (ip0) && !ip4_get_fragment_offset (ip0))
1138  {
1139  // this is a whole packet - no fragmentation
1140  if (CUSTOM != type)
1141  {
1142  next0 = IP4_FULL_REASS_NEXT_INPUT;
1143  }
1144  else
1145  {
1146  next0 = vnet_buffer (b0)->ip.reass.next_index;
1147  }
1148  goto packet_enqueue;
1149  }
1150  const u32 fragment_first = ip4_get_fragment_offset_bytes (ip0);
1151  const u32 fragment_length =
1152  clib_net_to_host_u16 (ip0->length) - ip4_header_bytes (ip0);
1153  const u32 fragment_last = fragment_first + fragment_length - 1;
1154  if (fragment_first > fragment_last || fragment_first + fragment_length > UINT16_MAX - 20 || (fragment_length < 8 && ip4_get_fragment_more (ip0))) // 8 is minimum frag length per RFC 791
1155  {
1156  next0 = IP4_FULL_REASS_NEXT_DROP;
1157  error0 = IP4_ERROR_REASS_MALFORMED_PACKET;
1158  goto packet_enqueue;
1159  }
1161  u8 do_handoff = 0;
1162 
1163  kv.k.as_u64[0] =
1165  vnet_buffer (b0)->sw_if_index[VLIB_RX]) |
1166  (u64) ip0->src_address.as_u32 << 32;
1167  kv.k.as_u64[1] =
1168  (u64) ip0->dst_address.
1169  as_u32 | (u64) ip0->fragment_id << 32 | (u64) ip0->protocol << 48;
1170 
1171  ip4_full_reass_t *reass =
1172  ip4_full_reass_find_or_create (vm, node, rm, rt, &kv,
1173  &do_handoff);
1174 
1175  if (reass)
1176  {
1177  const u32 fragment_first = ip4_get_fragment_offset_bytes (ip0);
1178  if (0 == fragment_first)
1179  {
1180  reass->sendout_thread_index = vm->thread_index;
1181  }
1182  }
1183 
1184  if (PREDICT_FALSE (do_handoff))
1185  {
1187  vnet_buffer (b0)->ip.reass.owner_thread_index =
1189  }
1190  else if (reass)
1191  {
1192  u32 handoff_thread_idx;
1193  switch (ip4_full_reass_update
1194  (vm, node, rm, rt, reass, &bi0, &next0,
1195  &error0, CUSTOM == type, &handoff_thread_idx))
1196  {
1197  case IP4_REASS_RC_OK:
1198  /* nothing to do here */
1199  break;
1200  case IP4_REASS_RC_HANDOFF:
1202  b0 = vlib_get_buffer (vm, bi0);
1203  vnet_buffer (b0)->ip.reass.owner_thread_index =
1204  handoff_thread_idx;
1205  break;
1208  IP4_ERROR_REASS_FRAGMENT_CHAIN_TOO_LONG,
1209  1);
1210  ip4_full_reass_drop_all (vm, node, rm, reass);
1211  ip4_full_reass_free (rm, rt, reass);
1212  goto next_packet;
1213  break;
1214  case IP4_REASS_RC_NO_BUF:
1216  IP4_ERROR_REASS_NO_BUF, 1);
1217  ip4_full_reass_drop_all (vm, node, rm, reass);
1218  ip4_full_reass_free (rm, rt, reass);
1219  goto next_packet;
1220  break;
1222  /* drop everything and start with a clean slate */
1224  IP4_ERROR_REASS_INTERNAL_ERROR,
1225  1);
1226  ip4_full_reass_drop_all (vm, node, rm, reass);
1227  ip4_full_reass_free (rm, rt, reass);
1228  goto next_packet;
1229  break;
1230  }
1231  }
1232  else
1233  {
1234  next0 = IP4_FULL_REASS_NEXT_DROP;
1235  error0 = IP4_ERROR_REASS_LIMIT_REACHED;
1236  }
1237 
1238 
1239  packet_enqueue:
1240 
1241  if (bi0 != ~0)
1242  {
1243  to_next[0] = bi0;
1244  to_next += 1;
1245  n_left_to_next -= 1;
1246 
1247  /* bi0 might have been updated by reass_finalize, reload */
1248  b0 = vlib_get_buffer (vm, bi0);
1249  if (IP4_ERROR_NONE != error0)
1250  {
1251  b0->error = node->errors[error0];
1252  }
1253 
1254  if (next0 == IP4_FULL_REASS_NEXT_HANDOFF)
1255  {
1256  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
1257  {
1258  ip4_full_reass_add_trace (vm, node, rm, NULL, bi0,
1259  HANDOFF, 0,
1260  vnet_buffer (b0)->ip.
1261  reass.owner_thread_index);
1262  }
1263  }
1264  else if (FEATURE == type && IP4_ERROR_NONE == error0)
1265  {
1266  vnet_feature_next (&next0, b0);
1267  }
1268  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1269  to_next, n_left_to_next,
1270  bi0, next0);
1271  IP4_REASS_DEBUG_BUFFER (bi0, enqueue_next);
1272  }
1273 
1274  next_packet:
1275  from += 1;
1276  n_left_from -= 1;
1277  }
1278 
1279  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1280  }
1281 
1282  clib_spinlock_unlock (&rt->lock);
1283  return frame->n_vectors;
1284 }
1285 
1287 #define _(sym, string) string,
1289 #undef _
1290 };
1291 
1292 VLIB_NODE_FN (ip4_full_reass_node) (vlib_main_t * vm,
1294  vlib_frame_t * frame)
1295 {
1296  return ip4_full_reass_inline (vm, node, frame, NORMAL);
1297 }
1298 
1299 /* *INDENT-OFF* */
1300 VLIB_REGISTER_NODE (ip4_full_reass_node) = {
1301  .name = "ip4-full-reassembly",
1302  .vector_size = sizeof (u32),
1303  .format_trace = format_ip4_full_reass_trace,
1304  .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
1305  .error_strings = ip4_full_reass_error_strings,
1306  .n_next_nodes = IP4_FULL_REASS_N_NEXT,
1307  .next_nodes =
1308  {
1309  [IP4_FULL_REASS_NEXT_INPUT] = "ip4-input",
1310  [IP4_FULL_REASS_NEXT_DROP] = "ip4-drop",
1311  [IP4_FULL_REASS_NEXT_HANDOFF] = "ip4-full-reassembly-handoff",
1312 
1313  },
1314 };
1315 /* *INDENT-ON* */
1316 
1317 VLIB_NODE_FN (ip4_full_reass_node_feature) (vlib_main_t * vm,
1319  vlib_frame_t * frame)
1320 {
1321  return ip4_full_reass_inline (vm, node, frame, FEATURE);
1322 }
1323 
1324 /* *INDENT-OFF* */
1325 VLIB_REGISTER_NODE (ip4_full_reass_node_feature) = {
1326  .name = "ip4-full-reassembly-feature",
1327  .vector_size = sizeof (u32),
1328  .format_trace = format_ip4_full_reass_trace,
1329  .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
1330  .error_strings = ip4_full_reass_error_strings,
1331  .n_next_nodes = IP4_FULL_REASS_N_NEXT,
1332  .next_nodes =
1333  {
1334  [IP4_FULL_REASS_NEXT_INPUT] = "ip4-input",
1335  [IP4_FULL_REASS_NEXT_DROP] = "ip4-drop",
1336  [IP4_FULL_REASS_NEXT_HANDOFF] = "ip4-full-reass-feature-hoff",
1337  },
1338 };
1339 /* *INDENT-ON* */
1340 
1341 /* *INDENT-OFF* */
1342 VNET_FEATURE_INIT (ip4_full_reass_feature, static) = {
1343  .arc_name = "ip4-unicast",
1344  .node_name = "ip4-full-reassembly-feature",
1345  .runs_before = VNET_FEATURES ("ip4-lookup",
1346  "ipsec4-input-feature"),
1347  .runs_after = 0,
1348 };
1349 /* *INDENT-ON* */
1350 
1351 VLIB_NODE_FN (ip4_full_reass_node_custom) (vlib_main_t * vm,
1353  vlib_frame_t * frame)
1354 {
1355  return ip4_full_reass_inline (vm, node, frame, CUSTOM);
1356 }
1357 
1358 /* *INDENT-OFF* */
1359 VLIB_REGISTER_NODE (ip4_full_reass_node_custom) = {
1360  .name = "ip4-full-reassembly-custom",
1361  .vector_size = sizeof (u32),
1362  .format_trace = format_ip4_full_reass_trace,
1363  .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
1364  .error_strings = ip4_full_reass_error_strings,
1365  .n_next_nodes = IP4_FULL_REASS_N_NEXT,
1366  .next_nodes =
1367  {
1368  [IP4_FULL_REASS_NEXT_INPUT] = "ip4-input",
1369  [IP4_FULL_REASS_NEXT_DROP] = "ip4-drop",
1370  [IP4_FULL_REASS_NEXT_HANDOFF] = "ip4-full-reass-custom-hoff",
1371  },
1372 };
1373 /* *INDENT-ON* */
1374 
1375 /* *INDENT-OFF* */
1376 VNET_FEATURE_INIT (ip4_full_reass_custom, static) = {
1377  .arc_name = "ip4-unicast",
1378  .node_name = "ip4-full-reassembly-feature",
1379  .runs_before = VNET_FEATURES ("ip4-lookup",
1380  "ipsec4-input-feature"),
1381  .runs_after = 0,
1382 };
1383 
1384 /* *INDENT-ON* */
1385 
1386 #ifndef CLIB_MARCH_VARIANT
1387 uword
1389 {
1390  return vlib_node_add_next (vlib_get_main (),
1391  ip4_full_reass_node_custom.index, node_index);
1392 }
1393 
1396 {
1398  u32 nbuckets;
1399  u8 i;
1400 
1401  nbuckets = (u32) (rm->max_reass_n / IP4_REASS_HT_LOAD_FACTOR);
1402 
1403  for (i = 0; i < 31; i++)
1404  if ((1 << i) >= nbuckets)
1405  break;
1406  nbuckets = 1 << i;
1407 
1408  return nbuckets;
1409 }
1410 #endif /* CLIB_MARCH_VARIANT */
1411 
1412 typedef enum
1413 {
1416 
1417 typedef struct
1418 {
1419  int failure;
1420  clib_bihash_16_8_t *new_hash;
1422 
1423 #ifndef CLIB_MARCH_VARIANT
1424 static int
1426 {
1427  ip4_rehash_cb_ctx *ctx = _ctx;
1428  if (clib_bihash_add_del_16_8 (ctx->new_hash, kv, 1))
1429  {
1430  ctx->failure = 1;
1431  }
1432  return (BIHASH_WALK_CONTINUE);
1433 }
1434 
1435 static void
1436 ip4_full_reass_set_params (u32 timeout_ms, u32 max_reassemblies,
1437  u32 max_reassembly_length,
1438  u32 expire_walk_interval_ms)
1439 {
1440  ip4_full_reass_main.timeout_ms = timeout_ms;
1441  ip4_full_reass_main.timeout = (f64) timeout_ms / (f64) MSEC_PER_SEC;
1442  ip4_full_reass_main.max_reass_n = max_reassemblies;
1443  ip4_full_reass_main.max_reass_len = max_reassembly_length;
1444  ip4_full_reass_main.expire_walk_interval_ms = expire_walk_interval_ms;
1445 }
1446 
1448 ip4_full_reass_set (u32 timeout_ms, u32 max_reassemblies,
1449  u32 max_reassembly_length, u32 expire_walk_interval_ms)
1450 {
1451  u32 old_nbuckets = ip4_full_reass_get_nbuckets ();
1452  ip4_full_reass_set_params (timeout_ms, max_reassemblies,
1453  max_reassembly_length, expire_walk_interval_ms);
1454  vlib_process_signal_event (ip4_full_reass_main.vlib_main,
1455  ip4_full_reass_main.ip4_full_reass_expire_node_idx,
1457  u32 new_nbuckets = ip4_full_reass_get_nbuckets ();
1458  if (ip4_full_reass_main.max_reass_n > 0 && new_nbuckets > old_nbuckets)
1459  {
1460  clib_bihash_16_8_t new_hash;
1461  clib_memset (&new_hash, 0, sizeof (new_hash));
1463  ctx.failure = 0;
1464  ctx.new_hash = &new_hash;
1465  clib_bihash_init_16_8 (&new_hash, "ip4-dr", new_nbuckets,
1466  new_nbuckets * 1024);
1467  clib_bihash_foreach_key_value_pair_16_8 (&ip4_full_reass_main.hash,
1468  ip4_rehash_cb, &ctx);
1469  if (ctx.failure)
1470  {
1471  clib_bihash_free_16_8 (&new_hash);
1472  return -1;
1473  }
1474  else
1475  {
1476  clib_bihash_free_16_8 (&ip4_full_reass_main.hash);
1477  clib_memcpy_fast (&ip4_full_reass_main.hash, &new_hash,
1478  sizeof (ip4_full_reass_main.hash));
1479  clib_bihash_copied (&ip4_full_reass_main.hash, &new_hash);
1480  }
1481  }
1482  return 0;
1483 }
1484 
1486 ip4_full_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
1487  u32 * max_reassembly_length,
1488  u32 * expire_walk_interval_ms)
1489 {
1490  *timeout_ms = ip4_full_reass_main.timeout_ms;
1491  *max_reassemblies = ip4_full_reass_main.max_reass_n;
1492  *max_reassembly_length = ip4_full_reass_main.max_reass_len;
1493  *expire_walk_interval_ms = ip4_full_reass_main.expire_walk_interval_ms;
1494  return 0;
1495 }
1496 
1497 static clib_error_t *
1499 {
1501  clib_error_t *error = 0;
1502  u32 nbuckets;
1503  vlib_node_t *node;
1504 
1505  rm->vlib_main = vm;
1506 
1509  vec_foreach (rt, rm->per_thread_data)
1510  {
1511  clib_spinlock_init (&rt->lock);
1512  pool_alloc (rt->pool, rm->max_reass_n);
1513  }
1514 
1515  node = vlib_get_node_by_name (vm, (u8 *) "ip4-full-reassembly-expire-walk");
1516  ASSERT (node);
1518 
1523 
1524  nbuckets = ip4_full_reass_get_nbuckets ();
1525  clib_bihash_init_16_8 (&rm->hash, "ip4-dr", nbuckets, nbuckets * 1024);
1526 
1527  node = vlib_get_node_by_name (vm, (u8 *) "ip4-drop");
1528  ASSERT (node);
1529  rm->ip4_drop_idx = node->index;
1530 
1531  rm->fq_index = vlib_frame_queue_main_init (ip4_full_reass_node.index, 0);
1532  rm->fq_feature_index =
1533  vlib_frame_queue_main_init (ip4_full_reass_node_feature.index, 0);
1534  rm->fq_custom_index =
1535  vlib_frame_queue_main_init (ip4_full_reass_node_custom.index, 0);
1536 
1537  rm->feature_use_refcount_per_intf = NULL;
1538  return error;
1539 }
1540 
1542 #endif /* CLIB_MARCH_VARIANT */
1543 
1544 static uword
1546  vlib_node_runtime_t * node, vlib_frame_t * f)
1547 {
1549  uword event_type, *event_data = 0;
1550 
1551  while (true)
1552  {
1554  (f64)
1556  (f64) MSEC_PER_SEC);
1557  event_type = vlib_process_get_events (vm, &event_data);
1558 
1559  switch (event_type)
1560  {
1561  case ~0: /* no events => timeout */
1562  /* nothing to do here */
1563  break;
1565  break;
1566  default:
1567  clib_warning ("BUG: event type 0x%wx", event_type);
1568  break;
1569  }
1570  f64 now = vlib_time_now (vm);
1571 
1572  ip4_full_reass_t *reass;
1573  int *pool_indexes_to_free = NULL;
1574 
1575  uword thread_index = 0;
1576  int index;
1577  const uword nthreads = vlib_num_workers () + 1;
1578  for (thread_index = 0; thread_index < nthreads; ++thread_index)
1579  {
1581  &rm->per_thread_data[thread_index];
1582  clib_spinlock_lock (&rt->lock);
1583 
1584  vec_reset_length (pool_indexes_to_free);
1585  /* *INDENT-OFF* */
1586  pool_foreach_index (index, rt->pool) {
1587  reass = pool_elt_at_index (rt->pool, index);
1588  if (now > reass->last_heard + rm->timeout)
1589  {
1590  vec_add1 (pool_indexes_to_free, index);
1591  }
1592  }
1593  /* *INDENT-ON* */
1594  int *i;
1595  /* *INDENT-OFF* */
1596  vec_foreach (i, pool_indexes_to_free)
1597  {
1598  ip4_full_reass_t *reass = pool_elt_at_index (rt->pool, i[0]);
1599  ip4_full_reass_drop_all (vm, node, rm, reass);
1600  ip4_full_reass_free (rm, rt, reass);
1601  }
1602  /* *INDENT-ON* */
1603 
1604  clib_spinlock_unlock (&rt->lock);
1605  }
1606 
1607  vec_free (pool_indexes_to_free);
1608  if (event_data)
1609  {
1610  _vec_len (event_data) = 0;
1611  }
1612  }
1613 
1614  return 0;
1615 }
1616 
1617 /* *INDENT-OFF* */
1619  .function = ip4_full_reass_walk_expired,
1620  .type = VLIB_NODE_TYPE_PROCESS,
1621  .name = "ip4-full-reassembly-expire-walk",
1622  .format_trace = format_ip4_full_reass_trace,
1623  .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
1624  .error_strings = ip4_full_reass_error_strings,
1625 
1626 };
1627 /* *INDENT-ON* */
1628 
1629 static u8 *
1630 format_ip4_full_reass_key (u8 * s, va_list * args)
1631 {
1632  ip4_full_reass_key_t *key = va_arg (*args, ip4_full_reass_key_t *);
1633  s =
1634  format (s,
1635  "xx_id: %u, src: %U, dst: %U, frag_id: %u, proto: %u",
1637  &key->dst, clib_net_to_host_u16 (key->frag_id), key->proto);
1638  return s;
1639 }
1640 
1641 static u8 *
1642 format_ip4_reass (u8 * s, va_list * args)
1643 {
1644  vlib_main_t *vm = va_arg (*args, vlib_main_t *);
1645  ip4_full_reass_t *reass = va_arg (*args, ip4_full_reass_t *);
1646 
1647  s = format (s, "ID: %lu, key: %U\n first_bi: %u, data_len: %u, "
1648  "last_packet_octet: %u, trace_op_counter: %u\n",
1649  reass->id, format_ip4_full_reass_key, &reass->key,
1650  reass->first_bi, reass->data_len,
1651  reass->last_packet_octet, reass->trace_op_counter);
1652 
1653  u32 bi = reass->first_bi;
1654  u32 counter = 0;
1655  while (~0 != bi)
1656  {
1657  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1658  vnet_buffer_opaque_t *vnb = vnet_buffer (b);
1659  s =
1660  format (s,
1661  " #%03u: range: [%u, %u], bi: %u, off: %d, len: %u, "
1662  "fragment[%u, %u]\n", counter, vnb->ip.reass.range_first,
1663  vnb->ip.reass.range_last, bi,
1666  vnb->ip.reass.fragment_first, vnb->ip.reass.fragment_last);
1667  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
1668  {
1669  bi = b->next_buffer;
1670  }
1671  else
1672  {
1673  bi = ~0;
1674  }
1675  }
1676  return s;
1677 }
1678 
1679 static clib_error_t *
1681  unformat_input_t * input,
1683 {
1685 
1686  vlib_cli_output (vm, "---------------------");
1687  vlib_cli_output (vm, "IP4 reassembly status");
1688  vlib_cli_output (vm, "---------------------");
1689  bool details = false;
1690  if (unformat (input, "details"))
1691  {
1692  details = true;
1693  }
1694 
1695  u32 sum_reass_n = 0;
1696  ip4_full_reass_t *reass;
1697  uword thread_index;
1698  const uword nthreads = vlib_num_workers () + 1;
1699  for (thread_index = 0; thread_index < nthreads; ++thread_index)
1700  {
1701  ip4_full_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
1702  clib_spinlock_lock (&rt->lock);
1703  if (details)
1704  {
1705  /* *INDENT-OFF* */
1706  pool_foreach (reass, rt->pool) {
1707  vlib_cli_output (vm, "%U", format_ip4_reass, vm, reass);
1708  }
1709  /* *INDENT-ON* */
1710  }
1711  sum_reass_n += rt->reass_n;
1712  clib_spinlock_unlock (&rt->lock);
1713  }
1714  vlib_cli_output (vm, "---------------------");
1715  vlib_cli_output (vm, "Current full IP4 reassemblies count: %lu\n",
1716  (long unsigned) sum_reass_n);
1717  vlib_cli_output (vm,
1718  "Maximum configured concurrent full IP4 reassemblies per worker-thread: %lu\n",
1719  (long unsigned) rm->max_reass_n);
1720  vlib_cli_output (vm,
1721  "Maximum configured full IP4 reassembly timeout: %lums\n",
1722  (long unsigned) rm->timeout_ms);
1723  vlib_cli_output (vm,
1724  "Maximum configured full IP4 reassembly expire walk interval: %lums\n",
1725  (long unsigned) rm->expire_walk_interval_ms);
1726  return 0;
1727 }
1728 
1729 /* *INDENT-OFF* */
1730 VLIB_CLI_COMMAND (show_ip4_full_reass_cmd, static) = {
1731  .path = "show ip4-full-reassembly",
1732  .short_help = "show ip4-full-reassembly [details]",
1733  .function = show_ip4_reass,
1734 };
1735 /* *INDENT-ON* */
1736 
1737 #ifndef CLIB_MARCH_VARIANT
1740 {
1741  return vnet_feature_enable_disable ("ip4-unicast",
1742  "ip4-full-reassembly-feature",
1743  sw_if_index, enable_disable, 0, 0);
1744 }
1745 #endif /* CLIB_MARCH_VARIANT */
1746 
1747 
1748 #define foreach_ip4_full_reass_handoff_error \
1749 _(CONGESTION_DROP, "congestion drop")
1750 
1751 
1752 typedef enum
1753 {
1754 #define _(sym,str) IP4_FULL_REASS_HANDOFF_ERROR_##sym,
1756 #undef _
1759 
1760 static char *ip4_full_reass_handoff_error_strings[] = {
1761 #define _(sym,string) string,
1763 #undef _
1764 };
1765 
1766 typedef struct
1767 {
1770 
1771 static u8 *
1773 {
1774  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1775  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1777  va_arg (*args, ip4_full_reass_handoff_trace_t *);
1778 
1779  s =
1780  format (s, "ip4-full-reassembly-handoff: next-worker %d",
1781  t->next_worker_index);
1782 
1783  return s;
1784 }
1785 
1788  vlib_node_runtime_t * node,
1789  vlib_frame_t * frame,
1790  ip4_full_reass_node_type_t type)
1791 {
1793 
1794  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
1795  u32 n_enq, n_left_from, *from;
1796  u16 thread_indices[VLIB_FRAME_SIZE], *ti;
1797  u32 fq_index;
1798 
1799  from = vlib_frame_vector_args (frame);
1800  n_left_from = frame->n_vectors;
1801  vlib_get_buffers (vm, from, bufs, n_left_from);
1802 
1803  b = bufs;
1804  ti = thread_indices;
1805 
1806  switch (type)
1807  {
1808  case NORMAL:
1809  fq_index = rm->fq_index;
1810  break;
1811  case FEATURE:
1812  fq_index = rm->fq_feature_index;
1813  break;
1814  case CUSTOM:
1815  fq_index = rm->fq_custom_index;
1816  break;
1817  default:
1818  clib_warning ("Unexpected `type' (%d)!", type);
1819  ASSERT (0);
1820  }
1821 
1822  while (n_left_from > 0)
1823  {
1824  ti[0] = vnet_buffer (b[0])->ip.reass.owner_thread_index;
1825 
1826  if (PREDICT_FALSE
1827  ((node->flags & VLIB_NODE_FLAG_TRACE)
1828  && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
1829  {
1831  vlib_add_trace (vm, node, b[0], sizeof (*t));
1832  t->next_worker_index = ti[0];
1833  }
1834 
1835  n_left_from -= 1;
1836  ti += 1;
1837  b += 1;
1838  }
1839  n_enq =
1840  vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices,
1841  frame->n_vectors, 1);
1842 
1843  if (n_enq < frame->n_vectors)
1845  IP4_FULL_REASS_HANDOFF_ERROR_CONGESTION_DROP,
1846  frame->n_vectors - n_enq);
1847  return frame->n_vectors;
1848 }
1849 
1850 VLIB_NODE_FN (ip4_full_reass_handoff_node) (vlib_main_t * vm,
1852  vlib_frame_t * frame)
1853 {
1854  return ip4_full_reass_handoff_node_inline (vm, node, frame, NORMAL);
1855 }
1856 
1857 
1858 /* *INDENT-OFF* */
1859 VLIB_REGISTER_NODE (ip4_full_reass_handoff_node) = {
1860  .name = "ip4-full-reassembly-handoff",
1861  .vector_size = sizeof (u32),
1862  .n_errors = ARRAY_LEN(ip4_full_reass_handoff_error_strings),
1863  .error_strings = ip4_full_reass_handoff_error_strings,
1864  .format_trace = format_ip4_full_reass_handoff_trace,
1865 
1866  .n_next_nodes = 1,
1867 
1868  .next_nodes = {
1869  [0] = "error-drop",
1870  },
1871 };
1872 /* *INDENT-ON* */
1873 
1874 
1875 /* *INDENT-OFF* */
1876 VLIB_NODE_FN (ip4_full_reass_feature_handoff_node) (vlib_main_t * vm,
1878  node,
1879  vlib_frame_t * frame)
1880 {
1881  return ip4_full_reass_handoff_node_inline (vm, node, frame, FEATURE);
1882 }
1883 /* *INDENT-ON* */
1884 
1885 
1886 /* *INDENT-OFF* */
1887 VLIB_REGISTER_NODE (ip4_full_reass_feature_handoff_node) = {
1888  .name = "ip4-full-reass-feature-hoff",
1889  .vector_size = sizeof (u32),
1890  .n_errors = ARRAY_LEN(ip4_full_reass_handoff_error_strings),
1891  .error_strings = ip4_full_reass_handoff_error_strings,
1892  .format_trace = format_ip4_full_reass_handoff_trace,
1893 
1894  .n_next_nodes = 1,
1895 
1896  .next_nodes = {
1897  [0] = "error-drop",
1898  },
1899 };
1900 /* *INDENT-ON* */
1901 
1902 /* *INDENT-OFF* */
1903 VLIB_NODE_FN (ip4_full_reass_custom_handoff_node) (vlib_main_t * vm,
1905  node,
1906  vlib_frame_t * frame)
1907 {
1908  return ip4_full_reass_handoff_node_inline (vm, node, frame, CUSTOM);
1909 }
1910 /* *INDENT-ON* */
1911 
1912 
1913 /* *INDENT-OFF* */
1914 VLIB_REGISTER_NODE (ip4_full_reass_custom_handoff_node) = {
1915  .name = "ip4-full-reass-custom-hoff",
1916  .vector_size = sizeof (u32),
1917  .n_errors = ARRAY_LEN(ip4_full_reass_handoff_error_strings),
1918  .error_strings = ip4_full_reass_handoff_error_strings,
1919  .format_trace = format_ip4_full_reass_handoff_trace,
1920 
1921  .n_next_nodes = 1,
1922 
1923  .next_nodes = {
1924  [0] = "error-drop",
1925  },
1926 };
1927 /* *INDENT-ON* */
1928 
1929 #ifndef CLIB_MARCH_VARIANT
1930 int
1932 {
1934  vec_validate (rm->feature_use_refcount_per_intf, sw_if_index);
1935  if (is_enable)
1936  {
1937  if (!rm->feature_use_refcount_per_intf[sw_if_index])
1938  {
1940  return vnet_feature_enable_disable ("ip4-unicast",
1941  "ip4-full-reassembly-feature",
1942  sw_if_index, 1, 0, 0);
1943  }
1945  }
1946  else
1947  {
1949  if (!rm->feature_use_refcount_per_intf[sw_if_index])
1950  return vnet_feature_enable_disable ("ip4-unicast",
1951  "ip4-full-reassembly-feature",
1952  sw_if_index, 0, 0, 0);
1953  }
1954  return -1;
1955 }
1956 #endif
1957 
1958 /*
1959  * fd.io coding-style-patch-verification: ON
1960  *
1961  * Local Variables:
1962  * eval: (c-set-style "gnu")
1963  * End:
1964  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:509
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:124
static ip4_full_reass_rc_t ip4_full_reass_finalize(vlib_main_t *vm, vlib_node_runtime_t *node, ip4_full_reass_main_t *rm, ip4_full_reass_per_thread_t *rt, ip4_full_reass_t *reass, u32 *bi0, u32 *next0, u32 *error0, bool is_custom)
vnet_api_error_t
Definition: api_errno.h:162
clib_bihash_16_8_t hash
static vlib_cli_command_t trace
(constructor) VLIB_CLI_COMMAND (trace)
Definition: vlib_api_cli.c:899
static u16 ip4_full_reass_buffer_get_data_len(vlib_buffer_t *b)
#define clib_min(x, y)
Definition: clib.h:328
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:121
#define pool_foreach_index(i, v)
Definition: pool.h:569
static_always_inline void clib_spinlock_lock(clib_spinlock_t *p)
Definition: lock.h:82
ip4_full_reass_key_t key
#define CLIB_UNUSED(x)
Definition: clib.h:87
vnet_api_error_t ip4_full_reass_enable_disable(u32 sw_if_index, u8 enable_disable)
ip4_full_reass_next_t
static uword ip4_full_reass_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, ip4_full_reass_node_type_t type)
static f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
Suspend a cooperative multi-tasking thread Waits for an event, or for the indicated number of seconds...
Definition: node_funcs.h:751
ip4_full_reass_node_type_t
ip4_full_reass_rc_t
static void vlib_buffer_free(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Free buffers Frees the entire buffer chain for each buffer.
Definition: buffer_funcs.h:937
static u32 vlib_buffer_get_trace_index(vlib_buffer_t *b)
Extract the trace (pool) index from a trace handle.
Definition: buffer.h:392
ip4_address_t src_address
Definition: ip4_packet.h:125
struct vnet_buffer_opaque_t::@174::@176 ip
#define pool_foreach(VAR, POOL)
Iterate through pool.
Definition: pool.h:527
ip4_full_reass_trace_operation_e
#define pool_alloc(P, N)
Allocate N more free elements to pool (unspecified alignment).
Definition: pool.h:360
format_function_t format_ip4_header
Definition: format.h:81
u64 as_u64
Definition: bihash_doc.h:63
static void ip4_full_reass_set_params(u32 timeout_ms, u32 max_reassemblies, u32 max_reassembly_length, u32 expire_walk_interval_ms)
unsigned long u64
Definition: types.h:89
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
static char * ip4_full_reass_error_strings[]
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
u32 index
Definition: node.h:280
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:334
ip4_full_reass_handoff_error_t
u32 vlib_frame_queue_main_init(u32 node_index, u32 frame_queue_nelts)
Definition: threads.c:1880
static u8 * format_ip4_full_reass_key(u8 *s, va_list *args)
u32 thread_index
Definition: main.h:250
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:113
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:592
static clib_error_t * show_ip4_reass(vlib_main_t *vm, unformat_input_t *input, CLIB_UNUSED(vlib_cli_command_t *lmd))
ip4_full_reass_val_t v
IPv4 Reassembly.
#define MSEC_PER_SEC
static void ip4_full_reass_init(ip4_full_reass_t *reass)
static u32 format_get_indent(u8 *s)
Definition: format.h:72
vlib_main_t * vm
Definition: in2out_ed.c:1580
u32 * fib_index_by_sw_if_index
Table index indexed by software interface.
Definition: ip4.h:123
u16 flags_and_fragment_offset
Definition: ip4_packet.h:106
#define VLIB_NODE_FN(node)
Definition: node.h:203
#define IP4_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:470
static uword vlib_buffer_length_in_chain(vlib_main_t *vm, vlib_buffer_t *b)
Get length in bytes of the buffer chain.
Definition: buffer_funcs.h:402
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:251
static uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:1173
unsigned char u8
Definition: types.h:56
#define vec_pop(V)
Returns last element of a vector and decrements its length.
Definition: vec.h:685
static int ip4_get_fragment_offset_bytes(const ip4_header_t *i)
Definition: ip4_packet.h:184
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
double f64
Definition: types.h:142
#define clib_memcpy(d, s, n)
Definition: string.h:180
vlib_main_t * vlib_main
format_function_t format_ip4_address
Definition: format.h:73
vlib_trace_header_t ** trace_buffer_pool
Definition: trace.h:86
#define IP4_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
static uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Return the first event type which has occurred and a vector of per-event data of that type...
Definition: node_funcs.h:579
static u16 ip4_get_fragment_more(const ip4_header_t *i)
Definition: ip4_packet.h:161
ip4_address_t dst_address
Definition: ip4_packet.h:125
u8 * format_white_space(u8 *s, va_list *va)
Definition: std-formats.c:129
description fragment has unexpected format
Definition: map.api:433
#define foreach_ip4_full_reass_handoff_error
static ip4_full_reass_rc_t ip4_full_reass_insert_range_in_chain(vlib_main_t *vm, ip4_full_reass_main_t *rm, ip4_full_reass_per_thread_t *rt, ip4_full_reass_t *reass, u32 prev_range_bi, u32 new_next_bi)
static uword ip4_full_reass_walk_expired(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *f)
clib_bihash_16_8_t * new_hash
const cJSON *const b
Definition: cJSON.h:255
static void ip4_full_reass_free(ip4_full_reass_main_t *rm, ip4_full_reass_per_thread_t *rt, ip4_full_reass_t *reass)
unsigned int u32
Definition: types.h:88
vlib_node_registration_t ip4_full_reass_node_feature
(constructor) VLIB_REGISTER_NODE (ip4_full_reass_node_feature)
#define VLIB_FRAME_SIZE
Definition: node.h:378
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:65
vl_api_fib_path_type_t type
Definition: fib_types.api:123
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:136
vlib_node_registration_t ip4_full_reass_node
(constructor) VLIB_REGISTER_NODE (ip4_full_reass_node)
static u32 vlib_buffer_chain_linearize(vlib_main_t *vm, vlib_buffer_t *b)
Definition: cJSON.c:84
u32 fq_index
Worker handoff.
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:546
vlib_node_t * vlib_get_node_by_name(vlib_main_t *vm, u8 *name)
Definition: node.c:45
static u32 ip4_full_reass_get_nbuckets()
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:1015
long ctx[MAX_CONNS]
Definition: main.c:144
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
u8 data_len
Definition: ikev2_types.api:24
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:233
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:301
static void ip4_full_reass_free_ctx(ip4_full_reass_per_thread_t *rt, ip4_full_reass_t *reass)
#define PREDICT_FALSE(x)
Definition: clib.h:121
ip4_full_reass_per_thread_t * per_thread_data
#define always_inline
Definition: ipsec.h:28
static void ip4_full_reass_add_trace(vlib_main_t *vm, vlib_node_runtime_t *node, ip4_full_reass_main_t *rm, ip4_full_reass_t *reass, u32 bi, ip4_full_reass_trace_operation_e action, u32 size_diff, u32 thread_id_to)
clib_bihash_kv_16_8_t kv
u32 node_index
Node index.
Definition: node.h:488
#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:224
#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:391
ip4_full_reass_event_t
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1231
ip4_full_reass_main_t ip4_full_reass_main
static ip4_full_reass_t * ip4_full_reass_find_or_create(vlib_main_t *vm, vlib_node_runtime_t *node, ip4_full_reass_main_t *rm, ip4_full_reass_per_thread_t *rt, ip4_full_reass_kv_t *kv, u8 *do_handoff)
static uword ip4_full_reass_handoff_node_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, ip4_full_reass_node_type_t type)
#define foreach_ip4_error
Definition: ip4_error.h:43
static int ip4_rehash_cb(clib_bihash_kv_16_8_t *kv, void *_ctx)
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:170
uword ip4_full_reass_custom_register_next_node(uword node_index)
static u8 * format_ip4_reass(u8 *s, va_list *args)
u16 n_vectors
Definition: node.h:397
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
static u16 ip4_get_fragment_offset(const ip4_header_t *i)
Definition: ip4_packet.h:155
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:380
static_always_inline void vnet_feature_next(u32 *next0, vlib_buffer_t *b0)
Definition: feature.h:322
#define clib_warning(format, args...)
Definition: error.h:59
vnet_api_error_t ip4_full_reass_get(u32 *timeout_ms, u32 *max_reassemblies, u32 *max_reassembly_length, u32 *expire_walk_interval_ms)
get ip4 reassembly configuration
vlib_node_registration_t ip4_full_reass_expire_node
(constructor) VLIB_REGISTER_NODE (ip4_full_reass_expire_node)
static u32 vlib_buffer_get_trace_thread(vlib_buffer_t *b)
Extract the thread id from a trace handle.
Definition: buffer.h:380
__clib_export void clib_bihash_copied(void *dst, void *src)
u8 * format_hexdump(u8 *s, va_list *va)
Definition: std-formats.c:352
static u8 * format_ip4_full_reass_trace(u8 *s, va_list *args)
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:298
#define ARRAY_LEN(x)
Definition: clib.h:67
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:483
static void ip4_full_reass_drop_all(vlib_main_t *vm, vlib_node_runtime_t *node, ip4_full_reass_main_t *rm, ip4_full_reass_t *reass)
static ip4_full_reass_rc_t ip4_full_reass_remove_range_from_chain(vlib_main_t *vm, vlib_node_runtime_t *node, ip4_full_reass_main_t *rm, ip4_full_reass_t *reass, u32 prev_range_bi, u32 discard_bi)
vlib_main_t vlib_node_runtime_t * node
Definition: in2out_ed.c:1580
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:158
signed int i32
Definition: types.h:77
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:511
static char * ip4_full_reass_handoff_error_strings[]
ip4_full_reass_key_t k
#define ASSERT(truth)
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:696
vnet_api_error_t ip4_full_reass_set(u32 timeout_ms, u32 max_reassemblies, u32 max_reassembly_length, u32 expire_walk_interval_ms)
set ip4 reassembly configuration
vlib_node_registration_t ip4_full_reass_node_custom
(constructor) VLIB_REGISTER_NODE (ip4_full_reass_node_custom)
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:252
vlib_trace_main_t trace_main
Definition: main.h:194
VNET_FEATURE_INIT(ip4_full_reass_feature, static)
ip4_full_reass_trace_operation_e action
#define VNET_FEATURES(...)
Definition: feature.h:470
static vlib_main_t * vlib_get_main(void)
Definition: global_funcs.h:23
static ip4_full_reass_rc_t ip4_full_reass_update(vlib_main_t *vm, vlib_node_runtime_t *node, ip4_full_reass_main_t *rm, ip4_full_reass_per_thread_t *rt, ip4_full_reass_t *reass, u32 *bi0, u32 *next0, u32 *error0, bool is_custom, u32 *handoff_thread_idx)
#define vec_elt(v, i)
Get vector value at index i.
typedef key
Definition: ipsec_types.api:86
struct _vlib_node_registration vlib_node_registration_t
static void ip4_full_reass_trace_details(vlib_main_t *vm, u32 bi, ip4_full_reass_range_trace_t *trace)
vl_api_address_t ip
Definition: l2.api:501
static u8 * format_ip4_full_reass_range_trace(u8 *s, va_list *args)
vl_api_mac_event_action_t action
Definition: l2.api:181
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
u32 next_buffer
Next buffer for this linked-list of buffers.
Definition: buffer.h:140
vlib_main_t vlib_node_runtime_t vlib_frame_t * frame
Definition: in2out_ed.c:1581
VLIB buffer representation.
Definition: buffer.h:102
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:297
u32 index
Definition: flow_types.api:221
ip4_full_reass_range_trace_t trace_range
static_always_inline u32 vlib_buffer_enqueue_to_thread(vlib_main_t *vm, u32 frame_queue_index, u32 *buffer_indices, u16 *thread_indices, u32 n_packets, int drop_on_congestion)
Definition: buffer_node.h:494
int ip4_full_reass_enable_disable_with_refcnt(u32 sw_if_index, int is_enable)
#define vnet_buffer(b)
Definition: buffer.h:417
static u32 ip4_full_reass_buffer_get_data_offset(vlib_buffer_t *b)
ip4_main_t ip4_main
Global ip4 main structure.
Definition: ip4_forward.c:1105
static u32 vlib_num_workers()
Definition: threads.h:377
static u8 * format_ip4_full_reass_handoff_trace(u8 *s, va_list *args)
static clib_error_t * ip4_full_reass_init_function(vlib_main_t *vm)
#define vec_foreach(var, vec)
Vector iterator.
u16 flags
Copy of main node flags.
Definition: node.h:501
void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace.c:634
#define IP4_REASS_MAX_REASSEMBLIES_DEFAULT
static void vlib_buffer_free_one(vlib_main_t *vm, u32 buffer_index)
Free one buffer Shorthand to free a single buffer chain.
Definition: buffer_funcs.h:970
static int ip4_header_bytes(const ip4_header_t *i)
Definition: ip4_packet.h:190
static_always_inline void vlib_get_buffers(vlib_main_t *vm, u32 *bi, vlib_buffer_t **b, int count)
Translate array of buffer indices into buffer pointers.
Definition: buffer_funcs.h:280
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:302
u32 total_length_not_including_first_buffer
Only valid for first buffer in chain.
Definition: buffer.h:167
#define IP4_REASS_HT_LOAD_FACTOR
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:85
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:314
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
Definition: defs.h:46
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
Definition: feature.c:303
CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers".
#define IP4_REASS_TIMEOUT_DEFAULT_MS
#define IP4_REASS_DEBUG_BUFFER(...)