acl-plugin: bihash-based ACL lookup
[vpp.git] / src / plugins / snat / nat64_db.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 NAT64 DB
18  */
19 #include <snat/nat64_db.h>
20
21 int
22 nat64_db_init (nat64_db_t * db)
23 {
24   u32 bib_buckets = 1024;
25   u32 bib_memory_size = 128 << 20;
26   u32 st_buckets = 2048;
27   u32 st_memory_size = 256 << 20;
28
29   clib_bihash_init_24_8 (&db->bib.in2out, "bib-in2out", bib_buckets,
30                          bib_memory_size);
31
32   clib_bihash_init_24_8 (&db->bib.out2in, "bib-out2in", bib_buckets,
33                          bib_memory_size);
34
35   clib_bihash_init_48_8 (&db->st.in2out, "st-in2out", st_buckets,
36                          st_memory_size);
37
38   clib_bihash_init_48_8 (&db->st.out2in, "st-out2in", st_buckets,
39                          st_memory_size);
40
41   return 0;
42 }
43
44 nat64_db_bib_entry_t *
45 nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr,
46                            ip4_address_t * out_addr, u16 in_port,
47                            u16 out_port, u32 fib_index, snat_protocol_t proto,
48                            u8 is_static)
49 {
50   nat64_db_bib_entry_t *bibe;
51   nat64_db_bib_entry_key_t bibe_key;
52   clib_bihash_kv_24_8_t kv;
53
54   /* create pool entry */
55   switch (proto)
56     {
57 /* *INDENT-OFF* */
58 #define _(N, i, n, s) \
59     case SNAT_PROTOCOL_##N: \
60       pool_get (db->bib._##n##_bib, bibe); \
61       kv.value = bibe - db->bib._##n##_bib; \
62       break;
63       foreach_snat_protocol
64 #undef _
65 /* *INDENT-ON* */
66     default:
67       clib_warning ("unknown protocol %u", proto);
68       return 0;
69     }
70   memset (bibe, 0, sizeof (*bibe));
71   bibe->in_addr.as_u64[0] = in_addr->as_u64[0];
72   bibe->in_addr.as_u64[1] = in_addr->as_u64[1];
73   bibe->in_port = in_port;
74   bibe->out_addr.as_u32 = out_addr->as_u32;
75   bibe->out_port = out_port;
76   bibe->fib_index = fib_index;
77   bibe->proto = proto;
78   bibe->is_static = is_static;
79
80   /* create hash lookup */
81   bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0];
82   bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1];
83   bibe_key.fib_index = bibe->fib_index;
84   bibe_key.port = bibe->in_port;
85   bibe_key.proto = bibe->proto;
86   bibe_key.rsvd = 0;
87   kv.key[0] = bibe_key.as_u64[0];
88   kv.key[1] = bibe_key.as_u64[1];
89   kv.key[2] = bibe_key.as_u64[2];
90   clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 1);
91
92   memset (&bibe_key.addr, 0, sizeof (bibe_key.addr));
93   bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32;
94   bibe_key.fib_index = 0;
95   bibe_key.port = bibe->out_port;
96   kv.key[0] = bibe_key.as_u64[0];
97   kv.key[1] = bibe_key.as_u64[1];
98   kv.key[2] = bibe_key.as_u64[2];
99   clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 1);
100
101   return bibe;
102 }
103
104 void
105 nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe)
106 {
107   nat64_db_bib_entry_key_t bibe_key;
108   clib_bihash_kv_24_8_t kv;
109   nat64_db_bib_entry_t *bib;
110   u32 *ste_to_be_free = 0, *ste_index, bibe_index;
111   nat64_db_st_entry_t *st, *ste;
112
113   switch (bibe->proto)
114     {
115 /* *INDENT-OFF* */
116 #define _(N, i, n, s) \
117     case SNAT_PROTOCOL_##N: \
118       bib = db->bib._##n##_bib; \
119       st = db->st._##n##_st; \
120       break;
121       foreach_snat_protocol
122 #undef _
123 /* *INDENT-ON* */
124     default:
125       clib_warning ("unknown protocol %u", bibe->proto);
126       return;
127     }
128
129   bibe_index = bibe - bib;
130
131   /* delete ST entries for static BIB entry */
132   if (bibe->is_static)
133     {
134       pool_foreach (ste, st, (
135                                {
136                                if (ste->bibe_index == bibe_index)
137                                vec_add1 (ste_to_be_free, ste - st);}
138                     ));
139       vec_foreach (ste_index, ste_to_be_free)
140         nat64_db_st_entry_free (db, pool_elt_at_index (st, ste_index[0]));
141       vec_free (ste_to_be_free);
142     }
143
144   /* delete hash lookup */
145   bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0];
146   bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1];
147   bibe_key.fib_index = bibe->fib_index;
148   bibe_key.port = bibe->in_port;
149   bibe_key.proto = bibe->proto;
150   bibe_key.rsvd = 0;
151   kv.key[0] = bibe_key.as_u64[0];
152   kv.key[1] = bibe_key.as_u64[1];
153   kv.key[2] = bibe_key.as_u64[2];
154   clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 0);
155
156   memset (&bibe_key.addr, 0, sizeof (bibe_key.addr));
157   bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32;
158   bibe_key.fib_index = 0;
159   bibe_key.port = bibe->out_port;
160   kv.key[0] = bibe_key.as_u64[0];
161   kv.key[1] = bibe_key.as_u64[1];
162   kv.key[2] = bibe_key.as_u64[2];
163   clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 0);
164
165   /* delete from pool */
166   pool_put (bib, bibe);
167
168 }
169
170 nat64_db_bib_entry_t *
171 nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port,
172                          snat_protocol_t proto, u32 fib_index, u8 is_ip6)
173 {
174   nat64_db_bib_entry_t *bibe = 0;
175   nat64_db_bib_entry_key_t bibe_key;
176   clib_bihash_kv_24_8_t kv, value;
177   nat64_db_bib_entry_t *bib;
178
179   switch (proto)
180     {
181 /* *INDENT-OFF* */
182 #define _(N, i, n, s) \
183     case SNAT_PROTOCOL_##N: \
184       bib = db->bib._##n##_bib; \
185       break;
186       foreach_snat_protocol
187 #undef _
188 /* *INDENT-ON* */
189     default:
190       clib_warning ("unknown protocol %u", proto);
191       return 0;
192     }
193
194   bibe_key.addr.as_u64[0] = addr->as_u64[0];
195   bibe_key.addr.as_u64[1] = addr->as_u64[1];
196   bibe_key.fib_index = fib_index;
197   bibe_key.port = port;
198   bibe_key.proto = proto;
199   bibe_key.rsvd = 0;
200
201   kv.key[0] = bibe_key.as_u64[0];
202   kv.key[1] = bibe_key.as_u64[1];
203   kv.key[2] = bibe_key.as_u64[2];
204
205   if (!clib_bihash_search_24_8
206       (is_ip6 ? &db->bib.in2out : &db->bib.out2in, &kv, &value))
207     bibe = pool_elt_at_index (bib, value.value);
208
209   return bibe;
210 }
211
212 void
213 nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
214                    nat64_db_bib_walk_fn_t fn, void *ctx)
215 {
216   nat64_db_bib_entry_t *bib, *bibe;
217
218   switch (proto)
219     {
220 /* *INDENT-OFF* */
221 #define _(N, i, n, s) \
222     case SNAT_PROTOCOL_##N: \
223       bib = db->bib._##n##_bib; \
224       break;
225       foreach_snat_protocol
226 #undef _
227 /* *INDENT-ON* */
228     default:
229       clib_warning ("unknown protocol");
230       return;
231     }
232
233   /* *INDENT-OFF* */
234   pool_foreach (bibe, bib,
235   ({
236     if (fn (bibe, ctx))
237       return;
238   }));
239   /* *INDENT-ON* */
240 }
241
242 nat64_db_bib_entry_t *
243 nat64_db_bib_entry_by_index (nat64_db_t * db, snat_protocol_t proto,
244                              u32 bibe_index)
245 {
246   nat64_db_bib_entry_t *bib;
247
248   switch (proto)
249     {
250 /* *INDENT-OFF* */
251 #define _(N, i, n, s) \
252     case SNAT_PROTOCOL_##N: \
253       bib = db->bib._##n##_bib; \
254       break;
255       foreach_snat_protocol
256 #undef _
257 /* *INDENT-ON* */
258     default:
259       clib_warning ("unknown protocol %u", proto);
260       return 0;
261     }
262
263   return pool_elt_at_index (bib, bibe_index);
264 }
265
266 void
267 nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
268                   nat64_db_st_walk_fn_t fn, void *ctx)
269 {
270   nat64_db_st_entry_t *st, *ste;
271
272   switch (proto)
273     {
274 /* *INDENT-OFF* */
275 #define _(N, i, n, s) \
276     case SNAT_PROTOCOL_##N: \
277       st = db->st._##n##_st; \
278       break;
279       foreach_snat_protocol
280 #undef _
281 /* *INDENT-ON* */
282     default:
283       clib_warning ("unknown protocol");
284       return;
285     }
286
287   /* *INDENT-OFF* */
288   pool_foreach (ste, st,
289   ({
290     if (fn (ste, ctx))
291       return;
292   }));
293   /* *INDENT-ON* */
294 }
295
296 nat64_db_st_entry_t *
297 nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe,
298                           ip6_address_t * in_r_addr,
299                           ip4_address_t * out_r_addr, u16 r_port)
300 {
301   nat64_db_st_entry_t *ste;
302   nat64_db_bib_entry_t *bib;
303   nat64_db_st_entry_key_t ste_key;
304   clib_bihash_kv_48_8_t kv;
305
306   /* create pool entry */
307   switch (bibe->proto)
308     {
309 /* *INDENT-OFF* */
310 #define _(N, i, n, s) \
311     case SNAT_PROTOCOL_##N: \
312       pool_get (db->st._##n##_st, ste); \
313       kv.value = ste - db->st._##n##_st; \
314       bib = db->bib._##n##_bib; \
315       break;
316       foreach_snat_protocol
317 #undef _
318 /* *INDENT-ON* */
319     default:
320       clib_warning ("unknown protocol %u", bibe->proto);
321       return 0;
322     }
323   memset (ste, 0, sizeof (*ste));
324   ste->in_r_addr.as_u64[0] = in_r_addr->as_u64[0];
325   ste->in_r_addr.as_u64[1] = in_r_addr->as_u64[1];
326   ste->out_r_addr.as_u32 = out_r_addr->as_u32;
327   ste->r_port = r_port;
328   ste->bibe_index = bibe - bib;
329   ste->proto = bibe->proto;
330
331   /* increment session number for BIB entry */
332   bibe->ses_num++;
333
334   /* create hash lookup */
335   memset (&ste_key, 0, sizeof (ste_key));
336   ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0];
337   ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1];
338   ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0];
339   ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1];
340   ste_key.fib_index = bibe->fib_index;
341   ste_key.l_port = bibe->in_port;
342   ste_key.r_port = ste->r_port;
343   ste_key.proto = ste->proto;
344   kv.key[0] = ste_key.as_u64[0];
345   kv.key[1] = ste_key.as_u64[1];
346   kv.key[2] = ste_key.as_u64[2];
347   kv.key[3] = ste_key.as_u64[3];
348   kv.key[4] = ste_key.as_u64[4];
349   kv.key[5] = ste_key.as_u64[5];
350   clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 1);
351
352   memset (&ste_key, 0, sizeof (ste_key));
353   ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32;
354   ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32;
355   ste_key.l_port = bibe->out_port;
356   ste_key.r_port = ste->r_port;
357   ste_key.proto = ste->proto;
358   kv.key[0] = ste_key.as_u64[0];
359   kv.key[1] = ste_key.as_u64[1];
360   kv.key[2] = ste_key.as_u64[2];
361   kv.key[3] = ste_key.as_u64[3];
362   kv.key[4] = ste_key.as_u64[4];
363   kv.key[5] = ste_key.as_u64[5];
364   clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 1);
365
366   return ste;
367 }
368
369 void
370 nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste)
371 {
372   nat64_db_st_entry_t *st;
373   nat64_db_bib_entry_t *bib, *bibe;
374   nat64_db_st_entry_key_t ste_key;
375   clib_bihash_kv_48_8_t kv;
376
377   switch (ste->proto)
378     {
379 /* *INDENT-OFF* */
380 #define _(N, i, n, s) \
381     case SNAT_PROTOCOL_##N: \
382       st = db->st._##n##_st; \
383       bib = db->bib._##n##_bib; \
384       break;
385       foreach_snat_protocol
386 #undef _
387 /* *INDENT-ON* */
388     default:
389       clib_warning ("unknown protocol %u", ste->proto);
390       return;
391     }
392
393   bibe = pool_elt_at_index (bib, ste->bibe_index);
394
395   /* delete hash lookup */
396   memset (&ste_key, 0, sizeof (ste_key));
397   ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0];
398   ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1];
399   ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0];
400   ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1];
401   ste_key.fib_index = bibe->fib_index;
402   ste_key.l_port = bibe->in_port;
403   ste_key.r_port = ste->r_port;
404   ste_key.proto = ste->proto;
405   kv.key[0] = ste_key.as_u64[0];
406   kv.key[1] = ste_key.as_u64[1];
407   kv.key[2] = ste_key.as_u64[2];
408   kv.key[3] = ste_key.as_u64[3];
409   kv.key[4] = ste_key.as_u64[4];
410   kv.key[5] = ste_key.as_u64[5];
411   clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 0);
412
413   memset (&ste_key, 0, sizeof (ste_key));
414   ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32;
415   ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32;
416   ste_key.l_port = bibe->out_port;
417   ste_key.r_port = ste->r_port;
418   ste_key.proto = ste->proto;
419   kv.key[0] = ste_key.as_u64[0];
420   kv.key[1] = ste_key.as_u64[1];
421   kv.key[2] = ste_key.as_u64[2];
422   kv.key[3] = ste_key.as_u64[3];
423   kv.key[4] = ste_key.as_u64[4];
424   kv.key[5] = ste_key.as_u64[5];
425   clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 0);
426
427   /* delete from pool */
428   pool_put (st, ste);
429
430   /* decrement session number for BIB entry */
431   bibe->ses_num--;
432
433   /* delete BIB entry if last session and dynamic */
434   if (!bibe->is_static && !bibe->ses_num)
435     nat64_db_bib_entry_free (db, bibe);
436 }
437
438 nat64_db_st_entry_t *
439 nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr,
440                         ip46_address_t * r_addr, u16 l_port, u16 r_port,
441                         snat_protocol_t proto, u32 fib_index, u8 is_ip6)
442 {
443   nat64_db_st_entry_t *ste = 0;
444   nat64_db_st_entry_t *st;
445   nat64_db_st_entry_key_t ste_key;
446   clib_bihash_kv_48_8_t kv, value;
447
448   switch (proto)
449     {
450 /* *INDENT-OFF* */
451 #define _(N, i, n, s) \
452     case SNAT_PROTOCOL_##N: \
453       st = db->st._##n##_st; \
454       break;
455       foreach_snat_protocol
456 #undef _
457 /* *INDENT-ON* */
458     default:
459       clib_warning ("unknown protocol %u", proto);
460       return ste;
461     }
462
463   memset (&ste_key, 0, sizeof (ste_key));
464   ste_key.l_addr.as_u64[0] = l_addr->as_u64[0];
465   ste_key.l_addr.as_u64[1] = l_addr->as_u64[1];
466   ste_key.r_addr.as_u64[0] = r_addr->as_u64[0];
467   ste_key.r_addr.as_u64[1] = r_addr->as_u64[1];
468   ste_key.fib_index = fib_index;
469   ste_key.l_port = l_port;
470   ste_key.r_port = r_port;
471   ste_key.proto = proto;
472   kv.key[0] = ste_key.as_u64[0];
473   kv.key[1] = ste_key.as_u64[1];
474   kv.key[2] = ste_key.as_u64[2];
475   kv.key[3] = ste_key.as_u64[3];
476   kv.key[4] = ste_key.as_u64[4];
477   kv.key[5] = ste_key.as_u64[5];
478
479   if (!clib_bihash_search_48_8
480       (is_ip6 ? &db->st.in2out : &db->st.out2in, &kv, &value))
481     ste = pool_elt_at_index (st, value.value);
482
483   return ste;
484 }
485
486 void
487 nad64_db_st_free_expired (nat64_db_t * db, u32 now)
488 {
489   u32 *ste_to_be_free = 0, *ste_index;
490   nat64_db_st_entry_t *st, *ste;
491
492 /* *INDENT-OFF* */
493 #define _(N, i, n, s) \
494   st = db->st._##n##_st; \
495   pool_foreach (ste, st, ({\
496     if (i == SNAT_PROTOCOL_TCP && !ste->tcp_state) \
497       continue; \
498     if (ste->expire < now) \
499       vec_add1 (ste_to_be_free, ste - st); \
500   })); \
501   vec_foreach (ste_index, ste_to_be_free) \
502     nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \
503   vec_free (ste_to_be_free); \
504   ste_to_be_free = 0;
505   foreach_snat_protocol
506 #undef _
507 /* *INDENT-ON* */
508 }
509
510 /*
511  * fd.io coding-style-patch-verification: ON
512  *
513  * Local Variables:
514  * eval: (c-set-style "gnu")
515  * End:
516  */