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