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