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