FD.io VPP  v18.07-rc0-415-g6c78436
Vector Packet Processing
ip6_reassembly.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 IPv6 Reassembly.
19  *
20  * This file contains the source code for IPv6 reassembly.
21  */
22 
23 #include <vppinfra/vec.h>
24 #include <vnet/vnet.h>
25 #include <vnet/ip/ip.h>
26 #include <vppinfra/bihash_48_8.h>
27 #include <vnet/ip/ip6_reassembly.h>
28 
29 #define MSEC_PER_SEC 1000
30 #define IP6_REASS_TIMEOUT_DEFAULT_MS 100
31 #define IP6_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS 10000 // 10 seconds default
32 #define IP6_REASS_MAX_REASSEMBLIES_DEFAULT 1024
33 #define IP6_REASS_HT_LOAD_FACTOR (0.75)
34 
36 
37 typedef struct
38 {
39  union
40  {
41  struct
42  {
45  // align by making this 4 octets even though its a 2 octets field
47  // align by making this 4 octets even though its a 2 octets field
49  // align by making this 4 octets even though its a 1 octet field
52  };
53  u64 as_u64[6];
54  };
56 
59 {
61  return vnb->ip.reass.range_first - vnb->ip.reass.fragment_first;
62 }
63 
66 {
68  ASSERT (vnb->ip.reass.range_first >= vnb->ip.reass.fragment_first);
70 }
71 
74 {
76  return clib_min (vnb->ip.reass.range_last, vnb->ip.reass.fragment_last) -
77  (vnb->ip.reass.fragment_first + ip6_reass_buffer_get_data_offset (b)) + 1;
78 }
79 
82 {
84  ASSERT (vnb->ip.reass.range_last > vnb->ip.reass.fragment_first);
86 }
87 
88 typedef struct
89 {
90  // hash table key
92  // time when last packet was received
94  // internal id of this reassembly
96  // buffer index of first buffer in this reassembly context
98  // last octet of packet, ~0 until fragment without more_fragments arrives
100  // length of data collected so far
102  // trace operation counter
104  // next index - used by non-feature node
106  // minimum fragment length for this reassembly - used to estimate MTU
108 } ip6_reass_t;
109 
110 typedef struct
111 {
118 
119 typedef struct
120 {
121  // IPv6 config
126 
127  // IPv6 runtime
128  clib_bihash_48_8_t hash;
129 
130  // per-thread data
132 
133  // convenience
136 
137  // node index of ip6-drop node
141 
143 
145 
146 typedef enum
147 {
153 
154 typedef enum
155 {
163 
164 typedef struct
165 {
173 
174 typedef struct
175 {
176  ip6_reass_trace_operation_e action;
185 
186 static void
189 {
190  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
192  trace->range_first = vnb->ip.reass.range_first;
193  trace->range_last = vnb->ip.reass.range_last;
196  trace->range_bi = bi;
197 }
198 
199 static u8 *
200 format_ip6_reass_range_trace (u8 * s, va_list * args)
201 {
203  s = format (s, "range: [%u, %u], off %d, len %u, bi %u", trace->range_first,
204  trace->range_last, trace->data_offset, trace->data_len,
205  trace->range_bi);
206  return s;
207 }
208 
209 static u8 *
210 format_ip6_reass_trace (u8 * s, va_list * args)
211 {
212  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
213  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
214  ip6_reass_trace_t *t = va_arg (*args, ip6_reass_trace_t *);
215  s = format (s, "reass id: %u, op id: %u ", t->reass_id, t->op_id);
216  u32 indent = format_get_indent (s);
217  s = format (s, "first bi: %u, data len: %u, ip/fragment[%u, %u]",
219  t->fragment_last);
220  switch (t->action)
221  {
222  case RANGE_NEW:
223  s = format (s, "\n%Unew %U", format_white_space, indent,
225  break;
226  case RANGE_OVERLAP:
227  s = format (s, "\n%Uoverlap %U", format_white_space, indent,
229  break;
231  s = format (s, "\n%Uicmp-error - frag_len > 65535 %U",
233  &t->trace_range);
234  break;
236  s = format (s, "\n%Uicmp-error - frag_len mod 8 != 0 %U",
238  &t->trace_range);
239  break;
241  s = format (s, "\n%Uicmp-error - reassembly time exceeded",
242  format_white_space, indent);
243  break;
244  case FINALIZE:
245  s = format (s, "\n%Ufinalize reassembly", format_white_space, indent);
246  break;
247  }
248  return s;
249 }
250 
251 static void
253  ip6_reass_main_t * rm, ip6_reass_t * reass,
254  u32 bi, ip6_reass_trace_operation_e action,
255  u32 size_diff)
256 {
257  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
260  {
261  // this buffer's trace is gone
262  b->flags &= ~VLIB_BUFFER_IS_TRACED;
263  return;
264  }
265  ip6_reass_trace_t *t = vlib_add_trace (vm, node, b, sizeof (t[0]));
266  t->reass_id = reass->id;
267  t->action = action;
268  ip6_reass_trace_details (vm, bi, &t->trace_range);
269  t->size_diff = size_diff;
270  t->op_id = reass->trace_op_counter;
271  ++reass->trace_op_counter;
272  t->fragment_first = vnb->ip.reass.fragment_first;
273  t->fragment_last = vnb->ip.reass.fragment_last;
274  t->trace_range.first_bi = reass->first_bi;
275  t->total_data_len = reass->data_len;
276 #if 0
277  static u8 *s = NULL;
278  s = format (s, "%U", format_ip6_reass_trace, NULL, NULL, t);
279  printf ("%.*s\n", vec_len (s), s);
280  fflush (stdout);
281  vec_reset_length (s);
282 #endif
283 }
284 
285 always_inline void
287  ip6_reass_t * reass)
288 {
290  kv.key[0] = reass->key.as_u64[0];
291  kv.key[1] = reass->key.as_u64[1];
292  kv.key[2] = reass->key.as_u64[2];
293  kv.key[3] = reass->key.as_u64[3];
294  kv.key[4] = reass->key.as_u64[4];
295  kv.key[5] = reass->key.as_u64[5];
296  clib_bihash_add_del_48_8 (&rm->hash, &kv, 0);
297  pool_put (rt->pool, reass);
298  --rt->reass_n;
299 }
300 
301 always_inline void
303  ip6_reass_t * reass, u32 ** vec_drop_bi)
304 {
305  u32 range_bi = reass->first_bi;
306  vlib_buffer_t *range_b;
307  vnet_buffer_opaque_t *range_vnb;
308  while (~0 != range_bi)
309  {
310  range_b = vlib_get_buffer (vm, range_bi);
311  range_vnb = vnet_buffer (range_b);
312  u32 bi = range_bi;
313  while (~0 != bi)
314  {
315  vec_add1 (*vec_drop_bi, bi);
316  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
317  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
318  {
319  bi = b->next_buffer;
320  b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
321  }
322  else
323  {
324  bi = ~0;
325  }
326  }
327  range_bi = range_vnb->ip.reass.next_range_bi;
328  }
329 }
330 
331 always_inline void
333  ip6_reass_main_t * rm, ip6_reass_t * reass,
334  u32 * icmp_bi, u32 ** vec_timeout)
335 {
336  if (~0 == reass->first_bi)
337  {
338  return;
339  }
340  vlib_buffer_t *b = vlib_get_buffer (vm, reass->first_bi);
341  if (0 == vnet_buffer (b)->ip.reass.fragment_first)
342  {
343  *icmp_bi = reass->first_bi;
344  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
345  {
346  ip6_reass_add_trace (vm, node, rm, reass, reass->first_bi,
348  }
349  // fragment with offset zero received - send icmp message back
350  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
351  {
352  // separate first buffer from chain and steer it towards icmp node
353  b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
354  reass->first_bi = b->next_buffer;
355  }
356  else
357  {
358  reass->first_bi = vnet_buffer (b)->ip.reass.next_range_bi;
359  }
360  icmp6_error_set_vnet_buffer (b, ICMP6_time_exceeded,
361  ICMP6_time_exceeded_fragment_reassembly_time_exceeded,
362  0);
363  }
364  ip6_reass_drop_all (vm, rm, reass, vec_timeout);
365 }
366 
370  ip6_reass_key_t * k, u32 * icmp_bi,
371  u32 ** vec_timeout)
372 {
373  ip6_reass_t *reass = NULL;
374  f64 now = vlib_time_now (rm->vlib_main);
375  clib_bihash_kv_48_8_t kv, value;
376  kv.key[0] = k->as_u64[0];
377  kv.key[1] = k->as_u64[1];
378  kv.key[2] = k->as_u64[2];
379  kv.key[3] = k->as_u64[3];
380  kv.key[4] = k->as_u64[4];
381  kv.key[5] = k->as_u64[5];
382 
383  if (!clib_bihash_search_48_8 (&rm->hash, &kv, &value))
384  {
385  reass = pool_elt_at_index (rt->pool, value.value);
386  if (now > reass->last_heard + rm->timeout)
387  {
388  ip6_reass_on_timeout (vm, node, rm, reass, icmp_bi, vec_timeout);
389  ip6_reass_free (rm, rt, reass);
390  reass = NULL;
391  }
392  }
393 
394  if (reass)
395  {
396  reass->last_heard = now;
397  return reass;
398  }
399 
400  if (rt->reass_n >= rm->max_reass_n)
401  {
402  reass = NULL;
403  return reass;
404  }
405  else
406  {
407  pool_get (rt->pool, reass);
408  memset (reass, 0, sizeof (*reass));
409  reass->id =
410  ((u64) os_get_thread_index () * 1000000000) + rt->id_counter;
411  ++rt->id_counter;
412  reass->first_bi = ~0;
413  reass->last_packet_octet = ~0;
414  reass->data_len = 0;
415  ++rt->reass_n;
416  }
417 
418  reass->key.as_u64[0] = kv.key[0] = k->as_u64[0];
419  reass->key.as_u64[1] = kv.key[1] = k->as_u64[1];
420  reass->key.as_u64[2] = kv.key[2] = k->as_u64[2];
421  reass->key.as_u64[3] = kv.key[3] = k->as_u64[3];
422  reass->key.as_u64[4] = kv.key[4] = k->as_u64[4];
423  reass->key.as_u64[5] = kv.key[5] = k->as_u64[5];
424  kv.value = reass - rt->pool;
425  reass->last_heard = now;
426 
427  if (clib_bihash_add_del_48_8 (&rm->hash, &kv, 1))
428  {
429  ip6_reass_free (rm, rt, reass);
430  reass = NULL;
431  }
432 
433  return reass;
434 }
435 
436 always_inline void
439  ip6_reass_t * reass, u32 * bi0, u32 * next0,
440  u32 * error0, u32 ** vec_drop_compress, bool is_feature)
441 {
442  ASSERT (~0 != reass->first_bi);
443  *bi0 = reass->first_bi;
444  *error0 = IP6_ERROR_NONE;
445  ip6_frag_hdr_t *frag_hdr;
446  vlib_buffer_t *last_b = NULL;
447  u32 sub_chain_bi = reass->first_bi;
448  u32 total_length = 0;
449  u32 buf_cnt = 0;
450  u32 dropped_cnt = 0;
451  do
452  {
453  u32 tmp_bi = sub_chain_bi;
454  vlib_buffer_t *tmp = vlib_get_buffer (vm, tmp_bi);
455  u32 data_len = ip6_reass_buffer_get_data_len (tmp);
456  u32 trim_front = vnet_buffer (tmp)->ip.reass.ip6_frag_hdr_offset +
457  sizeof (*frag_hdr) + ip6_reass_buffer_get_data_offset (tmp);
458  u32 trim_end =
459  vlib_buffer_length_in_chain (vm, tmp) - trim_front - data_len;
460  if (tmp_bi == reass->first_bi)
461  {
462  /* first buffer - keep ip6 header */
463  ASSERT (0 == ip6_reass_buffer_get_data_offset (tmp));
464  trim_front = 0;
465  trim_end = vlib_buffer_length_in_chain (vm, tmp) - data_len -
466  (vnet_buffer (tmp)->ip.reass.ip6_frag_hdr_offset +
467  sizeof (*frag_hdr));
468  ASSERT (vlib_buffer_length_in_chain (vm, tmp) - trim_end > 0);
469  }
470  u32 keep_data =
471  vlib_buffer_length_in_chain (vm, tmp) - trim_front - trim_end;
472  while (1)
473  {
474  ++buf_cnt;
475  if (trim_front)
476  {
477  if (trim_front > tmp->current_length)
478  {
479  /* drop whole buffer */
480  vec_add1 (*vec_drop_compress, tmp_bi);
481  ++dropped_cnt;
482  trim_front -= tmp->current_length;
483  ASSERT (tmp->flags & VLIB_BUFFER_NEXT_PRESENT);
484  tmp->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
485  tmp_bi = tmp->next_buffer;
486  tmp = vlib_get_buffer (vm, tmp_bi);
487  continue;
488  }
489  else
490  {
491  vlib_buffer_advance (tmp, trim_front);
492  trim_front = 0;
493  }
494  }
495  if (keep_data)
496  {
497  if (last_b)
498  {
499  last_b->flags |= VLIB_BUFFER_NEXT_PRESENT;
500  last_b->next_buffer = tmp_bi;
501  }
502  last_b = tmp;
503  if (keep_data <= tmp->current_length)
504  {
505  tmp->current_length = keep_data;
506  keep_data = 0;
507  }
508  else
509  {
510  keep_data -= tmp->current_length;
511  ASSERT (tmp->flags & VLIB_BUFFER_NEXT_PRESENT);
512  }
513  total_length += tmp->current_length;
514  }
515  else
516  {
517  vec_add1 (*vec_drop_compress, tmp_bi);
518  ASSERT (reass->first_bi != tmp_bi);
519  ++dropped_cnt;
520  }
521  if (tmp->flags & VLIB_BUFFER_NEXT_PRESENT)
522  {
523  tmp_bi = tmp->next_buffer;
524  tmp = vlib_get_buffer (vm, tmp->next_buffer);
525  }
526  else
527  {
528  break;
529  }
530  }
531  sub_chain_bi =
532  vnet_buffer (vlib_get_buffer (vm, sub_chain_bi))->ip.
533  reass.next_range_bi;
534  }
535  while (~0 != sub_chain_bi);
536 
537  ASSERT (last_b != NULL);
538  last_b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
539  vlib_buffer_t *first_b = vlib_get_buffer (vm, reass->first_bi);
540  ASSERT (total_length >= first_b->current_length);
541  total_length -= first_b->current_length;
542  first_b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
543  first_b->total_length_not_including_first_buffer = total_length;
544  // drop fragment header
545  vnet_buffer_opaque_t *first_b_vnb = vnet_buffer (first_b);
546  ip6_header_t *ip = vlib_buffer_get_current (first_b);
547  u16 ip6_frag_hdr_offset = first_b_vnb->ip.reass.ip6_frag_hdr_offset;
548  ip6_ext_header_t *prev_hdr;
549  ip6_ext_header_find_t (ip, prev_hdr, frag_hdr,
550  IP_PROTOCOL_IPV6_FRAGMENTATION);
551  if (prev_hdr)
552  {
553  prev_hdr->next_hdr = frag_hdr->next_hdr;
554  }
555  else
556  {
557  ip->protocol = frag_hdr->next_hdr;
558  }
559  ASSERT ((u8 *) frag_hdr - (u8 *) ip == ip6_frag_hdr_offset);
560  memmove (frag_hdr, (u8 *) frag_hdr + sizeof (*frag_hdr),
561  first_b->current_length - ip6_frag_hdr_offset -
562  sizeof (ip6_frag_hdr_t));
563  first_b->current_length -= sizeof (*frag_hdr);
564  ip->payload_length =
565  clib_host_to_net_u16 (total_length + first_b->current_length -
566  sizeof (*ip));
567  vlib_buffer_chain_compress (vm, first_b, vec_drop_compress);
568  if (PREDICT_FALSE (first_b->flags & VLIB_BUFFER_IS_TRACED))
569  {
570  ip6_reass_add_trace (vm, node, rm, reass, reass->first_bi, FINALIZE, 0);
571 #if 0
572  // following code does a hexdump of packet fragments to stdout ...
573  do
574  {
575  u32 bi = reass->first_bi;
576  u8 *s = NULL;
577  while (~0 != bi)
578  {
579  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
580  s = format (s, "%u: %U\n", bi, format_hexdump,
582  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
583  {
584  bi = b->next_buffer;
585  }
586  else
587  {
588  break;
589  }
590  }
591  printf ("%.*s\n", vec_len (s), s);
592  fflush (stdout);
593  vec_free (s);
594  }
595  while (0);
596 #endif
597  }
598  if (is_feature)
599  {
600  *next0 = IP6_REASSEMBLY_NEXT_INPUT;
601  }
602  else
603  {
604  *next0 = reass->next_index;
605  }
606  vnet_buffer (first_b)->ip.reass.estimated_mtu = reass->min_fragment_length;
607  ip6_reass_free (rm, rt, reass);
608  reass = NULL;
609 }
610 
613 {
614  u32 len = 0;
615  while (b)
616  {
617  ++len;
618  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_NEXT_PRESENT))
619  {
620  b = vlib_get_buffer (vm, b->next_buffer);
621  }
622  else
623  {
624  break;
625  }
626  }
627  return len;
628 }
629 
630 always_inline void
633  ip6_reass_t * reass, u32 prev_range_bi,
634  u32 new_next_bi)
635 {
636 
637  vlib_buffer_t *new_next_b = vlib_get_buffer (vm, new_next_bi);
638  vnet_buffer_opaque_t *new_next_vnb = vnet_buffer (new_next_b);
639  if (~0 != prev_range_bi)
640  {
641  vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_range_bi);
642  vnet_buffer_opaque_t *prev_vnb = vnet_buffer (prev_b);
643  new_next_vnb->ip.reass.next_range_bi = prev_vnb->ip.reass.next_range_bi;
644  prev_vnb->ip.reass.next_range_bi = new_next_bi;
645  }
646  else
647  {
648  if (~0 != reass->first_bi)
649  {
650  new_next_vnb->ip.reass.next_range_bi = reass->first_bi;
651  }
652  reass->first_bi = new_next_bi;
653  }
654  reass->data_len += ip6_reass_buffer_get_data_len (new_next_b);
655  rt->buffers_n += ip6_reass_get_buffer_chain_length (vm, new_next_b);
656 }
657 
658 always_inline void
661  ip6_reass_t * reass, u32 * bi0, u32 * next0,
662  u32 * error0, ip6_frag_hdr_t * frag_hdr,
663  u32 ** vec_drop_overlap, u32 ** vec_drop_compress,
664  bool is_feature)
665 {
666  int consumed = 0;
667  vlib_buffer_t *fb = vlib_get_buffer (vm, *bi0);
668  vnet_buffer_opaque_t *fvnb = vnet_buffer (fb);
669  fvnb->ip.reass.ip6_frag_hdr_offset =
670  (u8 *) frag_hdr - (u8 *) vlib_buffer_get_current (fb);
672  ASSERT (fb->current_length > sizeof (*fip));
673  ASSERT (fvnb->ip.reass.ip6_frag_hdr_offset > 0 &&
674  fvnb->ip.reass.ip6_frag_hdr_offset < fb->current_length);
675  u32 fragment_first = fvnb->ip.reass.fragment_first =
676  ip6_frag_hdr_offset_bytes (frag_hdr);
677  u32 fragment_length =
678  vlib_buffer_length_in_chain (vm, fb) -
679  (fvnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
680  u32 fragment_last = fvnb->ip.reass.fragment_last =
681  fragment_first + fragment_length - 1;
682  int more_fragments = ip6_frag_hdr_more (frag_hdr);
683  u32 candidate_range_bi = reass->first_bi;
684  u32 prev_range_bi = ~0;
685  fvnb->ip.reass.range_first = fragment_first;
686  fvnb->ip.reass.range_last = fragment_last;
687  fvnb->ip.reass.next_range_bi = ~0;
688  if (!more_fragments)
689  {
690  reass->last_packet_octet = fragment_last;
691  }
692  if (~0 == reass->first_bi)
693  {
694  // starting a new reassembly
695  ip6_reass_insert_range_in_chain (vm, rm, rt, reass, prev_range_bi,
696  *bi0);
697  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
698  {
699  ip6_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_NEW, 0);
700  }
701  reass->min_fragment_length = clib_net_to_host_u16 (fip->payload_length);
702  *bi0 = ~0;
703  return;
704  }
705  reass->min_fragment_length =
706  clib_min (clib_net_to_host_u16 (fip->payload_length),
707  fvnb->ip.reass.estimated_mtu);
708  while (~0 != candidate_range_bi)
709  {
710  vlib_buffer_t *candidate_b = vlib_get_buffer (vm, candidate_range_bi);
711  vnet_buffer_opaque_t *candidate_vnb = vnet_buffer (candidate_b);
712  if (fragment_first > candidate_vnb->ip.reass.range_last)
713  {
714  // this fragments starts after candidate range
715  prev_range_bi = candidate_range_bi;
716  candidate_range_bi = candidate_vnb->ip.reass.next_range_bi;
717  if (candidate_vnb->ip.reass.range_last < fragment_last &&
718  ~0 == candidate_range_bi)
719  {
720  // special case - this fragment falls beyond all known ranges
721  ip6_reass_insert_range_in_chain (vm, rm, rt, reass,
722  prev_range_bi, *bi0);
723  consumed = 1;
724  break;
725  }
726  continue;
727  }
728  if (fragment_last < candidate_vnb->ip.reass.range_first)
729  {
730  // this fragment ends before candidate range without any overlap
731  ip6_reass_insert_range_in_chain (vm, rm, rt, reass, prev_range_bi,
732  *bi0);
733  consumed = 1;
734  }
735  else if (fragment_first == candidate_vnb->ip.reass.range_first &&
736  fragment_last == candidate_vnb->ip.reass.range_last)
737  {
738  // duplicate fragment - ignore
739  }
740  else
741  {
742  // overlapping fragment - not allowed by RFC 8200
743  ip6_reass_drop_all (vm, rm, reass, vec_drop_overlap);
744  ip6_reass_free (rm, rt, reass);
745  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
746  {
747  ip6_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_OVERLAP,
748  0);
749  }
750  *next0 = IP6_REASSEMBLY_NEXT_DROP;
751  *error0 = IP6_ERROR_REASS_OVERLAPPING_FRAGMENT;
752  }
753  break;
754  }
755  if (consumed)
756  {
757  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
758  {
759  ip6_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_NEW, 0);
760  }
761  }
762  if (~0 != reass->last_packet_octet &&
763  reass->data_len == reass->last_packet_octet + 1)
764  {
765  ip6_reass_finalize (vm, node, rm, rt, reass, bi0, next0, error0,
766  vec_drop_compress, is_feature);
767  }
768  else
769  {
770  if (consumed)
771  {
772  *bi0 = ~0;
773  }
774  else
775  {
776  *next0 = IP6_REASSEMBLY_NEXT_DROP;
777  ;
778  *error0 = IP6_ERROR_REASS_DUPLICATE_FRAGMENT;
779  }
780  }
781 }
782 
783 always_inline bool
785  vlib_buffer_t * b,
786  ip6_frag_hdr_t * frag_hdr)
787 {
788  ip6_ext_header_t *tmp = (ip6_ext_header_t *) frag_hdr;
789  while (ip6_ext_hdr (tmp->next_hdr))
790  {
791  tmp = ip6_ext_next_header (tmp);
792  }
793  if (IP_PROTOCOL_IP6_NONXT == tmp->next_hdr)
794  {
795  icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
796  ICMP6_parameter_problem_first_fragment_has_incomplete_header_chain,
797  0);
798  b->error = node->errors[IP6_ERROR_REASS_MISSING_UPPER];
799 
800  return false;
801  }
802  return true;
803 }
804 
805 always_inline bool
807  vlib_node_runtime_t * node,
808  vlib_buffer_t * b,
809  ip6_frag_hdr_t * frag_hdr)
810 {
813  int more_fragments = ip6_frag_hdr_more (frag_hdr);
814  u32 fragment_length =
816  (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
817  if (more_fragments && 0 != fragment_length % 8)
818  {
819  icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
820  ICMP6_parameter_problem_erroneous_header_field,
821  (u8 *) & ip->payload_length - (u8 *) ip);
822  return false;
823  }
824  return true;
825 }
826 
827 always_inline bool
829  vlib_node_runtime_t * node,
830  vlib_buffer_t * b,
831  ip6_frag_hdr_t * frag_hdr)
832 {
834  u32 fragment_first = ip6_frag_hdr_offset_bytes (frag_hdr);
835  u32 fragment_length =
837  (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
838  if (fragment_first + fragment_length > 65535)
839  {
841  icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
842  ICMP6_parameter_problem_erroneous_header_field,
843  (u8 *) & frag_hdr->fragment_offset_and_more
844  - (u8 *) ip0);
845  return false;
846  }
847  return true;
848 }
849 
852  vlib_node_runtime_t * node,
853  vlib_frame_t * frame, bool is_feature)
854 {
855  u32 *from = vlib_frame_vector_args (frame);
856  u32 n_left_from, n_left_to_next, *to_next, next_index;
859  clib_spinlock_lock (&rt->lock);
860 
861  n_left_from = frame->n_vectors;
862  next_index = node->cached_next_index;
863  static u32 *vec_timeout = NULL; // indexes of buffers which timed out
864  static u32 *vec_drop_overlap = NULL; // indexes of buffers dropped due to overlap
865  static u32 *vec_drop_compress = NULL; // indexes of buffers dropped due to buffer compression
866  while (n_left_from > 0 || vec_len (vec_timeout) > 0 ||
867  vec_len (vec_drop_overlap) > 0 || vec_len (vec_drop_compress) > 0)
868  {
869  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
870 
871  while (vec_len (vec_timeout) > 0 && n_left_to_next > 0)
872  {
873  u32 bi = vec_pop (vec_timeout);
874  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
875  b->error = node->errors[IP6_ERROR_REASS_TIMEOUT];
876  to_next[0] = bi;
877  to_next += 1;
878  n_left_to_next -= 1;
879  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
880  n_left_to_next, bi,
882  ASSERT (rt->buffers_n > 0);
883  --rt->buffers_n;
884  }
885 
886  while (vec_len (vec_drop_overlap) > 0 && n_left_to_next > 0)
887  {
888  u32 bi = vec_pop (vec_drop_overlap);
889  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
890  b->error = node->errors[IP6_ERROR_REASS_OVERLAPPING_FRAGMENT];
891  to_next[0] = bi;
892  to_next += 1;
893  n_left_to_next -= 1;
894  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
895  n_left_to_next, bi,
897  ASSERT (rt->buffers_n > 0);
898  --rt->buffers_n;
899  }
900 
901  while (vec_len (vec_drop_compress) > 0 && n_left_to_next > 0)
902  {
903  u32 bi = vec_pop (vec_drop_compress);
904  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
905  b->error = node->errors[IP6_ERROR_NONE];
906  to_next[0] = bi;
907  to_next += 1;
908  n_left_to_next -= 1;
909  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
910  n_left_to_next, bi,
912  ASSERT (rt->buffers_n > 0);
913  --rt->buffers_n;
914  }
915 
916  while (n_left_from > 0 && n_left_to_next > 0)
917  {
918  u32 bi0;
919  vlib_buffer_t *b0;
920  u32 next0;
921  u32 error0 = IP6_ERROR_NONE;
922  u32 icmp_bi = ~0;
923 
924  bi0 = from[0];
925  b0 = vlib_get_buffer (vm, bi0);
926 
928  ip6_frag_hdr_t *frag_hdr = NULL;
929  ip6_ext_header_t *prev_hdr;
930  if (ip6_ext_hdr (ip0->protocol))
931  {
932  ip6_ext_header_find_t (ip0, prev_hdr, frag_hdr,
933  IP_PROTOCOL_IPV6_FRAGMENTATION);
934  }
935  if (!frag_hdr)
936  {
937  // this is a regular packet - no fragmentation
939  goto skip_reass;
940  }
941  if (0 == ip6_frag_hdr_offset (frag_hdr))
942  {
943  // first fragment - verify upper-layer is present
944  if (!ip6_reass_verify_upper_layer_present (node, b0, frag_hdr))
945  {
947  goto skip_reass;
948  }
949  }
950  if (!ip6_reass_verify_fragment_multiple_8 (vm, node, b0, frag_hdr)
951  || !ip6_reass_verify_packet_size_lt_64k (vm, node, b0,
952  frag_hdr))
953  {
955  goto skip_reass;
956  }
957  vnet_buffer (b0)->ip.reass.ip6_frag_hdr_offset =
958  (u8 *) frag_hdr - (u8 *) ip0;
959 
960  ip6_reass_key_t k;
961  k.src.as_u64[0] = ip0->src_address.as_u64[0];
962  k.src.as_u64[1] = ip0->src_address.as_u64[1];
963  k.dst.as_u64[0] = ip0->dst_address.as_u64[0];
964  k.dst.as_u64[1] = ip0->dst_address.as_u64[1];
965  k.xx_id = vnet_buffer (b0)->sw_if_index[VLIB_RX];
966  k.frag_id = frag_hdr->identification;
967  k.proto = ip0->protocol;
968  k.unused = 0;
969  ip6_reass_t *reass =
970  ip6_reass_find_or_create (vm, node, rm, rt, &k, &icmp_bi,
971  &vec_timeout);
972 
973  if (reass)
974  {
975  ip6_reass_update (vm, node, rm, rt, reass, &bi0, &next0,
976  &error0, frag_hdr, &vec_drop_overlap,
977  &vec_drop_compress, is_feature);
978  }
979  else
980  {
981  next0 = IP6_REASSEMBLY_NEXT_DROP;
982  error0 = IP6_ERROR_REASS_LIMIT_REACHED;
983  }
984 
985  b0->error = node->errors[error0];
986 
987  if (~0 != bi0)
988  {
989  skip_reass:
990  to_next[0] = bi0;
991  to_next += 1;
992  n_left_to_next -= 1;
993  if (is_feature && IP6_ERROR_NONE == error0)
994  {
995  vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX],
996  &next0, b0);
997  }
998  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
999  n_left_to_next, bi0, next0);
1000  }
1001 
1002  if (~0 != icmp_bi)
1003  {
1005  to_next[0] = icmp_bi;
1006  to_next += 1;
1007  n_left_to_next -= 1;
1008  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1009  n_left_to_next, icmp_bi,
1010  next0);
1011  }
1012  from += 1;
1013  n_left_from -= 1;
1014  }
1015 
1016  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1017  }
1018 
1019  clib_spinlock_unlock (&rt->lock);
1020  return frame->n_vectors;
1021 }
1022 
1024 #define _(sym, string) string,
1026 #undef _
1027 };
1028 
1029 static uword
1031  vlib_frame_t * frame)
1032 {
1033  return ip6_reassembly_inline (vm, node, frame, false /* is_feature */ );
1034 }
1035 
1036 /* *INDENT-OFF* */
1037 VLIB_REGISTER_NODE (ip6_reass_node, static) = {
1038  .function = ip6_reassembly,
1039  .name = "ip6-reassembly",
1040  .vector_size = sizeof (u32),
1041  .format_trace = format_ip6_reass_trace,
1042  .n_errors = ARRAY_LEN (ip6_reassembly_error_strings),
1043  .error_strings = ip6_reassembly_error_strings,
1044  .n_next_nodes = IP6_REASSEMBLY_N_NEXT,
1045  .next_nodes =
1046  {
1047  [IP6_REASSEMBLY_NEXT_INPUT] = "ip6-input",
1048  [IP6_REASSEMBLY_NEXT_DROP] = "ip6-drop",
1049  [IP6_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
1050  },
1051 };
1052 /* *INDENT-ON* */
1053 
1055 
1056 static uword
1058  vlib_node_runtime_t * node, vlib_frame_t * frame)
1059 {
1060  return ip6_reassembly_inline (vm, node, frame, true /* is_feature */ );
1061 }
1062 
1063 /* *INDENT-OFF* */
1065  .function = ip6_reassembly_feature,
1066  .name = "ip6-reassembly-feature",
1067  .vector_size = sizeof (u32),
1068  .format_trace = format_ip6_reass_trace,
1069  .n_errors = ARRAY_LEN (ip6_reassembly_error_strings),
1070  .error_strings = ip6_reassembly_error_strings,
1071  .n_next_nodes = IP6_REASSEMBLY_N_NEXT,
1072  .next_nodes =
1073  {
1074  [IP6_REASSEMBLY_NEXT_INPUT] = "ip6-input",
1075  [IP6_REASSEMBLY_NEXT_DROP] = "ip6-drop",
1076  [IP6_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
1077  },
1078 };
1079 /* *INDENT-ON* */
1080 
1082 
1083 /* *INDENT-OFF* */
1085  .arc_name = "ip6-unicast",
1086  .node_name = "ip6-reassembly-feature",
1087  .runs_before = VNET_FEATURES ("ip6-lookup"),
1088  .runs_after = 0,
1089 };
1090 /* *INDENT-ON* */
1091 
1092 static u32
1094 {
1096  u32 nbuckets;
1097  u8 i;
1098 
1099  nbuckets = (u32) (rm->max_reass_n / IP6_REASS_HT_LOAD_FACTOR);
1100 
1101  for (i = 0; i < 31; i++)
1102  if ((1 << i) >= nbuckets)
1103  break;
1104  nbuckets = 1 << i;
1105 
1106  return nbuckets;
1107 }
1108 
1109 typedef enum
1110 {
1113 
1114 typedef struct
1115 {
1116  int failure;
1117  clib_bihash_48_8_t *new_hash;
1119 
1120 static void
1122 {
1123  ip6_rehash_cb_ctx *ctx = _ctx;
1124  if (clib_bihash_add_del_48_8 (ctx->new_hash, kv, 1))
1125  {
1126  ctx->failure = 1;
1127  }
1128 }
1129 
1130 static void
1131 ip6_reass_set_params (u32 timeout_ms, u32 max_reassemblies,
1132  u32 expire_walk_interval_ms)
1133 {
1134  ip6_reass_main.timeout_ms = timeout_ms;
1135  ip6_reass_main.timeout = (f64) timeout_ms / (f64) MSEC_PER_SEC;
1136  ip6_reass_main.max_reass_n = max_reassemblies;
1137  ip6_reass_main.expire_walk_interval_ms = expire_walk_interval_ms;
1138 }
1139 
1141 ip6_reass_set (u32 timeout_ms, u32 max_reassemblies,
1142  u32 expire_walk_interval_ms)
1143 {
1144  u32 old_nbuckets = ip6_reass_get_nbuckets ();
1145  ip6_reass_set_params (timeout_ms, max_reassemblies,
1146  expire_walk_interval_ms);
1147  vlib_process_signal_event (ip6_reass_main.vlib_main,
1148  ip6_reass_main.ip6_reass_expire_node_idx,
1150  u32 new_nbuckets = ip6_reass_get_nbuckets ();
1151  if (ip6_reass_main.max_reass_n > 0 && new_nbuckets > old_nbuckets)
1152  {
1153  clib_bihash_48_8_t new_hash;
1154  memset (&new_hash, 0, sizeof (new_hash));
1156  ctx.failure = 0;
1157  ctx.new_hash = &new_hash;
1158  clib_bihash_init_48_8 (&new_hash, "ip6-reass", new_nbuckets,
1159  new_nbuckets * 1024);
1160  clib_bihash_foreach_key_value_pair_48_8 (&ip6_reass_main.hash,
1161  ip6_rehash_cb, &ctx);
1162  if (ctx.failure)
1163  {
1164  clib_bihash_free_48_8 (&new_hash);
1165  return -1;
1166  }
1167  else
1168  {
1169  clib_bihash_free_48_8 (&ip6_reass_main.hash);
1170  clib_memcpy (&ip6_reass_main.hash, &new_hash,
1171  sizeof (ip6_reass_main.hash));
1172  }
1173  }
1174  return 0;
1175 }
1176 
1178 ip6_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
1179  u32 * expire_walk_interval_ms)
1180 {
1181  *timeout_ms = ip6_reass_main.timeout_ms;
1182  *max_reassemblies = ip6_reass_main.max_reass_n;
1183  *expire_walk_interval_ms = ip6_reass_main.expire_walk_interval_ms;
1184  return 0;
1185 }
1186 
1187 static clib_error_t *
1189 {
1191  clib_error_t *error = 0;
1192  u32 nbuckets;
1193  vlib_node_t *node;
1194 
1195  rm->vlib_main = vm;
1196  rm->vnet_main = vnet_get_main ();
1197 
1200  vec_foreach (rt, rm->per_thread_data)
1201  {
1202  clib_spinlock_init (&rt->lock);
1203  pool_alloc (rt->pool, rm->max_reass_n);
1204  }
1205 
1206  node = vlib_get_node_by_name (vm, (u8 *) "ip6-reassembly-expire-walk");
1207  ASSERT (node);
1208  rm->ip6_reass_expire_node_idx = node->index;
1209 
1213 
1214  nbuckets = ip6_reass_get_nbuckets ();
1215  clib_bihash_init_48_8 (&rm->hash, "ip6-reass", nbuckets, nbuckets * 1024);
1216 
1217  node = vlib_get_node_by_name (vm, (u8 *) "ip6-drop");
1218  ASSERT (node);
1219  rm->ip6_drop_idx = node->index;
1220  node = vlib_get_node_by_name (vm, (u8 *) "ip6-icmp-error");
1221  ASSERT (node);
1222  rm->ip6_icmp_error_idx = node->index;
1223 
1224  if ((error = vlib_call_init_function (vm, ip_main_init)))
1225  return error;
1226  ip6_register_protocol (IP_PROTOCOL_IPV6_FRAGMENTATION,
1227  ip6_reass_node.index);
1228 
1229  return error;
1230 }
1231 
1233 
1234 static uword
1236  vlib_node_runtime_t * node, vlib_frame_t * f)
1237 {
1239  uword event_type, *event_data = 0;
1240 
1241  while (true)
1242  {
1245  / (f64) MSEC_PER_SEC);
1246  event_type = vlib_process_get_events (vm, &event_data);
1247 
1248  switch (event_type)
1249  {
1250  case ~0: /* no events => timeout */
1251  /* nothing to do here */
1252  break;
1254  break;
1255  default:
1256  clib_warning ("BUG: event type 0x%wx", event_type);
1257  break;
1258  }
1259  f64 now = vlib_time_now (vm);
1260 
1261  ip6_reass_t *reass;
1262  u32 *vec_timeout = NULL;
1263  int *pool_indexes_to_free = NULL;
1264 
1265  uword thread_index = 0;
1266  int index;
1267  const uword nthreads = os_get_nthreads ();
1268  u32 *vec_icmp_bi = NULL;
1269  for (thread_index = 0; thread_index < nthreads; ++thread_index)
1270  {
1271  ip6_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
1272  clib_spinlock_lock (&rt->lock);
1273 
1274  vec_reset_length (pool_indexes_to_free);
1275  /* *INDENT-OFF* */
1276  pool_foreach_index (index, rt->pool, ({
1277  reass = pool_elt_at_index (rt->pool, index);
1278  if (now > reass->last_heard + rm->timeout)
1279  {
1280  vec_add1 (pool_indexes_to_free, index);
1281  }
1282  }));
1283  /* *INDENT-ON* */
1284  int *i;
1285  /* *INDENT-OFF* */
1286  vec_foreach (i, pool_indexes_to_free)
1287  {
1288  ip6_reass_t *reass = pool_elt_at_index (rt->pool, i[0]);
1289  u32 icmp_bi = ~0;
1290  u32 before = vec_len (vec_timeout);
1291  vlib_buffer_t *b = vlib_get_buffer (vm, reass->first_bi);
1292  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
1293  {
1295  b->trace_index))
1296  {
1297  /* the trace is gone, don't trace this buffer anymore */
1298  b->flags &= ~VLIB_BUFFER_IS_TRACED;
1299  }
1300  }
1301  ip6_reass_on_timeout (vm, node, rm, reass, &icmp_bi, &vec_timeout);
1302  u32 after = vec_len (vec_timeout);
1303  ASSERT (rt->buffers_n >= (after - before));
1304  rt->buffers_n -= (after - before);
1305  if (~0 != icmp_bi)
1306  {
1307  vec_add1 (vec_icmp_bi, icmp_bi);
1308  ASSERT (rt->buffers_n > 0);
1309  --rt->buffers_n;
1310  }
1311  ip6_reass_free (rm, rt, reass);
1312  }
1313  /* *INDENT-ON* */
1314 
1315  clib_spinlock_unlock (&rt->lock);
1316  }
1317 
1318  while (vec_len (vec_timeout) > 0)
1319  {
1321  u32 *to_next = vlib_frame_vector_args (f);
1322  u32 n_left_to_next = VLIB_FRAME_SIZE - f->n_vectors;
1323  int trace_frame = 0;
1324  while (vec_len (vec_timeout) > 0 && n_left_to_next > 0)
1325  {
1326  u32 bi = vec_pop (vec_timeout);
1327  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1328  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
1329  {
1331  b->trace_index))
1332  {
1333  /* the trace is gone, don't trace this buffer anymore */
1334  b->flags &= ~VLIB_BUFFER_IS_TRACED;
1335  }
1336  else
1337  {
1338  trace_frame = 1;
1339  }
1340  }
1341  b->error = node->errors[IP6_ERROR_REASS_TIMEOUT];
1342  to_next[0] = bi;
1343  ++f->n_vectors;
1344  to_next += 1;
1345  n_left_to_next -= 1;
1346  }
1347  f->flags |= (trace_frame * VLIB_FRAME_TRACE);
1348  vlib_put_frame_to_node (vm, rm->ip6_drop_idx, f);
1349  }
1350 
1351  while (vec_len (vec_icmp_bi) > 0)
1352  {
1353  vlib_frame_t *f =
1355  u32 *to_next = vlib_frame_vector_args (f);
1356  u32 n_left_to_next = VLIB_FRAME_SIZE - f->n_vectors;
1357  int trace_frame = 0;
1358  while (vec_len (vec_icmp_bi) > 0 && n_left_to_next > 0)
1359  {
1360  u32 bi = vec_pop (vec_icmp_bi);
1361  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1362  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
1363  {
1365  b->trace_index))
1366  {
1367  /* the trace is gone, don't trace this buffer anymore */
1368  b->flags &= ~VLIB_BUFFER_IS_TRACED;
1369  }
1370  else
1371  {
1372  trace_frame = 1;
1373  }
1374  }
1375  b->error = node->errors[IP6_ERROR_REASS_TIMEOUT];
1376  to_next[0] = bi;
1377  ++f->n_vectors;
1378  to_next += 1;
1379  n_left_to_next -= 1;
1380  }
1381  f->flags |= (trace_frame * VLIB_FRAME_TRACE);
1383  }
1384 
1385  vec_free (pool_indexes_to_free);
1386  vec_free (vec_timeout);
1387  vec_free (vec_icmp_bi);
1388  if (event_data)
1389  {
1390  _vec_len (event_data) = 0;
1391  }
1392  }
1393 
1394  return 0;
1395 }
1396 
1398 
1399 /* *INDENT-OFF* */
1401  .function = ip6_reass_walk_expired,
1402  .format_trace = format_ip6_reass_trace,
1403  .type = VLIB_NODE_TYPE_PROCESS,
1404  .name = "ip6-reassembly-expire-walk",
1405 
1407  .error_strings = ip6_reassembly_error_strings,
1408 
1409 };
1410 /* *INDENT-ON* */
1411 
1412 static u8 *
1413 format_ip6_reass_key (u8 * s, va_list * args)
1414 {
1415  ip6_reass_key_t *key = va_arg (*args, ip6_reass_key_t *);
1416  s = format (s, "xx_id: %u, src: %U, dst: %U, frag_id: %u, proto: %u",
1418  &key->dst, clib_net_to_host_u16 (key->frag_id), key->proto);
1419  return s;
1420 }
1421 
1422 static u8 *
1423 format_ip6_reass (u8 * s, va_list * args)
1424 {
1425  vlib_main_t *vm = va_arg (*args, vlib_main_t *);
1426  ip6_reass_t *reass = va_arg (*args, ip6_reass_t *);
1427 
1428  s = format (s, "ID: %lu, key: %U\n first_bi: %u, data_len: %u, "
1429  "last_packet_octet: %u, trace_op_counter: %u\n",
1430  reass->id, format_ip6_reass_key, &reass->key, reass->first_bi,
1431  reass->data_len, reass->last_packet_octet,
1432  reass->trace_op_counter);
1433  u32 bi = reass->first_bi;
1434  u32 counter = 0;
1435  while (~0 != bi)
1436  {
1437  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1438  vnet_buffer_opaque_t *vnb = vnet_buffer (b);
1439  s = format (s, " #%03u: range: [%u, %u], bi: %u, off: %d, len: %u, "
1440  "fragment[%u, %u]\n",
1441  counter, vnb->ip.reass.range_first,
1442  vnb->ip.reass.range_last, bi,
1445  vnb->ip.reass.fragment_first, vnb->ip.reass.fragment_last);
1446  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
1447  {
1448  bi = b->next_buffer;
1449  }
1450  else
1451  {
1452  bi = ~0;
1453  }
1454  }
1455  return s;
1456 }
1457 
1458 static clib_error_t *
1461 {
1463 
1464  vlib_cli_output (vm, "---------------------");
1465  vlib_cli_output (vm, "IP6 reassembly status");
1466  vlib_cli_output (vm, "---------------------");
1467  bool details = false;
1468  if (unformat (input, "details"))
1469  {
1470  details = true;
1471  }
1472 
1473  u32 sum_reass_n = 0;
1474  u64 sum_buffers_n = 0;
1475  ip6_reass_t *reass;
1476  uword thread_index;
1477  const uword nthreads = os_get_nthreads ();
1478  for (thread_index = 0; thread_index < nthreads; ++thread_index)
1479  {
1480  ip6_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
1481  clib_spinlock_lock (&rt->lock);
1482  if (details)
1483  {
1484  /* *INDENT-OFF* */
1485  pool_foreach (reass, rt->pool, {
1486  vlib_cli_output (vm, "%U", format_ip6_reass, vm, reass);
1487  });
1488  /* *INDENT-ON* */
1489  }
1490  sum_reass_n += rt->reass_n;
1491  sum_buffers_n += rt->buffers_n;
1492  clib_spinlock_unlock (&rt->lock);
1493  }
1494  vlib_cli_output (vm, "---------------------");
1495  vlib_cli_output (vm, "Current IP6 reassemblies count: %lu\n",
1496  (long unsigned) sum_reass_n);
1497  vlib_cli_output (vm, "Maximum configured concurrent IP6 reassemblies per "
1498  "worker-thread: %lu\n", (long unsigned) rm->max_reass_n);
1499  vlib_cli_output (vm, "Buffers in use: %lu\n",
1500  (long unsigned) sum_buffers_n);
1501  return 0;
1502 }
1503 
1504 /* *INDENT-OFF* */
1506  .path = "show ip6-reassembly",
1507  .short_help = "show ip6-reassembly [details]",
1508  .function = show_ip6_reass,
1509 };
1510 /* *INDENT-ON* */
1511 
1513 ip6_reass_enable_disable (u32 sw_if_index, u8 enable_disable)
1514 {
1515  return vnet_feature_enable_disable ("ip6-unicast", "ip6-reassembly-feature",
1516  sw_if_index, enable_disable, 0, 0);
1517 }
1518 
1519 /*
1520  * fd.io coding-style-patch-verification: ON
1521  *
1522  * Local Variables:
1523  * eval: (c-set-style "gnu")
1524  * End:
1525  */
#define IP6_REASS_TIMEOUT_DEFAULT_MS
static u32 ip6_reass_buffer_get_data_offset_no_check(vlib_buffer_t *b)
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:437
vnet_api_error_t
Definition: api_errno.h:147
static vlib_cli_command_t trace
(constructor) VLIB_CLI_COMMAND (trace)
Definition: vlib_api_cli.c:862
#define clib_min(x, y)
Definition: clib.h:289
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:89
static_always_inline void clib_spinlock_lock(clib_spinlock_t *p)
Definition: lock.h:74
#define CLIB_UNUSED(x)
Definition: clib.h:79
ip6_reass_next_t
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:699
vnet_main_t * vnet_main
static uword ip6_reassembly(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
static ip6_reass_t * ip6_reass_find_or_create(vlib_main_t *vm, vlib_node_runtime_t *node, ip6_reass_main_t *rm, ip6_reass_per_thread_t *rt, ip6_reass_key_t *k, u32 *icmp_bi, u32 **vec_timeout)
void ip6_register_protocol(u32 protocol, u32 node_index)
Definition: ip6_forward.c:1426
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
#define pool_alloc(P, N)
Allocate N more free elements to pool (unspecified alignment).
Definition: pool.h:326
u64 as_u64
Definition: bihash_doc.h:63
IPv6 Reassembly.
#define IP6_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS
u64 as_u64[2]
Definition: ip6_packet.h:51
unsigned long u64
Definition: types.h:89
static void vlib_buffer_chain_compress(vlib_main_t *vm, vlib_buffer_t *first, u32 **discard_vector)
compress buffer chain in a way where the first buffer is at least VLIB_BUFFER_CHAIN_MIN_FIRST_DATA_SI...
#define NULL
Definition: clib.h:55
u32 index
Definition: node.h:273
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:225
static clib_error_t * ip6_reass_init_function(vlib_main_t *vm)
static vlib_node_registration_t ip6_reass_expire_node
(constructor) VLIB_REGISTER_NODE (ip6_reass_expire_node)
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:523
ip6_reass_trace_operation_e action
static void * ip6_ext_next_header(ip6_ext_header_t *ext_hdr)
Definition: ip6_packet.h:494
int i
static u32 format_get_indent(u8 *s)
Definition: format.h:72
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
ip6_reass_event_t
ip6_reass_per_thread_t * per_thread_data
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:451
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:250
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:227
ip6_address_t src_address
Definition: ip6_packet.h:347
static void ip6_reass_trace_details(vlib_main_t *vm, u32 bi, ip6_reass_range_trace_t *trace)
unsigned char u8
Definition: types.h:56
VLIB_NODE_FUNCTION_MULTIARCH(ip6_reass_node, ip6_reassembly)
#define vec_pop(V)
Returns last element of a vector and decrements its length.
Definition: vec.h:617
static u16 ip6_reass_buffer_get_data_len(vlib_buffer_t *b)
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
#define IP6_REASS_HT_LOAD_FACTOR
double f64
Definition: types.h:142
static u32 ip6_reass_get_nbuckets()
vlib_trace_header_t ** trace_buffer_pool
Definition: trace.h:72
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:440
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:156
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:542
static void ip6_reass_free(ip6_reass_main_t *rm, ip6_reass_per_thread_t *rt, ip6_reass_t *reass)
#define always_inline
Definition: clib.h:92
u8 * format_white_space(u8 *s, va_list *va)
Definition: std-formats.c:113
vlib_frame_t * vlib_get_frame_to_node(vlib_main_t *vm, u32 to_node_index)
Definition: main.c:182
vnet_api_error_t ip6_reass_set(u32 timeout_ms, u32 max_reassemblies, u32 expire_walk_interval_ms)
set ip6 reassembly configuration
clib_bihash_48_8_t hash
static u8 * format_ip6_reass_range_trace(u8 *s, va_list *args)
unsigned int u32
Definition: types.h:88
#define vlib_call_init_function(vm, x)
Definition: init.h:227
#define VLIB_FRAME_SIZE
Definition: node.h:364
struct vnet_buffer_opaque_t::@57::@59 ip
ip6_reass_main_t ip6_reass_main
void icmp6_error_set_vnet_buffer(vlib_buffer_t *b, u8 type, u8 code, u32 data)
Definition: icmp6.c:446
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:57
static char * ip6_reassembly_error_strings[]
#define ip6_frag_hdr_more(hdr)
Definition: ip6_packet.h:548
ip6_reass_key_t key
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:461
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:108
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:952
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
void vlib_put_frame_to_node(vlib_main_t *vm, u32 to_node_index, vlib_frame_t *f)
Definition: main.c:191
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:202
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:273
#define PREDICT_FALSE(x)
Definition: clib.h:105
static bool ip6_reass_verify_packet_size_lt_64k(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *b, ip6_frag_hdr_t *frag_hdr)
static u32 ip6_reass_buffer_get_data_offset(vlib_buffer_t *b)
static_always_inline void vnet_feature_next(u32 sw_if_index, u32 *next0, vlib_buffer_t *b0)
Definition: feature.h:237
#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
ip6_reass_range_trace_t trace_range
#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
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:135
VNET_FEATURE_INIT(ip6_reassembly_feature, static)
static vlib_cli_command_t show_ip6_reassembly_cmd
(constructor) VLIB_CLI_COMMAND (show_ip6_reassembly_cmd)
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:153
clib_error_t * ip_main_init(vlib_main_t *vm)
Definition: ip_init.c:45
u16 n_vectors
Definition: node.h:380
static vlib_node_registration_t ip6_reass_node_feature
(constructor) VLIB_REGISTER_NODE (ip6_reass_node_feature)
format_function_t format_ip6_address
Definition: format.h:99
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_warning(format, args...)
Definition: error.h:59
#define clib_memcpy(a, b, c)
Definition: string.h:75
static void ip6_rehash_cb(clib_bihash_kv_48_8_t *kv, void *_ctx)
u8 * format_hexdump(u8 *s, va_list *va)
Definition: std-formats.c:281
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:270
#define ip6_frag_hdr_offset_bytes(hdr)
Definition: ip6_packet.h:545
#define ARRAY_LEN(x)
Definition: clib.h:59
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:454
vlib_node_t * vlib_get_node_by_name(vlib_main_t *vm, u8 *name)
Definition: node.c:45
static void ip6_reass_on_timeout(vlib_main_t *vm, vlib_node_runtime_t *node, ip6_reass_main_t *rm, ip6_reass_t *reass, u32 *icmp_bi, u32 **vec_timeout)
static u8 ip6_ext_hdr(u8 nexthdr)
Definition: ip6_packet.h:478
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
signed int i32
Definition: types.h:81
static bool ip6_reass_verify_fragment_multiple_8(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *b, ip6_frag_hdr_t *frag_hdr)
#define ip6_frag_hdr_offset(hdr)
Definition: ip6_packet.h:542
clib_spinlock_t lock
static u8 * format_ip6_reass_key(u8 *s, va_list *args)
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:492
static uword ip6_reassembly_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, bool is_feature)
static vlib_node_registration_t ip6_reass_node
(constructor) VLIB_REGISTER_NODE (ip6_reass_node)
#define ASSERT(truth)
long ctx[MAX_CONNS]
Definition: main.c:126
static void ip6_reass_drop_all(vlib_main_t *vm, ip6_reass_main_t *rm, ip6_reass_t *reass, u32 **vec_drop_bi)
u16 flags
Definition: node.h:371
u32 next_buffer
Next buffer for this linked-list of buffers.
Definition: buffer.h:126
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:215
vlib_trace_main_t trace_main
Definition: main.h:138
static clib_error_t * show_ip6_reass(vlib_main_t *vm, unformat_input_t *input, CLIB_UNUSED(vlib_cli_command_t *lmd))
u16 min_fragment_length
#define VNET_FEATURES(...)
Definition: feature.h:391
static void ip6_reass_update(vlib_main_t *vm, vlib_node_runtime_t *node, ip6_reass_main_t *rm, ip6_reass_per_thread_t *rt, ip6_reass_t *reass, u32 *bi0, u32 *next0, u32 *error0, ip6_frag_hdr_t *frag_hdr, u32 **vec_drop_overlap, u32 **vec_drop_compress, bool is_feature)
vlib_main_t * vlib_main
static void ip6_reass_insert_range_in_chain(vlib_main_t *vm, ip6_reass_main_t *rm, ip6_reass_per_thread_t *rt, ip6_reass_t *reass, u32 prev_range_bi, u32 new_next_bi)
static u8 * format_ip6_reass_trace(u8 *s, va_list *args)
u32 last_packet_octet
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:55
static u8 * format_ip6_reass(u8 *s, va_list *args)
u32 total_length_not_including_first_buffer
Only valid for first buffer in chain.
Definition: buffer.h:152
struct _vlib_node_registration vlib_node_registration_t
u16 payload_length
Definition: ip6_packet.h:338
static u32 ip6_reass_get_buffer_chain_length(vlib_main_t *vm, vlib_buffer_t *b)
#define MSEC_PER_SEC
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
static uword ip6_reass_walk_expired(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *f)
u64 uword
Definition: types.h:112
static void ip6_reass_add_trace(vlib_main_t *vm, vlib_node_runtime_t *node, ip6_reass_main_t *rm, ip6_reass_t *reass, u32 bi, ip6_reass_trace_operation_e action, u32 size_diff)
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:267
ip6_reass_trace_operation_e
uword os_get_nthreads(void)
Definition: unix-misc.c:226
#define VLIB_FRAME_TRACE
Definition: node.h:415
static_always_inline uword os_get_thread_index(void)
Definition: os.h:62
#define foreach_ip6_error
Definition: ip6_error.h:43
#define IP6_REASS_MAX_REASSEMBLIES_DEFAULT
static u16 ip6_reass_buffer_get_data_len_no_check(vlib_buffer_t *b)
vnet_api_error_t ip6_reass_get(u32 *timeout_ms, u32 *max_reassemblies, u32 *expire_walk_interval_ms)
get ip6 reassembly configuration
#define vnet_buffer(b)
Definition: buffer.h:360
clib_bihash_48_8_t * new_hash
static u32 vlib_num_workers()
Definition: threads.h:375
static uword ip6_reassembly_feature(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
#define vec_foreach(var, vec)
Vector iterator.
vnet_api_error_t ip6_reass_enable_disable(u32 sw_if_index, u8 enable_disable)
#define ip6_ext_header_find_t(i, p, m, t)
Definition: ip6_packet.h:503
static void ip6_reass_set_params(u32 timeout_ms, u32 max_reassemblies, u32 expire_walk_interval_ms)
static void ip6_reass_finalize(vlib_main_t *vm, vlib_node_runtime_t *node, ip6_reass_main_t *rm, ip6_reass_per_thread_t *rt, ip6_reass_t *reass, u32 *bi0, u32 *next0, u32 *error0, u32 **vec_drop_compress, bool is_feature)
static bool ip6_reass_verify_upper_layer_present(vlib_node_runtime_t *node, vlib_buffer_t *b, ip6_frag_hdr_t *frag_hdr)
ip6_address_t dst
#define pool_foreach_index(i, v, body)
Iterate pool by index.
Definition: pool.h:485
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:111
ip6_address_t src
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:681
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
u32 trace_index
Specifies index into trace buffer if VLIB_PACKET_IS_TRACED flag is set.
Definition: buffer.h:147
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
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:233
CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers".
ip6_address_t dst_address
Definition: ip6_packet.h:347