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