FD.io VPP  v18.04-17-g3a0d853
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  vlib_error_t * error0, u32 ** vec_drop_compress,
441  bool is_feature)
442 {
443  ASSERT (~0 != reass->first_bi);
444  *bi0 = reass->first_bi;
445  *error0 = IP6_ERROR_NONE;
446  ip6_frag_hdr_t *frag_hdr;
447  vlib_buffer_t *last_b = NULL;
448  u32 sub_chain_bi = reass->first_bi;
449  u32 total_length = 0;
450  u32 buf_cnt = 0;
451  u32 dropped_cnt = 0;
452  do
453  {
454  u32 tmp_bi = sub_chain_bi;
455  vlib_buffer_t *tmp = vlib_get_buffer (vm, tmp_bi);
456  u32 data_len = ip6_reass_buffer_get_data_len (tmp);
457  u32 trim_front = vnet_buffer (tmp)->ip.reass.ip6_frag_hdr_offset +
458  sizeof (*frag_hdr) + ip6_reass_buffer_get_data_offset (tmp);
459  u32 trim_end =
460  vlib_buffer_length_in_chain (vm, tmp) - trim_front - data_len;
461  if (tmp_bi == reass->first_bi)
462  {
463  /* first buffer - keep ip6 header */
464  ASSERT (0 == ip6_reass_buffer_get_data_offset (tmp));
465  trim_front = 0;
466  trim_end = vlib_buffer_length_in_chain (vm, tmp) - data_len -
467  (vnet_buffer (tmp)->ip.reass.ip6_frag_hdr_offset +
468  sizeof (*frag_hdr));
469  ASSERT (vlib_buffer_length_in_chain (vm, tmp) - trim_end > 0);
470  }
471  u32 keep_data =
472  vlib_buffer_length_in_chain (vm, tmp) - trim_front - trim_end;
473  while (1)
474  {
475  ++buf_cnt;
476  if (trim_front)
477  {
478  if (trim_front > tmp->current_length)
479  {
480  /* drop whole buffer */
481  vec_add1 (*vec_drop_compress, tmp_bi);
482  ++dropped_cnt;
483  trim_front -= tmp->current_length;
484  ASSERT (tmp->flags & VLIB_BUFFER_NEXT_PRESENT);
485  tmp->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
486  tmp_bi = tmp->next_buffer;
487  tmp = vlib_get_buffer (vm, tmp_bi);
488  continue;
489  }
490  else
491  {
492  vlib_buffer_advance (tmp, trim_front);
493  trim_front = 0;
494  }
495  }
496  if (keep_data)
497  {
498  if (last_b)
499  {
500  last_b->flags |= VLIB_BUFFER_NEXT_PRESENT;
501  last_b->next_buffer = tmp_bi;
502  }
503  last_b = tmp;
504  if (keep_data <= tmp->current_length)
505  {
506  tmp->current_length = keep_data;
507  keep_data = 0;
508  }
509  else
510  {
511  keep_data -= tmp->current_length;
512  ASSERT (tmp->flags & VLIB_BUFFER_NEXT_PRESENT);
513  }
514  total_length += tmp->current_length;
515  }
516  else
517  {
518  vec_add1 (*vec_drop_compress, tmp_bi);
519  ASSERT (reass->first_bi != tmp_bi);
520  ++dropped_cnt;
521  }
522  if (tmp->flags & VLIB_BUFFER_NEXT_PRESENT)
523  {
524  tmp_bi = tmp->next_buffer;
525  tmp = vlib_get_buffer (vm, tmp->next_buffer);
526  }
527  else
528  {
529  break;
530  }
531  }
532  sub_chain_bi =
533  vnet_buffer (vlib_get_buffer (vm, sub_chain_bi))->ip.
534  reass.next_range_bi;
535  }
536  while (~0 != sub_chain_bi);
537 
538  ASSERT (last_b != NULL);
539  last_b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
540  vlib_buffer_t *first_b = vlib_get_buffer (vm, reass->first_bi);
541  ASSERT (total_length >= first_b->current_length);
542  total_length -= first_b->current_length;
543  first_b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
544  first_b->total_length_not_including_first_buffer = total_length;
545  // drop fragment header
546  vnet_buffer_opaque_t *first_b_vnb = vnet_buffer (first_b);
547  ip6_header_t *ip = vlib_buffer_get_current (first_b);
548  u16 ip6_frag_hdr_offset = first_b_vnb->ip.reass.ip6_frag_hdr_offset;
549  ip6_ext_header_t *prev_hdr;
550  ip6_ext_header_find_t (ip, prev_hdr, frag_hdr,
551  IP_PROTOCOL_IPV6_FRAGMENTATION);
552  if (prev_hdr)
553  {
554  prev_hdr->next_hdr = frag_hdr->next_hdr;
555  }
556  else
557  {
558  ip->protocol = frag_hdr->next_hdr;
559  }
560  ASSERT ((u8 *) frag_hdr - (u8 *) ip == ip6_frag_hdr_offset);
561  memmove (frag_hdr, (u8 *) frag_hdr + sizeof (*frag_hdr),
562  first_b->current_length - ip6_frag_hdr_offset -
563  sizeof (ip6_frag_hdr_t));
564  first_b->current_length -= sizeof (*frag_hdr);
565  ip->payload_length =
566  clib_host_to_net_u16 (total_length + first_b->current_length -
567  sizeof (*ip));
568  vlib_buffer_chain_compress (vm, first_b, vec_drop_compress);
569  if (PREDICT_FALSE (first_b->flags & VLIB_BUFFER_IS_TRACED))
570  {
571  ip6_reass_add_trace (vm, node, rm, reass, reass->first_bi, FINALIZE, 0);
572 #if 0
573  // following code does a hexdump of packet fragments to stdout ...
574  do
575  {
576  u32 bi = reass->first_bi;
577  u8 *s = NULL;
578  while (~0 != bi)
579  {
580  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
581  s = format (s, "%u: %U\n", bi, format_hexdump,
583  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
584  {
585  bi = b->next_buffer;
586  }
587  else
588  {
589  break;
590  }
591  }
592  printf ("%.*s\n", vec_len (s), s);
593  fflush (stdout);
594  vec_free (s);
595  }
596  while (0);
597 #endif
598  }
599  if (is_feature)
600  {
601  *next0 = IP6_REASSEMBLY_NEXT_INPUT;
602  }
603  else
604  {
605  *next0 = reass->next_index;
606  }
607  vnet_buffer (first_b)->ip.reass.estimated_mtu = reass->min_fragment_length;
608  ip6_reass_free (rm, rt, reass);
609  reass = NULL;
610 }
611 
614 {
615  u32 len = 0;
616  while (b)
617  {
618  ++len;
619  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_NEXT_PRESENT))
620  {
621  b = vlib_get_buffer (vm, b->next_buffer);
622  }
623  else
624  {
625  break;
626  }
627  }
628  return len;
629 }
630 
631 always_inline void
634  ip6_reass_t * reass, u32 prev_range_bi,
635  u32 new_next_bi)
636 {
637 
638  vlib_buffer_t *new_next_b = vlib_get_buffer (vm, new_next_bi);
639  vnet_buffer_opaque_t *new_next_vnb = vnet_buffer (new_next_b);
640  if (~0 != prev_range_bi)
641  {
642  vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_range_bi);
643  vnet_buffer_opaque_t *prev_vnb = vnet_buffer (prev_b);
644  new_next_vnb->ip.reass.next_range_bi = prev_vnb->ip.reass.next_range_bi;
645  prev_vnb->ip.reass.next_range_bi = new_next_bi;
646  }
647  else
648  {
649  if (~0 != reass->first_bi)
650  {
651  new_next_vnb->ip.reass.next_range_bi = reass->first_bi;
652  }
653  reass->first_bi = new_next_bi;
654  }
655  reass->data_len += ip6_reass_buffer_get_data_len (new_next_b);
656  rt->buffers_n += ip6_reass_get_buffer_chain_length (vm, new_next_b);
657 }
658 
659 always_inline void
662  ip6_reass_t * reass, u32 * bi0, u32 * next0,
663  vlib_error_t * error0, ip6_frag_hdr_t * frag_hdr,
664  u32 ** vec_drop_overlap, u32 ** vec_drop_compress,
665  bool is_feature)
666 {
667  int consumed = 0;
668  vlib_buffer_t *fb = vlib_get_buffer (vm, *bi0);
669  vnet_buffer_opaque_t *fvnb = vnet_buffer (fb);
670  fvnb->ip.reass.ip6_frag_hdr_offset =
671  (u8 *) frag_hdr - (u8 *) vlib_buffer_get_current (fb);
673  ASSERT (fb->current_length > sizeof (*fip));
674  ASSERT (fvnb->ip.reass.ip6_frag_hdr_offset > 0 &&
675  fvnb->ip.reass.ip6_frag_hdr_offset < fb->current_length);
676  u32 fragment_first = fvnb->ip.reass.fragment_first =
677  ip6_frag_hdr_offset_bytes (frag_hdr);
678  u32 fragment_length =
679  vlib_buffer_length_in_chain (vm, fb) -
680  (fvnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
681  u32 fragment_last = fvnb->ip.reass.fragment_last =
682  fragment_first + fragment_length - 1;
683  int more_fragments = ip6_frag_hdr_more (frag_hdr);
684  u32 candidate_range_bi = reass->first_bi;
685  u32 prev_range_bi = ~0;
686  fvnb->ip.reass.range_first = fragment_first;
687  fvnb->ip.reass.range_last = fragment_last;
688  fvnb->ip.reass.next_range_bi = ~0;
689  if (!more_fragments)
690  {
691  reass->last_packet_octet = fragment_last;
692  }
693  if (~0 == reass->first_bi)
694  {
695  // starting a new reassembly
696  ip6_reass_insert_range_in_chain (vm, rm, rt, reass, prev_range_bi,
697  *bi0);
698  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
699  {
700  ip6_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_NEW, 0);
701  }
702  reass->min_fragment_length = clib_net_to_host_u16 (fip->payload_length);
703  *bi0 = ~0;
704  return;
705  }
706  reass->min_fragment_length =
707  clib_min (clib_net_to_host_u16 (fip->payload_length),
708  fvnb->ip.reass.estimated_mtu);
709  while (~0 != candidate_range_bi)
710  {
711  vlib_buffer_t *candidate_b = vlib_get_buffer (vm, candidate_range_bi);
712  vnet_buffer_opaque_t *candidate_vnb = vnet_buffer (candidate_b);
713  if (fragment_first > candidate_vnb->ip.reass.range_last)
714  {
715  // this fragments starts after candidate range
716  prev_range_bi = candidate_range_bi;
717  candidate_range_bi = candidate_vnb->ip.reass.next_range_bi;
718  if (candidate_vnb->ip.reass.range_last < fragment_last &&
719  ~0 == candidate_range_bi)
720  {
721  // special case - this fragment falls beyond all known ranges
722  ip6_reass_insert_range_in_chain (vm, rm, rt, reass,
723  prev_range_bi, *bi0);
724  consumed = 1;
725  break;
726  }
727  continue;
728  }
729  if (fragment_last < candidate_vnb->ip.reass.range_first)
730  {
731  // this fragment ends before candidate range without any overlap
732  ip6_reass_insert_range_in_chain (vm, rm, rt, reass, prev_range_bi,
733  *bi0);
734  consumed = 1;
735  }
736  else if (fragment_first == candidate_vnb->ip.reass.range_first &&
737  fragment_last == candidate_vnb->ip.reass.range_last)
738  {
739  // duplicate fragment - ignore
740  }
741  else
742  {
743  // overlapping fragment - not allowed by RFC 8200
744  ip6_reass_drop_all (vm, rm, reass, vec_drop_overlap);
745  ip6_reass_free (rm, rt, reass);
746  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
747  {
748  ip6_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_OVERLAP,
749  0);
750  }
751  *next0 = IP6_REASSEMBLY_NEXT_DROP;
752  *error0 = IP6_ERROR_REASS_OVERLAPPING_FRAGMENT;
753  }
754  break;
755  }
756  if (consumed)
757  {
758  if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
759  {
760  ip6_reass_add_trace (vm, node, rm, reass, *bi0, RANGE_NEW, 0);
761  }
762  }
763  if (~0 != reass->last_packet_octet &&
764  reass->data_len == reass->last_packet_octet + 1)
765  {
766  ip6_reass_finalize (vm, node, rm, rt, reass, bi0, next0, error0,
767  vec_drop_compress, is_feature);
768  }
769  else
770  {
771  if (consumed)
772  {
773  *bi0 = ~0;
774  }
775  else
776  {
777  *next0 = IP6_REASSEMBLY_NEXT_DROP;
778  ;
779  *error0 = IP6_ERROR_REASS_DUPLICATE_FRAGMENT;
780  }
781  }
782 }
783 
784 always_inline bool
786  vlib_buffer_t * b,
787  ip6_frag_hdr_t * frag_hdr)
788 {
789  ip6_ext_header_t *tmp = (ip6_ext_header_t *) frag_hdr;
790  while (ip6_ext_hdr (tmp->next_hdr))
791  {
792  tmp = ip6_ext_next_header (tmp);
793  }
794  if (IP_PROTOCOL_IP6_NONXT == tmp->next_hdr)
795  {
796  icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
797  ICMP6_parameter_problem_first_fragment_has_incomplete_header_chain,
798  0);
799  b->error = node->errors[IP6_ERROR_REASS_MISSING_UPPER];
800 
801  return false;
802  }
803  return true;
804 }
805 
806 always_inline bool
808  vlib_node_runtime_t * node,
809  vlib_buffer_t * b,
810  ip6_frag_hdr_t * frag_hdr)
811 {
814  int more_fragments = ip6_frag_hdr_more (frag_hdr);
815  u32 fragment_length =
817  (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
818  if (more_fragments && 0 != fragment_length % 8)
819  {
820  icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
821  ICMP6_parameter_problem_erroneous_header_field,
822  (u8 *) & ip->payload_length - (u8 *) ip);
823  return false;
824  }
825  return true;
826 }
827 
828 always_inline bool
830  vlib_node_runtime_t * node,
831  vlib_buffer_t * b,
832  ip6_frag_hdr_t * frag_hdr)
833 {
835  u32 fragment_first = ip6_frag_hdr_offset_bytes (frag_hdr);
836  u32 fragment_length =
838  (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
839  if (fragment_first + fragment_length > 65535)
840  {
842  icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
843  ICMP6_parameter_problem_erroneous_header_field,
844  (u8 *) & frag_hdr->fragment_offset_and_more
845  - (u8 *) ip0);
846  return false;
847  }
848  return true;
849 }
850 
853  vlib_node_runtime_t * node,
854  vlib_frame_t * frame, bool is_feature)
855 {
856  u32 *from = vlib_frame_vector_args (frame);
857  u32 n_left_from, n_left_to_next, *to_next, next_index;
860  clib_spinlock_lock (&rt->lock);
861 
862  n_left_from = frame->n_vectors;
863  next_index = node->cached_next_index;
864  static u32 *vec_timeout = NULL; // indexes of buffers which timed out
865  static u32 *vec_drop_overlap = NULL; // indexes of buffers dropped due to overlap
866  static u32 *vec_drop_compress = NULL; // indexes of buffers dropped due to buffer compression
867  while (n_left_from > 0 || vec_len (vec_timeout) > 0 ||
868  vec_len (vec_drop_overlap) > 0 || vec_len (vec_drop_compress) > 0)
869  {
870  vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
871 
872  while (vec_len (vec_timeout) > 0 && n_left_to_next > 0)
873  {
874  u32 bi = vec_pop (vec_timeout);
875  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
876  b->error = node->errors[IP6_ERROR_REASS_TIMEOUT];
877  to_next[0] = bi;
878  to_next += 1;
879  n_left_to_next -= 1;
880  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
881  n_left_to_next, bi,
883  ASSERT (rt->buffers_n > 0);
884  --rt->buffers_n;
885  }
886 
887  while (vec_len (vec_drop_overlap) > 0 && n_left_to_next > 0)
888  {
889  u32 bi = vec_pop (vec_drop_overlap);
890  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
891  b->error = node->errors[IP6_ERROR_REASS_OVERLAPPING_FRAGMENT];
892  to_next[0] = bi;
893  to_next += 1;
894  n_left_to_next -= 1;
895  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
896  n_left_to_next, bi,
898  ASSERT (rt->buffers_n > 0);
899  --rt->buffers_n;
900  }
901 
902  while (vec_len (vec_drop_compress) > 0 && n_left_to_next > 0)
903  {
904  u32 bi = vec_pop (vec_drop_compress);
905  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
906  b->error = node->errors[IP6_ERROR_NONE];
907  to_next[0] = bi;
908  to_next += 1;
909  n_left_to_next -= 1;
910  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
911  n_left_to_next, bi,
913  ASSERT (rt->buffers_n > 0);
914  --rt->buffers_n;
915  }
916 
917  while (n_left_from > 0 && n_left_to_next > 0)
918  {
919  u32 bi0;
920  vlib_buffer_t *b0;
921  u32 next0;
922  u32 error0 = IP6_ERROR_NONE;
923  u32 icmp_bi = ~0;
924 
925  bi0 = from[0];
926  b0 = vlib_get_buffer (vm, bi0);
927 
929  ip6_frag_hdr_t *frag_hdr = NULL;
930  ip6_ext_header_t *prev_hdr;
931  if (ip6_ext_hdr (ip0->protocol))
932  {
933  ip6_ext_header_find_t (ip0, prev_hdr, frag_hdr,
934  IP_PROTOCOL_IPV6_FRAGMENTATION);
935  }
936  if (!frag_hdr)
937  {
938  // this is a regular packet - no fragmentation
940  goto skip_reass;
941  }
942  if (0 == ip6_frag_hdr_offset (frag_hdr))
943  {
944  // first fragment - verify upper-layer is present
945  if (!ip6_reass_verify_upper_layer_present (node, b0, frag_hdr))
946  {
948  goto skip_reass;
949  }
950  }
951  if (!ip6_reass_verify_fragment_multiple_8 (vm, node, b0, frag_hdr)
952  || !ip6_reass_verify_packet_size_lt_64k (vm, node, b0,
953  frag_hdr))
954  {
956  goto skip_reass;
957  }
958  vnet_buffer (b0)->ip.reass.ip6_frag_hdr_offset =
959  (u8 *) frag_hdr - (u8 *) ip0;
960 
961  ip6_reass_key_t k;
962  k.src.as_u64[0] = ip0->src_address.as_u64[0];
963  k.src.as_u64[1] = ip0->src_address.as_u64[1];
964  k.dst.as_u64[0] = ip0->dst_address.as_u64[0];
965  k.dst.as_u64[1] = ip0->dst_address.as_u64[1];
966  k.xx_id = vnet_buffer (b0)->sw_if_index[VLIB_RX];
967  k.frag_id = frag_hdr->identification;
968  k.proto = ip0->protocol;
969  k.unused = 0;
970  ip6_reass_t *reass =
971  ip6_reass_find_or_create (vm, node, rm, rt, &k, &icmp_bi,
972  &vec_timeout);
973 
974  if (reass)
975  {
976  ip6_reass_update (vm, node, rm, rt, reass, &bi0, &next0,
977  &error0, frag_hdr, &vec_drop_overlap,
978  &vec_drop_compress, is_feature);
979  }
980  else
981  {
982  next0 = IP6_REASSEMBLY_NEXT_DROP;
983  error0 = IP6_ERROR_REASS_LIMIT_REACHED;
984  }
985 
986  b0->error = node->errors[error0];
987 
988  if (~0 != bi0)
989  {
990  skip_reass:
991  to_next[0] = bi0;
992  to_next += 1;
993  n_left_to_next -= 1;
994  if (is_feature && IP6_ERROR_NONE == error0)
995  {
996  vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX],
997  &next0, b0);
998  }
999  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1000  n_left_to_next, bi0, next0);
1001  }
1002 
1003  if (~0 != icmp_bi)
1004  {
1006  to_next[0] = icmp_bi;
1007  to_next += 1;
1008  n_left_to_next -= 1;
1009  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1010  n_left_to_next, icmp_bi,
1011  next0);
1012  }
1013  from += 1;
1014  n_left_from -= 1;
1015  }
1016 
1017  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1018  }
1019 
1020  clib_spinlock_unlock (&rt->lock);
1021  return frame->n_vectors;
1022 }
1023 
1025 #define _(sym, string) string,
1027 #undef _
1028 };
1029 
1030 static uword
1032  vlib_frame_t * frame)
1033 {
1034  return ip6_reassembly_inline (vm, node, frame, false /* is_feature */ );
1035 }
1036 
1037 /* *INDENT-OFF* */
1038 VLIB_REGISTER_NODE (ip6_reass_node, static) = {
1039  .function = ip6_reassembly,
1040  .name = "ip6-reassembly",
1041  .vector_size = sizeof (u32),
1042  .format_trace = format_ip6_reass_trace,
1043  .n_errors = ARRAY_LEN (ip6_reassembly_error_strings),
1044  .error_strings = ip6_reassembly_error_strings,
1045  .n_next_nodes = IP6_REASSEMBLY_N_NEXT,
1046  .next_nodes =
1047  {
1048  [IP6_REASSEMBLY_NEXT_INPUT] = "ip6-input",
1049  [IP6_REASSEMBLY_NEXT_DROP] = "ip6-drop",
1050  [IP6_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
1051  },
1052 };
1053 /* *INDENT-ON* */
1054 
1056 
1057 static uword
1059  vlib_node_runtime_t * node, vlib_frame_t * frame)
1060 {
1061  return ip6_reassembly_inline (vm, node, frame, true /* is_feature */ );
1062 }
1063 
1064 /* *INDENT-OFF* */
1066  .function = ip6_reassembly_feature,
1067  .name = "ip6-reassembly-feature",
1068  .vector_size = sizeof (u32),
1069  .format_trace = format_ip6_reass_trace,
1070  .n_errors = ARRAY_LEN (ip6_reassembly_error_strings),
1071  .error_strings = ip6_reassembly_error_strings,
1072  .n_next_nodes = IP6_REASSEMBLY_N_NEXT,
1073  .next_nodes =
1074  {
1075  [IP6_REASSEMBLY_NEXT_INPUT] = "ip6-input",
1076  [IP6_REASSEMBLY_NEXT_DROP] = "ip6-drop",
1077  [IP6_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
1078  },
1079 };
1080 /* *INDENT-ON* */
1081 
1083 
1084 /* *INDENT-OFF* */
1086  .arc_name = "ip6-unicast",
1087  .node_name = "ip6-reassembly-feature",
1088  .runs_before = VNET_FEATURES ("ip6-lookup"),
1089  .runs_after = 0,
1090 };
1091 /* *INDENT-ON* */
1092 
1093 static u32
1095 {
1097  u32 nbuckets;
1098  u8 i;
1099 
1100  nbuckets = (u32) (rm->max_reass_n / IP6_REASS_HT_LOAD_FACTOR);
1101 
1102  for (i = 0; i < 31; i++)
1103  if ((1 << i) >= nbuckets)
1104  break;
1105  nbuckets = 1 << i;
1106 
1107  return nbuckets;
1108 }
1109 
1110 typedef enum
1111 {
1114 
1115 typedef struct
1116 {
1117  int failure;
1118  clib_bihash_48_8_t *new_hash;
1120 
1121 static void
1123 {
1124  ip6_rehash_cb_ctx *ctx = _ctx;
1125  if (clib_bihash_add_del_48_8 (ctx->new_hash, kv, 1))
1126  {
1127  ctx->failure = 1;
1128  }
1129 }
1130 
1131 static void
1132 ip6_reass_set_params (u32 timeout_ms, u32 max_reassemblies,
1133  u32 expire_walk_interval_ms)
1134 {
1135  ip6_reass_main.timeout_ms = timeout_ms;
1136  ip6_reass_main.timeout = (f64) timeout_ms / (f64) MSEC_PER_SEC;
1137  ip6_reass_main.max_reass_n = max_reassemblies;
1138  ip6_reass_main.expire_walk_interval_ms = expire_walk_interval_ms;
1139 }
1140 
1142 ip6_reass_set (u32 timeout_ms, u32 max_reassemblies,
1143  u32 expire_walk_interval_ms)
1144 {
1145  u32 old_nbuckets = ip6_reass_get_nbuckets ();
1146  ip6_reass_set_params (timeout_ms, max_reassemblies,
1147  expire_walk_interval_ms);
1148  vlib_process_signal_event (ip6_reass_main.vlib_main,
1149  ip6_reass_main.ip6_reass_expire_node_idx,
1151  u32 new_nbuckets = ip6_reass_get_nbuckets ();
1152  if (ip6_reass_main.max_reass_n > 0 && new_nbuckets > old_nbuckets)
1153  {
1154  clib_bihash_48_8_t new_hash;
1155  memset (&new_hash, 0, sizeof (new_hash));
1157  ctx.failure = 0;
1158  ctx.new_hash = &new_hash;
1159  clib_bihash_init_48_8 (&new_hash, "ip6-reass", new_nbuckets,
1160  new_nbuckets * 1024);
1161  clib_bihash_foreach_key_value_pair_48_8 (&ip6_reass_main.hash,
1162  ip6_rehash_cb, &ctx);
1163  if (ctx.failure)
1164  {
1165  clib_bihash_free_48_8 (&new_hash);
1166  return -1;
1167  }
1168  else
1169  {
1170  clib_bihash_free_48_8 (&ip6_reass_main.hash);
1171  clib_memcpy (&ip6_reass_main.hash, &new_hash,
1172  sizeof (ip6_reass_main.hash));
1173  }
1174  }
1175  return 0;
1176 }
1177 
1179 ip6_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
1180  u32 * expire_walk_interval_ms)
1181 {
1182  *timeout_ms = ip6_reass_main.timeout_ms;
1183  *max_reassemblies = ip6_reass_main.max_reass_n;
1184  *expire_walk_interval_ms = ip6_reass_main.expire_walk_interval_ms;
1185  return 0;
1186 }
1187 
1188 static clib_error_t *
1190 {
1192  clib_error_t *error = 0;
1193  u32 nbuckets;
1194  vlib_node_t *node;
1195 
1196  rm->vlib_main = vm;
1197  rm->vnet_main = vnet_get_main ();
1198 
1201  vec_foreach (rt, rm->per_thread_data)
1202  {
1203  clib_spinlock_init (&rt->lock);
1204  pool_alloc (rt->pool, rm->max_reass_n);
1205  }
1206 
1207  node = vlib_get_node_by_name (vm, (u8 *) "ip6-reassembly-expire-walk");
1208  ASSERT (node);
1209  rm->ip6_reass_expire_node_idx = node->index;
1210 
1214 
1215  nbuckets = ip6_reass_get_nbuckets ();
1216  clib_bihash_init_48_8 (&rm->hash, "ip6-reass", nbuckets, nbuckets * 1024);
1217 
1218  node = vlib_get_node_by_name (vm, (u8 *) "ip6-drop");
1219  ASSERT (node);
1220  rm->ip6_drop_idx = node->index;
1221  node = vlib_get_node_by_name (vm, (u8 *) "ip6-icmp-error");
1222  ASSERT (node);
1223  rm->ip6_icmp_error_idx = node->index;
1224 
1225  if ((error = vlib_call_init_function (vm, ip_main_init)))
1226  return error;
1227  ip6_register_protocol (IP_PROTOCOL_IPV6_FRAGMENTATION,
1228  ip6_reass_node.index);
1229 
1230  return error;
1231 }
1232 
1234 
1235 static uword
1237  vlib_node_runtime_t * node, vlib_frame_t * f)
1238 {
1240  uword event_type, *event_data = 0;
1241 
1242  while (true)
1243  {
1246  / (f64) MSEC_PER_SEC);
1247  event_type = vlib_process_get_events (vm, &event_data);
1248 
1249  switch (event_type)
1250  {
1251  case ~0: /* no events => timeout */
1252  /* nothing to do here */
1253  break;
1255  break;
1256  default:
1257  clib_warning ("BUG: event type 0x%wx", event_type);
1258  break;
1259  }
1260  f64 now = vlib_time_now (vm);
1261 
1262  ip6_reass_t *reass;
1263  u32 *vec_timeout = NULL;
1264  int *pool_indexes_to_free = NULL;
1265 
1266  uword thread_index = 0;
1267  int index;
1268  const uword nthreads = os_get_nthreads ();
1269  u32 *vec_icmp_bi = NULL;
1270  for (thread_index = 0; thread_index < nthreads; ++thread_index)
1271  {
1272  ip6_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
1273  clib_spinlock_lock (&rt->lock);
1274 
1275  vec_reset_length (pool_indexes_to_free);
1276  /* *INDENT-OFF* */
1277  pool_foreach_index (index, rt->pool, ({
1278  reass = pool_elt_at_index (rt->pool, index);
1279  if (now > reass->last_heard + rm->timeout)
1280  {
1281  vec_add1 (pool_indexes_to_free, index);
1282  }
1283  }));
1284  /* *INDENT-ON* */
1285  int *i;
1286  /* *INDENT-OFF* */
1287  vec_foreach (i, pool_indexes_to_free)
1288  {
1289  ip6_reass_t *reass = pool_elt_at_index (rt->pool, i[0]);
1290  u32 icmp_bi = ~0;
1291  u32 before = vec_len (vec_timeout);
1292  vlib_buffer_t *b = vlib_get_buffer (vm, reass->first_bi);
1293  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
1294  {
1296  b->trace_index))
1297  {
1298  /* the trace is gone, don't trace this buffer anymore */
1299  b->flags &= ~VLIB_BUFFER_IS_TRACED;
1300  }
1301  }
1302  ip6_reass_on_timeout (vm, node, rm, reass, &icmp_bi, &vec_timeout);
1303  u32 after = vec_len (vec_timeout);
1304  ASSERT (rt->buffers_n >= (after - before));
1305  rt->buffers_n -= (after - before);
1306  if (~0 != icmp_bi)
1307  {
1308  vec_add1 (vec_icmp_bi, icmp_bi);
1309  ASSERT (rt->buffers_n > 0);
1310  --rt->buffers_n;
1311  }
1312  ip6_reass_free (rm, rt, reass);
1313  }
1314  /* *INDENT-ON* */
1315 
1316  clib_spinlock_unlock (&rt->lock);
1317  }
1318 
1319  while (vec_len (vec_timeout) > 0)
1320  {
1322  u32 *to_next = vlib_frame_vector_args (f);
1323  u32 n_left_to_next = VLIB_FRAME_SIZE - f->n_vectors;
1324  int trace_frame = 0;
1325  while (vec_len (vec_timeout) > 0 && n_left_to_next > 0)
1326  {
1327  u32 bi = vec_pop (vec_timeout);
1328  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1329  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
1330  {
1332  b->trace_index))
1333  {
1334  /* the trace is gone, don't trace this buffer anymore */
1335  b->flags &= ~VLIB_BUFFER_IS_TRACED;
1336  }
1337  else
1338  {
1339  trace_frame = 1;
1340  }
1341  }
1342  b->error = node->errors[IP6_ERROR_REASS_TIMEOUT];
1343  to_next[0] = bi;
1344  ++f->n_vectors;
1345  to_next += 1;
1346  n_left_to_next -= 1;
1347  }
1348  f->flags |= (trace_frame * VLIB_FRAME_TRACE);
1349  vlib_put_frame_to_node (vm, rm->ip6_drop_idx, f);
1350  }
1351 
1352  while (vec_len (vec_icmp_bi) > 0)
1353  {
1354  vlib_frame_t *f =
1356  u32 *to_next = vlib_frame_vector_args (f);
1357  u32 n_left_to_next = VLIB_FRAME_SIZE - f->n_vectors;
1358  int trace_frame = 0;
1359  while (vec_len (vec_icmp_bi) > 0 && n_left_to_next > 0)
1360  {
1361  u32 bi = vec_pop (vec_icmp_bi);
1362  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1363  if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
1364  {
1366  b->trace_index))
1367  {
1368  /* the trace is gone, don't trace this buffer anymore */
1369  b->flags &= ~VLIB_BUFFER_IS_TRACED;
1370  }
1371  else
1372  {
1373  trace_frame = 1;
1374  }
1375  }
1376  b->error = node->errors[IP6_ERROR_REASS_TIMEOUT];
1377  to_next[0] = bi;
1378  ++f->n_vectors;
1379  to_next += 1;
1380  n_left_to_next -= 1;
1381  }
1382  f->flags |= (trace_frame * VLIB_FRAME_TRACE);
1384  }
1385 
1386  vec_free (pool_indexes_to_free);
1387  vec_free (vec_timeout);
1388  vec_free (vec_icmp_bi);
1389  if (event_data)
1390  {
1391  _vec_len (event_data) = 0;
1392  }
1393  }
1394 
1395  return 0;
1396 }
1397 
1399 
1400 /* *INDENT-OFF* */
1402  .function = ip6_reass_walk_expired,
1403  .format_trace = format_ip6_reass_trace,
1404  .type = VLIB_NODE_TYPE_PROCESS,
1405  .name = "ip6-reassembly-expire-walk",
1406 
1408  .error_strings = ip6_reassembly_error_strings,
1409 
1410 };
1411 /* *INDENT-ON* */
1412 
1413 static u8 *
1414 format_ip6_reass_key (u8 * s, va_list * args)
1415 {
1416  ip6_reass_key_t *key = va_arg (*args, ip6_reass_key_t *);
1417  s = format (s, "xx_id: %u, src: %U, dst: %U, frag_id: %u, proto: %u",
1419  &key->dst, clib_net_to_host_u16 (key->frag_id), key->proto);
1420  return s;
1421 }
1422 
1423 static u8 *
1424 format_ip6_reass (u8 * s, va_list * args)
1425 {
1426  vlib_main_t *vm = va_arg (*args, vlib_main_t *);
1427  ip6_reass_t *reass = va_arg (*args, ip6_reass_t *);
1428 
1429  s = format (s, "ID: %lu, key: %U\n first_bi: %u, data_len: %u, "
1430  "last_packet_octet: %u, trace_op_counter: %u\n",
1431  reass->id, format_ip6_reass_key, &reass->key, reass->first_bi,
1432  reass->data_len, reass->last_packet_octet,
1433  reass->trace_op_counter);
1434  u32 bi = reass->first_bi;
1435  u32 counter = 0;
1436  while (~0 != bi)
1437  {
1438  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
1439  vnet_buffer_opaque_t *vnb = vnet_buffer (b);
1440  s = format (s, " #%03u: range: [%u, %u], bi: %u, off: %d, len: %u, "
1441  "fragment[%u, %u]\n",
1442  counter, vnb->ip.reass.range_first,
1443  vnb->ip.reass.range_last, bi,
1446  vnb->ip.reass.fragment_first, vnb->ip.reass.fragment_last);
1447  if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
1448  {
1449  bi = b->next_buffer;
1450  }
1451  else
1452  {
1453  bi = ~0;
1454  }
1455  }
1456  return s;
1457 }
1458 
1459 static clib_error_t *
1462 {
1464 
1465  vlib_cli_output (vm, "---------------------");
1466  vlib_cli_output (vm, "IP6 reassembly status");
1467  vlib_cli_output (vm, "---------------------");
1468  bool details = false;
1469  if (unformat (input, "details"))
1470  {
1471  details = true;
1472  }
1473 
1474  u32 sum_reass_n = 0;
1475  u64 sum_buffers_n = 0;
1476  ip6_reass_t *reass;
1477  uword thread_index;
1478  const uword nthreads = os_get_nthreads ();
1479  for (thread_index = 0; thread_index < nthreads; ++thread_index)
1480  {
1481  ip6_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
1482  clib_spinlock_lock (&rt->lock);
1483  if (details)
1484  {
1485  /* *INDENT-OFF* */
1486  pool_foreach (reass, rt->pool, {
1487  vlib_cli_output (vm, "%U", format_ip6_reass, vm, reass);
1488  });
1489  /* *INDENT-ON* */
1490  }
1491  sum_reass_n += rt->reass_n;
1492  sum_buffers_n += rt->buffers_n;
1493  clib_spinlock_unlock (&rt->lock);
1494  }
1495  vlib_cli_output (vm, "---------------------");
1496  vlib_cli_output (vm, "Current IP6 reassemblies count: %lu\n",
1497  (long unsigned) sum_reass_n);
1498  vlib_cli_output (vm, "Maximum configured concurrent IP6 reassemblies per "
1499  "worker-thread: %lu\n", (long unsigned) rm->max_reass_n);
1500  vlib_cli_output (vm, "Buffers in use: %lu\n",
1501  (long unsigned) sum_buffers_n);
1502  return 0;
1503 }
1504 
1505 /* *INDENT-OFF* */
1507  .path = "show ip6-reassembly",
1508  .short_help = "show ip6-reassembly [details]",
1509  .function = show_ip6_reass,
1510 };
1511 /* *INDENT-ON* */
1512 
1514 ip6_reass_enable_disable (u32 sw_if_index, u8 enable_disable)
1515 {
1516  return vnet_feature_enable_disable ("ip6-unicast", "ip6-reassembly-feature",
1517  sw_if_index, enable_disable, 0, 0);
1518 }
1519 
1520 /*
1521  * fd.io coding-style-patch-verification: ON
1522  *
1523  * Local Variables:
1524  * eval: (c-set-style "gnu")
1525  * End:
1526  */
#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:434
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:340
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:1392
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
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, vlib_error_t *error0, u32 **vec_drop_compress, bool is_feature)
#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
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:237
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:227
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:520
ip6_reass_trace_operation_e action
static void * ip6_ext_next_header(ip6_ext_header_t *ext_hdr)
Definition: ip6_packet.h:489
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:415
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:107
#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:342
static void ip6_reass_trace_details(vlib_main_t *vm, u32 bi, ip6_reass_range_trace_t *trace)
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:614
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
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:111
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
int i32
Definition: types.h:81
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
unsigned long u64
Definition: types.h:89
static u8 * format_ip6_reass_range_trace(u8 *s, va_list *args)
#define vlib_call_init_function(vm, x)
Definition: init.h:162
#define VLIB_FRAME_SIZE
Definition: node.h:328
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:543
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
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:209
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:273
#define PREDICT_FALSE(x)
Definition: clib.h:105
u32 vlib_error_t
Definition: error.h:44
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:221
#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:130
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:143
clib_error_t * ip_main_init(vlib_main_t *vm)
Definition: ip_init.c:45
u16 n_vectors
Definition: node.h:344
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:95
vlib_main_t * vm
Definition: buffer.c:294
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
#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:540
#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:473
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
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:537
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:456
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)
unsigned int u32
Definition: types.h:88
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:335
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:222
vlib_trace_main_t trace_main
Definition: main.h:138
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, vlib_error_t *error0, ip6_frag_hdr_t *frag_hdr, u32 **vec_drop_overlap, u32 **vec_drop_compress, bool is_feature)
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:375
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)
u64 uword
Definition: types.h:112
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:159
struct _vlib_node_registration vlib_node_registration_t
unsigned short u16
Definition: types.h:57
u16 payload_length
Definition: ip6_packet.h:333
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)
double f64
Definition: types.h:142
static uword ip6_reass_walk_expired(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *f)
unsigned char u8
Definition: types.h:56
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:379
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:372
clib_bihash_48_8_t * new_hash
static u32 vlib_num_workers()
Definition: threads.h:368
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:498
static void ip6_reass_set_params(u32 timeout_ms, u32 max_reassemblies, u32 expire_walk_interval_ms)
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:482
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:680
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:154
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
Definition: defs.h:46
struct vnet_buffer_opaque_t::@56::@58 ip
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:342