50bfced2b4571e375b06d7c5e228aa8ab3f568ee
[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;
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       goto unlock;
253     }
254
255   if (srm->ip4_reass_n >= srm->ip4_max_reass)
256     {
257       oldest_index =
258         clib_dlist_remove_head (srm->ip4_reass_lru_list_pool,
259                                 srm->ip4_reass_head_index);
260       ASSERT (oldest_index != ~0);
261       oldest_elt =
262         pool_elt_at_index (srm->ip4_reass_lru_list_pool, oldest_index);
263       reass = pool_elt_at_index (srm->ip4_reass_pool, oldest_elt->value);
264       if (now < reass->last_heard + (f64) srm->ip4_timeout)
265         {
266           clib_dlist_addhead (srm->ip4_reass_lru_list_pool,
267                               srm->ip4_reass_head_index, oldest_index);
268           clib_warning ("no free resassembly slot");
269           reass = 0;
270           goto unlock;
271         }
272
273       clib_dlist_addtail (srm->ip4_reass_lru_list_pool,
274                           srm->ip4_reass_head_index, oldest_index);
275
276       kv.key[0] = k.as_u64[0];
277       kv.key[1] = k.as_u64[1];
278       if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 0))
279         {
280           reass = 0;
281           goto unlock;
282         }
283
284       nat_ip4_reass_get_frags_inline (reass, bi_to_drop);
285     }
286   else
287     {
288       pool_get (srm->ip4_reass_pool, reass);
289       pool_get (srm->ip4_reass_lru_list_pool, elt);
290       reass->lru_list_index = elt_index = elt - srm->ip4_reass_lru_list_pool;
291       clib_dlist_init (srm->ip4_reass_lru_list_pool, elt_index);
292       elt->value = reass - srm->ip4_reass_pool;
293       clib_dlist_addtail (srm->ip4_reass_lru_list_pool,
294                           srm->ip4_reass_head_index, elt_index);
295       pool_get (srm->ip4_frags_list_pool, per_reass_list_head_elt);
296       reass->frags_per_reass_list_head_index =
297         per_reass_list_head_elt - srm->ip4_frags_list_pool;
298       clib_dlist_init (srm->ip4_frags_list_pool,
299                        reass->frags_per_reass_list_head_index);
300       srm->ip4_reass_n++;
301     }
302
303   reass->key.as_u64[0] = kv.key[0] = k.as_u64[0];
304   reass->key.as_u64[1] = kv.key[1] = k.as_u64[1];
305   kv.value = reass - srm->ip4_reass_pool;
306   reass->sess_index = (u32) ~ 0;
307   reass->thread_index = (u32) ~ 0;
308   reass->last_heard = now;
309   reass->frag_n = 0;
310
311   if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 1))
312     {
313       reass = 0;
314       goto unlock;
315     }
316
317 unlock:
318   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
319   return reass;
320 }
321
322 int
323 nat_ip4_reass_add_fragment (nat_reass_ip4_t * reass, u32 bi)
324 {
325   nat_reass_main_t *srm = &nat_reass_main;
326   dlist_elt_t *elt;
327   u32 elt_index;
328
329   if (reass->frag_n >= srm->ip4_max_frag)
330     {
331       nat_ipfix_logging_max_fragments_ip4 (srm->ip4_max_frag,
332                                            &reass->key.src);
333       return -1;
334     }
335
336   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
337
338   pool_get (srm->ip4_frags_list_pool, elt);
339   elt_index = elt - srm->ip4_frags_list_pool;
340   clib_dlist_init (srm->ip4_frags_list_pool, elt_index);
341   elt->value = bi;
342   clib_dlist_addtail (srm->ip4_frags_list_pool,
343                       reass->frags_per_reass_list_head_index, elt_index);
344   reass->frag_n++;
345
346   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
347
348   return 0;
349 }
350
351 void
352 nat_ip4_reass_get_frags (nat_reass_ip4_t * reass, u32 ** bi)
353 {
354   nat_reass_main_t *srm = &nat_reass_main;
355
356   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
357
358   nat_ip4_reass_get_frags_inline (reass, bi);
359
360   clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
361 }
362
363 void
364 nat_ip4_reass_walk (nat_ip4_reass_walk_fn_t fn, void *ctx)
365 {
366   nat_reass_ip4_t *reass;
367   nat_reass_main_t *srm = &nat_reass_main;
368   f64 now = vlib_time_now (srm->vlib_main);
369
370   /* *INDENT-OFF* */
371   pool_foreach (reass, srm->ip4_reass_pool,
372   ({
373     if (now < reass->last_heard + (f64) srm->ip4_timeout)
374       {
375         if (fn (reass, ctx))
376           return;
377       }
378   }));
379   /* *INDENT-ON* */
380 }
381
382 static_always_inline nat_reass_ip6_t *
383 nat_ip6_reass_lookup (nat_reass_ip6_key_t * k, f64 now)
384 {
385   nat_reass_main_t *srm = &nat_reass_main;
386   clib_bihash_kv_48_8_t kv, value;
387   nat_reass_ip6_t *reass;
388
389   k->unused = 0;
390   kv.key[0] = k->as_u64[0];
391   kv.key[1] = k->as_u64[1];
392   kv.key[2] = k->as_u64[2];
393   kv.key[3] = k->as_u64[3];
394   kv.key[4] = k->as_u64[4];
395   kv.key[5] = k->as_u64[5];
396
397   if (clib_bihash_search_48_8 (&srm->ip6_reass_hash, &kv, &value))
398     return 0;
399
400   reass = pool_elt_at_index (srm->ip6_reass_pool, value.value);
401   if (now < reass->last_heard + (f64) srm->ip6_timeout)
402     return reass;
403
404   return 0;
405 }
406
407 nat_reass_ip6_t *
408 nat_ip6_reass_find_or_create (ip6_address_t src, ip6_address_t dst,
409                               u32 frag_id, u8 proto, u8 reset_timeout,
410                               u32 ** bi_to_drop)
411 {
412   nat_reass_main_t *srm = &nat_reass_main;
413   nat_reass_ip6_t *reass = 0;
414   nat_reass_ip6_key_t k;
415   f64 now = vlib_time_now (srm->vlib_main);
416   dlist_elt_t *oldest_elt, *elt;
417   dlist_elt_t *per_reass_list_head_elt;
418   u32 oldest_index, elt_index;
419   clib_bihash_kv_48_8_t kv;
420
421   k.src.as_u64[0] = src.as_u64[0];
422   k.src.as_u64[1] = src.as_u64[1];
423   k.dst.as_u64[0] = dst.as_u64[0];
424   k.dst.as_u64[1] = dst.as_u64[1];
425   k.frag_id = frag_id;
426   k.proto = proto;
427   k.unused = 0;
428
429   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
430
431   reass = nat_ip6_reass_lookup (&k, now);
432   if (reass)
433     {
434       if (reset_timeout)
435         {
436           reass->last_heard = now;
437           clib_dlist_remove (srm->ip6_reass_lru_list_pool,
438                              reass->lru_list_index);
439           clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
440                               srm->ip6_reass_head_index,
441                               reass->lru_list_index);
442         }
443       goto unlock;
444     }
445
446   if (srm->ip6_reass_n >= srm->ip6_max_reass)
447     {
448       oldest_index =
449         clib_dlist_remove_head (srm->ip6_reass_lru_list_pool,
450                                 srm->ip6_reass_head_index);
451       ASSERT (oldest_index != ~0);
452       oldest_elt =
453         pool_elt_at_index (srm->ip4_reass_lru_list_pool, oldest_index);
454       reass = pool_elt_at_index (srm->ip6_reass_pool, oldest_elt->value);
455       if (now < reass->last_heard + (f64) srm->ip6_timeout)
456         {
457           clib_dlist_addhead (srm->ip6_reass_lru_list_pool,
458                               srm->ip6_reass_head_index, oldest_index);
459           clib_warning ("no free resassembly slot");
460           reass = 0;
461           goto unlock;
462         }
463
464       clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
465                           srm->ip6_reass_head_index, oldest_index);
466
467       kv.key[0] = k.as_u64[0];
468       kv.key[1] = k.as_u64[1];
469       kv.key[2] = k.as_u64[2];
470       kv.key[3] = k.as_u64[4];
471       kv.key[4] = k.as_u64[5];
472       if (clib_bihash_add_del_48_8 (&srm->ip6_reass_hash, &kv, 0))
473         {
474           reass = 0;
475           goto unlock;
476         }
477
478       nat_ip6_reass_get_frags_inline (reass, bi_to_drop);
479     }
480   else
481     {
482       pool_get (srm->ip6_reass_pool, reass);
483       pool_get (srm->ip6_reass_lru_list_pool, elt);
484       reass->lru_list_index = elt_index = elt - srm->ip6_reass_lru_list_pool;
485       clib_dlist_init (srm->ip6_reass_lru_list_pool, elt_index);
486       elt->value = reass - srm->ip6_reass_pool;
487       clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
488                           srm->ip6_reass_head_index, elt_index);
489       pool_get (srm->ip6_frags_list_pool, per_reass_list_head_elt);
490       reass->frags_per_reass_list_head_index =
491         per_reass_list_head_elt - srm->ip6_frags_list_pool;
492       clib_dlist_init (srm->ip6_frags_list_pool,
493                        reass->frags_per_reass_list_head_index);
494       srm->ip6_reass_n++;
495     }
496
497   reass->key.as_u64[0] = kv.key[0] = k.as_u64[0];
498   reass->key.as_u64[1] = kv.key[1] = k.as_u64[1];
499   reass->key.as_u64[2] = kv.key[2] = k.as_u64[2];
500   reass->key.as_u64[3] = kv.key[3] = k.as_u64[3];
501   reass->key.as_u64[4] = kv.key[4] = k.as_u64[4];
502   reass->key.as_u64[5] = kv.key[5] = k.as_u64[5];
503   kv.value = reass - srm->ip6_reass_pool;
504   reass->sess_index = (u32) ~ 0;
505   reass->last_heard = now;
506
507   if (clib_bihash_add_del_48_8 (&srm->ip6_reass_hash, &kv, 1))
508     {
509       reass = 0;
510       goto unlock;
511     }
512
513 unlock:
514   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
515   return reass;
516 }
517
518 int
519 nat_ip6_reass_add_fragment (nat_reass_ip6_t * reass, u32 bi)
520 {
521   nat_reass_main_t *srm = &nat_reass_main;
522   dlist_elt_t *elt;
523   u32 elt_index;
524
525   if (reass->frag_n >= srm->ip6_max_frag)
526     {
527       nat_ipfix_logging_max_fragments_ip6 (srm->ip6_max_frag,
528                                            &reass->key.src);
529       return -1;
530     }
531
532   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
533
534   pool_get (srm->ip6_frags_list_pool, elt);
535   elt_index = elt - srm->ip6_frags_list_pool;
536   clib_dlist_init (srm->ip6_frags_list_pool, elt_index);
537   elt->value = bi;
538   clib_dlist_addtail (srm->ip6_frags_list_pool,
539                       reass->frags_per_reass_list_head_index, elt_index);
540   reass->frag_n++;
541
542   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
543
544   return 0;
545 }
546
547 void
548 nat_ip6_reass_get_frags (nat_reass_ip6_t * reass, u32 ** bi)
549 {
550   nat_reass_main_t *srm = &nat_reass_main;
551
552   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
553
554   nat_ip6_reass_get_frags_inline (reass, bi);
555
556   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
557 }
558
559 void
560 nat_ip6_reass_walk (nat_ip6_reass_walk_fn_t fn, void *ctx)
561 {
562   nat_reass_ip6_t *reass;
563   nat_reass_main_t *srm = &nat_reass_main;
564   f64 now = vlib_time_now (srm->vlib_main);
565
566   /* *INDENT-OFF* */
567   pool_foreach (reass, srm->ip6_reass_pool,
568   ({
569     if (now < reass->last_heard + (f64) srm->ip4_timeout)
570       {
571         if (fn (reass, ctx))
572           return;
573       }
574   }));
575   /* *INDENT-ON* */
576 }
577
578 clib_error_t *
579 nat_reass_init (vlib_main_t * vm)
580 {
581   nat_reass_main_t *srm = &nat_reass_main;
582   vlib_thread_main_t *tm = vlib_get_thread_main ();
583   clib_error_t *error = 0;
584   dlist_elt_t *head;
585   u32 nbuckets, head_index;
586
587   srm->vlib_main = vm;
588   srm->vnet_main = vnet_get_main ();
589
590   /* IPv4 */
591   srm->ip4_timeout = NAT_REASS_TIMEOUT_DEFAULT;
592   srm->ip4_max_reass = NAT_MAX_REASS_DEAFULT;
593   srm->ip4_max_frag = NAT_MAX_FRAG_DEFAULT;
594   srm->ip4_drop_frag = 0;
595   srm->ip4_reass_n = 0;
596
597   if (tm->n_vlib_mains > 1)
598     clib_spinlock_init (&srm->ip4_reass_lock);
599
600   pool_alloc (srm->ip4_reass_pool, srm->ip4_max_reass);
601
602   nbuckets = nat_reass_get_nbuckets (0);
603   clib_bihash_init_16_8 (&srm->ip4_reass_hash, "nat-ip4-reass", nbuckets,
604                          nbuckets * 1024);
605
606   pool_get (srm->ip4_reass_lru_list_pool, head);
607   srm->ip4_reass_head_index = head_index =
608     head - srm->ip4_reass_lru_list_pool;
609   clib_dlist_init (srm->ip4_reass_lru_list_pool, head_index);
610
611   /* IPv6 */
612   srm->ip6_timeout = NAT_REASS_TIMEOUT_DEFAULT;
613   srm->ip6_max_reass = NAT_MAX_REASS_DEAFULT;
614   srm->ip6_max_frag = NAT_MAX_FRAG_DEFAULT;
615   srm->ip6_drop_frag = 0;
616   srm->ip6_reass_n = 0;
617
618   if (tm->n_vlib_mains > 1)
619     clib_spinlock_init (&srm->ip6_reass_lock);
620
621   pool_alloc (srm->ip6_reass_pool, srm->ip6_max_reass);
622
623   nbuckets = nat_reass_get_nbuckets (1);
624   clib_bihash_init_48_8 (&srm->ip6_reass_hash, "nat-ip6-reass", nbuckets,
625                          nbuckets * 1024);
626
627   pool_get (srm->ip6_reass_lru_list_pool, head);
628   srm->ip6_reass_head_index = head_index =
629     head - srm->ip6_reass_lru_list_pool;
630   clib_dlist_init (srm->ip6_reass_lru_list_pool, head_index);
631
632   return error;
633 }
634
635 static clib_error_t *
636 nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
637                       vlib_cli_command_t * cmd)
638 {
639   clib_error_t *error = 0;
640   unformat_input_t _line_input, *line_input = &_line_input;
641   u32 timeout = 0, max_reass = 0, max_frag = 0;
642   u8 drop_frag = (u8) ~ 0, is_ip6 = 0;
643   int rv;
644
645   /* Get a line of input. */
646   if (!unformat_user (input, unformat_line_input, line_input))
647     return 0;
648
649   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
650     {
651       if (unformat (line_input, "max-reassemblies %u", &max_reass))
652         ;
653       else if (unformat (line_input, "max-fragments %u", &max_frag))
654         ;
655       else if (unformat (line_input, "timeout %u", &timeout))
656         ;
657       else if (unformat (line_input, "enable"))
658         drop_frag = 0;
659       else if (unformat (line_input, "disable"))
660         drop_frag = 1;
661       else if (unformat (line_input, "ip4"))
662         is_ip6 = 0;
663       else if (unformat (line_input, "ip6"))
664         is_ip6 = 1;
665       else
666         {
667           error = clib_error_return (0, "unknown input '%U'",
668                                      format_unformat_error, line_input);
669           goto done;
670         }
671     }
672
673   if (!timeout)
674     timeout = nat_reass_get_timeout (is_ip6);
675   if (!max_reass)
676     max_reass = nat_reass_get_max_reass (is_ip6);
677   if (!max_frag)
678     max_frag = nat_reass_get_max_frag (is_ip6);
679   if (drop_frag == (u8) ~ 0)
680     drop_frag = nat_reass_is_drop_frag (is_ip6);
681
682   rv =
683     nat_reass_set (timeout, (u16) max_reass, (u8) max_frag, drop_frag,
684                    is_ip6);
685   if (rv)
686     {
687       error = clib_error_return (0, "nat_set_reass return %d", rv);
688       goto done;
689     }
690
691 done:
692   unformat_free (line_input);
693
694   return error;
695 }
696
697 static int
698 nat_ip4_reass_walk_cli (nat_reass_ip4_t * reass, void *ctx)
699 {
700   vlib_main_t *vm = ctx;
701
702   vlib_cli_output (vm, "  src %U dst %U proto %u id 0x%04x cached %u",
703                    format_ip4_address, &reass->key.src,
704                    format_ip4_address, &reass->key.dst,
705                    reass->key.proto,
706                    clib_net_to_host_u16 (reass->key.frag_id), reass->frag_n);
707
708   return 0;
709 }
710
711 static int
712 nat_ip6_reass_walk_cli (nat_reass_ip6_t * reass, void *ctx)
713 {
714   vlib_main_t *vm = ctx;
715
716   vlib_cli_output (vm, "  src %U dst %U proto %u id 0x%08x cached %u",
717                    format_ip6_address, &reass->key.src,
718                    format_ip6_address, &reass->key.dst,
719                    reass->key.proto,
720                    clib_net_to_host_u32 (reass->key.frag_id), reass->frag_n);
721
722   return 0;
723 }
724
725 static clib_error_t *
726 show_nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
727                            vlib_cli_command_t * cmd)
728 {
729   vlib_cli_output (vm, "NAT IPv4 virtual fragmentation reassembly is %s",
730                    nat_reass_is_drop_frag (0) ? "DISABLED" : "ENABLED");
731   vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (0));
732   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (0));
733   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (0));
734   vlib_cli_output (vm, " reassemblies:");
735   nat_ip4_reass_walk (nat_ip4_reass_walk_cli, vm);
736
737   vlib_cli_output (vm, "NAT IPv6 virtual fragmentation reassembly is %s",
738                    nat_reass_is_drop_frag (1) ? "DISABLED" : "ENABLED");
739   vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (1));
740   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (1));
741   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (1));
742   vlib_cli_output (vm, " reassemblies:");
743   nat_ip6_reass_walk (nat_ip6_reass_walk_cli, vm);
744
745   return 0;
746 }
747
748 /* *INDENT-OFF* */
749 VLIB_CLI_COMMAND (nat_reass_command, static) =
750 {
751   .path = "nat virtual-reassembly",
752   .short_help = "nat virtual-reassembly ip4|ip6 [max-reassemblies <n>] "
753                 "[max-fragments <n>] [timeout <sec>] [enable|disable]",
754   .function = nat_reass_command_fn,
755 };
756
757 VLIB_CLI_COMMAND (show_nat_reass_command, static) =
758 {
759   .path = "show nat virtual-reassembly",
760   .short_help = "show nat virtual-reassembly",
761   .function = show_nat_reass_command_fn,
762 };
763 /* *INDENT-ON* */
764
765 /*
766  * fd.io coding-style-patch-verification: ON
767  *
768  * Local Variables:
769  * eval: (c-set-style "gnu")
770  * End:
771  */