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