LCOV - code coverage report
Current view: top level - vnet/ip/reass - ip6_full_reass.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 744 912 81.6 %
Date: 2023-10-26 01:39:38 Functions: 90 123 73.2 %

          Line data    Source code
       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 Full Reassembly.
      19             :  *
      20             :  * This file contains the source code for IPv6 full 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/reass/ip6_full_reass.h>
      28             : #include <vnet/ip/ip6_inlines.h>
      29             : 
      30             : #define MSEC_PER_SEC 1000
      31             : #define IP6_FULL_REASS_TIMEOUT_DEFAULT_MS 200
      32             : /* As there are only 1024 reass context per thread, either the DDOS attacks
      33             :  * or fractions of real timeouts, would consume these contexts quickly and
      34             :  * running out context space and unable to perform reassembly */
      35             : #define IP6_FULL_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS 50 // 50 ms default
      36             : #define IP6_FULL_REASS_MAX_REASSEMBLIES_DEFAULT 1024
      37             : #define IP6_FULL_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT 3
      38             : #define IP6_FULL_REASS_HT_LOAD_FACTOR (0.75)
      39             : 
      40             : typedef enum
      41             : {
      42             :   IP6_FULL_REASS_RC_OK,
      43             :   IP6_FULL_REASS_RC_INTERNAL_ERROR,
      44             :   IP6_FULL_REASS_RC_TOO_MANY_FRAGMENTS,
      45             :   IP6_FULL_REASS_RC_NO_BUF,
      46             :   IP6_FULL_REASS_RC_HANDOFF,
      47             :   IP6_FULL_REASS_RC_INVALID_FRAG_LEN,
      48             :   IP6_FULL_REASS_RC_OVERLAP,
      49             : } ip6_full_reass_rc_t;
      50             : 
      51             : typedef struct
      52             : {
      53             :   union
      54             :   {
      55             :     struct
      56             :     {
      57             :       ip6_address_t src;
      58             :       ip6_address_t dst;
      59             :       u32 xx_id;
      60             :       u32 frag_id;
      61             :       u8 unused[7];
      62             :       u8 proto;
      63             :     };
      64             :     u64 as_u64[6];
      65             :   };
      66             : } ip6_full_reass_key_t;
      67             : 
      68             : typedef union
      69             : {
      70             :   struct
      71             :   {
      72             :     u32 reass_index;
      73             :     u32 memory_owner_thread_index;
      74             :   };
      75             :   u64 as_u64;
      76             : } ip6_full_reass_val_t;
      77             : 
      78             : typedef union
      79             : {
      80             :   struct
      81             :   {
      82             :     ip6_full_reass_key_t k;
      83             :     ip6_full_reass_val_t v;
      84             :   };
      85             :   clib_bihash_kv_48_8_t kv;
      86             : } ip6_full_reass_kv_t;
      87             : 
      88             : 
      89             : always_inline u32
      90       44480 : ip6_full_reass_buffer_get_data_offset (vlib_buffer_t * b)
      91             : {
      92       44480 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
      93       44480 :   return vnb->ip.reass.range_first - vnb->ip.reass.fragment_first;
      94             : }
      95             : 
      96             : always_inline u16
      97       25770 : ip6_full_reass_buffer_get_data_len (vlib_buffer_t * b)
      98             : {
      99       25770 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
     100       51544 :   return clib_min (vnb->ip.reass.range_last, vnb->ip.reass.fragment_last) -
     101       51544 :     (vnb->ip.reass.fragment_first +
     102       25770 :      ip6_full_reass_buffer_get_data_offset (b)) + 1;
     103             : }
     104             : 
     105             : typedef struct
     106             : {
     107             :   // hash table key
     108             :   ip6_full_reass_key_t key;
     109             :   // time when last packet was received
     110             :   f64 last_heard;
     111             :   // internal id of this reassembly
     112             :   u64 id;
     113             :   // buffer index of first buffer in this reassembly context
     114             :   u32 first_bi;
     115             :   // last octet of packet, ~0 until fragment without more_fragments arrives
     116             :   u32 last_packet_octet;
     117             :   // length of data collected so far
     118             :   u32 data_len;
     119             :   // trace operation counter
     120             :   u32 trace_op_counter;
     121             :   // next index - used by custom apps (~0 if not set)
     122             :   u32 next_index;
     123             :   // error next index - used by custom apps (~0 if not set)
     124             :   u32 error_next_index;
     125             :   // minimum fragment length for this reassembly - used to estimate MTU
     126             :   u16 min_fragment_length;
     127             :   // number of fragments for this reassembly
     128             :   u32 fragments_n;
     129             :   // thread owning memory for this context (whose pool contains this ctx)
     130             :   u32 memory_owner_thread_index;
     131             :   // thread which received fragment with offset 0 and which sends out the
     132             :   // completed reassembly
     133             :   u32 sendout_thread_index;
     134             : } ip6_full_reass_t;
     135             : 
     136             : typedef struct
     137             : {
     138             :   ip6_full_reass_t *pool;
     139             :   u32 reass_n;
     140             :   u32 id_counter;
     141             :   // for pacing the main thread timeouts
     142             :   u32 last_id;
     143             :   clib_spinlock_t lock;
     144             : } ip6_full_reass_per_thread_t;
     145             : 
     146             : typedef struct
     147             : {
     148             :   // IPv6 config
     149             :   u32 timeout_ms;
     150             :   f64 timeout;
     151             :   u32 expire_walk_interval_ms;
     152             :   // maximum number of fragments in one reassembly
     153             :   u32 max_reass_len;
     154             :   // maximum number of reassemblies
     155             :   u32 max_reass_n;
     156             : 
     157             :   // IPv6 runtime
     158             :   clib_bihash_48_8_t hash;
     159             : 
     160             :   // per-thread data
     161             :   ip6_full_reass_per_thread_t *per_thread_data;
     162             : 
     163             :   // convenience
     164             :   vlib_main_t *vlib_main;
     165             : 
     166             :   u32 ip6_icmp_error_idx;
     167             :   u32 ip6_full_reass_expire_node_idx;
     168             : 
     169             :   /** Worker handoff */
     170             :   u32 fq_index;
     171             :   u32 fq_local_index;
     172             :   u32 fq_feature_index;
     173             :   u32 fq_custom_index;
     174             : 
     175             :   // reference count for enabling/disabling feature - per interface
     176             :   u32 *feature_use_refcount_per_intf;
     177             : 
     178             :   // whether local fragmented packets are reassembled or not
     179             :   int is_local_reass_enabled;
     180             : } ip6_full_reass_main_t;
     181             : 
     182             : extern ip6_full_reass_main_t ip6_full_reass_main;
     183             : 
     184             : #ifndef CLIB_MARCH_VARIANT
     185             : ip6_full_reass_main_t ip6_full_reass_main;
     186             : #endif /* CLIB_MARCH_VARIANT */
     187             : 
     188             : typedef enum
     189             : {
     190             :   IP6_FULL_REASSEMBLY_NEXT_INPUT,
     191             :   IP6_FULL_REASSEMBLY_NEXT_DROP,
     192             :   IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR,
     193             :   IP6_FULL_REASSEMBLY_NEXT_HANDOFF,
     194             :   IP6_FULL_REASSEMBLY_N_NEXT,
     195             : } ip6_full_reass_next_t;
     196             : 
     197             : typedef enum
     198             : {
     199             :   NORMAL,
     200             :   FEATURE,
     201             :   CUSTOM
     202             : } ip6_full_reass_node_type_t;
     203             : 
     204             : typedef enum
     205             : {
     206             :   RANGE_NEW,
     207             :   RANGE_DISCARD,
     208             :   RANGE_OVERLAP,
     209             :   ICMP_ERROR_RT_EXCEEDED,
     210             :   ICMP_ERROR_FL_TOO_BIG,
     211             :   ICMP_ERROR_FL_NOT_MULT_8,
     212             :   FINALIZE,
     213             :   HANDOFF,
     214             :   PASSTHROUGH,
     215             : } ip6_full_reass_trace_operation_e;
     216             : 
     217             : typedef struct
     218             : {
     219             :   u16 range_first;
     220             :   u16 range_last;
     221             :   u32 range_bi;
     222             :   i32 data_offset;
     223             :   u32 data_len;
     224             :   u32 first_bi;
     225             : } ip6_full_reass_range_trace_t;
     226             : 
     227             : typedef struct
     228             : {
     229             :   ip6_full_reass_trace_operation_e action;
     230             :   u32 reass_id;
     231             :   ip6_full_reass_range_trace_t trace_range;
     232             :   u32 op_id;
     233             :   u32 fragment_first;
     234             :   u32 fragment_last;
     235             :   u32 total_data_len;
     236             :   u32 thread_id;
     237             :   u32 thread_id_to;
     238             :   bool is_after_handoff;
     239             :   ip6_header_t ip6_header;
     240             :   ip6_frag_hdr_t ip6_frag_header;
     241             : } ip6_full_reass_trace_t;
     242             : 
     243             : static void
     244        9719 : ip6_full_reass_trace_details (vlib_main_t * vm, u32 bi,
     245             :                               ip6_full_reass_range_trace_t * trace)
     246             : {
     247        9719 :   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     248        9718 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
     249        9718 :   trace->range_first = vnb->ip.reass.range_first;
     250        9718 :   trace->range_last = vnb->ip.reass.range_last;
     251        9718 :   trace->data_offset = ip6_full_reass_buffer_get_data_offset (b);
     252        9718 :   trace->data_len = ip6_full_reass_buffer_get_data_len (b);
     253        9718 :   trace->range_bi = bi;
     254        9718 : }
     255             : 
     256             : static u8 *
     257        3823 : format_ip6_full_reass_range_trace (u8 * s, va_list * args)
     258             : {
     259        3823 :   ip6_full_reass_range_trace_t *trace =
     260             :     va_arg (*args, ip6_full_reass_range_trace_t *);
     261             :   s =
     262        3823 :     format (s, "range: [%u, %u], off %d, len %u, bi %u", trace->range_first,
     263        3823 :             trace->range_last, trace->data_offset, trace->data_len,
     264             :             trace->range_bi);
     265        3823 :   return s;
     266             : }
     267             : 
     268             : static u8 *
     269        5826 : format_ip6_full_reass_trace (u8 * s, va_list * args)
     270             : {
     271        5826 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     272        5826 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     273        5826 :   ip6_full_reass_trace_t *t = va_arg (*args, ip6_full_reass_trace_t *);
     274        5826 :   u32 indent = 0;
     275        5826 :   if (~0 != t->reass_id)
     276             :     {
     277        4276 :       if (t->is_after_handoff)
     278             :         {
     279             :           s =
     280         159 :             format (s, "%U\n", format_ip6_header, &t->ip6_header,
     281             :                     sizeof (t->ip6_header));
     282             :           s =
     283         159 :             format (s, "  %U\n", format_ip6_frag_hdr, &t->ip6_frag_header,
     284             :                     sizeof (t->ip6_frag_header));
     285         159 :           indent = 2;
     286             :         }
     287             :       s =
     288        4276 :         format (s, "%Ureass id: %u, op id: %u, ", format_white_space, indent,
     289             :                 t->reass_id, t->op_id);
     290        4276 :       indent = format_get_indent (s);
     291        4276 :       s = format (s, "first bi: %u, data len: %u, ip/fragment[%u, %u]",
     292             :                   t->trace_range.first_bi, t->total_data_len,
     293             :                   t->fragment_first, t->fragment_last);
     294             :     }
     295        5826 :   switch (t->action)
     296             :     {
     297        3612 :     case RANGE_NEW:
     298        3612 :       s = format (s, "\n%Unew %U", format_white_space, indent,
     299             :                   format_ip6_full_reass_range_trace, &t->trace_range);
     300        3612 :       break;
     301           0 :     case RANGE_DISCARD:
     302           0 :       s = format (s, "\n%Udiscard %U", format_white_space, indent,
     303             :                   format_ip6_full_reass_range_trace, &t->trace_range);
     304           0 :       break;
     305         211 :     case RANGE_OVERLAP:
     306         211 :       s = format (s, "\n%Uoverlap %U", format_white_space, indent,
     307             :                   format_ip6_full_reass_range_trace, &t->trace_range);
     308         211 :       break;
     309           0 :     case ICMP_ERROR_FL_TOO_BIG:
     310           0 :       s = format (s, "\n%Uicmp-error - frag_len > 65535 %U",
     311             :                   format_white_space, indent,
     312             :                   format_ip6_full_reass_range_trace, &t->trace_range);
     313           0 :       break;
     314           0 :     case ICMP_ERROR_FL_NOT_MULT_8:
     315           0 :       s = format (s, "\n%Uicmp-error - frag_len mod 8 != 0 %U",
     316             :                   format_white_space, indent,
     317             :                   format_ip6_full_reass_range_trace, &t->trace_range);
     318           0 :       break;
     319          25 :     case ICMP_ERROR_RT_EXCEEDED:
     320          25 :       s = format (s, "\n%Uicmp-error - reassembly time exceeded",
     321             :                   format_white_space, indent);
     322          25 :       break;
     323         428 :     case FINALIZE:
     324         428 :       s = format (s, "\n%Ufinalize reassembly", format_white_space, indent);
     325         428 :       break;
     326         289 :     case HANDOFF:
     327             :       s =
     328         289 :         format (s, "handoff from thread #%u to thread #%u", t->thread_id,
     329             :                 t->thread_id_to);
     330         289 :       break;
     331        1261 :     case PASSTHROUGH:
     332        1261 :       s = format (s, "passthrough - not a fragment");
     333        1261 :       break;
     334             :     }
     335        5826 :   return s;
     336             : }
     337             : 
     338             : static void
     339        9830 : ip6_full_reass_add_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
     340             :                           ip6_full_reass_t * reass, u32 bi,
     341             :                           ip6_frag_hdr_t * ip6_frag_header,
     342             :                           ip6_full_reass_trace_operation_e action,
     343             :                           u32 thread_id_to)
     344             : {
     345        9830 :   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     346        9831 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
     347        9831 :   bool is_after_handoff = false;
     348        9834 :   if (pool_is_free_index
     349        9831 :       (vm->trace_main.trace_buffer_pool, vlib_buffer_get_trace_index (b)))
     350             :     {
     351             :       // this buffer's trace is gone
     352         115 :       b->flags &= ~VLIB_BUFFER_IS_TRACED;
     353         115 :       return;
     354             :     }
     355        9719 :   if (vlib_buffer_get_trace_thread (b) != vm->thread_index)
     356             :     {
     357         313 :       is_after_handoff = true;
     358             :     }
     359        9719 :   ip6_full_reass_trace_t *t = vlib_add_trace (vm, node, b, sizeof (t[0]));
     360        9720 :   t->is_after_handoff = is_after_handoff;
     361        9720 :   if (t->is_after_handoff)
     362             :     {
     363         312 :       clib_memcpy (&t->ip6_header, vlib_buffer_get_current (b),
     364             :                    clib_min (sizeof (t->ip6_header), b->current_length));
     365         310 :       if (ip6_frag_header)
     366             :         {
     367         310 :           clib_memcpy (&t->ip6_frag_header, ip6_frag_header,
     368             :                        sizeof (t->ip6_frag_header));
     369             :         }
     370             :       else
     371             :         {
     372           0 :           clib_memset (&t->ip6_frag_header, 0, sizeof (t->ip6_frag_header));
     373             :         }
     374             :     }
     375        9718 :   if (reass)
     376             :     {
     377        7824 :       t->reass_id = reass->id;
     378        7824 :       t->op_id = reass->trace_op_counter;
     379        7824 :       t->trace_range.first_bi = reass->first_bi;
     380        7824 :       t->total_data_len = reass->data_len;
     381        7824 :       ++reass->trace_op_counter;
     382             :     }
     383             :   else
     384             :     {
     385        1894 :       t->reass_id = ~0;
     386             :     }
     387        9718 :   t->action = action;
     388        9718 :   t->thread_id = vm->thread_index;
     389        9718 :   t->thread_id_to = thread_id_to;
     390        9718 :   ip6_full_reass_trace_details (vm, bi, &t->trace_range);
     391        9718 :   t->fragment_first = vnb->ip.reass.fragment_first;
     392        9718 :   t->fragment_last = vnb->ip.reass.fragment_last;
     393             : #if 0
     394             :   static u8 *s = NULL;
     395             :   s = format (s, "%U", format_ip6_full_reass_trace, NULL, NULL, t);
     396             :   printf ("%.*s\n", vec_len (s), s);
     397             :   fflush (stdout);
     398             :   vec_reset_length (s);
     399             : #endif
     400             : }
     401             : 
     402             : always_inline void
     403        2239 : ip6_full_reass_free_ctx (ip6_full_reass_per_thread_t * rt,
     404             :                          ip6_full_reass_t * reass)
     405             : {
     406        2239 :   pool_put (rt->pool, reass);
     407        2239 :   --rt->reass_n;
     408        2239 : }
     409             : 
     410             : always_inline void
     411        2239 : ip6_full_reass_free (ip6_full_reass_main_t * rm,
     412             :                      ip6_full_reass_per_thread_t * rt,
     413             :                      ip6_full_reass_t * reass)
     414             : {
     415             :   clib_bihash_kv_48_8_t kv;
     416        2239 :   kv.key[0] = reass->key.as_u64[0];
     417        2239 :   kv.key[1] = reass->key.as_u64[1];
     418        2239 :   kv.key[2] = reass->key.as_u64[2];
     419        2239 :   kv.key[3] = reass->key.as_u64[3];
     420        2239 :   kv.key[4] = reass->key.as_u64[4];
     421        2239 :   kv.key[5] = reass->key.as_u64[5];
     422        2239 :   clib_bihash_add_del_48_8 (&rm->hash, &kv, 0);
     423        2239 :   ip6_full_reass_free_ctx (rt, reass);
     424        2239 : }
     425             : 
     426             : /* n_left_to_next, and to_next are taken as input params, as this function
     427             :  * could be called from a graphnode, where its managing local copy of these
     428             :  * variables, and ignoring those and still trying to enqueue the buffers
     429             :  * with local variables would cause either buffer leak or corruption */
     430             : always_inline void
     431         605 : ip6_full_reass_drop_all (vlib_main_t *vm, vlib_node_runtime_t *node,
     432             :                          ip6_full_reass_t *reass, u32 *n_left_to_next,
     433             :                          u32 **to_next)
     434             : {
     435         605 :   u32 range_bi = reass->first_bi;
     436             :   vlib_buffer_t *range_b;
     437             :   vnet_buffer_opaque_t *range_vnb;
     438         605 :   u32 *to_free = NULL;
     439             : 
     440        1994 :   while (~0 != range_bi)
     441             :     {
     442        1389 :       range_b = vlib_get_buffer (vm, range_bi);
     443        1389 :       range_vnb = vnet_buffer (range_b);
     444             : 
     445        1389 :       if (~0 != range_bi)
     446             :         {
     447        1389 :           vec_add1 (to_free, range_bi);
     448             :         }
     449        1389 :       range_bi = range_vnb->ip.reass.next_range_bi;
     450             :     }
     451             : 
     452             :   /* send to next_error_index */
     453         605 :   if (~0 != reass->error_next_index &&
     454           0 :       reass->error_next_index < node->n_next_nodes)
     455           0 :     {
     456             :       u32 next_index;
     457             : 
     458           0 :       next_index = reass->error_next_index;
     459           0 :       u32 bi = ~0;
     460             : 
     461             :       /* record number of packets sent to custom app */
     462           0 :       vlib_node_increment_counter (vm, node->node_index,
     463             :                                    IP6_ERROR_REASS_TO_CUSTOM_APP,
     464           0 :                                    vec_len (to_free));
     465             : 
     466           0 :       while (vec_len (to_free) > 0)
     467             :         {
     468           0 :           vlib_get_next_frame (vm, node, next_index, *to_next,
     469             :                                (*n_left_to_next));
     470             : 
     471           0 :           while (vec_len (to_free) > 0 && (*n_left_to_next) > 0)
     472             :             {
     473           0 :               bi = vec_pop (to_free);
     474             : 
     475           0 :               if (~0 != bi)
     476             :                 {
     477           0 :                   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     478           0 :                   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
     479             :                     {
     480           0 :                       ip6_full_reass_add_trace (vm, node, reass, bi, NULL,
     481             :                                                 RANGE_DISCARD, ~0);
     482             :                     }
     483           0 :                   *to_next[0] = bi;
     484           0 :                   (*to_next) += 1;
     485           0 :                   (*n_left_to_next) -= 1;
     486             :                 }
     487             :             }
     488           0 :           vlib_put_next_frame (vm, node, next_index, (*n_left_to_next));
     489             :         }
     490             :     }
     491             :   else
     492             :     {
     493         605 :       vlib_buffer_free (vm, to_free, vec_len (to_free));
     494             :     }
     495         605 :   vec_free (to_free);
     496         605 : }
     497             : 
     498             : always_inline void
     499           0 : sanitize_reass_buffers_add_missing (vlib_main_t *vm, ip6_full_reass_t *reass,
     500             :                                     u32 *bi0)
     501             : {
     502           0 :   u32 range_bi = reass->first_bi;
     503             :   vlib_buffer_t *range_b;
     504             :   vnet_buffer_opaque_t *range_vnb;
     505             : 
     506           0 :   while (~0 != range_bi)
     507             :     {
     508           0 :       range_b = vlib_get_buffer (vm, range_bi);
     509           0 :       range_vnb = vnet_buffer (range_b);
     510           0 :       u32 bi = range_bi;
     511           0 :       if (~0 != bi)
     512             :         {
     513           0 :           if (bi == *bi0)
     514           0 :             *bi0 = ~0;
     515           0 :           if (range_b->flags & VLIB_BUFFER_NEXT_PRESENT)
     516             :             {
     517           0 :               u32 _bi = bi;
     518           0 :               vlib_buffer_t *_b = vlib_get_buffer (vm, _bi);
     519           0 :               while (_b->flags & VLIB_BUFFER_NEXT_PRESENT)
     520             :                 {
     521           0 :                   if (_b->next_buffer != range_vnb->ip.reass.next_range_bi)
     522             :                     {
     523           0 :                       _bi = _b->next_buffer;
     524           0 :                       _b = vlib_get_buffer (vm, _bi);
     525             :                     }
     526             :                   else
     527             :                     {
     528           0 :                       _b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
     529           0 :                       break;
     530             :                     }
     531             :                 }
     532             :             }
     533           0 :           range_bi = range_vnb->ip.reass.next_range_bi;
     534             :         }
     535             :     }
     536           0 :   if (*bi0 != ~0)
     537             :     {
     538           0 :       vlib_buffer_t *fb = vlib_get_buffer (vm, *bi0);
     539           0 :       vnet_buffer_opaque_t *fvnb = vnet_buffer (fb);
     540           0 :       if (~0 != reass->first_bi)
     541             :         {
     542           0 :           fvnb->ip.reass.next_range_bi = reass->first_bi;
     543           0 :           reass->first_bi = *bi0;
     544             :         }
     545             :       else
     546             :         {
     547           0 :           reass->first_bi = *bi0;
     548           0 :           fvnb->ip.reass.next_range_bi = ~0;
     549             :         }
     550           0 :       *bi0 = ~0;
     551             :     }
     552           0 : }
     553             : 
     554             : always_inline void
     555         392 : ip6_full_reass_on_timeout (vlib_main_t *vm, vlib_node_runtime_t *node,
     556             :                            ip6_full_reass_t *reass, u32 *icmp_bi,
     557             :                            u32 *n_left_to_next, u32 **to_next)
     558             : {
     559         392 :   if (~0 == reass->first_bi)
     560             :     {
     561           0 :       return;
     562             :     }
     563         392 :   if (~0 == reass->next_index)       // custom apps don't want icmp
     564             :     {
     565         392 :       vlib_buffer_t *b = vlib_get_buffer (vm, reass->first_bi);
     566         392 :       if (0 == vnet_buffer (b)->ip.reass.fragment_first)
     567             :         {
     568          50 :           *icmp_bi = reass->first_bi;
     569          50 :           if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
     570             :             {
     571          50 :               ip6_full_reass_add_trace (vm, node, reass, reass->first_bi, NULL,
     572             :                                         ICMP_ERROR_RT_EXCEEDED, ~0);
     573             :             }
     574             :           // fragment with offset zero received - send icmp message back
     575          50 :           if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     576             :             {
     577             :               // separate first buffer from chain and steer it towards icmp node
     578           0 :               b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
     579           0 :               reass->first_bi = b->next_buffer;
     580             :             }
     581             :           else
     582             :             {
     583          50 :               reass->first_bi = vnet_buffer (b)->ip.reass.next_range_bi;
     584             :             }
     585          50 :           icmp6_error_set_vnet_buffer (b, ICMP6_time_exceeded,
     586             :                                        ICMP6_time_exceeded_fragment_reassembly_time_exceeded,
     587             :                                        0);
     588             :         }
     589             :     }
     590         392 :   ip6_full_reass_drop_all (vm, node, reass, n_left_to_next, to_next);
     591             : }
     592             : 
     593             : always_inline ip6_full_reass_t *
     594       10456 : ip6_full_reass_find_or_create (vlib_main_t *vm, vlib_node_runtime_t *node,
     595             :                                ip6_full_reass_main_t *rm,
     596             :                                ip6_full_reass_per_thread_t *rt,
     597             :                                ip6_full_reass_kv_t *kv, u32 *icmp_bi,
     598             :                                u8 *do_handoff, int skip_bihash,
     599             :                                u32 *n_left_to_next, u32 **to_next)
     600             : {
     601             :   ip6_full_reass_t *reass;
     602             :   f64 now;
     603             : 
     604       10456 : again:
     605             : 
     606       10456 :   reass = NULL;
     607       10456 :   now = vlib_time_now (vm);
     608             : 
     609       10457 :   if (!skip_bihash && !clib_bihash_search_48_8 (&rm->hash, &kv->kv, &kv->kv))
     610             :     {
     611        8197 :       if (vm->thread_index != kv->v.memory_owner_thread_index)
     612             :         {
     613         415 :           *do_handoff = 1;
     614         415 :           return NULL;
     615             :         }
     616             : 
     617        7780 :       reass =
     618        7782 :         pool_elt_at_index (rm->per_thread_data
     619             :                            [kv->v.memory_owner_thread_index].pool,
     620             :                            kv->v.reass_index);
     621             : 
     622        7780 :       if (now > reass->last_heard + rm->timeout)
     623             :         {
     624         249 :           vlib_node_increment_counter (vm, node->node_index,
     625             :                                        IP6_ERROR_REASS_TIMEOUT, 1);
     626         249 :           ip6_full_reass_on_timeout (vm, node, reass, icmp_bi, n_left_to_next,
     627             :                                      to_next);
     628         249 :           ip6_full_reass_free (rm, rt, reass);
     629         249 :           reass = NULL;
     630             :         }
     631             :     }
     632             : 
     633       10041 :   if (reass)
     634             :     {
     635        7531 :       reass->last_heard = now;
     636        7531 :       return reass;
     637             :     }
     638             : 
     639        2510 :   if (rt->reass_n >= rm->max_reass_n)
     640             :     {
     641         274 :       reass = NULL;
     642         274 :       return reass;
     643             :     }
     644             :   else
     645             :     {
     646        2236 :       pool_get (rt->pool, reass);
     647        2238 :       clib_memset (reass, 0, sizeof (*reass));
     648        2238 :       reass->id = ((u64) vm->thread_index * 1000000000) + rt->id_counter;
     649        2238 :       ++rt->id_counter;
     650        2238 :       reass->first_bi = ~0;
     651        2238 :       reass->last_packet_octet = ~0;
     652        2238 :       reass->data_len = 0;
     653        2238 :       reass->next_index = ~0;
     654        2238 :       reass->error_next_index = ~0;
     655        2238 :       reass->memory_owner_thread_index = vm->thread_index;
     656        2238 :       ++rt->reass_n;
     657             :     }
     658             : 
     659        2238 :   kv->v.reass_index = (reass - rt->pool);
     660        2238 :   kv->v.memory_owner_thread_index = vm->thread_index;
     661        2238 :   reass->last_heard = now;
     662             : 
     663        2238 :   if (!skip_bihash)
     664             :     {
     665        2236 :       reass->key.as_u64[0] = kv->kv.key[0];
     666        2236 :       reass->key.as_u64[1] = kv->kv.key[1];
     667        2236 :       reass->key.as_u64[2] = kv->kv.key[2];
     668        2236 :       reass->key.as_u64[3] = kv->kv.key[3];
     669        2236 :       reass->key.as_u64[4] = kv->kv.key[4];
     670        2236 :       reass->key.as_u64[5] = kv->kv.key[5];
     671             : 
     672        2236 :       int rv = clib_bihash_add_del_48_8 (&rm->hash, &kv->kv, 2);
     673        2237 :       if (rv)
     674             :         {
     675           0 :           ip6_full_reass_free (rm, rt, reass);
     676           0 :           reass = NULL;
     677             :           // if other worker created a context already work with the other copy
     678           0 :           if (-2 == rv)
     679           0 :             goto again;
     680             :         }
     681             :     }
     682             :   else
     683             :     {
     684           2 :       reass->key.as_u64[0] = ~0;
     685           2 :       reass->key.as_u64[1] = ~0;
     686           2 :       reass->key.as_u64[2] = ~0;
     687           2 :       reass->key.as_u64[3] = ~0;
     688           2 :       reass->key.as_u64[4] = ~0;
     689           2 :       reass->key.as_u64[5] = ~0;
     690             :     }
     691             : 
     692        2239 :   return reass;
     693             : }
     694             : 
     695             : always_inline ip6_full_reass_rc_t
     696        1634 : ip6_full_reass_finalize (vlib_main_t * vm, vlib_node_runtime_t * node,
     697             :                          ip6_full_reass_main_t * rm,
     698             :                          ip6_full_reass_per_thread_t * rt,
     699             :                          ip6_full_reass_t * reass, u32 * bi0, u32 * next0,
     700             :                          u32 * error0, bool is_custom_app)
     701             : {
     702        1634 :   *bi0 = reass->first_bi;
     703        1634 :   *error0 = IP6_ERROR_NONE;
     704             :   ip6_frag_hdr_t *frag_hdr;
     705        1634 :   vlib_buffer_t *last_b = NULL;
     706        1634 :   u32 sub_chain_bi = reass->first_bi;
     707        1634 :   u32 total_length = 0;
     708        1634 :   u32 *vec_drop_compress = NULL;
     709        1634 :   ip6_full_reass_rc_t rv = IP6_FULL_REASS_RC_OK;
     710             :   do
     711             :     {
     712        7275 :       u32 tmp_bi = sub_chain_bi;
     713        7275 :       vlib_buffer_t *tmp = vlib_get_buffer (vm, tmp_bi);
     714        7275 :       vnet_buffer_opaque_t *vnb = vnet_buffer (tmp);
     715        7275 :       if (!(vnb->ip.reass.range_first >= vnb->ip.reass.fragment_first) &&
     716           0 :           !(vnb->ip.reass.range_last > vnb->ip.reass.fragment_first))
     717             :         {
     718           0 :           rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     719           0 :           goto free_buffers_and_return;
     720             :         }
     721             : 
     722        7275 :       u32 data_len = ip6_full_reass_buffer_get_data_len (tmp);
     723       14550 :       u32 trim_front = vnet_buffer (tmp)->ip.reass.ip6_frag_hdr_offset +
     724        7275 :         sizeof (*frag_hdr) + ip6_full_reass_buffer_get_data_offset (tmp);
     725        7275 :       u32 trim_end =
     726        7275 :         vlib_buffer_length_in_chain (vm, tmp) - trim_front - data_len;
     727        7275 :       if (tmp_bi == reass->first_bi)
     728             :         {
     729             :           /* first buffer - keep ip6 header */
     730        1634 :           if (0 != ip6_full_reass_buffer_get_data_offset (tmp))
     731             :             {
     732           0 :               rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     733           0 :               goto free_buffers_and_return;
     734             :             }
     735        1634 :           trim_front = 0;
     736        1634 :           trim_end = vlib_buffer_length_in_chain (vm, tmp) - data_len -
     737        1634 :             (vnet_buffer (tmp)->ip.reass.ip6_frag_hdr_offset +
     738             :              sizeof (*frag_hdr));
     739        1634 :           if (!(vlib_buffer_length_in_chain (vm, tmp) - trim_end > 0))
     740             :             {
     741           0 :               rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     742           0 :               goto free_buffers_and_return;
     743             :             }
     744             :         }
     745        7275 :       u32 keep_data =
     746        7275 :         vlib_buffer_length_in_chain (vm, tmp) - trim_front - trim_end;
     747             :       while (1)
     748             :         {
     749        7275 :           if (trim_front)
     750             :             {
     751        5641 :               if (trim_front > tmp->current_length)
     752             :                 {
     753             :                   /* drop whole buffer */
     754           0 :                   if (!(tmp->flags & VLIB_BUFFER_NEXT_PRESENT))
     755             :                     {
     756           0 :                       rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     757           0 :                       goto free_buffers_and_return;
     758             :                     }
     759           0 :                   trim_front -= tmp->current_length;
     760           0 :                   vec_add1 (vec_drop_compress, tmp_bi);
     761           0 :                   tmp->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
     762           0 :                   tmp_bi = tmp->next_buffer;
     763           0 :                   tmp = vlib_get_buffer (vm, tmp_bi);
     764           0 :                   continue;
     765             :                 }
     766             :               else
     767             :                 {
     768        5641 :                   vlib_buffer_advance (tmp, trim_front);
     769        5641 :                   trim_front = 0;
     770             :                 }
     771             :             }
     772        7275 :           if (keep_data)
     773             :             {
     774        7275 :               if (last_b)
     775             :                 {
     776        5641 :                   last_b->flags |= VLIB_BUFFER_NEXT_PRESENT;
     777        5641 :                   last_b->next_buffer = tmp_bi;
     778             :                 }
     779        7275 :               last_b = tmp;
     780        7275 :               if (keep_data <= tmp->current_length)
     781             :                 {
     782        7275 :                   tmp->current_length = keep_data;
     783        7275 :                   keep_data = 0;
     784             :                 }
     785             :               else
     786             :                 {
     787           0 :                   keep_data -= tmp->current_length;
     788           0 :                   if (!(tmp->flags & VLIB_BUFFER_NEXT_PRESENT))
     789             :                     {
     790           0 :                       rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     791           0 :                       goto free_buffers_and_return;
     792             :                     }
     793             :                 }
     794        7275 :               total_length += tmp->current_length;
     795             :             }
     796             :           else
     797             :             {
     798           0 :               if (reass->first_bi == tmp_bi)
     799             :                 {
     800           0 :                   rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     801           0 :                   goto free_buffers_and_return;
     802             :                 }
     803           0 :               vec_add1 (vec_drop_compress, tmp_bi);
     804             :             }
     805        7275 :           if (tmp->flags & VLIB_BUFFER_NEXT_PRESENT)
     806             :             {
     807           0 :               tmp_bi = tmp->next_buffer;
     808           0 :               tmp = vlib_get_buffer (vm, tmp->next_buffer);
     809             :             }
     810             :           else
     811             :             {
     812        7275 :               break;
     813             :             }
     814             :         }
     815        7275 :       sub_chain_bi =
     816        7275 :         vnet_buffer (vlib_get_buffer (vm, sub_chain_bi))->ip.
     817             :         reass.next_range_bi;
     818             :     }
     819        7275 :   while (~0 != sub_chain_bi);
     820             : 
     821        1634 :   if (!last_b)
     822             :     {
     823           0 :       rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     824           0 :       goto free_buffers_and_return;
     825             :     }
     826        1634 :   last_b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
     827        1634 :   vlib_buffer_t *first_b = vlib_get_buffer (vm, reass->first_bi);
     828        1634 :   if (total_length < first_b->current_length)
     829             :     {
     830           0 :       rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     831           0 :       goto free_buffers_and_return;
     832             :     }
     833        1634 :   total_length -= first_b->current_length;
     834        1634 :   first_b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
     835        1634 :   first_b->total_length_not_including_first_buffer = total_length;
     836             :   // drop fragment header
     837        1634 :   vnet_buffer_opaque_t *first_b_vnb = vnet_buffer (first_b);
     838        1634 :   ip6_header_t *ip = vlib_buffer_get_current (first_b);
     839        1634 :   u16 ip6_frag_hdr_offset = first_b_vnb->ip.reass.ip6_frag_hdr_offset;
     840             :   ip6_ext_hdr_chain_t hdr_chain;
     841        1634 :   ip6_ext_header_t *prev_hdr = 0;
     842        1634 :   int res = ip6_ext_header_walk (first_b, ip, IP_PROTOCOL_IPV6_FRAGMENTATION,
     843             :                                  &hdr_chain);
     844        1634 :   if (res < 0 ||
     845        1634 :       (hdr_chain.eh[res].protocol != IP_PROTOCOL_IPV6_FRAGMENTATION))
     846             :     {
     847           0 :       rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     848           0 :       goto free_buffers_and_return;
     849             :     }
     850        1634 :   frag_hdr = ip6_ext_next_header_offset (ip, hdr_chain.eh[res].offset);
     851        1634 :   if (res > 0)
     852             :     {
     853           0 :       prev_hdr = ip6_ext_next_header_offset (ip, hdr_chain.eh[res - 1].offset);
     854           0 :       prev_hdr->next_hdr = frag_hdr->next_hdr;
     855             :     }
     856             :   else
     857             :     {
     858        1634 :       ip->protocol = frag_hdr->next_hdr;
     859             :     }
     860        1634 :   if (hdr_chain.eh[res].offset != ip6_frag_hdr_offset)
     861             :     {
     862           0 :       rv = IP6_FULL_REASS_RC_INTERNAL_ERROR;
     863           0 :       goto free_buffers_and_return;
     864             :     }
     865        1634 :   memmove (frag_hdr, (u8 *) frag_hdr + sizeof (*frag_hdr),
     866        1634 :            first_b->current_length - ip6_frag_hdr_offset -
     867             :            sizeof (ip6_frag_hdr_t));
     868        1634 :   first_b->current_length -= sizeof (*frag_hdr);
     869        1634 :   ip->payload_length =
     870        1634 :     clib_host_to_net_u16 (total_length + first_b->current_length -
     871             :                           sizeof (*ip));
     872        1634 :   if (!vlib_buffer_chain_linearize (vm, first_b))
     873             :     {
     874           0 :       rv = IP6_FULL_REASS_RC_NO_BUF;
     875           0 :       goto free_buffers_and_return;
     876             :     }
     877        1634 :   first_b->flags &= ~VLIB_BUFFER_EXT_HDR_VALID;
     878        1634 :   if (PREDICT_FALSE (first_b->flags & VLIB_BUFFER_IS_TRACED))
     879             :     {
     880         968 :       ip6_full_reass_add_trace (vm, node, reass, reass->first_bi, NULL,
     881             :                                 FINALIZE, ~0);
     882             : #if 0
     883             :       // following code does a hexdump of packet fragments to stdout ...
     884             :       do
     885             :         {
     886             :           u32 bi = reass->first_bi;
     887             :           u8 *s = NULL;
     888             :           while (~0 != bi)
     889             :             {
     890             :               vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     891             :               s = format (s, "%u: %U\n", bi, format_hexdump,
     892             :                           vlib_buffer_get_current (b), b->current_length);
     893             :               if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
     894             :                 {
     895             :                   bi = b->next_buffer;
     896             :                 }
     897             :               else
     898             :                 {
     899             :                   break;
     900             :                 }
     901             :             }
     902             :           printf ("%.*s\n", vec_len (s), s);
     903             :           fflush (stdout);
     904             :           vec_free (s);
     905             :         }
     906             :       while (0);
     907             : #endif
     908             :     }
     909        1634 :   if (!is_custom_app)
     910             :     {
     911        1634 :       *next0 = IP6_FULL_REASSEMBLY_NEXT_INPUT;
     912             :     }
     913             :   else
     914             :     {
     915           0 :       *next0 = reass->next_index;
     916             :     }
     917        1634 :   vnet_buffer (first_b)->ip.reass.estimated_mtu = reass->min_fragment_length;
     918             :   /* Keep track of number of successfully reassembled packets and number of
     919             :    * fragments reassembled */
     920        1634 :   vlib_node_increment_counter (vm, node->node_index, IP6_ERROR_REASS_SUCCESS,
     921             :                                1);
     922             : 
     923        1634 :   vlib_node_increment_counter (vm, node->node_index,
     924             :                                IP6_ERROR_REASS_FRAGMENTS_REASSEMBLED,
     925        1634 :                                reass->fragments_n);
     926             : 
     927        1634 :   ip6_full_reass_free (rm, rt, reass);
     928        1634 :   reass = NULL;
     929        1634 : free_buffers_and_return:
     930        1634 :   vlib_buffer_free (vm, vec_drop_compress, vec_len (vec_drop_compress));
     931        1634 :   vec_free (vec_drop_compress);
     932        1634 :   return rv;
     933             : }
     934             : 
     935             : always_inline void
     936        8709 : ip6_full_reass_insert_range_in_chain (vlib_main_t * vm,
     937             :                                       ip6_full_reass_t * reass,
     938             :                                       u32 prev_range_bi, u32 new_next_bi)
     939             : {
     940             : 
     941        8709 :   vlib_buffer_t *new_next_b = vlib_get_buffer (vm, new_next_bi);
     942        8709 :   vnet_buffer_opaque_t *new_next_vnb = vnet_buffer (new_next_b);
     943        8709 :   if (~0 != prev_range_bi)
     944             :     {
     945        5761 :       vlib_buffer_t *prev_b = vlib_get_buffer (vm, prev_range_bi);
     946        5761 :       vnet_buffer_opaque_t *prev_vnb = vnet_buffer (prev_b);
     947        5761 :       new_next_vnb->ip.reass.next_range_bi = prev_vnb->ip.reass.next_range_bi;
     948        5761 :       prev_vnb->ip.reass.next_range_bi = new_next_bi;
     949             :     }
     950             :   else
     951             :     {
     952        2948 :       if (~0 != reass->first_bi)
     953             :         {
     954         710 :           new_next_vnb->ip.reass.next_range_bi = reass->first_bi;
     955             :         }
     956        2948 :       reass->first_bi = new_next_bi;
     957             :     }
     958        8709 :   reass->data_len += ip6_full_reass_buffer_get_data_len (new_next_b);
     959        8710 : }
     960             : 
     961             : always_inline ip6_full_reass_rc_t
     962        9770 : ip6_full_reass_update (vlib_main_t *vm, vlib_node_runtime_t *node,
     963             :                        ip6_full_reass_main_t *rm,
     964             :                        ip6_full_reass_per_thread_t *rt,
     965             :                        ip6_full_reass_t *reass, u32 *bi0, u32 *next0,
     966             :                        u32 *error0, ip6_frag_hdr_t *frag_hdr,
     967             :                        bool is_custom_app, u32 *handoff_thread_idx,
     968             :                        int skip_bihash)
     969             : {
     970        9770 :   int consumed = 0;
     971        9770 :   vlib_buffer_t *fb = vlib_get_buffer (vm, *bi0);
     972        9773 :   vnet_buffer_opaque_t *fvnb = vnet_buffer (fb);
     973        9773 :   if (is_custom_app)
     974             :     {
     975           0 :       reass->next_index = fvnb->ip.reass.next_index;      // store next_index before it's overwritten
     976           0 :       reass->error_next_index = fvnb->ip.reass.error_next_index;  // store error_next_index before it is overwritten
     977             :     }
     978             : 
     979        9773 :   fvnb->ip.reass.ip6_frag_hdr_offset =
     980        9773 :     (u8 *) frag_hdr - (u8 *) vlib_buffer_get_current (fb);
     981        9773 :   ip6_header_t *fip = vlib_buffer_get_current (fb);
     982        9771 :   if (fb->current_length < sizeof (*fip) ||
     983        9771 :       fvnb->ip.reass.ip6_frag_hdr_offset == 0 ||
     984        9771 :       fvnb->ip.reass.ip6_frag_hdr_offset >= fb->current_length)
     985             :     {
     986           0 :       return IP6_FULL_REASS_RC_INTERNAL_ERROR;
     987             :     }
     988             : 
     989        9771 :   u32 fragment_first = fvnb->ip.reass.fragment_first =
     990        9771 :     ip6_frag_hdr_offset_bytes (frag_hdr);
     991        9770 :   u32 fragment_length =
     992        9771 :     vlib_buffer_length_in_chain (vm, fb) -
     993        9770 :     (fvnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
     994        9770 :   if (0 == fragment_length)
     995             :     {
     996           1 :       return IP6_FULL_REASS_RC_INVALID_FRAG_LEN;
     997             :     }
     998        9769 :   u32 fragment_last = fvnb->ip.reass.fragment_last =
     999        9769 :     fragment_first + fragment_length - 1;
    1000        9769 :   int more_fragments = ip6_frag_hdr_more (frag_hdr);
    1001        9769 :   u32 candidate_range_bi = reass->first_bi;
    1002        9769 :   u32 prev_range_bi = ~0;
    1003        9769 :   fvnb->ip.reass.range_first = fragment_first;
    1004        9769 :   fvnb->ip.reass.range_last = fragment_last;
    1005        9769 :   fvnb->ip.reass.next_range_bi = ~0;
    1006        9769 :   if (!more_fragments)
    1007             :     {
    1008        2373 :       reass->last_packet_octet = fragment_last;
    1009             :     }
    1010        9769 :   if (~0 == reass->first_bi)
    1011             :     {
    1012             :       // starting a new reassembly
    1013        2238 :       ip6_full_reass_insert_range_in_chain (vm, reass, prev_range_bi, *bi0);
    1014        2238 :       reass->min_fragment_length = clib_net_to_host_u16 (fip->payload_length);
    1015        2225 :       consumed = 1;
    1016        2225 :       reass->fragments_n = 1;
    1017        2225 :       goto check_if_done_maybe;
    1018             :     }
    1019        7531 :   reass->min_fragment_length =
    1020        7531 :     clib_min (clib_net_to_host_u16 (fip->payload_length),
    1021             :               fvnb->ip.reass.estimated_mtu);
    1022       37770 :   while (~0 != candidate_range_bi)
    1023             :     {
    1024       37757 :       vlib_buffer_t *candidate_b = vlib_get_buffer (vm, candidate_range_bi);
    1025       37749 :       vnet_buffer_opaque_t *candidate_vnb = vnet_buffer (candidate_b);
    1026       37749 :       if (fragment_first > candidate_vnb->ip.reass.range_last)
    1027             :         {
    1028             :           // this fragments starts after candidate range
    1029       35438 :           prev_range_bi = candidate_range_bi;
    1030       35438 :           candidate_range_bi = candidate_vnb->ip.reass.next_range_bi;
    1031       35438 :           if (candidate_vnb->ip.reass.range_last < fragment_last &&
    1032             :               ~0 == candidate_range_bi)
    1033             :             {
    1034             :               // special case - this fragment falls beyond all known ranges
    1035        5199 :               ip6_full_reass_insert_range_in_chain (vm, reass, prev_range_bi,
    1036             :                                                     *bi0);
    1037        5200 :               consumed = 1;
    1038        5200 :               break;
    1039             :             }
    1040       30239 :           continue;
    1041             :         }
    1042        2311 :       if (fragment_last < candidate_vnb->ip.reass.range_first)
    1043             :         {
    1044             :           // this fragment ends before candidate range without any overlap
    1045        1272 :           ip6_full_reass_insert_range_in_chain (vm, reass, prev_range_bi,
    1046             :                                                 *bi0);
    1047        1272 :           consumed = 1;
    1048             :         }
    1049        1039 :       else if (fragment_first == candidate_vnb->ip.reass.range_first &&
    1050         902 :                fragment_last == candidate_vnb->ip.reass.range_last)
    1051             :         {
    1052             :           // duplicate fragment - ignore
    1053             :         }
    1054             :       else
    1055             :         {
    1056             :           // overlapping fragment - not allowed by RFC 8200
    1057         187 :           if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
    1058             :             {
    1059         211 :               ip6_full_reass_add_trace (vm, node, reass, *bi0, frag_hdr,
    1060             :                                         RANGE_OVERLAP, ~0);
    1061             :             }
    1062         211 :           return IP6_FULL_REASS_RC_OVERLAP;
    1063             :         }
    1064        2124 :       break;
    1065             :     }
    1066        7337 :   ++reass->fragments_n;
    1067        9562 : check_if_done_maybe:
    1068        9562 :   if (consumed)
    1069             :     {
    1070        8710 :       if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
    1071             :         {
    1072        6710 :           ip6_full_reass_add_trace (vm, node, reass, *bi0, frag_hdr, RANGE_NEW,
    1073             :                                     ~0);
    1074             :         }
    1075             :     }
    1076         852 :   else if (skip_bihash)
    1077             :     {
    1078             :       // if this reassembly is not in bihash, then the packet must have been
    1079             :       // consumed
    1080           0 :       return IP6_FULL_REASS_RC_INTERNAL_ERROR;
    1081             :     }
    1082        9559 :   if (~0 != reass->last_packet_octet &&
    1083        3100 :       reass->data_len == reass->last_packet_octet + 1)
    1084             :     {
    1085        1634 :       *handoff_thread_idx = reass->sendout_thread_index;
    1086        1634 :       int handoff =
    1087        1634 :         reass->memory_owner_thread_index != reass->sendout_thread_index;
    1088             :       ip6_full_reass_rc_t rc =
    1089        1634 :         ip6_full_reass_finalize (vm, node, rm, rt, reass, bi0, next0, error0,
    1090             :                                  is_custom_app);
    1091        1634 :       if (IP6_FULL_REASS_RC_OK == rc && handoff)
    1092             :         {
    1093           0 :           return IP6_FULL_REASS_RC_HANDOFF;
    1094             :         }
    1095        1634 :       return rc;
    1096             :     }
    1097             :   else
    1098             :     {
    1099        7925 :       if (skip_bihash)
    1100             :         {
    1101             :           // if this reassembly is not in bihash, it should've been an atomic
    1102             :           // fragment and thus finalized
    1103           0 :           return IP6_FULL_REASS_RC_INTERNAL_ERROR;
    1104             :         }
    1105        7925 :       if (consumed)
    1106             :         {
    1107        7073 :           *bi0 = ~0;
    1108        7073 :           if (reass->fragments_n > rm->max_reass_len)
    1109             :             {
    1110           1 :               return IP6_FULL_REASS_RC_TOO_MANY_FRAGMENTS;
    1111             :             }
    1112             :         }
    1113             :       else
    1114             :         {
    1115         852 :           *next0 = IP6_FULL_REASSEMBLY_NEXT_DROP;
    1116         852 :           *error0 = IP6_ERROR_REASS_DUPLICATE_FRAGMENT;
    1117             :         }
    1118             :     }
    1119        7924 :   return IP6_FULL_REASS_RC_OK;
    1120             : }
    1121             : 
    1122             : always_inline bool
    1123        1888 : ip6_full_reass_verify_upper_layer_present (vlib_node_runtime_t *node,
    1124             :                                            vlib_buffer_t *b,
    1125             :                                            ip6_ext_hdr_chain_t *hc)
    1126             : {
    1127        1888 :   int nh = hc->eh[hc->length - 1].protocol;
    1128             :   /* Checking to see if it's a terminating header */
    1129        1888 :   if (ip6_ext_hdr (nh))
    1130             :     {
    1131           2 :       icmp6_error_set_vnet_buffer (
    1132             :         b, ICMP6_parameter_problem,
    1133             :         ICMP6_parameter_problem_first_fragment_has_incomplete_header_chain, 0);
    1134           2 :       b->error = node->errors[IP6_ERROR_REASS_MISSING_UPPER];
    1135           2 :       return false;
    1136             :     }
    1137        1886 :   return true;
    1138             : }
    1139             : 
    1140             : always_inline bool
    1141       10464 : ip6_full_reass_verify_fragment_multiple_8 (vlib_main_t *vm,
    1142             :                                            vlib_node_runtime_t *node,
    1143             :                                            vlib_buffer_t *b,
    1144             :                                            ip6_frag_hdr_t *frag_hdr)
    1145             : {
    1146       10464 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
    1147       10464 :   ip6_header_t *ip = vlib_buffer_get_current (b);
    1148       10463 :   int more_fragments = ip6_frag_hdr_more (frag_hdr);
    1149       10463 :   u32 fragment_length =
    1150       10464 :     vlib_buffer_length_in_chain (vm, b) -
    1151       10463 :     (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
    1152       10463 :   if (more_fragments && 0 != fragment_length % 8)
    1153             :     {
    1154           4 :       icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
    1155             :                                    ICMP6_parameter_problem_erroneous_header_field,
    1156             :                                    (u8 *) & ip->payload_length - (u8 *) ip);
    1157           4 :       b->error = node->errors[IP6_ERROR_REASS_INVALID_FRAG_SIZE];
    1158           4 :       return false;
    1159             :     }
    1160       10459 :   return true;
    1161             : }
    1162             : 
    1163             : always_inline bool
    1164       10459 : ip6_full_reass_verify_packet_size_lt_64k (vlib_main_t *vm,
    1165             :                                           vlib_node_runtime_t *node,
    1166             :                                           vlib_buffer_t *b,
    1167             :                                           ip6_frag_hdr_t *frag_hdr)
    1168             : {
    1169       10459 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
    1170       10459 :   u32 fragment_first = ip6_frag_hdr_offset_bytes (frag_hdr);
    1171       10458 :   u32 fragment_length =
    1172       10458 :     vlib_buffer_length_in_chain (vm, b) -
    1173       10458 :     (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
    1174       10458 :   if (fragment_first + fragment_length > 65535)
    1175             :     {
    1176           2 :       ip6_header_t *ip0 = vlib_buffer_get_current (b);
    1177           2 :       icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
    1178             :                                    ICMP6_parameter_problem_erroneous_header_field,
    1179           2 :                                    (u8 *) & frag_hdr->fragment_offset_and_more
    1180           2 :                                    - (u8 *) ip0);
    1181           2 :       b->error = node->errors[IP6_ERROR_REASS_INVALID_FRAG_SIZE];
    1182           2 :       return false;
    1183             :     }
    1184       10456 :   return true;
    1185             : }
    1186             : 
    1187             : always_inline uword
    1188         146 : ip6_full_reassembly_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
    1189             :                             vlib_frame_t *frame, bool is_feature,
    1190             :                             bool is_custom_app, bool is_local)
    1191             : {
    1192         146 :   u32 *from = vlib_frame_vector_args (frame);
    1193             :   u32 n_left_from, n_left_to_next, *to_next, next_index;
    1194         146 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    1195         146 :   ip6_full_reass_per_thread_t *rt = &rm->per_thread_data[vm->thread_index];
    1196         146 :   clib_spinlock_lock (&rt->lock);
    1197             : 
    1198         147 :   n_left_from = frame->n_vectors;
    1199         147 :   next_index = node->cached_next_index;
    1200         294 :   while (n_left_from > 0)
    1201             :     {
    1202         147 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
    1203             : 
    1204       12093 :       while (n_left_from > 0 && n_left_to_next > 0)
    1205             :         {
    1206             :           u32 bi0;
    1207             :           vlib_buffer_t *b0;
    1208       11946 :           u32 next0 = IP6_FULL_REASSEMBLY_NEXT_DROP;
    1209       11946 :           u32 error0 = IP6_ERROR_NONE;
    1210       11946 :           u32 icmp_bi = ~0;
    1211             : 
    1212       11946 :           bi0 = from[0];
    1213       11946 :           b0 = vlib_get_buffer (vm, bi0);
    1214             : 
    1215       11945 :           ip6_header_t *ip0 = vlib_buffer_get_current (b0);
    1216       11944 :           ip6_frag_hdr_t *frag_hdr = NULL;
    1217             :           ip6_ext_hdr_chain_t hdr_chain;
    1218       11944 :           vnet_buffer_opaque_t *fvnb = vnet_buffer (b0);
    1219             : 
    1220       11944 :           int res = ip6_ext_header_walk (
    1221             :             b0, ip0, IP_PROTOCOL_IPV6_FRAGMENTATION, &hdr_chain);
    1222       11947 :           if (res < 0 ||
    1223       11944 :               hdr_chain.eh[res].protocol != IP_PROTOCOL_IPV6_FRAGMENTATION)
    1224             :             {
    1225        1478 :               vlib_node_increment_counter (vm, node->node_index,
    1226             :                                            IP6_ERROR_REASS_NO_FRAG_HDR, 1);
    1227             :               // this is a mangled packet - no fragmentation
    1228        1478 :               next0 = is_custom_app ? fvnb->ip.reass.error_next_index :
    1229             :                                             IP6_FULL_REASSEMBLY_NEXT_DROP;
    1230        1478 :               ip6_full_reass_add_trace (vm, node, NULL, bi0, NULL, PASSTHROUGH,
    1231             :                                         ~0);
    1232        1478 :               goto skip_reass;
    1233             :             }
    1234       10469 :           if (is_local && !rm->is_local_reass_enabled)
    1235             :             {
    1236           0 :               next0 = IP6_FULL_REASSEMBLY_NEXT_DROP;
    1237           0 :               goto skip_reass;
    1238             :             }
    1239             : 
    1240             :           /* Keep track of received fragments */
    1241       10469 :           vlib_node_increment_counter (vm, node->node_index,
    1242             :                                        IP6_ERROR_REASS_FRAGMENTS_RCVD, 1);
    1243             :           frag_hdr =
    1244       10469 :             ip6_ext_next_header_offset (ip0, hdr_chain.eh[res].offset);
    1245       10469 :           vnet_buffer (b0)->ip.reass.ip6_frag_hdr_offset =
    1246       10469 :             hdr_chain.eh[res].offset;
    1247             : 
    1248       10469 :           if (0 == ip6_frag_hdr_offset (frag_hdr))
    1249             :             {
    1250             :               // first fragment - verify upper-layer is present
    1251        1888 :               if (!ip6_full_reass_verify_upper_layer_present (node, b0,
    1252             :                                                               &hdr_chain))
    1253             :                 {
    1254           2 :                   next0 = is_custom_app ? fvnb->ip.reass.error_next_index :
    1255             :                                                 IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR;
    1256           2 :                   goto skip_reass;
    1257             :                 }
    1258             :             }
    1259             : 
    1260       10465 :           if (!ip6_full_reass_verify_fragment_multiple_8 (vm, node, b0,
    1261       10458 :                                                           frag_hdr) ||
    1262       10459 :               !ip6_full_reass_verify_packet_size_lt_64k (vm, node, b0,
    1263             :                                                          frag_hdr))
    1264             :             {
    1265           6 :               next0 = is_custom_app ? fvnb->ip.reass.error_next_index :
    1266             :                                             IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR;
    1267           6 :               goto skip_reass;
    1268             :             }
    1269             : 
    1270       10456 :           int skip_bihash = 0;
    1271             :           ip6_full_reass_kv_t kv;
    1272       10456 :           u8 do_handoff = 0;
    1273             : 
    1274       10456 :           if (0 == ip6_frag_hdr_offset (frag_hdr) &&
    1275        1884 :               !ip6_frag_hdr_more (frag_hdr))
    1276             :             {
    1277             :               // this is atomic fragment and needs to be processed separately
    1278           2 :               skip_bihash = 1;
    1279             :             }
    1280             :           else
    1281             :             {
    1282       10454 :               u32 fib_index =
    1283       10454 :                 (vnet_buffer (b0)->sw_if_index[VLIB_TX] == (u32) ~0) ?
    1284       10454 :                         vec_elt (ip6_main.fib_index_by_sw_if_index,
    1285       20908 :                            vnet_buffer (b0)->sw_if_index[VLIB_RX]) :
    1286           0 :                         vnet_buffer (b0)->sw_if_index[VLIB_TX];
    1287       10454 :               kv.k.as_u64[0] = ip0->src_address.as_u64[0];
    1288       10454 :               kv.k.as_u64[1] = ip0->src_address.as_u64[1];
    1289       10454 :               kv.k.as_u64[2] = ip0->dst_address.as_u64[0];
    1290       10454 :               kv.k.as_u64[3] = ip0->dst_address.as_u64[1];
    1291       10454 :               kv.k.as_u64[4] =
    1292       10454 :                 ((u64) fib_index) << 32 | (u64) frag_hdr->identification;
    1293             :               /* RFC 8200: The Next Header values in the Fragment headers of
    1294             :                * different fragments of the same original packet may differ.
    1295             :                * Only the value from the Offset zero fragment packet is used
    1296             :                * for reassembly.
    1297             :                *
    1298             :                * Also, IPv6 Header doesnt contain the protocol value unlike
    1299             :                * IPv4.*/
    1300       10454 :               kv.k.as_u64[5] = 0;
    1301             :             }
    1302             : 
    1303       10456 :           ip6_full_reass_t *reass = ip6_full_reass_find_or_create (
    1304             :             vm, node, rm, rt, &kv, &icmp_bi, &do_handoff, skip_bihash,
    1305             :             &n_left_to_next, &to_next);
    1306             : 
    1307       10459 :           if (reass)
    1308             :             {
    1309        9770 :               const u32 fragment_first = ip6_frag_hdr_offset (frag_hdr);
    1310        9769 :               if (0 == fragment_first)
    1311             :                 {
    1312        1809 :                   reass->sendout_thread_index = vm->thread_index;
    1313             :                 }
    1314             :             }
    1315       10458 :           if (PREDICT_FALSE (do_handoff))
    1316             :             {
    1317         415 :               next0 = IP6_FULL_REASSEMBLY_NEXT_HANDOFF;
    1318         415 :               vnet_buffer (b0)->ip.reass.owner_thread_index =
    1319         415 :                 kv.v.memory_owner_thread_index;
    1320             :             }
    1321       10043 :           else if (reass)
    1322             :             {
    1323             :               u32 handoff_thread_idx;
    1324        9769 :               u32 counter = ~0;
    1325        9769 :               switch (ip6_full_reass_update (
    1326             :                 vm, node, rm, rt, reass, &bi0, &next0, &error0, frag_hdr,
    1327             :                 is_custom_app, &handoff_thread_idx, skip_bihash))
    1328             :                 {
    1329        9558 :                 case IP6_FULL_REASS_RC_OK:
    1330             :                   /* nothing to do here */
    1331        9558 :                   break;
    1332           0 :                 case IP6_FULL_REASS_RC_HANDOFF:
    1333           0 :                   next0 = IP6_FULL_REASSEMBLY_NEXT_HANDOFF;
    1334           0 :                   b0 = vlib_get_buffer (vm, bi0);
    1335           0 :                   vnet_buffer (b0)->ip.reass.owner_thread_index =
    1336             :                     handoff_thread_idx;
    1337           0 :                   break;
    1338           1 :                 case IP6_FULL_REASS_RC_TOO_MANY_FRAGMENTS:
    1339           1 :                   counter = IP6_ERROR_REASS_FRAGMENT_CHAIN_TOO_LONG;
    1340           1 :                   break;
    1341           0 :                 case IP6_FULL_REASS_RC_NO_BUF:
    1342           0 :                   counter = IP6_ERROR_REASS_NO_BUF;
    1343           0 :                   break;
    1344           1 :                 case IP6_FULL_REASS_RC_INVALID_FRAG_LEN:
    1345           1 :                   counter = IP6_ERROR_REASS_INVALID_FRAG_LEN;
    1346           1 :                   break;
    1347         211 :                 case IP6_FULL_REASS_RC_OVERLAP:
    1348         211 :                   counter = IP6_ERROR_REASS_OVERLAPPING_FRAGMENT;
    1349         211 :                   break;
    1350           0 :                 case IP6_FULL_REASS_RC_INTERNAL_ERROR:
    1351           0 :                   counter = IP6_ERROR_REASS_INTERNAL_ERROR;
    1352             :                   /* Sanitization is needed in internal error cases only, as
    1353             :                    * the incoming packet is already dropped in other cases,
    1354             :                    * also adding bi0 back to the reassembly list, fixes the
    1355             :                    * leaking of buffers during internal errors.
    1356             :                    *
    1357             :                    * Also it doesnt make sense to send these buffers custom
    1358             :                    * app, these fragments are with internal errors */
    1359           0 :                   sanitize_reass_buffers_add_missing (vm, reass, &bi0);
    1360           0 :                   reass->error_next_index = ~0;
    1361           0 :                   break;
    1362             :                 }
    1363        9771 :               if (~0 != counter)
    1364             :                 {
    1365         213 :                   vlib_node_increment_counter (vm, node->node_index, counter,
    1366             :                                                1);
    1367         213 :                   ip6_full_reass_drop_all (vm, node, reass, &n_left_to_next,
    1368             :                                            &to_next);
    1369         213 :                   ip6_full_reass_free (rm, rt, reass);
    1370         213 :                   goto next_packet;
    1371             :                   break;
    1372             :                 }
    1373             :             }
    1374             :           else
    1375             :             {
    1376         274 :               if (is_feature)
    1377             :                 {
    1378         274 :                   next0 = IP6_FULL_REASSEMBLY_NEXT_DROP;
    1379             :                 }
    1380             :               else
    1381             :                 {
    1382           0 :                   next0 = fvnb->ip.reass.error_next_index;
    1383             :                 }
    1384         274 :               error0 = IP6_ERROR_REASS_LIMIT_REACHED;
    1385             :             }
    1386             : 
    1387       10247 :           if (~0 != bi0)
    1388             :             {
    1389        3175 :             skip_reass:
    1390        4661 :               to_next[0] = bi0;
    1391        4661 :               to_next += 1;
    1392        4661 :               n_left_to_next -= 1;
    1393             : 
    1394             :               /* bi0 might have been updated by reass_finalize, reload */
    1395        4661 :               b0 = vlib_get_buffer (vm, bi0);
    1396        4661 :               if (IP6_ERROR_NONE != error0)
    1397             :                 {
    1398        1126 :                   b0->error = node->errors[error0];
    1399             :                 }
    1400             : 
    1401        4661 :               if (next0 == IP6_FULL_REASSEMBLY_NEXT_HANDOFF)
    1402             :                 {
    1403         415 :                   if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
    1404             :                     {
    1405         415 :                       ip6_full_reass_add_trace (
    1406             :                         vm, node, NULL, bi0, frag_hdr, HANDOFF,
    1407         415 :                         vnet_buffer (b0)->ip.reass.owner_thread_index);
    1408             :                     }
    1409             :                 }
    1410        4246 :               else if (is_feature && IP6_ERROR_NONE == error0)
    1411             :                 {
    1412        3114 :                   vnet_feature_next (&next0, b0);
    1413             :                 }
    1414             : 
    1415             :               /* Increment the counter to-custom-app also as this fragment is
    1416             :                * also going to application */
    1417        4662 :               if (is_custom_app)
    1418             :                 {
    1419           0 :                   vlib_node_increment_counter (
    1420             :                     vm, node->node_index, IP6_ERROR_REASS_TO_CUSTOM_APP, 1);
    1421             :                 }
    1422             : 
    1423        4662 :               vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
    1424             :                                                n_left_to_next, bi0, next0);
    1425             :             }
    1426             : 
    1427       11734 :           if (~0 != icmp_bi)
    1428             :             {
    1429          25 :               next0 = IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR;
    1430          25 :               to_next[0] = icmp_bi;
    1431          25 :               to_next += 1;
    1432          25 :               n_left_to_next -= 1;
    1433          25 :               vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
    1434             :                                                n_left_to_next, icmp_bi,
    1435             :                                                next0);
    1436             :             }
    1437       11729 :         next_packet:
    1438       11947 :           from += 1;
    1439       11947 :           n_left_from -= 1;
    1440             :         }
    1441             : 
    1442         147 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    1443             :     }
    1444             : 
    1445         147 :   clib_spinlock_unlock (&rt->lock);
    1446         147 :   return frame->n_vectors;
    1447             : }
    1448             : 
    1449        2300 : VLIB_NODE_FN (ip6_full_reass_node) (vlib_main_t * vm,
    1450             :                                     vlib_node_runtime_t * node,
    1451             :                                     vlib_frame_t * frame)
    1452             : {
    1453           0 :   return ip6_full_reassembly_inline (vm, node, frame, false /* is_feature */,
    1454             :                                      false /* is_custom_app */,
    1455             :                                      false /* is_local */);
    1456             : }
    1457             : 
    1458      183788 : VLIB_REGISTER_NODE (ip6_full_reass_node) = {
    1459             :     .name = "ip6-full-reassembly",
    1460             :     .vector_size = sizeof (u32),
    1461             :     .format_trace = format_ip6_full_reass_trace,
    1462             :     .n_errors = IP6_N_ERROR,
    1463             :     .error_counters = ip6_error_counters,
    1464             :     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
    1465             :     .next_nodes =
    1466             :         {
    1467             :                 [IP6_FULL_REASSEMBLY_NEXT_INPUT] = "ip6-input",
    1468             :                 [IP6_FULL_REASSEMBLY_NEXT_DROP] = "ip6-drop",
    1469             :                 [IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
    1470             :                 [IP6_FULL_REASSEMBLY_NEXT_HANDOFF] = "ip6-full-reassembly-handoff",
    1471             :         },
    1472             : };
    1473             : 
    1474        2307 : VLIB_NODE_FN (ip6_local_full_reass_node)
    1475             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
    1476             : {
    1477           7 :   return ip6_full_reassembly_inline (vm, node, frame, false /* is_feature */,
    1478             :                                      false /* is_custom_app */,
    1479             :                                      true /* is_local */);
    1480             : }
    1481             : 
    1482      183788 : VLIB_REGISTER_NODE (ip6_local_full_reass_node) = {
    1483             :     .name = "ip6-local-full-reassembly",
    1484             :     .vector_size = sizeof (u32),
    1485             :     .format_trace = format_ip6_full_reass_trace,
    1486             :     .n_errors = IP6_N_ERROR,
    1487             :     .error_counters = ip6_error_counters,
    1488             :     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
    1489             :     .next_nodes =
    1490             :         {
    1491             :                 [IP6_FULL_REASSEMBLY_NEXT_INPUT] = "ip6-input",
    1492             :                 [IP6_FULL_REASSEMBLY_NEXT_DROP] = "ip6-drop",
    1493             :                 [IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
    1494             :                 [IP6_FULL_REASSEMBLY_NEXT_HANDOFF] = "ip6-local-full-reassembly-handoff",
    1495             :         },
    1496             : };
    1497             : 
    1498        2439 : VLIB_NODE_FN (ip6_full_reass_node_feature) (vlib_main_t * vm,
    1499             :                                             vlib_node_runtime_t * node,
    1500             :                                             vlib_frame_t * frame)
    1501             : {
    1502         139 :   return ip6_full_reassembly_inline (vm, node, frame, true /* is_feature */,
    1503             :                                      false /* is_custom_app */,
    1504             :                                      false /* is_local */);
    1505             : }
    1506             : 
    1507      183788 : VLIB_REGISTER_NODE (ip6_full_reass_node_feature) = {
    1508             :     .name = "ip6-full-reassembly-feature",
    1509             :     .vector_size = sizeof (u32),
    1510             :     .format_trace = format_ip6_full_reass_trace,
    1511             :     .n_errors = IP6_N_ERROR,
    1512             :     .error_counters = ip6_error_counters,
    1513             :     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
    1514             :     .next_nodes =
    1515             :         {
    1516             :                 [IP6_FULL_REASSEMBLY_NEXT_INPUT] = "ip6-input",
    1517             :                 [IP6_FULL_REASSEMBLY_NEXT_DROP] = "ip6-drop",
    1518             :                 [IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
    1519             :                 [IP6_FULL_REASSEMBLY_NEXT_HANDOFF] = "ip6-full-reass-feature-hoff",
    1520             :         },
    1521             : };
    1522             : 
    1523       76635 : VNET_FEATURE_INIT (ip6_full_reassembly_feature, static) = {
    1524             :     .arc_name = "ip6-unicast",
    1525             :     .node_name = "ip6-full-reassembly-feature",
    1526             :     .runs_before = VNET_FEATURES ("ip6-lookup",
    1527             :                                   "ipsec6-input-feature"),
    1528             :     .runs_after = 0,
    1529             : };
    1530             : 
    1531        2300 : VLIB_NODE_FN (ip6_full_reass_node_custom)
    1532             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
    1533             : {
    1534           0 :   return ip6_full_reassembly_inline (vm, node, frame, false /* is_feature */,
    1535             :                                      true /* is_custom_app */,
    1536             :                                      false /* is_local */);
    1537             : }
    1538             : 
    1539      183788 : VLIB_REGISTER_NODE (ip6_full_reass_node_custom) = {
    1540             :     .name = "ip6-full-reassembly-custom",
    1541             :     .vector_size = sizeof (u32),
    1542             :     .format_trace = format_ip6_full_reass_trace,
    1543             :     .n_errors = IP6_N_ERROR,
    1544             :     .error_counters = ip6_error_counters,
    1545             :     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
    1546             :     .next_nodes =
    1547             :         {
    1548             :                 [IP6_FULL_REASSEMBLY_NEXT_INPUT] = "ip6-input",
    1549             :                 [IP6_FULL_REASSEMBLY_NEXT_DROP] = "ip6-drop",
    1550             :                 [IP6_FULL_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
    1551             :                 [IP6_FULL_REASSEMBLY_NEXT_HANDOFF] = "ip6-full-reass-custom-hoff",
    1552             :         },
    1553             : };
    1554             : 
    1555             : #ifndef CLIB_MARCH_VARIANT
    1556             : static u32
    1557         677 : ip6_full_reass_get_nbuckets ()
    1558             : {
    1559         677 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    1560             :   u32 nbuckets;
    1561             :   u8 i;
    1562             : 
    1563             :   /* need more mem with more workers */
    1564         677 :   nbuckets = (u32) (rm->max_reass_n * (vlib_num_workers () + 1) /
    1565             :                     IP6_FULL_REASS_HT_LOAD_FACTOR);
    1566             : 
    1567        8150 :   for (i = 0; i < 31; i++)
    1568        8150 :     if ((1 << i) >= nbuckets)
    1569         677 :       break;
    1570         677 :   nbuckets = 1 << i;
    1571             : 
    1572         677 :   return nbuckets;
    1573             : }
    1574             : #endif /* CLIB_MARCH_VARIANT */
    1575             : 
    1576             : typedef enum
    1577             : {
    1578             :   IP6_EVENT_CONFIG_CHANGED = 1,
    1579             : } ip6_full_reass_event_t;
    1580             : 
    1581             : #ifndef CLIB_MARCH_VARIANT
    1582             : typedef struct
    1583             : {
    1584             :   int failure;
    1585             :   clib_bihash_48_8_t *new_hash;
    1586             : } ip6_rehash_cb_ctx;
    1587             : 
    1588             : static int
    1589           0 : ip6_rehash_cb (clib_bihash_kv_48_8_t * kv, void *_ctx)
    1590             : {
    1591           0 :   ip6_rehash_cb_ctx *ctx = _ctx;
    1592           0 :   if (clib_bihash_add_del_48_8 (ctx->new_hash, kv, 1))
    1593             :     {
    1594           0 :       ctx->failure = 1;
    1595             :     }
    1596           0 :   return (BIHASH_WALK_CONTINUE);
    1597             : }
    1598             : 
    1599             : static void
    1600         626 : ip6_full_reass_set_params (u32 timeout_ms, u32 max_reassemblies,
    1601             :                            u32 max_reassembly_length,
    1602             :                            u32 expire_walk_interval_ms)
    1603             : {
    1604         626 :   ip6_full_reass_main.timeout_ms = timeout_ms;
    1605         626 :   ip6_full_reass_main.timeout = (f64) timeout_ms / (f64) MSEC_PER_SEC;
    1606         626 :   ip6_full_reass_main.max_reass_n = max_reassemblies;
    1607         626 :   ip6_full_reass_main.max_reass_len = max_reassembly_length;
    1608         626 :   ip6_full_reass_main.expire_walk_interval_ms = expire_walk_interval_ms;
    1609         626 : }
    1610             : 
    1611             : vnet_api_error_t
    1612          51 : ip6_full_reass_set (u32 timeout_ms, u32 max_reassemblies,
    1613             :                     u32 max_reassembly_length, u32 expire_walk_interval_ms)
    1614             : {
    1615          51 :   u32 old_nbuckets = ip6_full_reass_get_nbuckets ();
    1616          51 :   ip6_full_reass_set_params (timeout_ms, max_reassemblies,
    1617             :                              max_reassembly_length, expire_walk_interval_ms);
    1618          51 :   vlib_process_signal_event (ip6_full_reass_main.vlib_main,
    1619          51 :                              ip6_full_reass_main.ip6_full_reass_expire_node_idx,
    1620             :                              IP6_EVENT_CONFIG_CHANGED, 0);
    1621          51 :   u32 new_nbuckets = ip6_full_reass_get_nbuckets ();
    1622          51 :   if (ip6_full_reass_main.max_reass_n > 0 && new_nbuckets > old_nbuckets)
    1623             :     {
    1624             :       clib_bihash_48_8_t new_hash;
    1625           1 :       clib_memset (&new_hash, 0, sizeof (new_hash));
    1626             :       ip6_rehash_cb_ctx ctx;
    1627           1 :       ctx.failure = 0;
    1628           1 :       ctx.new_hash = &new_hash;
    1629           1 :       clib_bihash_init_48_8 (&new_hash, "ip6-full-reass", new_nbuckets,
    1630           1 :                              new_nbuckets * 1024);
    1631           1 :       clib_bihash_foreach_key_value_pair_48_8 (&ip6_full_reass_main.hash,
    1632             :                                                ip6_rehash_cb, &ctx);
    1633           1 :       if (ctx.failure)
    1634             :         {
    1635           0 :           clib_bihash_free_48_8 (&new_hash);
    1636           0 :           return -1;
    1637             :         }
    1638             :       else
    1639             :         {
    1640           1 :           clib_bihash_free_48_8 (&ip6_full_reass_main.hash);
    1641           1 :           clib_memcpy_fast (&ip6_full_reass_main.hash, &new_hash,
    1642             :                             sizeof (ip6_full_reass_main.hash));
    1643           1 :           clib_bihash_copied (&ip6_full_reass_main.hash, &new_hash);
    1644             :         }
    1645             :     }
    1646          51 :   return 0;
    1647             : }
    1648             : 
    1649             : vnet_api_error_t
    1650           0 : ip6_full_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
    1651             :                     u32 * max_reassembly_length,
    1652             :                     u32 * expire_walk_interval_ms)
    1653             : {
    1654           0 :   *timeout_ms = ip6_full_reass_main.timeout_ms;
    1655           0 :   *max_reassemblies = ip6_full_reass_main.max_reass_n;
    1656           0 :   *max_reassembly_length = ip6_full_reass_main.max_reass_len;
    1657           0 :   *expire_walk_interval_ms = ip6_full_reass_main.expire_walk_interval_ms;
    1658           0 :   return 0;
    1659             : }
    1660             : 
    1661             : static clib_error_t *
    1662         575 : ip6_full_reass_init_function (vlib_main_t * vm)
    1663             : {
    1664         575 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    1665         575 :   clib_error_t *error = 0;
    1666             :   u32 nbuckets;
    1667             :   vlib_node_t *node;
    1668             : 
    1669         575 :   rm->vlib_main = vm;
    1670             : 
    1671         575 :   vec_validate (rm->per_thread_data, vlib_num_workers ());
    1672             :   ip6_full_reass_per_thread_t *rt;
    1673        1205 :   vec_foreach (rt, rm->per_thread_data)
    1674             :   {
    1675         630 :     clib_spinlock_init (&rt->lock);
    1676         630 :     pool_alloc (rt->pool, rm->max_reass_n);
    1677             :   }
    1678             : 
    1679         575 :   node = vlib_get_node_by_name (vm, (u8 *) "ip6-full-reassembly-expire-walk");
    1680         575 :   ASSERT (node);
    1681         575 :   rm->ip6_full_reass_expire_node_idx = node->index;
    1682             : 
    1683         575 :   ip6_full_reass_set_params (IP6_FULL_REASS_TIMEOUT_DEFAULT_MS,
    1684             :                              IP6_FULL_REASS_MAX_REASSEMBLIES_DEFAULT,
    1685             :                              IP6_FULL_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT,
    1686             :                              IP6_FULL_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS);
    1687             : 
    1688         575 :   nbuckets = ip6_full_reass_get_nbuckets ();
    1689         575 :   clib_bihash_init_48_8 (&rm->hash, "ip6-full-reass", nbuckets,
    1690         575 :                          nbuckets * 1024);
    1691             : 
    1692         575 :   node = vlib_get_node_by_name (vm, (u8 *) "ip6-icmp-error");
    1693         575 :   ASSERT (node);
    1694         575 :   rm->ip6_icmp_error_idx = node->index;
    1695             : 
    1696         575 :   if ((error = vlib_call_init_function (vm, ip_main_init)))
    1697           0 :     return error;
    1698         575 :   ip6_register_protocol (IP_PROTOCOL_IPV6_FRAGMENTATION,
    1699             :                          ip6_local_full_reass_node.index);
    1700         575 :   rm->is_local_reass_enabled = 1;
    1701             : 
    1702         575 :   rm->fq_index = vlib_frame_queue_main_init (ip6_full_reass_node.index, 0);
    1703         575 :   rm->fq_local_index =
    1704         575 :     vlib_frame_queue_main_init (ip6_local_full_reass_node.index, 0);
    1705         575 :   rm->fq_feature_index =
    1706         575 :     vlib_frame_queue_main_init (ip6_full_reass_node_feature.index, 0);
    1707         575 :   rm->fq_custom_index =
    1708         575 :     vlib_frame_queue_main_init (ip6_full_reass_node_custom.index, 0);
    1709             : 
    1710         575 :   rm->feature_use_refcount_per_intf = NULL;
    1711         575 :   return error;
    1712             : }
    1713             : 
    1714       43199 : VLIB_INIT_FUNCTION (ip6_full_reass_init_function);
    1715             : #endif /* CLIB_MARCH_VARIANT */
    1716             : 
    1717             : static uword
    1718         575 : ip6_full_reass_walk_expired (vlib_main_t *vm, vlib_node_runtime_t *node,
    1719             :                              CLIB_UNUSED (vlib_frame_t *f))
    1720             : {
    1721         575 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    1722         575 :   uword event_type, *event_data = 0;
    1723             : 
    1724             :   while (true)
    1725      148374 :     {
    1726      148949 :       vlib_process_wait_for_event_or_clock (vm,
    1727      148949 :                                             (f64) rm->expire_walk_interval_ms
    1728             :                                             / (f64) MSEC_PER_SEC);
    1729      148374 :       event_type = vlib_process_get_events (vm, &event_data);
    1730             : 
    1731      148374 :       switch (event_type)
    1732             :         {
    1733      148374 :         case ~0:
    1734             :           /* no events => timeout */
    1735             :           /* fallthrough */
    1736             :         case IP6_EVENT_CONFIG_CHANGED:
    1737             :           /* nothing to do here */
    1738      148374 :           break;
    1739           0 :         default:
    1740           0 :           clib_warning ("BUG: event type 0x%wx", event_type);
    1741           0 :           break;
    1742             :         }
    1743      148374 :       f64 now = vlib_time_now (vm);
    1744             : 
    1745             :       ip6_full_reass_t *reass;
    1746      148374 :       int *pool_indexes_to_free = NULL;
    1747             : 
    1748      148374 :       uword thread_index = 0;
    1749             :       int index;
    1750      148374 :       const uword nthreads = vlib_num_workers () + 1;
    1751      148374 :       u32 *vec_icmp_bi = NULL;
    1752             :       u32 n_left_to_next, *to_next;
    1753             : 
    1754      338426 :       for (thread_index = 0; thread_index < nthreads; ++thread_index)
    1755             :         {
    1756      190052 :           ip6_full_reass_per_thread_t *rt =
    1757      190052 :             &rm->per_thread_data[thread_index];
    1758      190052 :           u32 reass_timeout_cnt = 0;
    1759      190052 :           clib_spinlock_lock (&rt->lock);
    1760             : 
    1761      190052 :           vec_reset_length (pool_indexes_to_free);
    1762             :           /* Pace the number of timeouts handled per thread,to avoid barrier
    1763             :            * sync issues in real world scenarios */
    1764             : 
    1765      190052 :           u32 beg = rt->last_id;
    1766             :           /* to ensure we walk at least once per sec per context */
    1767      190052 :           u32 end = beg + (IP6_FULL_REASS_MAX_REASSEMBLIES_DEFAULT *
    1768             :                              IP6_FULL_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS /
    1769             :                              MSEC_PER_SEC +
    1770             :                            1);
    1771      190052 :           if (end > vec_len (rt->pool))
    1772             :             {
    1773      190052 :               end = vec_len (rt->pool);
    1774      190052 :               rt->last_id = 0;
    1775             :             }
    1776             :           else
    1777             :             {
    1778           0 :               rt->last_id = end;
    1779             :             }
    1780             : 
    1781      190297 :           pool_foreach_stepping_index (index, beg, end, rt->pool)
    1782             :           {
    1783         245 :             reass = pool_elt_at_index (rt->pool, index);
    1784         245 :             if (now > reass->last_heard + rm->timeout)
    1785             :               {
    1786         143 :                 vec_add1 (pool_indexes_to_free, index);
    1787             :               }
    1788             :           }
    1789             : 
    1790             :           int *i;
    1791      190195 :           vec_foreach (i, pool_indexes_to_free)
    1792             :           {
    1793         143 :             ip6_full_reass_t *reass = pool_elt_at_index (rt->pool, i[0]);
    1794         143 :             u32 icmp_bi = ~0;
    1795             : 
    1796         143 :             reass_timeout_cnt += reass->fragments_n;
    1797         143 :             ip6_full_reass_on_timeout (vm, node, reass, &icmp_bi,
    1798             :                                        &n_left_to_next, &to_next);
    1799         143 :             if (~0 != icmp_bi)
    1800          25 :               vec_add1 (vec_icmp_bi, icmp_bi);
    1801             : 
    1802         143 :             ip6_full_reass_free (rm, rt, reass);
    1803             :           }
    1804             : 
    1805      190052 :           clib_spinlock_unlock (&rt->lock);
    1806      190052 :           if (reass_timeout_cnt)
    1807           8 :             vlib_node_increment_counter (vm, node->node_index,
    1808             :                                          IP6_ERROR_REASS_TIMEOUT,
    1809             :                                          reass_timeout_cnt);
    1810             :         }
    1811             : 
    1812      148375 :       while (vec_len (vec_icmp_bi) > 0)
    1813             :         {
    1814             :           vlib_frame_t *f =
    1815           1 :             vlib_get_frame_to_node (vm, rm->ip6_icmp_error_idx);
    1816           1 :           u32 *to_next = vlib_frame_vector_args (f);
    1817           1 :           u32 n_left_to_next = VLIB_FRAME_SIZE - f->n_vectors;
    1818           1 :           int trace_frame = 0;
    1819          26 :           while (vec_len (vec_icmp_bi) > 0 && n_left_to_next > 0)
    1820             :             {
    1821          25 :               u32 bi = vec_pop (vec_icmp_bi);
    1822          25 :               vlib_buffer_t *b = vlib_get_buffer (vm, bi);
    1823          25 :               if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
    1824          25 :                 trace_frame = 1;
    1825          25 :               to_next[0] = bi;
    1826          25 :               ++f->n_vectors;
    1827          25 :               to_next += 1;
    1828          25 :               n_left_to_next -= 1;
    1829             :             }
    1830           1 :           f->frame_flags |= (trace_frame * VLIB_FRAME_TRACE);
    1831           1 :           vlib_put_frame_to_node (vm, rm->ip6_icmp_error_idx, f);
    1832             :         }
    1833             : 
    1834      148374 :       vec_free (pool_indexes_to_free);
    1835      148374 :       vec_free (vec_icmp_bi);
    1836      148374 :       if (event_data)
    1837             :         {
    1838         673 :           vec_set_len (event_data, 0);
    1839             :         }
    1840             :     }
    1841             : 
    1842             :   return 0;
    1843             : }
    1844             : 
    1845      183788 : VLIB_REGISTER_NODE (ip6_full_reass_expire_node) = {
    1846             :   .function = ip6_full_reass_walk_expired,
    1847             :   .format_trace = format_ip6_full_reass_trace,
    1848             :   .type = VLIB_NODE_TYPE_PROCESS,
    1849             :   .name = "ip6-full-reassembly-expire-walk",
    1850             : 
    1851             :   .n_errors = IP6_N_ERROR,
    1852             :   .error_counters = ip6_error_counters,
    1853             : };
    1854             : 
    1855             : static u8 *
    1856          92 : format_ip6_full_reass_key (u8 * s, va_list * args)
    1857             : {
    1858          92 :   ip6_full_reass_key_t *key = va_arg (*args, ip6_full_reass_key_t *);
    1859          92 :   s = format (s, "xx_id: %u, src: %U, dst: %U, frag_id: %u, proto: %u",
    1860             :               key->xx_id, format_ip6_address, &key->src, format_ip6_address,
    1861          92 :               &key->dst, clib_net_to_host_u16 (key->frag_id), key->proto);
    1862          92 :   return s;
    1863             : }
    1864             : 
    1865             : static u8 *
    1866          92 : format_ip6_full_reass (u8 * s, va_list * args)
    1867             : {
    1868          92 :   vlib_main_t *vm = va_arg (*args, vlib_main_t *);
    1869          92 :   ip6_full_reass_t *reass = va_arg (*args, ip6_full_reass_t *);
    1870             : 
    1871          92 :   s = format (s, "ID: %lu, key: %U\n  first_bi: %u, data_len: %u, "
    1872             :               "last_packet_octet: %u, trace_op_counter: %u\n",
    1873             :               reass->id, format_ip6_full_reass_key, &reass->key,
    1874             :               reass->first_bi, reass->data_len, reass->last_packet_octet,
    1875             :               reass->trace_op_counter);
    1876          92 :   u32 bi = reass->first_bi;
    1877          92 :   u32 counter = 0;
    1878         184 :   while (~0 != bi)
    1879             :     {
    1880          92 :       vlib_buffer_t *b = vlib_get_buffer (vm, bi);
    1881          92 :       vnet_buffer_opaque_t *vnb = vnet_buffer (b);
    1882          92 :       s = format (s, "  #%03u: range: [%u, %u], bi: %u, off: %d, len: %u, "
    1883             :                   "fragment[%u, %u]\n",
    1884          92 :                   counter, vnb->ip.reass.range_first,
    1885          92 :                   vnb->ip.reass.range_last, bi,
    1886             :                   ip6_full_reass_buffer_get_data_offset (b),
    1887          92 :                   ip6_full_reass_buffer_get_data_len (b),
    1888          92 :                   vnb->ip.reass.fragment_first, vnb->ip.reass.fragment_last);
    1889          92 :       if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
    1890             :         {
    1891           0 :           bi = b->next_buffer;
    1892             :         }
    1893             :       else
    1894             :         {
    1895          92 :           bi = ~0;
    1896             :         }
    1897             :     }
    1898          92 :   return s;
    1899             : }
    1900             : 
    1901             : static clib_error_t *
    1902          44 : show_ip6_full_reass (vlib_main_t * vm, unformat_input_t * input,
    1903             :                      CLIB_UNUSED (vlib_cli_command_t * lmd))
    1904             : {
    1905          44 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    1906             : 
    1907          44 :   vlib_cli_output (vm, "---------------------");
    1908          44 :   vlib_cli_output (vm, "IP6 reassembly status");
    1909          44 :   vlib_cli_output (vm, "---------------------");
    1910          44 :   bool details = false;
    1911          44 :   if (unformat (input, "details"))
    1912             :     {
    1913          44 :       details = true;
    1914             :     }
    1915             : 
    1916          44 :   u32 sum_reass_n = 0;
    1917          44 :   u64 sum_buffers_n = 0;
    1918             :   ip6_full_reass_t *reass;
    1919             :   uword thread_index;
    1920          44 :   const uword nthreads = vlib_num_workers () + 1;
    1921          94 :   for (thread_index = 0; thread_index < nthreads; ++thread_index)
    1922             :     {
    1923          50 :       ip6_full_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
    1924          50 :       clib_spinlock_lock (&rt->lock);
    1925          50 :       if (details)
    1926             :         {
    1927         142 :           pool_foreach (reass, rt->pool) {
    1928          92 :             vlib_cli_output (vm, "%U", format_ip6_full_reass, vm, reass);
    1929             :           }
    1930             :         }
    1931          50 :       sum_reass_n += rt->reass_n;
    1932          50 :       clib_spinlock_unlock (&rt->lock);
    1933             :     }
    1934          44 :   vlib_cli_output (vm, "---------------------");
    1935          44 :   vlib_cli_output (vm, "Current IP6 reassemblies count: %lu\n",
    1936             :                    (long unsigned) sum_reass_n);
    1937          44 :   vlib_cli_output (vm,
    1938             :                    "Maximum configured concurrent full IP6 reassemblies per worker-thread: %lu\n",
    1939          44 :                    (long unsigned) rm->max_reass_n);
    1940          44 :   vlib_cli_output (vm,
    1941             :                    "Maximum configured amount of fragments "
    1942             :                    "per full IP6 reassembly: %lu\n",
    1943          44 :                    (long unsigned) rm->max_reass_len);
    1944          44 :   vlib_cli_output (vm,
    1945             :                    "Maximum configured full IP6 reassembly timeout: %lums\n",
    1946          44 :                    (long unsigned) rm->timeout_ms);
    1947          44 :   vlib_cli_output (vm,
    1948             :                    "Maximum configured full IP6 reassembly expire walk interval: %lums\n",
    1949          44 :                    (long unsigned) rm->expire_walk_interval_ms);
    1950          44 :   vlib_cli_output (vm, "Buffers in use: %lu\n",
    1951             :                    (long unsigned) sum_buffers_n);
    1952          44 :   return 0;
    1953             : }
    1954             : 
    1955      285289 : VLIB_CLI_COMMAND (show_ip6_full_reassembly_cmd, static) = {
    1956             :     .path = "show ip6-full-reassembly",
    1957             :     .short_help = "show ip6-full-reassembly [details]",
    1958             :     .function = show_ip6_full_reass,
    1959             : };
    1960             : 
    1961             : #ifndef CLIB_MARCH_VARIANT
    1962             : vnet_api_error_t
    1963         144 : ip6_full_reass_enable_disable (u32 sw_if_index, u8 enable_disable)
    1964             : {
    1965         144 :   return vnet_feature_enable_disable ("ip6-unicast",
    1966             :                                       "ip6-full-reassembly-feature",
    1967             :                                       sw_if_index, enable_disable, 0, 0);
    1968             : }
    1969             : #endif /* CLIB_MARCH_VARIANT */
    1970             : 
    1971             : #define foreach_ip6_full_reassembly_handoff_error                       \
    1972             : _(CONGESTION_DROP, "congestion drop")
    1973             : 
    1974             : 
    1975             : typedef enum
    1976             : {
    1977             : #define _(sym,str) IP6_FULL_REASSEMBLY_HANDOFF_ERROR_##sym,
    1978             :   foreach_ip6_full_reassembly_handoff_error
    1979             : #undef _
    1980             :     IP6_FULL_REASSEMBLY_HANDOFF_N_ERROR,
    1981             : } ip6_full_reassembly_handoff_error_t;
    1982             : 
    1983             : static char *ip6_full_reassembly_handoff_error_strings[] = {
    1984             : #define _(sym,string) string,
    1985             :   foreach_ip6_full_reassembly_handoff_error
    1986             : #undef _
    1987             : };
    1988             : 
    1989             : typedef struct
    1990             : {
    1991             :   u32 next_worker_index;
    1992             : } ip6_full_reassembly_handoff_trace_t;
    1993             : 
    1994             : static u8 *
    1995         289 : format_ip6_full_reassembly_handoff_trace (u8 * s, va_list * args)
    1996             : {
    1997         289 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
    1998         289 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
    1999         289 :   ip6_full_reassembly_handoff_trace_t *t =
    2000             :     va_arg (*args, ip6_full_reassembly_handoff_trace_t *);
    2001             : 
    2002             :   s =
    2003         289 :     format (s, "ip6-full-reassembly-handoff: next-worker %d",
    2004             :             t->next_worker_index);
    2005             : 
    2006         289 :   return s;
    2007             : }
    2008             : 
    2009             : always_inline uword
    2010          12 : ip6_full_reassembly_handoff_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
    2011             :                                     vlib_frame_t *frame,
    2012             :                                     ip6_full_reass_node_type_t type,
    2013             :                                     bool is_local)
    2014             : {
    2015          12 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    2016             : 
    2017             :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
    2018             :   u32 n_enq, n_left_from, *from;
    2019             :   u16 thread_indices[VLIB_FRAME_SIZE], *ti;
    2020             :   u32 fq_index;
    2021             : 
    2022          12 :   from = vlib_frame_vector_args (frame);
    2023          12 :   n_left_from = frame->n_vectors;
    2024          12 :   vlib_get_buffers (vm, from, bufs, n_left_from);
    2025             : 
    2026          12 :   b = bufs;
    2027          12 :   ti = thread_indices;
    2028             : 
    2029          12 :   switch (type)
    2030             :     {
    2031           0 :     case NORMAL:
    2032           0 :       if (is_local)
    2033             :         {
    2034           0 :           fq_index = rm->fq_local_index;
    2035             :         }
    2036             :       else
    2037             :         {
    2038           0 :           fq_index = rm->fq_index;
    2039             :         }
    2040           0 :       break;
    2041          12 :     case FEATURE:
    2042          12 :       fq_index = rm->fq_feature_index;
    2043          12 :       break;
    2044           0 :     case CUSTOM:
    2045           0 :       fq_index = rm->fq_custom_index;
    2046           0 :       break;
    2047           0 :     default:
    2048           0 :       clib_warning ("Unexpected `type' (%d)!", type);
    2049           0 :       ASSERT (0);
    2050             :     }
    2051         428 :   while (n_left_from > 0)
    2052             :     {
    2053         416 :       ti[0] = vnet_buffer (b[0])->ip.reass.owner_thread_index;
    2054             : 
    2055         416 :       if (PREDICT_FALSE
    2056             :           ((node->flags & VLIB_NODE_FLAG_TRACE)
    2057             :            && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
    2058             :         {
    2059             :           ip6_full_reassembly_handoff_trace_t *t =
    2060         416 :             vlib_add_trace (vm, node, b[0], sizeof (*t));
    2061         416 :           t->next_worker_index = ti[0];
    2062             :         }
    2063             : 
    2064         416 :       n_left_from -= 1;
    2065         416 :       ti += 1;
    2066         416 :       b += 1;
    2067             :     }
    2068          12 :   n_enq = vlib_buffer_enqueue_to_thread (vm, node, fq_index, from,
    2069          12 :                                          thread_indices, frame->n_vectors, 1);
    2070             : 
    2071          12 :   if (n_enq < frame->n_vectors)
    2072           0 :     vlib_node_increment_counter (vm, node->node_index,
    2073             :                                  IP6_FULL_REASSEMBLY_HANDOFF_ERROR_CONGESTION_DROP,
    2074           0 :                                  frame->n_vectors - n_enq);
    2075          12 :   return frame->n_vectors;
    2076             : }
    2077             : 
    2078        2300 : VLIB_NODE_FN (ip6_full_reassembly_handoff_node) (vlib_main_t * vm,
    2079             :                                                  vlib_node_runtime_t * node,
    2080             :                                                  vlib_frame_t * frame)
    2081             : {
    2082           0 :   return ip6_full_reassembly_handoff_inline (vm, node, frame, NORMAL,
    2083             :                                              false /* is_local */);
    2084             : }
    2085             : 
    2086      183788 : VLIB_REGISTER_NODE (ip6_full_reassembly_handoff_node) = {
    2087             :   .name = "ip6-full-reassembly-handoff",
    2088             :   .vector_size = sizeof (u32),
    2089             :   .n_errors = ARRAY_LEN(ip6_full_reassembly_handoff_error_strings),
    2090             :   .error_strings = ip6_full_reassembly_handoff_error_strings,
    2091             :   .format_trace = format_ip6_full_reassembly_handoff_trace,
    2092             : 
    2093             :   .n_next_nodes = 1,
    2094             : 
    2095             :   .next_nodes = {
    2096             :     [0] = "error-drop",
    2097             :   },
    2098             : };
    2099             : 
    2100        2300 : VLIB_NODE_FN (ip6_local_full_reassembly_handoff_node)
    2101             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
    2102             : {
    2103           0 :   return ip6_full_reassembly_handoff_inline (vm, node, frame, NORMAL,
    2104             :                                              true /* is_feature */);
    2105             : }
    2106             : 
    2107      183788 : VLIB_REGISTER_NODE (ip6_local_full_reassembly_handoff_node) = {
    2108             :   .name = "ip6-local-full-reassembly-handoff",
    2109             :   .vector_size = sizeof (u32),
    2110             :   .n_errors = ARRAY_LEN(ip6_full_reassembly_handoff_error_strings),
    2111             :   .error_strings = ip6_full_reassembly_handoff_error_strings,
    2112             :   .format_trace = format_ip6_full_reassembly_handoff_trace,
    2113             : 
    2114             :   .n_next_nodes = 1,
    2115             : 
    2116             :   .next_nodes = {
    2117             :     [0] = "error-drop",
    2118             :   },
    2119             : };
    2120             : 
    2121        2312 : VLIB_NODE_FN (ip6_full_reassembly_feature_handoff_node) (vlib_main_t * vm,
    2122             :                                vlib_node_runtime_t * node, vlib_frame_t * frame)
    2123             : {
    2124          12 :   return ip6_full_reassembly_handoff_inline (vm, node, frame, FEATURE,
    2125             :                                              false /* is_local */);
    2126             : }
    2127             : 
    2128      183788 : VLIB_REGISTER_NODE (ip6_full_reassembly_feature_handoff_node) = {
    2129             :   .name = "ip6-full-reass-feature-hoff",
    2130             :   .vector_size = sizeof (u32),
    2131             :   .n_errors = ARRAY_LEN(ip6_full_reassembly_handoff_error_strings),
    2132             :   .error_strings = ip6_full_reassembly_handoff_error_strings,
    2133             :   .format_trace = format_ip6_full_reassembly_handoff_trace,
    2134             : 
    2135             :   .n_next_nodes = 1,
    2136             : 
    2137             :   .next_nodes = {
    2138             :     [0] = "error-drop",
    2139             :   },
    2140             : };
    2141             : 
    2142        2300 : VLIB_NODE_FN (ip6_full_reassembly_custom_handoff_node)
    2143             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
    2144             : {
    2145           0 :   return ip6_full_reassembly_handoff_inline (vm, node, frame, CUSTOM,
    2146             :                                              false /* is_local */);
    2147             : }
    2148             : 
    2149      183788 : VLIB_REGISTER_NODE (ip6_full_reassembly_custom_handoff_node) = {
    2150             :   .name = "ip6-full-reass-custom-hoff",
    2151             :   .vector_size = sizeof (u32),
    2152             :   .n_errors = ARRAY_LEN(ip6_full_reassembly_handoff_error_strings),
    2153             :   .error_strings = ip6_full_reassembly_handoff_error_strings,
    2154             :   .format_trace = format_ip6_full_reassembly_handoff_trace,
    2155             : 
    2156             :   .n_next_nodes = 1,
    2157             : 
    2158             :   .next_nodes = {
    2159             :     [0] = "error-drop",
    2160             :   },
    2161             : };
    2162             : 
    2163             : #ifndef CLIB_MARCH_VARIANT
    2164             : int
    2165          10 : ip6_full_reass_enable_disable_with_refcnt (u32 sw_if_index, int is_enable)
    2166             : {
    2167          10 :   ip6_full_reass_main_t *rm = &ip6_full_reass_main;
    2168          10 :   vec_validate (rm->feature_use_refcount_per_intf, sw_if_index);
    2169          10 :   if (is_enable)
    2170             :     {
    2171           5 :       if (!rm->feature_use_refcount_per_intf[sw_if_index])
    2172             :         {
    2173           5 :           ++rm->feature_use_refcount_per_intf[sw_if_index];
    2174           5 :           return vnet_feature_enable_disable ("ip6-unicast",
    2175             :                                               "ip6-full-reassembly-feature",
    2176             :                                               sw_if_index, 1, 0, 0);
    2177             :         }
    2178           0 :       ++rm->feature_use_refcount_per_intf[sw_if_index];
    2179             :     }
    2180             :   else
    2181             :     {
    2182           5 :       --rm->feature_use_refcount_per_intf[sw_if_index];
    2183           5 :       if (!rm->feature_use_refcount_per_intf[sw_if_index])
    2184           5 :         return vnet_feature_enable_disable ("ip6-unicast",
    2185             :                                             "ip6-full-reassembly-feature",
    2186             :                                             sw_if_index, 0, 0, 0);
    2187             :     }
    2188           0 :   return -1;
    2189             : }
    2190             : 
    2191             : void
    2192           6 : ip6_local_full_reass_enable_disable (int enable)
    2193             : {
    2194           6 :   if (enable)
    2195             :     {
    2196           2 :       if (!ip6_full_reass_main.is_local_reass_enabled)
    2197             :         {
    2198           1 :           ip6_full_reass_main.is_local_reass_enabled = 1;
    2199           1 :           ip6_register_protocol (IP_PROTOCOL_IPV6_FRAGMENTATION,
    2200             :                                  ip6_local_full_reass_node.index);
    2201             :         }
    2202             :     }
    2203             :   else
    2204             :     {
    2205           4 :       if (ip6_full_reass_main.is_local_reass_enabled)
    2206             :         {
    2207           2 :           ip6_full_reass_main.is_local_reass_enabled = 0;
    2208           2 :           ip6_unregister_protocol (IP_PROTOCOL_IPV6_FRAGMENTATION);
    2209             :         }
    2210             :     }
    2211           6 : }
    2212             : 
    2213             : int
    2214           0 : ip6_local_full_reass_enabled ()
    2215             : {
    2216           0 :   return ip6_full_reass_main.is_local_reass_enabled;
    2217             : }
    2218             : 
    2219             : #endif
    2220             : 
    2221             : /*
    2222             :  * fd.io coding-style-patch-verification: ON
    2223             :  *
    2224             :  * Local Variables:
    2225             :  * eval: (c-set-style "gnu")
    2226             :  * End:
    2227             :  */

Generated by: LCOV version 1.14