NAT44: fix snat_get_worker_out2in_cb (VPP-1536)
[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_log_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_log_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_log_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_log_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[4];
550       kv.key[4] = k.as_u64[5];
551       if (clib_bihash_add_del_48_8 (&srm->ip6_reass_hash, &kv, 0))
552         {
553           reass = 0;
554           goto unlock;
555         }
556
557       nat_ip6_reass_get_frags_inline (reass, bi_to_drop);
558     }
559   else
560     {
561       pool_get (srm->ip6_reass_pool, reass);
562       pool_get (srm->ip6_reass_lru_list_pool, elt);
563       reass->lru_list_index = elt_index = elt - srm->ip6_reass_lru_list_pool;
564       clib_dlist_init (srm->ip6_reass_lru_list_pool, elt_index);
565       elt->value = reass - srm->ip6_reass_pool;
566       clib_dlist_addtail (srm->ip6_reass_lru_list_pool,
567                           srm->ip6_reass_head_index, elt_index);
568       pool_get (srm->ip6_frags_list_pool, per_reass_list_head_elt);
569       reass->frags_per_reass_list_head_index =
570         per_reass_list_head_elt - srm->ip6_frags_list_pool;
571       clib_dlist_init (srm->ip6_frags_list_pool,
572                        reass->frags_per_reass_list_head_index);
573       srm->ip6_reass_n++;
574     }
575
576   reass->key.as_u64[0] = kv.key[0] = k.as_u64[0];
577   reass->key.as_u64[1] = kv.key[1] = k.as_u64[1];
578   reass->key.as_u64[2] = kv.key[2] = k.as_u64[2];
579   reass->key.as_u64[3] = kv.key[3] = k.as_u64[3];
580   reass->key.as_u64[4] = kv.key[4] = k.as_u64[4];
581   reass->key.as_u64[5] = kv.key[5] = k.as_u64[5];
582   kv.value = reass - srm->ip6_reass_pool;
583   reass->sess_index = (u32) ~ 0;
584   reass->last_heard = now;
585
586   if (clib_bihash_add_del_48_8 (&srm->ip6_reass_hash, &kv, 1))
587     {
588       reass = 0;
589       goto unlock;
590     }
591
592 unlock:
593   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
594   return reass;
595 }
596
597 int
598 nat_ip6_reass_add_fragment (u32 thread_index, nat_reass_ip6_t * reass,
599                             u32 bi, u32 ** bi_to_drop)
600 {
601   nat_reass_main_t *srm = &nat_reass_main;
602   dlist_elt_t *elt;
603   u32 elt_index;
604
605   if (reass->frag_n >= srm->ip6_max_frag)
606     {
607       nat_ipfix_logging_max_fragments_ip6 (thread_index, srm->ip6_max_frag,
608                                            &reass->key.src);
609       reass->flags |= NAT_REASS_FLAG_MAX_FRAG_DROP;
610       nat_ip6_reass_get_frags_inline (reass, bi_to_drop);
611       return -1;
612     }
613
614   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
615
616   pool_get (srm->ip6_frags_list_pool, elt);
617   elt_index = elt - srm->ip6_frags_list_pool;
618   clib_dlist_init (srm->ip6_frags_list_pool, elt_index);
619   elt->value = bi;
620   clib_dlist_addtail (srm->ip6_frags_list_pool,
621                       reass->frags_per_reass_list_head_index, elt_index);
622   reass->frag_n++;
623
624   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
625
626   return 0;
627 }
628
629 void
630 nat_ip6_reass_get_frags (nat_reass_ip6_t * reass, u32 ** bi)
631 {
632   nat_reass_main_t *srm = &nat_reass_main;
633
634   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
635
636   nat_ip6_reass_get_frags_inline (reass, bi);
637
638   clib_spinlock_unlock_if_init (&srm->ip6_reass_lock);
639 }
640
641 void
642 nat_ip6_reass_walk (nat_ip6_reass_walk_fn_t fn, void *ctx)
643 {
644   nat_reass_ip6_t *reass;
645   nat_reass_main_t *srm = &nat_reass_main;
646   f64 now = vlib_time_now (srm->vlib_main);
647
648   /* *INDENT-OFF* */
649   pool_foreach (reass, srm->ip6_reass_pool,
650   ({
651     if (now < reass->last_heard + (f64) srm->ip4_timeout)
652       {
653         if (fn (reass, ctx))
654           return;
655       }
656   }));
657   /* *INDENT-ON* */
658 }
659
660 clib_error_t *
661 nat_reass_init (vlib_main_t * vm)
662 {
663   nat_reass_main_t *srm = &nat_reass_main;
664   vlib_thread_main_t *tm = vlib_get_thread_main ();
665   clib_error_t *error = 0;
666   dlist_elt_t *head;
667   u32 nbuckets, head_index;
668
669   srm->vlib_main = vm;
670   srm->vnet_main = vnet_get_main ();
671
672   /* IPv4 */
673   srm->ip4_timeout = NAT_REASS_TIMEOUT_DEFAULT;
674   srm->ip4_max_reass = NAT_MAX_REASS_DEAFULT;
675   srm->ip4_max_frag = NAT_MAX_FRAG_DEFAULT;
676   srm->ip4_drop_frag = 0;
677   srm->ip4_reass_n = 0;
678
679   if (tm->n_vlib_mains > 1)
680     clib_spinlock_init (&srm->ip4_reass_lock);
681
682   pool_alloc (srm->ip4_reass_pool, srm->ip4_max_reass);
683
684   nbuckets = nat_reass_get_nbuckets (0);
685   clib_bihash_init_16_8 (&srm->ip4_reass_hash, "nat-ip4-reass", nbuckets,
686                          nbuckets * 1024);
687
688   pool_get (srm->ip4_reass_lru_list_pool, head);
689   srm->ip4_reass_head_index = head_index =
690     head - srm->ip4_reass_lru_list_pool;
691   clib_dlist_init (srm->ip4_reass_lru_list_pool, head_index);
692
693   /* IPv6 */
694   srm->ip6_timeout = NAT_REASS_TIMEOUT_DEFAULT;
695   srm->ip6_max_reass = NAT_MAX_REASS_DEAFULT;
696   srm->ip6_max_frag = NAT_MAX_FRAG_DEFAULT;
697   srm->ip6_drop_frag = 0;
698   srm->ip6_reass_n = 0;
699
700   if (tm->n_vlib_mains > 1)
701     clib_spinlock_init (&srm->ip6_reass_lock);
702
703   pool_alloc (srm->ip6_reass_pool, srm->ip6_max_reass);
704
705   nbuckets = nat_reass_get_nbuckets (1);
706   clib_bihash_init_48_8 (&srm->ip6_reass_hash, "nat-ip6-reass", nbuckets,
707                          nbuckets * 1024);
708
709   pool_get (srm->ip6_reass_lru_list_pool, head);
710   srm->ip6_reass_head_index = head_index =
711     head - srm->ip6_reass_lru_list_pool;
712   clib_dlist_init (srm->ip6_reass_lru_list_pool, head_index);
713
714   return error;
715 }
716
717 static clib_error_t *
718 nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
719                       vlib_cli_command_t * cmd)
720 {
721   clib_error_t *error = 0;
722   unformat_input_t _line_input, *line_input = &_line_input;
723   u32 timeout = 0, max_reass = 0, max_frag = 0;
724   u8 drop_frag = (u8) ~ 0, is_ip6 = 0;
725   int rv;
726
727   /* Get a line of input. */
728   if (!unformat_user (input, unformat_line_input, line_input))
729     return 0;
730
731   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
732     {
733       if (unformat (line_input, "max-reassemblies %u", &max_reass))
734         ;
735       else if (unformat (line_input, "max-fragments %u", &max_frag))
736         ;
737       else if (unformat (line_input, "timeout %u", &timeout))
738         ;
739       else if (unformat (line_input, "enable"))
740         drop_frag = 0;
741       else if (unformat (line_input, "disable"))
742         drop_frag = 1;
743       else if (unformat (line_input, "ip4"))
744         is_ip6 = 0;
745       else if (unformat (line_input, "ip6"))
746         is_ip6 = 1;
747       else
748         {
749           error = clib_error_return (0, "unknown input '%U'",
750                                      format_unformat_error, line_input);
751           goto done;
752         }
753     }
754
755   if (!timeout)
756     timeout = nat_reass_get_timeout (is_ip6);
757   if (!max_reass)
758     max_reass = nat_reass_get_max_reass (is_ip6);
759   if (!max_frag)
760     max_frag = nat_reass_get_max_frag (is_ip6);
761   if (drop_frag == (u8) ~ 0)
762     drop_frag = nat_reass_is_drop_frag (is_ip6);
763
764   rv =
765     nat_reass_set (timeout, (u16) max_reass, (u8) max_frag, drop_frag,
766                    is_ip6);
767   if (rv)
768     {
769       error = clib_error_return (0, "nat_set_reass return %d", rv);
770       goto done;
771     }
772
773 done:
774   unformat_free (line_input);
775
776   return error;
777 }
778
779 static int
780 nat_ip4_reass_walk_cli (nat_reass_ip4_t * reass, void *ctx)
781 {
782   vlib_main_t *vm = ctx;
783   u8 *flags_str = 0;
784   const char *classify_next_str;
785
786   if (reass->flags & NAT_REASS_FLAG_MAX_FRAG_DROP)
787     flags_str = format (flags_str, "MAX_FRAG_DROP");
788   if (reass->flags & NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE)
789     {
790       if (flags_str)
791         flags_str = format (flags_str, " | ");
792       flags_str = format (flags_str, "CLASSIFY_ED_CONTINUE");
793     }
794   if (reass->flags & NAT_REASS_FLAG_ED_DONT_TRANSLATE)
795     {
796       if (flags_str)
797         flags_str = format (flags_str, " | ");
798       flags_str = format (flags_str, "CLASSIFY_ED_DONT_TRANSLATE");
799     }
800   if (!flags_str)
801     flags_str = format (flags_str, "0");
802   flags_str = format (flags_str, "%c", 0);
803
804   switch (reass->classify_next)
805     {
806     case NAT_REASS_IP4_CLASSIFY_NONE:
807       classify_next_str = "NONE";
808       break;
809     case NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT:
810       classify_next_str = "IN2OUT";
811       break;
812     case NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN:
813       classify_next_str = "OUT2IN";
814       break;
815     default:
816       classify_next_str = "invalid value";
817     }
818
819   vlib_cli_output (vm, "  src %U dst %U proto %u id 0x%04x cached %u "
820                    "flags %s classify_next %s",
821                    format_ip4_address, &reass->key.src,
822                    format_ip4_address, &reass->key.dst,
823                    reass->key.proto,
824                    clib_net_to_host_u16 (reass->key.frag_id), reass->frag_n,
825                    flags_str, classify_next_str);
826
827   vec_free (flags_str);
828
829   return 0;
830 }
831
832 static int
833 nat_ip6_reass_walk_cli (nat_reass_ip6_t * reass, void *ctx)
834 {
835   vlib_main_t *vm = ctx;
836
837   vlib_cli_output (vm, "  src %U dst %U proto %u id 0x%08x cached %u",
838                    format_ip6_address, &reass->key.src,
839                    format_ip6_address, &reass->key.dst,
840                    reass->key.proto,
841                    clib_net_to_host_u32 (reass->key.frag_id), reass->frag_n);
842
843   return 0;
844 }
845
846 static clib_error_t *
847 show_nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
848                            vlib_cli_command_t * cmd)
849 {
850   vlib_cli_output (vm, "NAT IPv4 virtual fragmentation reassembly is %s",
851                    nat_reass_is_drop_frag (0) ? "DISABLED" : "ENABLED");
852   vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (0));
853   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (0));
854   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (0));
855   vlib_cli_output (vm, " reassemblies:");
856   nat_ip4_reass_walk (nat_ip4_reass_walk_cli, vm);
857
858   vlib_cli_output (vm, "NAT IPv6 virtual fragmentation reassembly is %s",
859                    nat_reass_is_drop_frag (1) ? "DISABLED" : "ENABLED");
860   vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (1));
861   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (1));
862   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (1));
863   vlib_cli_output (vm, " reassemblies:");
864   nat_ip6_reass_walk (nat_ip6_reass_walk_cli, vm);
865
866   return 0;
867 }
868
869 /* *INDENT-OFF* */
870 VLIB_CLI_COMMAND (nat_reass_command, static) =
871 {
872   .path = "nat virtual-reassembly",
873   .short_help = "nat virtual-reassembly ip4|ip6 [max-reassemblies <n>] "
874                 "[max-fragments <n>] [timeout <sec>] [enable|disable]",
875   .function = nat_reass_command_fn,
876 };
877
878 VLIB_CLI_COMMAND (show_nat_reass_command, static) =
879 {
880   .path = "show nat virtual-reassembly",
881   .short_help = "show nat virtual-reassembly",
882   .function = show_nat_reass_command_fn,
883 };
884 /* *INDENT-ON* */
885
886 /*
887  * fd.io coding-style-patch-verification: ON
888  *
889  * Local Variables:
890  * eval: (c-set-style "gnu")
891  * End:
892  */