8fd370de2fce987752a387508837307f106f85c3
[vpp.git] / src / plugins / nat / nat_reass.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief NAT plugin virtual fragmentation reassembly
18  */
19
20 #include <vnet/vnet.h>
21 #include <nat/nat_reass.h>
22 #include <nat/nat_ipfix_logging.h>
23
24 nat_reass_main_t nat_reass_main;
25
26 static u32
27 nat_reass_get_nbuckets (u8 is_ip6)
28 {
29   nat_reass_main_t *srm = &nat_reass_main;
30   u32 nbuckets;
31   u8 i;
32
33   if (is_ip6)
34     nbuckets = (u32) (srm->ip6_max_reass / NAT_REASS_HT_LOAD_FACTOR);
35   else
36     nbuckets = (u32) (srm->ip4_max_reass / NAT_REASS_HT_LOAD_FACTOR);
37
38   for (i = 0; i < 31; i++)
39     if ((1 << i) >= nbuckets)
40       break;
41   nbuckets = 1 << i;
42
43   return nbuckets;
44 }
45
46 static_always_inline void
47 nat_ip4_reass_get_frags_inline (nat_reass_ip4_t * reass, u32 ** bi)
48 {
49   nat_reass_main_t *srm = &nat_reass_main;
50   u32 elt_index;
51   dlist_elt_t *elt;
52
53   while ((elt_index =
54           clib_dlist_remove_head (srm->ip4_frags_list_pool,
55                                   reass->frags_per_reass_list_head_index)) !=
56          ~0)
57     {
58       elt = pool_elt_at_index (srm->ip4_frags_list_pool, elt_index);
59       vec_add1 (*bi, elt->value);
60       reass->frag_n--;
61       pool_put_index (srm->ip4_frags_list_pool, elt_index);
62     }
63 }
64
65 static_always_inline void
66 nat_ip6_reass_get_frags_inline (nat_reass_ip6_t * reass, u32 ** bi)
67 {
68   nat_reass_main_t *srm = &nat_reass_main;
69   u32 elt_index;
70   dlist_elt_t *elt;
71
72   while ((elt_index =
73           clib_dlist_remove_head (srm->ip6_frags_list_pool,
74                                   reass->frags_per_reass_list_head_index)) !=
75          ~0)
76     {
77       elt = pool_elt_at_index (srm->ip6_frags_list_pool, elt_index);
78       vec_add1 (*bi, elt->value);
79       reass->frag_n--;
80       pool_put_index (srm->ip6_frags_list_pool, elt_index);
81     }
82 }
83
84 int
85 nat_reass_set (u32 timeout, u16 max_reass, u8 max_frag, u8 drop_frag,
86                u8 is_ip6)
87 {
88   nat_reass_main_t *srm = &nat_reass_main;
89   u32 nbuckets;
90
91   if (is_ip6)
92     {
93       if (srm->ip6_max_reass != max_reass)
94         {
95           clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
96
97           srm->ip6_max_reass = max_reass;
98           pool_free (srm->ip6_reass_pool);
99           pool_alloc (srm->ip6_reass_pool, srm->ip4_max_reass);
100           nbuckets = nat_reass_get_nbuckets (0);
101           clib_bihash_free_48_8 (&srm->ip6_reass_hash);
102           clib_bihash_init_48_8 (&srm->ip6_reass_hash, "nat-ip6-reass",
103                                  nbuckets, nbuckets * 1024);
104
105           clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
106         }
107       srm->ip6_timeout = timeout;
108       srm->ip6_max_frag = max_frag;
109       srm->ip6_drop_frag = drop_frag;
110     }
111   else
112     {
113       if (srm->ip4_max_reass != max_reass)
114         {
115           clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
116
117           srm->ip4_max_reass = max_reass;
118           pool_free (srm->ip4_reass_pool);
119           pool_alloc (srm->ip4_reass_pool, srm->ip4_max_reass);
120           nbuckets = nat_reass_get_nbuckets (0);
121           clib_bihash_free_16_8 (&srm->ip4_reass_hash);
122           clib_bihash_init_16_8 (&srm->ip4_reass_hash, "nat-ip4-reass",
123                                  nbuckets, nbuckets * 1024);
124           clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
125         }
126       srm->ip4_timeout = timeout;
127       srm->ip4_max_frag = max_frag;
128       srm->ip4_drop_frag = drop_frag;
129     }
130
131   return 0;
132 }
133
134 u32
135 nat_reass_get_timeout (u8 is_ip6)
136 {
137   nat_reass_main_t *srm = &nat_reass_main;
138
139   if (is_ip6)
140     return srm->ip6_timeout;
141
142   return srm->ip4_timeout;
143 }
144
145 u16
146 nat_reass_get_max_reass (u8 is_ip6)
147 {
148   nat_reass_main_t *srm = &nat_reass_main;
149
150   if (is_ip6)
151     return srm->ip6_max_reass;
152
153   return srm->ip4_max_reass;
154 }
155
156 u8
157 nat_reass_get_max_frag (u8 is_ip6)
158 {
159   nat_reass_main_t *srm = &nat_reass_main;
160
161   if (is_ip6)
162     return srm->ip6_max_frag;
163
164   return srm->ip4_max_frag;
165 }
166
167 u8
168 nat_reass_is_drop_frag (u8 is_ip6)
169 {
170   nat_reass_main_t *srm = &nat_reass_main;
171
172   if (is_ip6)
173     return srm->ip6_drop_frag;
174
175   return srm->ip4_drop_frag;
176 }
177
178 static_always_inline nat_reass_ip4_t *
179 nat_ip4_reass_lookup (nat_reass_ip4_key_t * k, f64 now)
180 {
181   nat_reass_main_t *srm = &nat_reass_main;
182   clib_bihash_kv_16_8_t kv, value;
183   nat_reass_ip4_t *reass;
184
185   kv.key[0] = k->as_u64[0];
186   kv.key[1] = k->as_u64[1];
187
188   if (clib_bihash_search_16_8 (&srm->ip4_reass_hash, &kv, &value))
189     return 0;
190
191   reass = pool_elt_at_index (srm->ip4_reass_pool, value.value);
192   if (now < reass->last_heard + (f64) srm->ip4_timeout)
193     return reass;
194
195   return 0;
196 }
197
198 nat_reass_ip4_t *
199 nat_ip4_reass_find (ip4_address_t src, ip4_address_t dst, u16 frag_id,
200                     u8 proto)
201 {
202   nat_reass_main_t *srm = &nat_reass_main;
203   nat_reass_ip4_t *reass = 0;
204   nat_reass_ip4_key_t k;
205   f64 now = vlib_time_now (srm->vlib_main);
206
207   k.src.as_u32 = src.as_u32;
208   k.dst.as_u32 = dst.as_u32;
209   k.frag_id = frag_id;
210   k.proto = proto;
211
212   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
213   reass = nat_ip4_reass_lookup (&k, now);
214   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
215
216   return reass;
217 }
218
219 nat_reass_ip4_t *
220 nat_ip4_reass_find_or_create (ip4_address_t src, ip4_address_t dst,
221                               u16 frag_id, u8 proto, u8 reset_timeout,
222                               u32 ** bi_to_drop)
223 {
224   nat_reass_main_t *srm = &nat_reass_main;
225   nat_reass_ip4_t *reass = 0;
226   nat_reass_ip4_key_t k;
227   f64 now = vlib_time_now (srm->vlib_main);
228   dlist_elt_t *oldest_elt, *elt;
229   dlist_elt_t *per_reass_list_head_elt;
230   u32 oldest_index, elt_index;
231   clib_bihash_kv_16_8_t kv, value;
232
233   k.src.as_u32 = src.as_u32;
234   k.dst.as_u32 = dst.as_u32;
235   k.frag_id = frag_id;
236   k.proto = proto;
237
238   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
239
240   reass = nat_ip4_reass_lookup (&k, now);
241   if (reass)
242     {
243       if (reset_timeout)
244         {
245           reass->last_heard = now;
246           clib_dlist_remove (srm->ip4_reass_lru_list_pool,
247                              reass->lru_list_index);
248           clib_dlist_addtail (srm->ip4_reass_lru_list_pool,
249                               srm->ip4_reass_head_index,
250                               reass->lru_list_index);
251         }
252
253       if (reass->flags && NAT_REASS_FLAG_MAX_FRAG_DROP)
254         {
255           reass = 0;
256           goto unlock;
257         }
258
259       goto unlock;
260     }
261
262   if (srm->ip4_reass_n >= srm->ip4_max_reass)
263     {
264       oldest_index =
265         clib_dlist_remove_head (srm->ip4_reass_lru_list_pool,
266                                 srm->ip4_reass_head_index);
267       ASSERT (oldest_index != ~0);
268       oldest_elt =
269         pool_elt_at_index (srm->ip4_reass_lru_list_pool, oldest_index);
270       reass = pool_elt_at_index (srm->ip4_reass_pool, oldest_elt->value);
271       if (now < reass->last_heard + (f64) srm->ip4_timeout)
272         {
273           clib_dlist_addhead (srm->ip4_reass_lru_list_pool,
274                               srm->ip4_reass_head_index, oldest_index);
275           clib_warning ("no free resassembly slot");
276           reass = 0;
277           goto unlock;
278         }
279
280       clib_dlist_addtail (srm->ip4_reass_lru_list_pool,
281                           srm->ip4_reass_head_index, oldest_index);
282
283       kv.key[0] = reass->key.as_u64[0];
284       kv.key[1] = reass->key.as_u64[1];
285       if (!clib_bihash_search_16_8 (&srm->ip4_reass_hash, &kv, &value))
286         {
287           if (value.value == (reass - srm->ip4_reass_pool))
288             {
289               if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 0))
290                 {
291                   reass = 0;
292                   goto unlock;
293                 }
294             }
295         }
296
297       nat_ip4_reass_get_frags_inline (reass, bi_to_drop);
298     }
299   else
300     {
301       pool_get (srm->ip4_reass_pool, reass);
302       pool_get (srm->ip4_reass_lru_list_pool, elt);
303       reass->lru_list_index = elt_index = elt - srm->ip4_reass_lru_list_pool;
304       clib_dlist_init (srm->ip4_reass_lru_list_pool, elt_index);
305       elt->value = reass - srm->ip4_reass_pool;
306       clib_dlist_addtail (srm->ip4_reass_lru_list_pool,
307                           srm->ip4_reass_head_index, elt_index);
308       pool_get (srm->ip4_frags_list_pool, per_reass_list_head_elt);
309       reass->frags_per_reass_list_head_index =
310         per_reass_list_head_elt - srm->ip4_frags_list_pool;
311       clib_dlist_init (srm->ip4_frags_list_pool,
312                        reass->frags_per_reass_list_head_index);
313       srm->ip4_reass_n++;
314     }
315
316   reass->key.as_u64[0] = kv.key[0] = k.as_u64[0];
317   reass->key.as_u64[1] = kv.key[1] = k.as_u64[1];
318   kv.value = reass - srm->ip4_reass_pool;
319   reass->sess_index = (u32) ~ 0;
320   reass->thread_index = (u32) ~ 0;
321   reass->last_heard = now;
322   reass->frag_n = 0;
323
324   if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 1))
325     {
326       reass = 0;
327       goto unlock;
328     }
329
330 unlock:
331   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
332   return reass;
333 }
334
335 int
336 nat_ip4_reass_add_fragment (nat_reass_ip4_t * reass, u32 bi,
337                             u32 ** bi_to_drop)
338 {
339   nat_reass_main_t *srm = &nat_reass_main;
340   dlist_elt_t *elt;
341   u32 elt_index;
342
343   if (reass->frag_n >= srm->ip4_max_frag)
344     {
345       nat_ipfix_logging_max_fragments_ip4 (srm->ip4_max_frag,
346                                            &reass->key.src);
347       reass->flags |= NAT_REASS_FLAG_MAX_FRAG_DROP;
348       nat_ip4_reass_get_frags_inline (reass, bi_to_drop);
349       return -1;
350     }
351
352   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
353
354   pool_get (srm->ip4_frags_list_pool, elt);
355   elt_index = elt - srm->ip4_frags_list_pool;
356   clib_dlist_init (srm->ip4_frags_list_pool, elt_index);
357   elt->value = bi;
358   clib_dlist_addtail (srm->ip4_frags_list_pool,
359                       reass->frags_per_reass_list_head_index, elt_index);
360   reass->frag_n++;
361
362   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
363
364   return 0;
365 }
366
367 void
368 nat_ip4_reass_get_frags (nat_reass_ip4_t * reass, u32 ** bi)
369 {
370   nat_reass_main_t *srm = &nat_reass_main;
371
372   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
373
374   nat_ip4_reass_get_frags_inline (reass, bi);
375
376   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
377 }
378
379 void
380 nat_ip4_reass_walk (nat_ip4_reass_walk_fn_t fn, void *ctx)
381 {
382   nat_reass_ip4_t *reass;
383   nat_reass_main_t *srm = &nat_reass_main;
384   f64 now = vlib_time_now (srm->vlib_main);
385
386   /* *INDENT-OFF* */
387   pool_foreach (reass, srm->ip4_reass_pool,
388   ({
389     if (now < reass->last_heard + (f64) srm->ip4_timeout)
390       {
391         if (fn (reass, ctx))
392           return;
393       }
394   }));
395   /* *INDENT-ON* */
396 }
397
398 static_always_inline nat_reass_ip6_t *
399 nat_ip6_reass_lookup (nat_reass_ip6_key_t * k, f64 now)
400 {
401   nat_reass_main_t *srm = &nat_reass_main;
402   clib_bihash_kv_48_8_t kv, value;
403   nat_reass_ip6_t *reass;
404
405   k->unused = 0;
406   kv.key[0] = k->as_u64[0];
407   kv.key[1] = k->as_u64[1];
408   kv.key[2] = k->as_u64[2];
409   kv.key[3] = k->as_u64[3];
410   kv.key[4] = k->as_u64[4];
411   kv.key[5] = k->as_u64[5];
412
413   if (clib_bihash_search_48_8 (&srm->ip6_reass_hash, &kv, &value))
414     return 0;
415
416   reass = pool_elt_at_index (srm->ip6_reass_pool, value.value);
417   if (now < reass->last_heard + (f64) srm->ip6_timeout)
418     return reass;
419
420   return 0;
421 }
422
423 nat_reass_ip6_t *
424 nat_ip6_reass_find_or_create (ip6_address_t src, ip6_address_t dst,
425                               u32 frag_id, u8 proto, u8 reset_timeout,
426                               u32 ** bi_to_drop)
427 {
428   nat_reass_main_t *srm = &nat_reass_main;
429   nat_reass_ip6_t *reass = 0;
430   nat_reass_ip6_key_t k;
431   f64 now = vlib_time_now (srm->vlib_main);
432   dlist_elt_t *oldest_elt, *elt;
433   dlist_elt_t *per_reass_list_head_elt;
434   u32 oldest_index, elt_index;
435   clib_bihash_kv_48_8_t kv;
436
437   k.src.as_u64[0] = src.as_u64[0];
438   k.src.as_u64[1] = src.as_u64[1];
439   k.dst.as_u64[0] = dst.as_u64[0];
440   k.dst.as_u64[1] = dst.as_u64[1];
441   k.frag_id = frag_id;
442   k.proto = proto;
443   k.unused = 0;
444
445   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
446
447   reass = nat_ip6_reass_lookup (&k, now);
448   if (reass)
449     {
450       if (reset_timeout)
451         {
452           reass->last_heard = now;
453           clib_dlist_remove (srm->ip6_reass_lru_list_pool,
454                              reass->lru_list_index);
455           clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
456                               srm->ip6_reass_head_index,
457                               reass->lru_list_index);
458         }
459
460       if (reass->flags && NAT_REASS_FLAG_MAX_FRAG_DROP)
461         {
462           reass = 0;
463           goto unlock;
464         }
465
466       goto unlock;
467     }
468
469   if (srm->ip6_reass_n >= srm->ip6_max_reass)
470     {
471       oldest_index =
472         clib_dlist_remove_head (srm->ip6_reass_lru_list_pool,
473                                 srm->ip6_reass_head_index);
474       ASSERT (oldest_index != ~0);
475       oldest_elt =
476         pool_elt_at_index (srm->ip4_reass_lru_list_pool, oldest_index);
477       reass = pool_elt_at_index (srm->ip6_reass_pool, oldest_elt->value);
478       if (now < reass->last_heard + (f64) srm->ip6_timeout)
479         {
480           clib_dlist_addhead (srm->ip6_reass_lru_list_pool,
481                               srm->ip6_reass_head_index, oldest_index);
482           clib_warning ("no free resassembly slot");
483           reass = 0;
484           goto unlock;
485         }
486
487       clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
488                           srm->ip6_reass_head_index, oldest_index);
489
490       kv.key[0] = k.as_u64[0];
491       kv.key[1] = k.as_u64[1];
492       kv.key[2] = k.as_u64[2];
493       kv.key[3] = k.as_u64[4];
494       kv.key[4] = k.as_u64[5];
495       if (clib_bihash_add_del_48_8 (&srm->ip6_reass_hash, &kv, 0))
496         {
497           reass = 0;
498           goto unlock;
499         }
500
501       nat_ip6_reass_get_frags_inline (reass, bi_to_drop);
502     }
503   else
504     {
505       pool_get (srm->ip6_reass_pool, reass);
506       pool_get (srm->ip6_reass_lru_list_pool, elt);
507       reass->lru_list_index = elt_index = elt - srm->ip6_reass_lru_list_pool;
508       clib_dlist_init (srm->ip6_reass_lru_list_pool, elt_index);
509       elt->value = reass - srm->ip6_reass_pool;
510       clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
511                           srm->ip6_reass_head_index, elt_index);
512       pool_get (srm->ip6_frags_list_pool, per_reass_list_head_elt);
513       reass->frags_per_reass_list_head_index =
514         per_reass_list_head_elt - srm->ip6_frags_list_pool;
515       clib_dlist_init (srm->ip6_frags_list_pool,
516                        reass->frags_per_reass_list_head_index);
517       srm->ip6_reass_n++;
518     }
519
520   reass->key.as_u64[0] = kv.key[0] = k.as_u64[0];
521   reass->key.as_u64[1] = kv.key[1] = k.as_u64[1];
522   reass->key.as_u64[2] = kv.key[2] = k.as_u64[2];
523   reass->key.as_u64[3] = kv.key[3] = k.as_u64[3];
524   reass->key.as_u64[4] = kv.key[4] = k.as_u64[4];
525   reass->key.as_u64[5] = kv.key[5] = k.as_u64[5];
526   kv.value = reass - srm->ip6_reass_pool;
527   reass->sess_index = (u32) ~ 0;
528   reass->last_heard = now;
529
530   if (clib_bihash_add_del_48_8 (&srm->ip6_reass_hash, &kv, 1))
531     {
532       reass = 0;
533       goto unlock;
534     }
535
536 unlock:
537   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
538   return reass;
539 }
540
541 int
542 nat_ip6_reass_add_fragment (nat_reass_ip6_t * reass, u32 bi,
543                             u32 ** bi_to_drop)
544 {
545   nat_reass_main_t *srm = &nat_reass_main;
546   dlist_elt_t *elt;
547   u32 elt_index;
548
549   if (reass->frag_n >= srm->ip6_max_frag)
550     {
551       nat_ipfix_logging_max_fragments_ip6 (srm->ip6_max_frag,
552                                            &reass->key.src);
553       reass->flags |= NAT_REASS_FLAG_MAX_FRAG_DROP;
554       nat_ip6_reass_get_frags_inline (reass, bi_to_drop);
555       return -1;
556     }
557
558   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
559
560   pool_get (srm->ip6_frags_list_pool, elt);
561   elt_index = elt - srm->ip6_frags_list_pool;
562   clib_dlist_init (srm->ip6_frags_list_pool, elt_index);
563   elt->value = bi;
564   clib_dlist_addtail (srm->ip6_frags_list_pool,
565                       reass->frags_per_reass_list_head_index, elt_index);
566   reass->frag_n++;
567
568   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
569
570   return 0;
571 }
572
573 void
574 nat_ip6_reass_get_frags (nat_reass_ip6_t * reass, u32 ** bi)
575 {
576   nat_reass_main_t *srm = &nat_reass_main;
577
578   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
579
580   nat_ip6_reass_get_frags_inline (reass, bi);
581
582   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
583 }
584
585 void
586 nat_ip6_reass_walk (nat_ip6_reass_walk_fn_t fn, void *ctx)
587 {
588   nat_reass_ip6_t *reass;
589   nat_reass_main_t *srm = &nat_reass_main;
590   f64 now = vlib_time_now (srm->vlib_main);
591
592   /* *INDENT-OFF* */
593   pool_foreach (reass, srm->ip6_reass_pool,
594   ({
595     if (now < reass->last_heard + (f64) srm->ip4_timeout)
596       {
597         if (fn (reass, ctx))
598           return;
599       }
600   }));
601   /* *INDENT-ON* */
602 }
603
604 clib_error_t *
605 nat_reass_init (vlib_main_t * vm)
606 {
607   nat_reass_main_t *srm = &nat_reass_main;
608   vlib_thread_main_t *tm = vlib_get_thread_main ();
609   clib_error_t *error = 0;
610   dlist_elt_t *head;
611   u32 nbuckets, head_index;
612
613   srm->vlib_main = vm;
614   srm->vnet_main = vnet_get_main ();
615
616   /* IPv4 */
617   srm->ip4_timeout = NAT_REASS_TIMEOUT_DEFAULT;
618   srm->ip4_max_reass = NAT_MAX_REASS_DEAFULT;
619   srm->ip4_max_frag = NAT_MAX_FRAG_DEFAULT;
620   srm->ip4_drop_frag = 0;
621   srm->ip4_reass_n = 0;
622
623   if (tm->n_vlib_mains > 1)
624     clib_spinlock_init (&srm->ip4_reass_lock);
625
626   pool_alloc (srm->ip4_reass_pool, srm->ip4_max_reass);
627
628   nbuckets = nat_reass_get_nbuckets (0);
629   clib_bihash_init_16_8 (&srm->ip4_reass_hash, "nat-ip4-reass", nbuckets,
630                          nbuckets * 1024);
631
632   pool_get (srm->ip4_reass_lru_list_pool, head);
633   srm->ip4_reass_head_index = head_index =
634     head - srm->ip4_reass_lru_list_pool;
635   clib_dlist_init (srm->ip4_reass_lru_list_pool, head_index);
636
637   /* IPv6 */
638   srm->ip6_timeout = NAT_REASS_TIMEOUT_DEFAULT;
639   srm->ip6_max_reass = NAT_MAX_REASS_DEAFULT;
640   srm->ip6_max_frag = NAT_MAX_FRAG_DEFAULT;
641   srm->ip6_drop_frag = 0;
642   srm->ip6_reass_n = 0;
643
644   if (tm->n_vlib_mains > 1)
645     clib_spinlock_init (&srm->ip6_reass_lock);
646
647   pool_alloc (srm->ip6_reass_pool, srm->ip6_max_reass);
648
649   nbuckets = nat_reass_get_nbuckets (1);
650   clib_bihash_init_48_8 (&srm->ip6_reass_hash, "nat-ip6-reass", nbuckets,
651                          nbuckets * 1024);
652
653   pool_get (srm->ip6_reass_lru_list_pool, head);
654   srm->ip6_reass_head_index = head_index =
655     head - srm->ip6_reass_lru_list_pool;
656   clib_dlist_init (srm->ip6_reass_lru_list_pool, head_index);
657
658   return error;
659 }
660
661 static clib_error_t *
662 nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
663                       vlib_cli_command_t * cmd)
664 {
665   clib_error_t *error = 0;
666   unformat_input_t _line_input, *line_input = &_line_input;
667   u32 timeout = 0, max_reass = 0, max_frag = 0;
668   u8 drop_frag = (u8) ~ 0, is_ip6 = 0;
669   int rv;
670
671   /* Get a line of input. */
672   if (!unformat_user (input, unformat_line_input, line_input))
673     return 0;
674
675   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
676     {
677       if (unformat (line_input, "max-reassemblies %u", &max_reass))
678         ;
679       else if (unformat (line_input, "max-fragments %u", &max_frag))
680         ;
681       else if (unformat (line_input, "timeout %u", &timeout))
682         ;
683       else if (unformat (line_input, "enable"))
684         drop_frag = 0;
685       else if (unformat (line_input, "disable"))
686         drop_frag = 1;
687       else if (unformat (line_input, "ip4"))
688         is_ip6 = 0;
689       else if (unformat (line_input, "ip6"))
690         is_ip6 = 1;
691       else
692         {
693           error = clib_error_return (0, "unknown input '%U'",
694                                      format_unformat_error, line_input);
695           goto done;
696         }
697     }
698
699   if (!timeout)
700     timeout = nat_reass_get_timeout (is_ip6);
701   if (!max_reass)
702     max_reass = nat_reass_get_max_reass (is_ip6);
703   if (!max_frag)
704     max_frag = nat_reass_get_max_frag (is_ip6);
705   if (drop_frag == (u8) ~ 0)
706     drop_frag = nat_reass_is_drop_frag (is_ip6);
707
708   rv =
709     nat_reass_set (timeout, (u16) max_reass, (u8) max_frag, drop_frag,
710                    is_ip6);
711   if (rv)
712     {
713       error = clib_error_return (0, "nat_set_reass return %d", rv);
714       goto done;
715     }
716
717 done:
718   unformat_free (line_input);
719
720   return error;
721 }
722
723 static int
724 nat_ip4_reass_walk_cli (nat_reass_ip4_t * reass, void *ctx)
725 {
726   vlib_main_t *vm = ctx;
727
728   vlib_cli_output (vm, "  src %U dst %U proto %u id 0x%04x cached %u",
729                    format_ip4_address, &reass->key.src,
730                    format_ip4_address, &reass->key.dst,
731                    reass->key.proto,
732                    clib_net_to_host_u16 (reass->key.frag_id), reass->frag_n);
733
734   return 0;
735 }
736
737 static int
738 nat_ip6_reass_walk_cli (nat_reass_ip6_t * reass, void *ctx)
739 {
740   vlib_main_t *vm = ctx;
741
742   vlib_cli_output (vm, "  src %U dst %U proto %u id 0x%08x cached %u",
743                    format_ip6_address, &reass->key.src,
744                    format_ip6_address, &reass->key.dst,
745                    reass->key.proto,
746                    clib_net_to_host_u32 (reass->key.frag_id), reass->frag_n);
747
748   return 0;
749 }
750
751 static clib_error_t *
752 show_nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
753                            vlib_cli_command_t * cmd)
754 {
755   vlib_cli_output (vm, "NAT IPv4 virtual fragmentation reassembly is %s",
756                    nat_reass_is_drop_frag (0) ? "DISABLED" : "ENABLED");
757   vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (0));
758   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (0));
759   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (0));
760   vlib_cli_output (vm, " reassemblies:");
761   nat_ip4_reass_walk (nat_ip4_reass_walk_cli, vm);
762
763   vlib_cli_output (vm, "NAT IPv6 virtual fragmentation reassembly is %s",
764                    nat_reass_is_drop_frag (1) ? "DISABLED" : "ENABLED");
765   vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (1));
766   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (1));
767   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (1));
768   vlib_cli_output (vm, " reassemblies:");
769   nat_ip6_reass_walk (nat_ip6_reass_walk_cli, vm);
770
771   return 0;
772 }
773
774 /* *INDENT-OFF* */
775 VLIB_CLI_COMMAND (nat_reass_command, static) =
776 {
777   .path = "nat virtual-reassembly",
778   .short_help = "nat virtual-reassembly ip4|ip6 [max-reassemblies <n>] "
779                 "[max-fragments <n>] [timeout <sec>] [enable|disable]",
780   .function = nat_reass_command_fn,
781 };
782
783 VLIB_CLI_COMMAND (show_nat_reass_command, static) =
784 {
785   .path = "show nat virtual-reassembly",
786   .short_help = "show nat virtual-reassembly",
787   .function = show_nat_reass_command_fn,
788 };
789 /* *INDENT-ON* */
790
791 /*
792  * fd.io coding-style-patch-verification: ON
793  *
794  * Local Variables:
795  * eval: (c-set-style "gnu")
796  * End:
797  */