LCOV - code coverage report
Current view: top level - vnet/ip/reass - ip6_sv_reass.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 474 597 79.4 %
Date: 2023-07-05 22:20:52 Functions: 66 93 71.0 %

          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 Shallow Virtual Reassembly.
      19             :  *
      20             :  * This file contains the source code for IPv6 Shallow Virtual reassembly.
      21             :  */
      22             : 
      23             : #include <vppinfra/vec.h>
      24             : #include <vnet/vnet.h>
      25             : #include <vnet/ip/ip.h>
      26             : #include <vnet/ip/ip6_to_ip4.h>
      27             : #include <vppinfra/bihash_48_8.h>
      28             : #include <vnet/ip/reass/ip6_sv_reass.h>
      29             : #include <vnet/ip/ip6_inlines.h>
      30             : 
      31             : #define MSEC_PER_SEC 1000
      32             : #define IP6_SV_REASS_TIMEOUT_DEFAULT_MS 100
      33             : #define IP6_SV_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS 10000      // 10 seconds default
      34             : #define IP6_SV_REASS_MAX_REASSEMBLIES_DEFAULT 1024
      35             : #define IP6_SV_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT 3
      36             : #define IP6_SV_REASS_HT_LOAD_FACTOR (0.75)
      37             : 
      38             : typedef enum
      39             : {
      40             :   IP6_SV_REASS_RC_OK,
      41             :   IP6_SV_REASS_RC_TOO_MANY_FRAGMENTS,
      42             :   IP6_SV_REASS_RC_INTERNAL_ERROR,
      43             :   IP6_SV_REASS_RC_UNSUPP_IP_PROTO,
      44             :   IP6_SV_REASS_RC_INVALID_FRAG_LEN,
      45             : } ip6_sv_reass_rc_t;
      46             : 
      47             : typedef struct
      48             : {
      49             :   union
      50             :   {
      51             :     struct
      52             :     {
      53             :       ip6_address_t src;
      54             :       ip6_address_t dst;
      55             :       u32 fib_index;
      56             :       u32 frag_id;
      57             :       u8 unused[7];
      58             :       u8 proto;
      59             :     };
      60             :     u64 as_u64[6];
      61             :   };
      62             : } ip6_sv_reass_key_t;
      63             : 
      64             : typedef union
      65             : {
      66             :   struct
      67             :   {
      68             :     u32 reass_index;
      69             :     u32 thread_index;
      70             :   };
      71             :   u64 as_u64;
      72             : } ip6_sv_reass_val_t;
      73             : 
      74             : typedef union
      75             : {
      76             :   struct
      77             :   {
      78             :     ip6_sv_reass_key_t k;
      79             :     ip6_sv_reass_val_t v;
      80             :   };
      81             :   clib_bihash_kv_48_8_t kv;
      82             : } ip6_sv_reass_kv_t;
      83             : 
      84             : typedef struct
      85             : {
      86             :   // hash table key
      87             :   ip6_sv_reass_key_t key;
      88             :   // time when last packet was received
      89             :   f64 last_heard;
      90             :   // internal id of this reassembly
      91             :   u64 id;
      92             :   // trace operation counter
      93             :   u32 trace_op_counter;
      94             :   // buffer indexes of buffers in this reassembly in chronological order -
      95             :   // including overlaps and duplicate fragments
      96             :   u32 *cached_buffers;
      97             :   // set to true when this reassembly is completed
      98             :   bool is_complete;
      99             :   // ip protocol
     100             :   u8 ip_proto;
     101             :   u8 icmp_type_or_tcp_flags;
     102             :   u32 tcp_ack_number;
     103             :   u32 tcp_seq_number;
     104             :   // l4 src port
     105             :   u16 l4_src_port;
     106             :   // l4 dst port
     107             :   u16 l4_dst_port;
     108             :   // lru indexes
     109             :   u32 lru_prev;
     110             :   u32 lru_next;
     111             : } ip6_sv_reass_t;
     112             : 
     113             : typedef struct
     114             : {
     115             :   ip6_sv_reass_t *pool;
     116             :   u32 reass_n;
     117             :   u32 id_counter;
     118             :   clib_spinlock_t lock;
     119             :   // lru indexes
     120             :   u32 lru_first;
     121             :   u32 lru_last;
     122             : } ip6_sv_reass_per_thread_t;
     123             : 
     124             : typedef struct
     125             : {
     126             :   // IPv6 config
     127             :   u32 timeout_ms;
     128             :   f64 timeout;
     129             :   u32 expire_walk_interval_ms;
     130             :   // maximum number of fragments in one reassembly
     131             :   u32 max_reass_len;
     132             :   // maximum number of reassemblies
     133             :   u32 max_reass_n;
     134             : 
     135             :   // IPv6 runtime
     136             :   clib_bihash_48_8_t hash;
     137             : 
     138             :   // per-thread data
     139             :   ip6_sv_reass_per_thread_t *per_thread_data;
     140             : 
     141             :   // convenience
     142             :   vlib_main_t *vlib_main;
     143             :   vnet_main_t *vnet_main;
     144             : 
     145             :   // node index of ip6-drop node
     146             :   u32 ip6_drop_idx;
     147             :   u32 ip6_icmp_error_idx;
     148             :   u32 ip6_sv_reass_expire_node_idx;
     149             : 
     150             :   /** Worker handoff */
     151             :   u32 fq_index;
     152             :   u32 fq_feature_index;
     153             :   u32 fq_custom_context_index;
     154             : 
     155             :   // reference count for enabling/disabling feature - per interface
     156             :   u32 *feature_use_refcount_per_intf;
     157             : } ip6_sv_reass_main_t;
     158             : 
     159             : extern ip6_sv_reass_main_t ip6_sv_reass_main;
     160             : 
     161             : #ifndef CLIB_MARCH_VARIANT
     162             : ip6_sv_reass_main_t ip6_sv_reass_main;
     163             : #endif /* CLIB_MARCH_VARIANT */
     164             : 
     165             : typedef enum
     166             : {
     167             :   IP6_SV_REASSEMBLY_NEXT_INPUT,
     168             :   IP6_SV_REASSEMBLY_NEXT_DROP,
     169             :   IP6_SV_REASSEMBLY_NEXT_ICMP_ERROR,
     170             :   IP6_SV_REASSEMBLY_NEXT_HANDOFF,
     171             :   IP6_SV_REASSEMBLY_N_NEXT,
     172             : } ip6_sv_reass_next_t;
     173             : 
     174             : typedef enum
     175             : {
     176             :   REASS_FRAGMENT_CACHE,
     177             :   REASS_FINISH,
     178             :   REASS_FRAGMENT_FORWARD,
     179             :   REASS_PASSTHROUGH,
     180             : } ip6_sv_reass_trace_operation_e;
     181             : 
     182             : typedef struct
     183             : {
     184             :   ip6_sv_reass_trace_operation_e action;
     185             :   u32 reass_id;
     186             :   u32 op_id;
     187             :   u8 ip_proto;
     188             :   u16 l4_src_port;
     189             :   u16 l4_dst_port;
     190             : } ip6_sv_reass_trace_t;
     191             : 
     192             : static u8 *
     193         203 : format_ip6_sv_reass_trace (u8 * s, va_list * args)
     194             : {
     195         203 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     196         203 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     197         203 :   ip6_sv_reass_trace_t *t = va_arg (*args, ip6_sv_reass_trace_t *);
     198         203 :   if (REASS_PASSTHROUGH != t->action)
     199             :     {
     200         164 :       s = format (s, "reass id: %u, op id: %u ", t->reass_id, t->op_id);
     201             :     }
     202         203 :   switch (t->action)
     203             :     {
     204           6 :     case REASS_FRAGMENT_CACHE:
     205           6 :       s = format (s, "[cached]");
     206           6 :       break;
     207          16 :     case REASS_FINISH:
     208             :       s =
     209          32 :         format (s, "[finish, ip proto=%u, src_port=%u, dst_port=%u]",
     210          16 :                 t->ip_proto, clib_net_to_host_u16 (t->l4_src_port),
     211          16 :                 clib_net_to_host_u16 (t->l4_dst_port));
     212          16 :       break;
     213         142 :     case REASS_FRAGMENT_FORWARD:
     214             :       s =
     215         284 :         format (s, "[forward, ip proto=%u, src_port=%u, dst_port=%u]",
     216         142 :                 t->ip_proto, clib_net_to_host_u16 (t->l4_src_port),
     217         142 :                 clib_net_to_host_u16 (t->l4_dst_port));
     218         142 :       break;
     219          39 :     case REASS_PASSTHROUGH:
     220          39 :       s = format (s, "[not fragmented or atomic fragment]");
     221          39 :       break;
     222             :     }
     223         203 :   return s;
     224             : }
     225             : 
     226             : static void
     227         807 : ip6_sv_reass_add_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
     228             :                         ip6_sv_reass_t * reass, u32 bi,
     229             :                         ip6_sv_reass_trace_operation_e action,
     230             :                         u32 ip_proto, u16 l4_src_port, u16 l4_dst_port)
     231             : {
     232         807 :   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     233         807 :   if (pool_is_free_index
     234         807 :       (vm->trace_main.trace_buffer_pool, vlib_buffer_get_trace_index (b)))
     235             :     {
     236             :       // this buffer's trace is gone
     237           0 :       b->flags &= ~VLIB_BUFFER_IS_TRACED;
     238           0 :       return;
     239             :     }
     240         807 :   ip6_sv_reass_trace_t *t = vlib_add_trace (vm, node, b, sizeof (t[0]));
     241         807 :   if (reass)
     242             :     {
     243         730 :       t->reass_id = reass->id;
     244         730 :       t->op_id = reass->trace_op_counter;
     245         730 :       ++reass->trace_op_counter;
     246             :     }
     247         807 :   t->action = action;
     248         807 :   t->ip_proto = ip_proto;
     249         807 :   t->l4_src_port = l4_src_port;
     250         807 :   t->l4_dst_port = l4_dst_port;
     251             : #if 0
     252             :   static u8 *s = NULL;
     253             :   s = format (s, "%U", format_ip6_sv_reass_trace, NULL, NULL, t);
     254             :   printf ("%.*s\n", vec_len (s), s);
     255             :   fflush (stdout);
     256             :   vec_reset_length (s);
     257             : #endif
     258             : }
     259             : 
     260             : always_inline void
     261          18 : ip6_sv_reass_free (vlib_main_t * vm, ip6_sv_reass_main_t * rm,
     262             :                    ip6_sv_reass_per_thread_t * rt, ip6_sv_reass_t * reass)
     263             : {
     264             :   clib_bihash_kv_48_8_t kv;
     265          18 :   kv.key[0] = reass->key.as_u64[0];
     266          18 :   kv.key[1] = reass->key.as_u64[1];
     267          18 :   kv.key[2] = reass->key.as_u64[2];
     268          18 :   kv.key[3] = reass->key.as_u64[3];
     269          18 :   kv.key[4] = reass->key.as_u64[4];
     270          18 :   kv.key[5] = reass->key.as_u64[5];
     271          18 :   clib_bihash_add_del_48_8 (&rm->hash, &kv, 0);
     272          18 :   vlib_buffer_free (vm, reass->cached_buffers,
     273          18 :                     vec_len (reass->cached_buffers));
     274          18 :   vec_free (reass->cached_buffers);
     275          18 :   reass->cached_buffers = NULL;
     276          18 :   if (~0 != reass->lru_prev)
     277             :     {
     278           0 :       ip6_sv_reass_t *lru_prev =
     279           0 :         pool_elt_at_index (rt->pool, reass->lru_prev);
     280           0 :       lru_prev->lru_next = reass->lru_next;
     281             :     }
     282          18 :   if (~0 != reass->lru_next)
     283             :     {
     284           1 :       ip6_sv_reass_t *lru_next =
     285           1 :         pool_elt_at_index (rt->pool, reass->lru_next);
     286           1 :       lru_next->lru_prev = reass->lru_prev;
     287             :     }
     288          18 :   if (rt->lru_first == reass - rt->pool)
     289             :     {
     290          18 :       rt->lru_first = reass->lru_next;
     291             :     }
     292          18 :   if (rt->lru_last == reass - rt->pool)
     293             :     {
     294          17 :       rt->lru_last = reass->lru_prev;
     295             :     }
     296          18 :   pool_put (rt->pool, reass);
     297          18 :   --rt->reass_n;
     298          18 : }
     299             : 
     300             : always_inline void
     301          20 : ip6_sv_reass_init (ip6_sv_reass_t * reass)
     302             : {
     303          20 :   reass->cached_buffers = NULL;
     304          20 :   reass->is_complete = false;
     305          20 : }
     306             : 
     307             : always_inline ip6_sv_reass_t *
     308         706 : ip6_sv_reass_find_or_create (vlib_main_t *vm, ip6_sv_reass_main_t *rm,
     309             :                              ip6_sv_reass_per_thread_t *rt,
     310             :                              ip6_sv_reass_kv_t *kv, u8 *do_handoff)
     311             : {
     312         706 :   ip6_sv_reass_t *reass = NULL;
     313         706 :   f64 now = vlib_time_now (vm);
     314             : 
     315         706 : again:
     316             : 
     317         706 :   if (!clib_bihash_search_48_8 (&rm->hash, &kv->kv, &kv->kv))
     318             :     {
     319         686 :       if (vm->thread_index != kv->v.thread_index)
     320             :         {
     321           0 :           *do_handoff = 1;
     322           0 :           return NULL;
     323             :         }
     324         686 :       reass = pool_elt_at_index (rt->pool, kv->v.reass_index);
     325             : 
     326         686 :       if (now > reass->last_heard + rm->timeout)
     327             :         {
     328           0 :           ip6_sv_reass_free (vm, rm, rt, reass);
     329           0 :           reass = NULL;
     330             :         }
     331             :     }
     332             : 
     333         706 :   if (reass)
     334             :     {
     335         686 :       reass->last_heard = now;
     336         686 :       return reass;
     337             :     }
     338             : 
     339          20 :   if (rt->reass_n >= rm->max_reass_n)
     340             :     {
     341           9 :       reass = pool_elt_at_index (rt->pool, rt->lru_first);
     342           9 :       ip6_sv_reass_free (vm, rm, rt, reass);
     343             :     }
     344             : 
     345          20 :   pool_get (rt->pool, reass);
     346          20 :   clib_memset (reass, 0, sizeof (*reass));
     347          20 :   reass->id = ((u64) vm->thread_index * 1000000000) + rt->id_counter;
     348          20 :   ++rt->id_counter;
     349          20 :   ip6_sv_reass_init (reass);
     350          20 :   ++rt->reass_n;
     351             : 
     352          20 :   reass->lru_prev = reass->lru_next = ~0;
     353             : 
     354          20 :   if (~0 != rt->lru_last)
     355             :     {
     356           1 :       ip6_sv_reass_t *lru_last = pool_elt_at_index (rt->pool, rt->lru_last);
     357           1 :       reass->lru_prev = rt->lru_last;
     358           1 :       lru_last->lru_next = rt->lru_last = reass - rt->pool;
     359             :     }
     360             : 
     361          20 :   if (~0 == rt->lru_first)
     362             :     {
     363          19 :       rt->lru_first = rt->lru_last = reass - rt->pool;
     364             :     }
     365             : 
     366          20 :   reass->key.as_u64[0] = kv->kv.key[0];
     367          20 :   reass->key.as_u64[1] = kv->kv.key[1];
     368          20 :   reass->key.as_u64[2] = kv->kv.key[2];
     369          20 :   reass->key.as_u64[3] = kv->kv.key[3];
     370          20 :   reass->key.as_u64[4] = kv->kv.key[4];
     371          20 :   reass->key.as_u64[5] = kv->kv.key[5];
     372          20 :   kv->v.reass_index = (reass - rt->pool);
     373          20 :   kv->v.thread_index = vm->thread_index;
     374          20 :   reass->last_heard = now;
     375             : 
     376          20 :   int rv = clib_bihash_add_del_48_8 (&rm->hash, &kv->kv, 2);
     377          20 :   if (rv)
     378             :     {
     379           0 :       ip6_sv_reass_free (vm, rm, rt, reass);
     380           0 :       reass = NULL;
     381             :       // if other worker created a context already work with the other copy
     382           0 :       if (-2 == rv)
     383           0 :         goto again;
     384             :     }
     385             : 
     386          20 :   return reass;
     387             : }
     388             : 
     389             : always_inline ip6_sv_reass_rc_t
     390          28 : ip6_sv_reass_update (vlib_main_t *vm, vlib_node_runtime_t *node,
     391             :                      ip6_sv_reass_main_t *rm, ip6_sv_reass_t *reass, u32 bi0,
     392             :                      ip6_frag_hdr_t *frag_hdr)
     393             : {
     394          28 :   vlib_buffer_t *fb = vlib_get_buffer (vm, bi0);
     395          28 :   vnet_buffer_opaque_t *fvnb = vnet_buffer (fb);
     396          28 :   fvnb->ip.reass.ip6_frag_hdr_offset =
     397          28 :     (u8 *) frag_hdr - (u8 *) vlib_buffer_get_current (fb);
     398          28 :   ip6_header_t *fip = vlib_buffer_get_current (fb);
     399          28 :   if (fb->current_length < sizeof (*fip) ||
     400          28 :       fvnb->ip.reass.ip6_frag_hdr_offset == 0 ||
     401          28 :       fvnb->ip.reass.ip6_frag_hdr_offset >= fb->current_length)
     402             :     {
     403           0 :       return IP6_SV_REASS_RC_INTERNAL_ERROR;
     404             :     }
     405             : 
     406          28 :   u32 fragment_first = fvnb->ip.reass.fragment_first =
     407          28 :     ip6_frag_hdr_offset_bytes (frag_hdr);
     408          28 :   u32 fragment_length =
     409          28 :     vlib_buffer_length_in_chain (vm, fb) -
     410          28 :     (fvnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
     411          28 :   if (0 == fragment_length)
     412             :     {
     413           0 :       return IP6_SV_REASS_RC_INVALID_FRAG_LEN;
     414             :     }
     415          28 :   u32 fragment_last = fvnb->ip.reass.fragment_last =
     416          28 :     fragment_first + fragment_length - 1;
     417          28 :   fvnb->ip.reass.range_first = fragment_first;
     418          28 :   fvnb->ip.reass.range_last = fragment_last;
     419          28 :   fvnb->ip.reass.next_range_bi = ~0;
     420          28 :   if (0 == fragment_first)
     421             :     {
     422          19 :       if (!ip6_get_port
     423          19 :           (vm, fb, fip, fb->current_length, &reass->ip_proto,
     424             :            &reass->l4_src_port, &reass->l4_dst_port,
     425             :            &reass->icmp_type_or_tcp_flags, &reass->tcp_ack_number,
     426             :            &reass->tcp_seq_number))
     427           0 :         return IP6_SV_REASS_RC_UNSUPP_IP_PROTO;
     428             : 
     429          19 :       reass->is_complete = true;
     430          19 :       vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
     431          19 :       if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
     432             :         {
     433          19 :           ip6_sv_reass_add_trace (vm, node, reass, bi0, REASS_FINISH,
     434          19 :                                   reass->ip_proto, reass->l4_src_port,
     435          19 :                                   reass->l4_dst_port);
     436             :         }
     437             :     }
     438          28 :   vec_add1 (reass->cached_buffers, bi0);
     439          28 :   if (!reass->is_complete)
     440             :     {
     441           9 :       if (PREDICT_FALSE (fb->flags & VLIB_BUFFER_IS_TRACED))
     442             :         {
     443           9 :           ip6_sv_reass_add_trace (vm, node, reass, bi0, REASS_FRAGMENT_CACHE,
     444           9 :                                   reass->ip_proto, reass->l4_src_port,
     445           9 :                                   reass->l4_dst_port);
     446             :         }
     447           9 :       if (vec_len (reass->cached_buffers) > rm->max_reass_len)
     448             :         {
     449           0 :           return IP6_SV_REASS_RC_TOO_MANY_FRAGMENTS;
     450             :         }
     451             :     }
     452          28 :   return IP6_SV_REASS_RC_OK;
     453             : }
     454             : 
     455             : always_inline bool
     456          19 : ip6_sv_reass_verify_upper_layer_present (vlib_node_runtime_t *node,
     457             :                                          vlib_buffer_t *b,
     458             :                                          ip6_ext_hdr_chain_t *hc)
     459             : {
     460          19 :   int nh = hc->eh[hc->length - 1].protocol;
     461             :   /* Checking to see if it's a terminating header */
     462          19 :   if (ip6_ext_hdr (nh))
     463             :     {
     464           0 :       icmp6_error_set_vnet_buffer (
     465             :         b, ICMP6_parameter_problem,
     466             :         ICMP6_parameter_problem_first_fragment_has_incomplete_header_chain, 0);
     467           0 :       b->error = node->errors[IP6_ERROR_REASS_MISSING_UPPER];
     468           0 :       return false;
     469             :     }
     470          19 :   return true;
     471             : }
     472             : 
     473             : always_inline bool
     474         706 : ip6_sv_reass_verify_fragment_multiple_8 (vlib_main_t * vm,
     475             :                                          vlib_buffer_t * b,
     476             :                                          ip6_frag_hdr_t * frag_hdr)
     477             : {
     478         706 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
     479         706 :   ip6_header_t *ip = vlib_buffer_get_current (b);
     480         706 :   int more_fragments = ip6_frag_hdr_more (frag_hdr);
     481         706 :   u32 fragment_length =
     482         706 :     vlib_buffer_length_in_chain (vm, b) -
     483         706 :     (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
     484         706 :   if (more_fragments && 0 != fragment_length % 8)
     485             :     {
     486           0 :       icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
     487             :                                    ICMP6_parameter_problem_erroneous_header_field,
     488             :                                    (u8 *) & ip->payload_length - (u8 *) ip);
     489           0 :       return false;
     490             :     }
     491         706 :   return true;
     492             : }
     493             : 
     494             : always_inline bool
     495         706 : ip6_sv_reass_verify_packet_size_lt_64k (vlib_main_t * vm,
     496             :                                         vlib_buffer_t * b,
     497             :                                         ip6_frag_hdr_t * frag_hdr)
     498             : {
     499         706 :   vnet_buffer_opaque_t *vnb = vnet_buffer (b);
     500         706 :   u32 fragment_first = ip6_frag_hdr_offset_bytes (frag_hdr);
     501         706 :   u32 fragment_length =
     502         706 :     vlib_buffer_length_in_chain (vm, b) -
     503         706 :     (vnb->ip.reass.ip6_frag_hdr_offset + sizeof (*frag_hdr));
     504         706 :   if (fragment_first + fragment_length > 65535)
     505             :     {
     506           0 :       ip6_header_t *ip0 = vlib_buffer_get_current (b);
     507           0 :       icmp6_error_set_vnet_buffer (b, ICMP6_parameter_problem,
     508             :                                    ICMP6_parameter_problem_erroneous_header_field,
     509           0 :                                    (u8 *) & frag_hdr->fragment_offset_and_more
     510           0 :                                    - (u8 *) ip0);
     511           0 :       return false;
     512             :     }
     513         706 :   return true;
     514             : }
     515             : 
     516             : always_inline uword
     517          69 : ip6_sv_reassembly_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
     518             :                           vlib_frame_t *frame, bool is_feature,
     519             :                           bool custom_next, bool custom_context)
     520             : {
     521          69 :   u32 *from = vlib_frame_vector_args (frame);
     522             :   u32 n_left_from, n_left_to_next, *to_next, *to_next_aux, next_index;
     523          69 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
     524          69 :   ip6_sv_reass_per_thread_t *rt = &rm->per_thread_data[vm->thread_index];
     525             :   u32 *context;
     526          69 :   if (custom_context)
     527           0 :     context = vlib_frame_aux_args (frame);
     528             : 
     529          69 :   clib_spinlock_lock (&rt->lock);
     530             : 
     531          69 :   n_left_from = frame->n_vectors;
     532          69 :   next_index = node->cached_next_index;
     533             : 
     534         138 :   while (n_left_from > 0)
     535             :     {
     536          69 :       if (custom_context)
     537           0 :         vlib_get_next_frame_with_aux_safe (vm, node, next_index, to_next,
     538             :                                            to_next_aux, n_left_to_next);
     539             :       else
     540          69 :         vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     541             : 
     542         853 :       while (n_left_from > 0 && n_left_to_next > 0)
     543             :         {
     544             :           u32 bi0;
     545             :           vlib_buffer_t *b0;
     546         784 :           u32 next0 = IP6_SV_REASSEMBLY_NEXT_DROP;
     547         784 :           u32 error0 = IP6_ERROR_NONE;
     548         784 :           u8 forward_context = 0;
     549         784 :           bi0 = from[0];
     550         784 :           b0 = vlib_get_buffer (vm, bi0);
     551             : 
     552         784 :           ip6_header_t *ip0 = vlib_buffer_get_current (b0);
     553             :           ip6_frag_hdr_t *frag_hdr;
     554             :           ip6_ext_hdr_chain_t hdr_chain;
     555         784 :           bool is_atomic_fragment = false;
     556             : 
     557         784 :           int res = ip6_ext_header_walk (
     558             :             b0, ip0, IP_PROTOCOL_IPV6_FRAGMENTATION, &hdr_chain);
     559         784 :           if (res >= 0 &&
     560         783 :               hdr_chain.eh[res].protocol == IP_PROTOCOL_IPV6_FRAGMENTATION)
     561             :             {
     562             :               frag_hdr =
     563         708 :                 ip6_ext_next_header_offset (ip0, hdr_chain.eh[res].offset);
     564         729 :               is_atomic_fragment = (0 == ip6_frag_hdr_offset (frag_hdr) &&
     565          21 :                                     !ip6_frag_hdr_more (frag_hdr));
     566             :             }
     567             : 
     568         784 :           if (res < 0 ||
     569         783 :               hdr_chain.eh[res].protocol != IP_PROTOCOL_IPV6_FRAGMENTATION ||
     570             :               is_atomic_fragment)
     571             :             {
     572             :               // this is a regular unfragmented packet or an atomic fragment
     573          78 :               if (!ip6_get_port
     574          78 :                   (vm, b0, ip0, b0->current_length,
     575          78 :                    &(vnet_buffer (b0)->ip.reass.ip_proto),
     576          78 :                    &(vnet_buffer (b0)->ip.reass.l4_src_port),
     577          78 :                    &(vnet_buffer (b0)->ip.reass.l4_dst_port),
     578          78 :                    &(vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags),
     579          78 :                    &(vnet_buffer (b0)->ip.reass.tcp_ack_number),
     580          78 :                    &(vnet_buffer (b0)->ip.reass.tcp_seq_number)))
     581             :                 {
     582           1 :                   error0 = IP6_ERROR_REASS_UNSUPP_IP_PROTO;
     583           1 :                   b0->error = node->errors[error0];
     584           1 :                   next0 = IP6_SV_REASSEMBLY_NEXT_DROP;
     585           1 :                   goto packet_enqueue;
     586             :                 }
     587          77 :               vnet_buffer (b0)->ip.reass.is_non_first_fragment = 0;
     588          77 :               next0 = custom_next ? vnet_buffer (b0)->ip.reass.next_index :
     589             :                                           IP6_SV_REASSEMBLY_NEXT_INPUT;
     590          77 :               if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
     591             :                 {
     592          77 :                   ip6_sv_reass_add_trace (
     593             :                     vm, node, NULL, bi0, REASS_PASSTHROUGH,
     594          77 :                     vnet_buffer (b0)->ip.reass.ip_proto,
     595          77 :                     vnet_buffer (b0)->ip.reass.l4_src_port,
     596          77 :                     vnet_buffer (b0)->ip.reass.l4_dst_port);
     597             :                 }
     598          77 :               goto packet_enqueue;
     599             :             }
     600             : 
     601         706 :           vnet_buffer (b0)->ip.reass.ip6_frag_hdr_offset =
     602         706 :             hdr_chain.eh[res].offset;
     603             : 
     604         706 :           if (0 == ip6_frag_hdr_offset (frag_hdr))
     605             :             {
     606             :               // first fragment - verify upper-layer is present
     607          19 :               if (!ip6_sv_reass_verify_upper_layer_present (node, b0,
     608             :                                                             &hdr_chain))
     609             :                 {
     610           0 :                   next0 = IP6_SV_REASSEMBLY_NEXT_ICMP_ERROR;
     611           0 :                   goto packet_enqueue;
     612             :                 }
     613             :             }
     614         706 :           if (!ip6_sv_reass_verify_fragment_multiple_8 (vm, b0, frag_hdr) ||
     615         706 :               !ip6_sv_reass_verify_packet_size_lt_64k (vm, b0, frag_hdr))
     616             :             {
     617           0 :               next0 = IP6_SV_REASSEMBLY_NEXT_ICMP_ERROR;
     618           0 :               goto packet_enqueue;
     619             :             }
     620             : 
     621             :           ip6_sv_reass_kv_t kv;
     622         706 :           u8 do_handoff = 0;
     623             : 
     624         706 :           kv.k.as_u64[0] = ip0->src_address.as_u64[0];
     625         706 :           kv.k.as_u64[1] = ip0->src_address.as_u64[1];
     626         706 :           kv.k.as_u64[2] = ip0->dst_address.as_u64[0];
     627         706 :           kv.k.as_u64[3] = ip0->dst_address.as_u64[1];
     628         706 :           if (custom_context)
     629           0 :             kv.k.as_u64[4] =
     630           0 :               (u64) *context << 32 | (u64) frag_hdr->identification;
     631             :           else
     632         706 :             kv.k.as_u64[4] =
     633         706 :               ((u64) vec_elt (ip6_main.fib_index_by_sw_if_index,
     634             :                               vnet_buffer (b0)->sw_if_index[VLIB_RX]))
     635         706 :                 << 32 |
     636         706 :               (u64) frag_hdr->identification;
     637         706 :           kv.k.as_u64[5] = ip0->protocol;
     638             : 
     639             :           ip6_sv_reass_t *reass =
     640         706 :             ip6_sv_reass_find_or_create (vm, rm, rt, &kv, &do_handoff);
     641             : 
     642         706 :           if (PREDICT_FALSE (do_handoff))
     643             :             {
     644           0 :               next0 = IP6_SV_REASSEMBLY_NEXT_HANDOFF;
     645           0 :               vnet_buffer (b0)->ip.reass.owner_thread_index =
     646           0 :                 kv.v.thread_index;
     647           0 :               if (custom_context)
     648           0 :                 forward_context = 1;
     649           0 :               goto packet_enqueue;
     650             :             }
     651             : 
     652         706 :           if (!reass)
     653             :             {
     654           0 :               next0 = IP6_SV_REASSEMBLY_NEXT_DROP;
     655           0 :               error0 = IP6_ERROR_REASS_LIMIT_REACHED;
     656           0 :               b0->error = node->errors[error0];
     657           0 :               goto packet_enqueue;
     658             :             }
     659             : 
     660         706 :           if (reass->is_complete)
     661             :             {
     662         678 :               vnet_buffer (b0)->ip.reass.is_non_first_fragment =
     663         678 :                 ! !ip6_frag_hdr_offset (frag_hdr);
     664         678 :               vnet_buffer (b0)->ip.reass.ip_proto = reass->ip_proto;
     665         678 :               vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags =
     666         678 :                 reass->icmp_type_or_tcp_flags;
     667         678 :               vnet_buffer (b0)->ip.reass.tcp_ack_number =
     668         678 :                 reass->tcp_ack_number;
     669         678 :               vnet_buffer (b0)->ip.reass.tcp_seq_number =
     670         678 :                 reass->tcp_seq_number;
     671         678 :               vnet_buffer (b0)->ip.reass.l4_src_port = reass->l4_src_port;
     672         678 :               vnet_buffer (b0)->ip.reass.l4_dst_port = reass->l4_dst_port;
     673         678 :               next0 = custom_next ? vnet_buffer (b0)->ip.reass.next_index :
     674             :                                           IP6_SV_REASSEMBLY_NEXT_INPUT;
     675         678 :               if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
     676             :                 {
     677         678 :                   ip6_sv_reass_add_trace (
     678             :                     vm, node, reass, bi0, REASS_FRAGMENT_FORWARD,
     679         678 :                     reass->ip_proto, reass->l4_src_port, reass->l4_dst_port);
     680             :                 }
     681         678 :               goto packet_enqueue;
     682             :             }
     683             : 
     684          28 :           u32 counter = ~0;
     685          28 :           switch (ip6_sv_reass_update (vm, node, rm, reass, bi0, frag_hdr))
     686             :             {
     687          28 :             case IP6_SV_REASS_RC_OK:
     688             :               /* nothing to do here */
     689          28 :               break;
     690           0 :             case IP6_SV_REASS_RC_TOO_MANY_FRAGMENTS:
     691           0 :               counter = IP6_ERROR_REASS_FRAGMENT_CHAIN_TOO_LONG;
     692           0 :               break;
     693           0 :             case IP6_SV_REASS_RC_UNSUPP_IP_PROTO:
     694           0 :               counter = IP6_ERROR_REASS_UNSUPP_IP_PROTO;
     695           0 :               break;
     696           0 :             case IP6_SV_REASS_RC_INTERNAL_ERROR:
     697           0 :               counter = IP6_ERROR_REASS_INTERNAL_ERROR;
     698           0 :               break;
     699           0 :             case IP6_SV_REASS_RC_INVALID_FRAG_LEN:
     700           0 :               counter = IP6_ERROR_REASS_INVALID_FRAG_LEN;
     701           0 :               break;
     702             :             }
     703          28 :           if (~0 != counter)
     704             :             {
     705           0 :               vlib_node_increment_counter (vm, node->node_index, counter, 1);
     706           0 :               ip6_sv_reass_free (vm, rm, rt, reass);
     707           0 :               goto next_packet;
     708             :             }
     709             : 
     710          28 :           if (reass->is_complete)
     711             :             {
     712             :               u32 idx;
     713          43 :               vec_foreach_index (idx, reass->cached_buffers)
     714             :               {
     715          24 :                 u32 bi0 = vec_elt (reass->cached_buffers, idx);
     716          24 :                 if (0 == n_left_to_next)
     717             :                   {
     718           0 :                     vlib_put_next_frame (vm, node, next_index,
     719             :                                          n_left_to_next);
     720           0 :                     vlib_get_next_frame (vm, node, next_index, to_next,
     721             :                                          n_left_to_next);
     722             :                   }
     723          24 :                 to_next[0] = bi0;
     724          24 :                 to_next += 1;
     725          24 :                 n_left_to_next -= 1;
     726          24 :                 b0 = vlib_get_buffer (vm, bi0);
     727          24 :                 if (is_feature)
     728             :                   {
     729          24 :                     vnet_feature_next (&next0, b0);
     730             :                   }
     731          24 :                 frag_hdr =
     732          24 :                   vlib_buffer_get_current (b0) +
     733          24 :                   vnet_buffer (b0)->ip.reass.ip6_frag_hdr_offset;
     734          24 :                 vnet_buffer (b0)->ip.reass.is_non_first_fragment =
     735          24 :                   ! !ip6_frag_hdr_offset (frag_hdr);
     736          24 :                 vnet_buffer (b0)->ip.reass.ip_proto = reass->ip_proto;
     737          24 :                 vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags =
     738          24 :                   reass->icmp_type_or_tcp_flags;
     739          24 :                 vnet_buffer (b0)->ip.reass.tcp_ack_number =
     740          24 :                   reass->tcp_ack_number;
     741          24 :                 vnet_buffer (b0)->ip.reass.tcp_seq_number =
     742          24 :                   reass->tcp_seq_number;
     743          24 :                 vnet_buffer (b0)->ip.reass.l4_src_port = reass->l4_src_port;
     744          24 :                 vnet_buffer (b0)->ip.reass.l4_dst_port = reass->l4_dst_port;
     745          24 :                 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
     746             :                   {
     747          24 :                     ip6_sv_reass_add_trace (
     748             :                       vm, node, reass, bi0, REASS_FRAGMENT_FORWARD,
     749          24 :                       reass->ip_proto, reass->l4_src_port, reass->l4_dst_port);
     750             :                   }
     751          24 :                 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
     752             :                                                  to_next, n_left_to_next, bi0,
     753             :                                                  next0);
     754             :               }
     755          19 :               vec_set_len (reass->cached_buffers,
     756             :                            0); // buffers are owned by frame now
     757             :             }
     758          28 :           goto next_packet;
     759             : 
     760         756 :         packet_enqueue:
     761         756 :           to_next[0] = bi0;
     762         756 :           to_next += 1;
     763         756 :           n_left_to_next -= 1;
     764         756 :           if (is_feature && IP6_ERROR_NONE == error0)
     765             :             {
     766         755 :               b0 = vlib_get_buffer (vm, bi0);
     767         755 :               vnet_feature_next (&next0, b0);
     768             :             }
     769         756 :           if (custom_context && forward_context)
     770             :             {
     771           0 :               if (to_next_aux)
     772             :                 {
     773           0 :                   to_next_aux[0] = *context;
     774           0 :                   to_next_aux += 1;
     775             :                 }
     776           0 :               vlib_validate_buffer_enqueue_with_aux_x1 (
     777             :                 vm, node, next_index, to_next, to_next_aux, n_left_to_next,
     778             :                 bi0, *context, next0);
     779             :             }
     780             :           else
     781         756 :             vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     782             :                                              n_left_to_next, bi0, next0);
     783             : 
     784         750 :         next_packet:
     785         784 :           from += 1;
     786         784 :           if (custom_context)
     787           0 :             context += 1;
     788         784 :           n_left_from -= 1;
     789             :         }
     790             : 
     791          69 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     792             :     }
     793             : 
     794          69 :   clib_spinlock_unlock (&rt->lock);
     795          69 :   return frame->n_vectors;
     796             : }
     797             : 
     798        2236 : VLIB_NODE_FN (ip6_sv_reass_node) (vlib_main_t * vm,
     799             :                                   vlib_node_runtime_t * node,
     800             :                                   vlib_frame_t * frame)
     801             : {
     802           0 :   return ip6_sv_reassembly_inline (vm, node, frame, false /* is_feature */,
     803             :                                    false /* custom next */,
     804             :                                    false /* custom context */);
     805             : }
     806             : 
     807             : /* *INDENT-OFF* */
     808      178120 : VLIB_REGISTER_NODE (ip6_sv_reass_node) = {
     809             :     .name = "ip6-sv-reassembly",
     810             :     .vector_size = sizeof (u32),
     811             :     .format_trace = format_ip6_sv_reass_trace,
     812             :     .n_errors = IP6_N_ERROR,
     813             :     .error_counters = ip6_error_counters,
     814             :     .n_next_nodes = IP6_SV_REASSEMBLY_N_NEXT,
     815             :     .next_nodes =
     816             :         {
     817             :                 [IP6_SV_REASSEMBLY_NEXT_INPUT] = "ip6-input",
     818             :                 [IP6_SV_REASSEMBLY_NEXT_DROP] = "ip6-drop",
     819             :                 [IP6_SV_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
     820             :                 [IP6_SV_REASSEMBLY_NEXT_HANDOFF] = "ip6-sv-reassembly-handoff",
     821             :         },
     822             : };
     823             : /* *INDENT-ON* */
     824             : 
     825        2305 : VLIB_NODE_FN (ip6_sv_reass_node_feature) (vlib_main_t * vm,
     826             :                                           vlib_node_runtime_t * node,
     827             :                                           vlib_frame_t * frame)
     828             : {
     829          69 :   return ip6_sv_reassembly_inline (vm, node, frame, true /* is_feature */,
     830             :                                    false /* custom next */,
     831             :                                    false /* custom context */);
     832             : }
     833             : 
     834             : /* *INDENT-OFF* */
     835      178120 : VLIB_REGISTER_NODE (ip6_sv_reass_node_feature) = {
     836             :     .name = "ip6-sv-reassembly-feature",
     837             :     .vector_size = sizeof (u32),
     838             :     .format_trace = format_ip6_sv_reass_trace,
     839             :     .n_errors = IP6_N_ERROR,
     840             :     .error_counters = ip6_error_counters,
     841             :     .n_next_nodes = IP6_SV_REASSEMBLY_N_NEXT,
     842             :     .next_nodes =
     843             :         {
     844             :                 [IP6_SV_REASSEMBLY_NEXT_INPUT] = "ip6-input",
     845             :                 [IP6_SV_REASSEMBLY_NEXT_DROP] = "ip6-drop",
     846             :                 [IP6_SV_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
     847             :                 [IP6_SV_REASSEMBLY_NEXT_HANDOFF] = "ip6-sv-reass-feature-hoff",
     848             :         },
     849             : };
     850             : /* *INDENT-ON* */
     851             : 
     852             : /* *INDENT-OFF* */
     853       70583 : VNET_FEATURE_INIT (ip6_sv_reassembly_feature) = {
     854             :     .arc_name = "ip6-unicast",
     855             :     .node_name = "ip6-sv-reassembly-feature",
     856             :     .runs_before = VNET_FEATURES ("ip6-lookup"),
     857             :     .runs_after = 0,
     858             : };
     859             : /* *INDENT-ON* */
     860             : 
     861        2236 : VLIB_NODE_FN (ip6_sv_reass_custom_context_node)
     862             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     863             : {
     864           0 :   return ip6_sv_reassembly_inline (vm, node, frame, false /* is_feature */,
     865             :                                    true /* custom next */,
     866             :                                    true /* custom context */);
     867             : }
     868             : 
     869      178120 : VLIB_REGISTER_NODE (ip6_sv_reass_custom_context_node) = {
     870             :     .name = "ip6-sv-reassembly-custom-context",
     871             :     .vector_size = sizeof (u32),
     872             :     .aux_size = sizeof (u32),
     873             :     .format_trace = format_ip6_sv_reass_trace,
     874             :     .n_errors = IP6_N_ERROR,
     875             :     .error_counters = ip6_error_counters,
     876             :     .n_next_nodes = IP6_SV_REASSEMBLY_N_NEXT,
     877             :     .next_nodes =
     878             :         {
     879             :                 [IP6_SV_REASSEMBLY_NEXT_INPUT] = "ip6-input",
     880             :                 [IP6_SV_REASSEMBLY_NEXT_DROP] = "ip6-drop",
     881             :                 [IP6_SV_REASSEMBLY_NEXT_ICMP_ERROR] = "ip6-icmp-error",
     882             :                 [IP6_SV_REASSEMBLY_NEXT_HANDOFF] = "ip6-sv-reassembly-custom-context-handoff",
     883             :         },
     884             : };
     885             : 
     886             : #ifndef CLIB_MARCH_VARIANT
     887             : static u32
     888         591 : ip6_sv_reass_get_nbuckets ()
     889             : {
     890         591 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
     891             :   u32 nbuckets;
     892             :   u8 i;
     893             : 
     894         591 :   nbuckets = (u32) (rm->max_reass_n / IP6_SV_REASS_HT_LOAD_FACTOR);
     895             : 
     896        7070 :   for (i = 0; i < 31; i++)
     897        7070 :     if ((1 << i) >= nbuckets)
     898         591 :       break;
     899         591 :   nbuckets = 1 << i;
     900             : 
     901         591 :   return nbuckets;
     902             : }
     903             : #endif /* CLIB_MARCH_VARIANT */
     904             : 
     905             : typedef enum
     906             : {
     907             :   IP6_EVENT_CONFIG_CHANGED = 1,
     908             : } ip6_sv_reass_event_t;
     909             : 
     910             : #ifndef CLIB_MARCH_VARIANT
     911             : typedef struct
     912             : {
     913             :   int failure;
     914             :   clib_bihash_48_8_t *new_hash;
     915             : } ip6_rehash_cb_ctx;
     916             : 
     917             : static int
     918           1 : ip6_rehash_cb (clib_bihash_kv_48_8_t * kv, void *_ctx)
     919             : {
     920           1 :   ip6_rehash_cb_ctx *ctx = _ctx;
     921           1 :   if (clib_bihash_add_del_48_8 (ctx->new_hash, kv, 1))
     922             :     {
     923           0 :       ctx->failure = 1;
     924             :     }
     925           1 :   return (BIHASH_WALK_CONTINUE);
     926             : }
     927             : 
     928             : static void
     929         575 : ip6_sv_reass_set_params (u32 timeout_ms, u32 max_reassemblies,
     930             :                          u32 max_reassembly_length,
     931             :                          u32 expire_walk_interval_ms)
     932             : {
     933         575 :   ip6_sv_reass_main.timeout_ms = timeout_ms;
     934         575 :   ip6_sv_reass_main.timeout = (f64) timeout_ms / (f64) MSEC_PER_SEC;
     935         575 :   ip6_sv_reass_main.max_reass_n = max_reassemblies;
     936         575 :   ip6_sv_reass_main.max_reass_len = max_reassembly_length;
     937         575 :   ip6_sv_reass_main.expire_walk_interval_ms = expire_walk_interval_ms;
     938         575 : }
     939             : 
     940             : vnet_api_error_t
     941          16 : ip6_sv_reass_set (u32 timeout_ms, u32 max_reassemblies,
     942             :                   u32 max_reassembly_length, u32 expire_walk_interval_ms)
     943             : {
     944          16 :   u32 old_nbuckets = ip6_sv_reass_get_nbuckets ();
     945          16 :   ip6_sv_reass_set_params (timeout_ms, max_reassemblies,
     946             :                            max_reassembly_length, expire_walk_interval_ms);
     947          16 :   vlib_process_signal_event (ip6_sv_reass_main.vlib_main,
     948          16 :                              ip6_sv_reass_main.ip6_sv_reass_expire_node_idx,
     949             :                              IP6_EVENT_CONFIG_CHANGED, 0);
     950          16 :   u32 new_nbuckets = ip6_sv_reass_get_nbuckets ();
     951          16 :   if (ip6_sv_reass_main.max_reass_n > 0 && new_nbuckets > old_nbuckets)
     952             :     {
     953             :       clib_bihash_48_8_t new_hash;
     954           1 :       clib_memset (&new_hash, 0, sizeof (new_hash));
     955             :       ip6_rehash_cb_ctx ctx;
     956           1 :       ctx.failure = 0;
     957           1 :       ctx.new_hash = &new_hash;
     958           1 :       clib_bihash_init_48_8 (&new_hash, "ip6-sv-reass", new_nbuckets,
     959           1 :                              new_nbuckets * 1024);
     960           1 :       clib_bihash_foreach_key_value_pair_48_8 (&ip6_sv_reass_main.hash,
     961             :                                                ip6_rehash_cb, &ctx);
     962           1 :       if (ctx.failure)
     963             :         {
     964           0 :           clib_bihash_free_48_8 (&new_hash);
     965           0 :           return -1;
     966             :         }
     967             :       else
     968             :         {
     969           1 :           clib_bihash_free_48_8 (&ip6_sv_reass_main.hash);
     970           1 :           clib_memcpy_fast (&ip6_sv_reass_main.hash, &new_hash,
     971             :                             sizeof (ip6_sv_reass_main.hash));
     972           1 :           clib_bihash_copied (&ip6_sv_reass_main.hash, &new_hash);
     973             :         }
     974             :     }
     975          16 :   return 0;
     976             : }
     977             : 
     978             : vnet_api_error_t
     979           0 : ip6_sv_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
     980             :                   u32 * max_reassembly_length, u32 * expire_walk_interval_ms)
     981             : {
     982           0 :   *timeout_ms = ip6_sv_reass_main.timeout_ms;
     983           0 :   *max_reassemblies = ip6_sv_reass_main.max_reass_n;
     984           0 :   *max_reassembly_length = ip6_sv_reass_main.max_reass_len;
     985           0 :   *expire_walk_interval_ms = ip6_sv_reass_main.expire_walk_interval_ms;
     986           0 :   return 0;
     987             : }
     988             : 
     989             : static clib_error_t *
     990         559 : ip6_sv_reass_init_function (vlib_main_t * vm)
     991             : {
     992         559 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
     993         559 :   clib_error_t *error = 0;
     994             :   u32 nbuckets;
     995             :   vlib_node_t *node;
     996             : 
     997         559 :   rm->vlib_main = vm;
     998         559 :   rm->vnet_main = vnet_get_main ();
     999             : 
    1000         559 :   vec_validate (rm->per_thread_data, vlib_num_workers ());
    1001             :   ip6_sv_reass_per_thread_t *rt;
    1002        1172 :   vec_foreach (rt, rm->per_thread_data)
    1003             :   {
    1004         613 :     clib_spinlock_init (&rt->lock);
    1005         613 :     pool_alloc (rt->pool, rm->max_reass_n);
    1006         613 :     rt->lru_first = rt->lru_last = ~0;
    1007             :   }
    1008             : 
    1009         559 :   node = vlib_get_node_by_name (vm, (u8 *) "ip6-sv-reassembly-expire-walk");
    1010         559 :   ASSERT (node);
    1011         559 :   rm->ip6_sv_reass_expire_node_idx = node->index;
    1012             : 
    1013         559 :   ip6_sv_reass_set_params (IP6_SV_REASS_TIMEOUT_DEFAULT_MS,
    1014             :                            IP6_SV_REASS_MAX_REASSEMBLIES_DEFAULT,
    1015             :                            IP6_SV_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT,
    1016             :                            IP6_SV_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS);
    1017             : 
    1018         559 :   nbuckets = ip6_sv_reass_get_nbuckets ();
    1019         559 :   clib_bihash_init_48_8 (&rm->hash, "ip6-sv-reass", nbuckets,
    1020         559 :                          nbuckets * 1024);
    1021             : 
    1022         559 :   node = vlib_get_node_by_name (vm, (u8 *) "ip6-drop");
    1023         559 :   ASSERT (node);
    1024         559 :   rm->ip6_drop_idx = node->index;
    1025         559 :   node = vlib_get_node_by_name (vm, (u8 *) "ip6-icmp-error");
    1026         559 :   ASSERT (node);
    1027         559 :   rm->ip6_icmp_error_idx = node->index;
    1028             : 
    1029         559 :   if ((error = vlib_call_init_function (vm, ip_main_init)))
    1030           0 :     return error;
    1031             : 
    1032         559 :   rm->fq_index = vlib_frame_queue_main_init (ip6_sv_reass_node.index, 0);
    1033         559 :   rm->fq_feature_index =
    1034         559 :     vlib_frame_queue_main_init (ip6_sv_reass_node_feature.index, 0);
    1035         559 :   rm->fq_custom_context_index =
    1036         559 :     vlib_frame_queue_main_init (ip6_sv_reass_custom_context_node.index, 0);
    1037             : 
    1038         559 :   rm->feature_use_refcount_per_intf = NULL;
    1039             : 
    1040         559 :   return error;
    1041             : }
    1042             : 
    1043       43119 : VLIB_INIT_FUNCTION (ip6_sv_reass_init_function);
    1044             : #endif /* CLIB_MARCH_VARIANT */
    1045             : 
    1046             : static uword
    1047         559 : ip6_sv_reass_walk_expired (vlib_main_t *vm,
    1048             :                            CLIB_UNUSED (vlib_node_runtime_t *node),
    1049             :                            CLIB_UNUSED (vlib_frame_t *f))
    1050             : {
    1051         559 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
    1052         559 :   uword event_type, *event_data = 0;
    1053             : 
    1054             :   while (true)
    1055         730 :     {
    1056        1289 :       vlib_process_wait_for_event_or_clock (vm,
    1057        1289 :                                             (f64) rm->expire_walk_interval_ms
    1058             :                                             / (f64) MSEC_PER_SEC);
    1059         730 :       event_type = vlib_process_get_events (vm, &event_data);
    1060             : 
    1061         730 :       switch (event_type)
    1062             :         {
    1063         730 :         case ~0:
    1064             :           /* no events => timeout */
    1065             :           /* fallthrough */
    1066             :         case IP6_EVENT_CONFIG_CHANGED:
    1067             :           /* nothing to do here */
    1068         730 :           break;
    1069           0 :         default:
    1070           0 :           clib_warning ("BUG: event type 0x%wx", event_type);
    1071           0 :           break;
    1072             :         }
    1073         730 :       f64 now = vlib_time_now (vm);
    1074             : 
    1075             :       ip6_sv_reass_t *reass;
    1076         730 :       int *pool_indexes_to_free = NULL;
    1077             : 
    1078         730 :       uword thread_index = 0;
    1079             :       int index;
    1080         730 :       const uword nthreads = vlib_num_workers () + 1;
    1081        1642 :       for (thread_index = 0; thread_index < nthreads; ++thread_index)
    1082             :         {
    1083         912 :           ip6_sv_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
    1084         912 :           clib_spinlock_lock (&rt->lock);
    1085             : 
    1086         912 :           vec_reset_length (pool_indexes_to_free);
    1087             :           /* *INDENT-OFF* */
    1088         925 :           pool_foreach_index (index, rt->pool)  {
    1089          13 :                                 reass = pool_elt_at_index (rt->pool, index);
    1090          13 :                                 if (now > reass->last_heard + rm->timeout)
    1091             :                                   {
    1092           9 :                                     vec_add1 (pool_indexes_to_free, index);
    1093             :                                   }
    1094             :                               }
    1095             :           /* *INDENT-ON* */
    1096             :           int *i;
    1097             :           /* *INDENT-OFF* */
    1098         921 :           vec_foreach (i, pool_indexes_to_free)
    1099             :           {
    1100           9 :             ip6_sv_reass_t *reass = pool_elt_at_index (rt->pool, i[0]);
    1101           9 :             ip6_sv_reass_free (vm, rm, rt, reass);
    1102             :           }
    1103             :           /* *INDENT-ON* */
    1104             : 
    1105         912 :           clib_spinlock_unlock (&rt->lock);
    1106             :         }
    1107             : 
    1108         730 :       vec_free (pool_indexes_to_free);
    1109         730 :       if (event_data)
    1110             :         {
    1111         233 :           vec_set_len (event_data, 0);
    1112             :         }
    1113             :     }
    1114             : 
    1115             :   return 0;
    1116             : }
    1117             : 
    1118             : /* *INDENT-OFF* */
    1119      178120 : VLIB_REGISTER_NODE (ip6_sv_reass_expire_node) = {
    1120             :   .function = ip6_sv_reass_walk_expired,
    1121             :   .format_trace = format_ip6_sv_reass_trace,
    1122             :   .type = VLIB_NODE_TYPE_PROCESS,
    1123             :   .name = "ip6-sv-reassembly-expire-walk",
    1124             : 
    1125             :   .n_errors = IP6_N_ERROR,
    1126             :   .error_counters = ip6_error_counters,
    1127             : };
    1128             : /* *INDENT-ON* */
    1129             : 
    1130             : static u8 *
    1131           7 : format_ip6_sv_reass_key (u8 * s, va_list * args)
    1132             : {
    1133           7 :   ip6_sv_reass_key_t *key = va_arg (*args, ip6_sv_reass_key_t *);
    1134             :   s =
    1135           7 :     format (s, "fib_index: %u, src: %U, dst: %U, frag_id: %u, proto: %u",
    1136             :             key->fib_index, format_ip6_address, &key->src, format_ip6_address,
    1137           7 :             &key->dst, clib_net_to_host_u16 (key->frag_id), key->proto);
    1138           7 :   return s;
    1139             : }
    1140             : 
    1141             : static u8 *
    1142           7 : format_ip6_sv_reass (u8 * s, va_list * args)
    1143             : {
    1144           7 :   vlib_main_t *vm = va_arg (*args, vlib_main_t *);
    1145           7 :   ip6_sv_reass_t *reass = va_arg (*args, ip6_sv_reass_t *);
    1146             : 
    1147           7 :   s = format (s, "ID: %lu, key: %U, trace_op_counter: %u\n",
    1148             :               reass->id, format_ip6_sv_reass_key, &reass->key,
    1149             :               reass->trace_op_counter);
    1150             :   vlib_buffer_t *b;
    1151             :   u32 *bip;
    1152           7 :   u32 counter = 0;
    1153           8 :   vec_foreach (bip, reass->cached_buffers)
    1154             :   {
    1155           1 :     u32 bi = *bip;
    1156             :     do
    1157             :       {
    1158           1 :         b = vlib_get_buffer (vm, bi);
    1159           1 :         s = format (s, "  #%03u: bi: %u\n", counter, bi);
    1160           1 :         ++counter;
    1161           1 :         bi = b->next_buffer;
    1162             :       }
    1163           1 :     while (b->flags & VLIB_BUFFER_NEXT_PRESENT);
    1164             :   }
    1165           7 :   return s;
    1166             : }
    1167             : 
    1168             : static clib_error_t *
    1169           9 : show_ip6_sv_reass (vlib_main_t * vm, unformat_input_t * input,
    1170             :                    CLIB_UNUSED (vlib_cli_command_t * lmd))
    1171             : {
    1172           9 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
    1173             : 
    1174           9 :   vlib_cli_output (vm, "---------------------");
    1175           9 :   vlib_cli_output (vm, "IP6 reassembly status");
    1176           9 :   vlib_cli_output (vm, "---------------------");
    1177           9 :   bool details = false;
    1178           9 :   if (unformat (input, "details"))
    1179             :     {
    1180           9 :       details = true;
    1181             :     }
    1182             : 
    1183           9 :   u32 sum_reass_n = 0;
    1184           9 :   u64 sum_buffers_n = 0;
    1185             :   ip6_sv_reass_t *reass;
    1186             :   uword thread_index;
    1187           9 :   const uword nthreads = vlib_num_workers () + 1;
    1188          18 :   for (thread_index = 0; thread_index < nthreads; ++thread_index)
    1189             :     {
    1190           9 :       ip6_sv_reass_per_thread_t *rt = &rm->per_thread_data[thread_index];
    1191           9 :       clib_spinlock_lock (&rt->lock);
    1192           9 :       if (details)
    1193             :         {
    1194             :           /* *INDENT-OFF* */
    1195          16 :           pool_foreach (reass, rt->pool) {
    1196           7 :             vlib_cli_output (vm, "%U", format_ip6_sv_reass, vm, reass);
    1197             :           }
    1198             :           /* *INDENT-ON* */
    1199             :         }
    1200           9 :       sum_reass_n += rt->reass_n;
    1201           9 :       clib_spinlock_unlock (&rt->lock);
    1202             :     }
    1203           9 :   vlib_cli_output (vm, "---------------------");
    1204           9 :   vlib_cli_output (vm, "Current IP6 reassemblies count: %lu\n",
    1205             :                    (long unsigned) sum_reass_n);
    1206           9 :   vlib_cli_output (vm,
    1207             :                    "Maximum configured concurrent shallow virtual IP6 reassemblies per worker-thread: %lu\n",
    1208           9 :                    (long unsigned) rm->max_reass_n);
    1209           9 :   vlib_cli_output (vm,
    1210             :                    "Maximum configured amount of fragments per shallow "
    1211             :                    "virtual IP6 reassembly: %lu\n",
    1212           9 :                    (long unsigned) rm->max_reass_len);
    1213           9 :   vlib_cli_output (vm,
    1214             :                    "Maximum configured shallow virtual IP6 reassembly timeout: %lums\n",
    1215           9 :                    (long unsigned) rm->timeout_ms);
    1216           9 :   vlib_cli_output (vm,
    1217             :                    "Maximum configured shallow virtual IP6 reassembly expire walk interval: %lums\n",
    1218           9 :                    (long unsigned) rm->expire_walk_interval_ms);
    1219           9 :   vlib_cli_output (vm, "Buffers in use: %lu\n",
    1220             :                    (long unsigned) sum_buffers_n);
    1221           9 :   return 0;
    1222             : }
    1223             : 
    1224             : /* *INDENT-OFF* */
    1225      272887 : VLIB_CLI_COMMAND (show_ip6_sv_reassembly_cmd, static) = {
    1226             :     .path = "show ip6-sv-reassembly",
    1227             :     .short_help = "show ip6-sv-reassembly [details]",
    1228             :     .function = show_ip6_sv_reass,
    1229             : };
    1230             : /* *INDENT-ON* */
    1231             : 
    1232             : #ifndef CLIB_MARCH_VARIANT
    1233             : vnet_api_error_t
    1234          12 : ip6_sv_reass_enable_disable (u32 sw_if_index, u8 enable_disable)
    1235             : {
    1236          12 :   return ip6_sv_reass_enable_disable_with_refcnt (sw_if_index,
    1237             :                                                   enable_disable);
    1238             : }
    1239             : #endif /* CLIB_MARCH_VARIANT */
    1240             : 
    1241             : #define foreach_ip6_sv_reassembly_handoff_error                       \
    1242             : _(CONGESTION_DROP, "congestion drop")
    1243             : 
    1244             : 
    1245             : typedef enum
    1246             : {
    1247             : #define _(sym,str) IP6_SV_REASSEMBLY_HANDOFF_ERROR_##sym,
    1248             :   foreach_ip6_sv_reassembly_handoff_error
    1249             : #undef _
    1250             :     IP6_SV_REASSEMBLY_HANDOFF_N_ERROR,
    1251             : } ip6_sv_reassembly_handoff_error_t;
    1252             : 
    1253             : static char *ip6_sv_reassembly_handoff_error_strings[] = {
    1254             : #define _(sym,string) string,
    1255             :   foreach_ip6_sv_reassembly_handoff_error
    1256             : #undef _
    1257             : };
    1258             : 
    1259             : typedef struct
    1260             : {
    1261             :   u32 next_worker_index;
    1262             : } ip6_sv_reassembly_handoff_trace_t;
    1263             : 
    1264             : static u8 *
    1265           0 : format_ip6_sv_reassembly_handoff_trace (u8 * s, va_list * args)
    1266             : {
    1267           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
    1268           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
    1269           0 :   ip6_sv_reassembly_handoff_trace_t *t =
    1270             :     va_arg (*args, ip6_sv_reassembly_handoff_trace_t *);
    1271             : 
    1272             :   s =
    1273           0 :     format (s, "ip6-sv-reassembly-handoff: next-worker %d",
    1274             :             t->next_worker_index);
    1275             : 
    1276           0 :   return s;
    1277             : }
    1278             : 
    1279             : always_inline uword
    1280           0 : ip6_sv_reassembly_handoff_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
    1281             :                                   vlib_frame_t *frame, bool is_feature,
    1282             :                                   bool custom_context)
    1283             : {
    1284           0 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
    1285             : 
    1286             :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
    1287             :   u32 n_enq, n_left_from, *from, *context;
    1288             :   u16 thread_indices[VLIB_FRAME_SIZE], *ti;
    1289             :   u32 fq_index;
    1290             : 
    1291           0 :   from = vlib_frame_vector_args (frame);
    1292           0 :   if (custom_context)
    1293           0 :     context = vlib_frame_aux_args (frame);
    1294           0 :   n_left_from = frame->n_vectors;
    1295           0 :   vlib_get_buffers (vm, from, bufs, n_left_from);
    1296             : 
    1297           0 :   b = bufs;
    1298           0 :   ti = thread_indices;
    1299             : 
    1300           0 :   fq_index = (is_feature) ?
    1301           0 :                      rm->fq_feature_index :
    1302           0 :                      (custom_context ? rm->fq_custom_context_index : rm->fq_index);
    1303             : 
    1304           0 :   while (n_left_from > 0)
    1305             :     {
    1306           0 :       ti[0] = vnet_buffer (b[0])->ip.reass.owner_thread_index;
    1307             : 
    1308           0 :       if (PREDICT_FALSE
    1309             :           ((node->flags & VLIB_NODE_FLAG_TRACE)
    1310             :            && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
    1311             :         {
    1312             :           ip6_sv_reassembly_handoff_trace_t *t =
    1313           0 :             vlib_add_trace (vm, node, b[0], sizeof (*t));
    1314           0 :           t->next_worker_index = ti[0];
    1315             :         }
    1316             : 
    1317           0 :       n_left_from -= 1;
    1318           0 :       ti += 1;
    1319           0 :       b += 1;
    1320             :     }
    1321           0 :   if (custom_context)
    1322           0 :     n_enq = vlib_buffer_enqueue_to_thread_with_aux (
    1323           0 :       vm, node, fq_index, from, context, thread_indices, frame->n_vectors, 1);
    1324             :   else
    1325           0 :     n_enq = vlib_buffer_enqueue_to_thread (
    1326           0 :       vm, node, fq_index, from, thread_indices, frame->n_vectors, 1);
    1327             : 
    1328           0 :   if (n_enq < frame->n_vectors)
    1329           0 :     vlib_node_increment_counter (vm, node->node_index,
    1330             :                                  IP6_SV_REASSEMBLY_HANDOFF_ERROR_CONGESTION_DROP,
    1331           0 :                                  frame->n_vectors - n_enq);
    1332           0 :   return frame->n_vectors;
    1333             : }
    1334             : 
    1335        2236 : VLIB_NODE_FN (ip6_sv_reassembly_handoff_node) (vlib_main_t * vm,
    1336             :                                                vlib_node_runtime_t * node,
    1337             :                                                vlib_frame_t * frame)
    1338             : {
    1339           0 :   return ip6_sv_reassembly_handoff_inline (
    1340             :     vm, node, frame, false /* is_feature */, false /* custom_context */);
    1341             : }
    1342             : 
    1343             : /* *INDENT-OFF* */
    1344      178120 : VLIB_REGISTER_NODE (ip6_sv_reassembly_handoff_node) = {
    1345             :   .name = "ip6-sv-reassembly-handoff",
    1346             :   .vector_size = sizeof (u32),
    1347             :   .n_errors = ARRAY_LEN(ip6_sv_reassembly_handoff_error_strings),
    1348             :   .error_strings = ip6_sv_reassembly_handoff_error_strings,
    1349             :   .format_trace = format_ip6_sv_reassembly_handoff_trace,
    1350             : 
    1351             :   .n_next_nodes = 1,
    1352             : 
    1353             :   .next_nodes = {
    1354             :     [0] = "error-drop",
    1355             :   },
    1356             : };
    1357             : 
    1358             : 
    1359        2236 : VLIB_NODE_FN (ip6_sv_reassembly_feature_handoff_node) (vlib_main_t * vm,
    1360             :                                vlib_node_runtime_t * node, vlib_frame_t * frame)
    1361             : {
    1362           0 :   return ip6_sv_reassembly_handoff_inline (
    1363             :     vm, node, frame, true /* is_feature */, false /* custom_context */);
    1364             : }
    1365             : 
    1366             : 
    1367             : /* *INDENT-OFF* */
    1368      178120 : VLIB_REGISTER_NODE (ip6_sv_reassembly_feature_handoff_node) = {
    1369             :   .name = "ip6-sv-reass-feature-hoff",
    1370             :   .vector_size = sizeof (u32),
    1371             :   .n_errors = ARRAY_LEN(ip6_sv_reassembly_handoff_error_strings),
    1372             :   .error_strings = ip6_sv_reassembly_handoff_error_strings,
    1373             :   .format_trace = format_ip6_sv_reassembly_handoff_trace,
    1374             : 
    1375             :   .n_next_nodes = 1,
    1376             : 
    1377             :   .next_nodes = {
    1378             :     [0] = "error-drop",
    1379             :   },
    1380             : };
    1381             : /* *INDENT-ON* */
    1382             : 
    1383        2236 : VLIB_NODE_FN (ip6_sv_reassembly_custom_context_handoff_node)
    1384             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
    1385             : {
    1386           0 :   return ip6_sv_reassembly_handoff_inline (
    1387             :     vm, node, frame, false /* is_feature */, true /* custom_context */);
    1388             : }
    1389             : 
    1390      178120 : VLIB_REGISTER_NODE (ip6_sv_reassembly_custom_context_handoff_node) = {
    1391             :   .name = "ip6-sv-reassembly-custom-context-handoff",
    1392             :   .vector_size = sizeof (u32),
    1393             :   .aux_size = sizeof (u32),
    1394             :   .n_errors = ARRAY_LEN(ip6_sv_reassembly_handoff_error_strings),
    1395             :   .error_strings = ip6_sv_reassembly_handoff_error_strings,
    1396             :   .format_trace = format_ip6_sv_reassembly_handoff_trace,
    1397             : 
    1398             :   .n_next_nodes = 1,
    1399             : 
    1400             :   .next_nodes = {
    1401             :     [0] = "error-drop",
    1402             :   },
    1403             : };
    1404             : 
    1405             : #ifndef CLIB_MARCH_VARIANT
    1406             : int
    1407          74 : ip6_sv_reass_enable_disable_with_refcnt (u32 sw_if_index, int is_enable)
    1408             : {
    1409          74 :   ip6_sv_reass_main_t *rm = &ip6_sv_reass_main;
    1410          74 :   vec_validate (rm->feature_use_refcount_per_intf, sw_if_index);
    1411          74 :   if (is_enable)
    1412             :     {
    1413          33 :       if (!rm->feature_use_refcount_per_intf[sw_if_index])
    1414             :         {
    1415          27 :           ++rm->feature_use_refcount_per_intf[sw_if_index];
    1416          27 :           return vnet_feature_enable_disable ("ip6-unicast",
    1417             :                                               "ip6-sv-reassembly-feature",
    1418             :                                               sw_if_index, 1, 0, 0);
    1419             :         }
    1420           6 :       ++rm->feature_use_refcount_per_intf[sw_if_index];
    1421             :     }
    1422             :   else
    1423             :     {
    1424          41 :       --rm->feature_use_refcount_per_intf[sw_if_index];
    1425          41 :       if (!rm->feature_use_refcount_per_intf[sw_if_index])
    1426          22 :         return vnet_feature_enable_disable ("ip6-unicast",
    1427             :                                             "ip6-sv-reassembly-feature",
    1428             :                                             sw_if_index, 0, 0, 0);
    1429             :     }
    1430          25 :   return 0;
    1431             : }
    1432             : 
    1433             : uword
    1434           0 : ip6_sv_reass_custom_context_register_next_node (uword node_index)
    1435             : {
    1436           0 :   return vlib_node_add_next (
    1437           0 :     vlib_get_main (), ip6_sv_reassembly_custom_context_handoff_node.index,
    1438             :     node_index);
    1439             : }
    1440             : #endif
    1441             : 
    1442             : /*
    1443             :  * fd.io coding-style-patch-verification: ON
    1444             :  *
    1445             :  * Local Variables:
    1446             :  * eval: (c-set-style "gnu")
    1447             :  * End:
    1448             :  */

Generated by: LCOV version 1.14