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