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