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