9b6b3c8a5acf9b850880cf1abca4061338534024
[vpp.git] / src / plugins / snat / nat64.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 implementation
18  */
19
20 #include <snat/nat64.h>
21 #include <snat/nat64_db.h>
22 #include <vnet/fib/ip4_fib.h>
23
24
25 nat64_main_t nat64_main;
26
27 /* *INDENT-OFF* */
28
29 /* Hook up input features */
30 VNET_FEATURE_INIT (nat64_in2out, static) = {
31   .arc_name = "ip6-unicast",
32   .node_name = "nat64-in2out",
33   .runs_before = VNET_FEATURES ("ip6-lookup"),
34 };
35 VNET_FEATURE_INIT (nat64_out2in, static) = {
36   .arc_name = "ip4-unicast",
37   .node_name = "nat64-out2in",
38   .runs_before = VNET_FEATURES ("ip4-lookup"),
39 };
40
41 /* *INDENT-ON* */
42
43 clib_error_t *
44 nat64_init (vlib_main_t * vm)
45 {
46   nat64_main_t *nm = &nat64_main;
47   clib_error_t *error = 0;
48
49   if (nat64_db_init (&nm->db))
50     error = clib_error_return (0, "NAT64 DB init failed");
51
52   /* set session timeouts to default values */
53   nm->udp_timeout = SNAT_UDP_TIMEOUT;
54   nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
55   nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
56   nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
57   nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
58
59   return error;
60 }
61
62 int
63 nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add)
64 {
65   nat64_main_t *nm = &nat64_main;
66   snat_address_t *a = 0;
67   snat_interface_t *interface;
68   int i;
69
70   /* Check if address already exists */
71   for (i = 0; i < vec_len (nm->addr_pool); i++)
72     {
73       if (nm->addr_pool[i].addr.as_u32 == addr->as_u32)
74         {
75           a = nm->addr_pool + i;
76           break;
77         }
78     }
79
80   if (is_add)
81     {
82       if (a)
83         return VNET_API_ERROR_VALUE_EXIST;
84
85       vec_add2 (nm->addr_pool, a, 1);
86       a->addr = *addr;
87       a->fib_index = ip4_fib_index_from_table_id (vrf_id);
88 #define _(N, i, n, s) \
89       clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535);
90       foreach_snat_protocol
91 #undef _
92     }
93   else
94     {
95       if (!a)
96         return VNET_API_ERROR_NO_SUCH_ENTRY;
97
98 #define _(N, id, n, s) \
99       clib_bitmap_free (a->busy_##n##_port_bitmap);
100       foreach_snat_protocol
101 #undef _
102         vec_del1 (nm->addr_pool, i);
103     }
104
105   /* Add/del external address to FIB */
106   /* *INDENT-OFF* */
107   pool_foreach (interface, nm->interfaces,
108   ({
109     if (interface->is_inside)
110       continue;
111
112     snat_add_del_addr_to_fib (addr, 32, interface->sw_if_index, is_add);
113     break;
114   }));
115   /* *INDENT-ON* */
116
117   return 0;
118 }
119
120 void
121 nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx)
122 {
123   nat64_main_t *nm = &nat64_main;
124   snat_address_t *a = 0;
125
126   /* *INDENT-OFF* */
127   vec_foreach (a, nm->addr_pool)
128     {
129       if (fn (a, ctx))
130         break;
131     };
132   /* *INDENT-ON* */
133 }
134
135 int
136 nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add)
137 {
138   nat64_main_t *nm = &nat64_main;
139   snat_interface_t *interface = 0, *i;
140   snat_address_t *ap;
141   const char *feature_name, *arc_name;
142
143   /* Check if address already exists */
144   /* *INDENT-OFF* */
145   pool_foreach (i, nm->interfaces,
146   ({
147     if (i->sw_if_index == sw_if_index)
148       {
149         interface = i;
150         break;
151       }
152   }));
153   /* *INDENT-ON* */
154
155   if (is_add)
156     {
157       if (interface)
158         return VNET_API_ERROR_VALUE_EXIST;
159
160       pool_get (nm->interfaces, interface);
161       interface->sw_if_index = sw_if_index;
162       interface->is_inside = is_inside;
163
164     }
165   else
166     {
167       if (!interface)
168         return VNET_API_ERROR_NO_SUCH_ENTRY;
169
170       pool_put (nm->interfaces, interface);
171     }
172
173   if (!is_inside)
174     {
175       /* *INDENT-OFF* */
176       vec_foreach (ap, nm->addr_pool)
177         snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, is_add);
178       /* *INDENT-ON* */
179     }
180
181   arc_name = is_inside ? "ip6-unicast" : "ip4-unicast";
182   feature_name = is_inside ? "nat64-in2out" : "nat64-out2in";
183
184   return vnet_feature_enable_disable (arc_name, feature_name, sw_if_index,
185                                       is_add, 0, 0);
186 }
187
188 void
189 nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx)
190 {
191   nat64_main_t *nm = &nat64_main;
192   snat_interface_t *i = 0;
193
194   /* *INDENT-OFF* */
195   pool_foreach (i, nm->interfaces,
196   ({
197     if (fn (i, ctx))
198       break;
199   }));
200   /* *INDENT-ON* */
201 }
202
203 int
204 nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
205                                ip4_address_t * addr, u16 * port)
206 {
207   nat64_main_t *nm = &nat64_main;
208   snat_main_t *sm = &snat_main;
209   int i;
210   snat_address_t *a;
211   u32 portnum;
212
213   for (i = 0; i < vec_len (nm->addr_pool); i++)
214     {
215       a = nm->addr_pool + i;
216       switch (proto)
217         {
218 #define _(N, j, n, s) \
219         case SNAT_PROTOCOL_##N: \
220           if (a->busy_##n##_ports < (65535-1024)) \
221             { \
222               while (1) \
223                 { \
224                   portnum = random_u32 (&sm->random_seed); \
225                   portnum &= 0xFFFF; \
226                   if (portnum < 1024) \
227                     continue; \
228                   if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
229                                                 portnum)) \
230                     continue; \
231                   clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
232                                             portnum, 1); \
233                   a->busy_##n##_ports++; \
234                   *port = portnum; \
235                   addr->as_u32 = a->addr.as_u32; \
236                   return 0; \
237                 } \
238             } \
239           break;
240           foreach_snat_protocol
241 #undef _
242         default:
243           clib_warning ("unknown protocol");
244           return 1;
245         }
246
247     }
248   /* Totally out of translations to use... */
249   //TODO: IPFix
250   return 1;
251 }
252
253 void
254 nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port,
255                               snat_protocol_t proto)
256 {
257   nat64_main_t *nm = &nat64_main;
258   int i;
259   snat_address_t *a;
260
261   for (i = 0; i < vec_len (nm->addr_pool); i++)
262     {
263       a = nm->addr_pool + i;
264       if (addr->as_u32 != a->addr.as_u32)
265         continue;
266       switch (proto)
267         {
268 #define _(N, j, n, s) \
269         case SNAT_PROTOCOL_##N: \
270           ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
271                   port) == 1); \
272           clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port, 0); \
273           a->busy_##n##_ports--; \
274           break;
275           foreach_snat_protocol
276 #undef _
277         default:
278           clib_warning ("unknown protocol");
279           return;
280         }
281       break;
282     }
283 }
284
285 int
286 nat64_add_del_static_bib_entry (ip6_address_t * in_addr,
287                                 ip4_address_t * out_addr, u16 in_port,
288                                 u16 out_port, u8 proto, u32 vrf_id, u8 is_add)
289 {
290   nat64_main_t *nm = &nat64_main;
291   nat64_db_bib_entry_t *bibe;
292   u32 fib_index =
293     fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id);
294   snat_protocol_t p = ip_proto_to_snat_proto (proto);
295   ip46_address_t addr;
296   int i;
297   snat_address_t *a;
298
299   addr.as_u64[0] = in_addr->as_u64[0];
300   addr.as_u64[1] = in_addr->as_u64[1];
301   bibe =
302     nat64_db_bib_entry_find (&nm->db, &addr, clib_host_to_net_u16 (in_port),
303                              p, fib_index, 1);
304
305   if (is_add)
306     {
307       if (bibe)
308         return VNET_API_ERROR_VALUE_EXIST;
309
310       for (i = 0; i < vec_len (nm->addr_pool); i++)
311         {
312           a = nm->addr_pool + i;
313           if (out_addr->as_u32 != a->addr.as_u32)
314             continue;
315           switch (p)
316             {
317 #define _(N, j, n, s) \
318             case SNAT_PROTOCOL_##N: \
319               if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
320                                             out_port)) \
321                 return VNET_API_ERROR_INVALID_VALUE; \
322               clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
323                                         out_port, 1); \
324               if (out_port > 1024) \
325                 a->busy_##n##_ports++; \
326               break;
327               foreach_snat_protocol
328 #undef _
329             default:
330               clib_warning ("unknown protocol");
331               return VNET_API_ERROR_INVALID_VALUE_2;
332             }
333           break;
334         }
335       bibe =
336         nat64_db_bib_entry_create (&nm->db, in_addr, out_addr,
337                                    clib_host_to_net_u16 (in_port),
338                                    clib_host_to_net_u16 (out_port), fib_index,
339                                    p, 1);
340       if (!bibe)
341         return VNET_API_ERROR_UNSPECIFIED;
342     }
343   else
344     {
345       if (!bibe)
346         return VNET_API_ERROR_NO_SUCH_ENTRY;
347
348       nat64_free_out_addr_and_port (out_addr, out_port, p);
349       nat64_db_bib_entry_free (&nm->db, bibe);
350     }
351
352   return 0;
353 }
354
355 int
356 nat64_set_udp_timeout (u32 timeout)
357 {
358   nat64_main_t *nm = &nat64_main;
359
360   if (timeout == 0)
361     nm->udp_timeout = SNAT_UDP_TIMEOUT;
362   else if (timeout < SNAT_UDP_TIMEOUT_MIN)
363     return VNET_API_ERROR_INVALID_VALUE;
364   else
365     nm->udp_timeout = timeout;
366
367   return 0;
368 }
369
370 u32
371 nat64_get_udp_timeout (void)
372 {
373   nat64_main_t *nm = &nat64_main;
374
375   return nm->udp_timeout;
376 }
377
378 int
379 nat64_set_icmp_timeout (u32 timeout)
380 {
381   nat64_main_t *nm = &nat64_main;
382
383   if (timeout == 0)
384     nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
385   else
386     nm->icmp_timeout = timeout;
387
388   return 0;
389 }
390
391 u32
392 nat64_get_icmp_timeout (void)
393 {
394   nat64_main_t *nm = &nat64_main;
395
396   return nm->icmp_timeout;
397 }
398
399 int
400 nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn)
401 {
402   nat64_main_t *nm = &nat64_main;
403
404   if (trans == 0)
405     nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
406   else
407     nm->tcp_trans_timeout = trans;
408
409   if (est == 0)
410     nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
411   else
412     nm->tcp_est_timeout = est;
413
414   if (incoming_syn == 0)
415     nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
416   else
417     nm->tcp_incoming_syn_timeout = incoming_syn;
418
419   return 0;
420 }
421
422 u32
423 nat64_get_tcp_trans_timeout (void)
424 {
425   nat64_main_t *nm = &nat64_main;
426
427   return nm->tcp_trans_timeout;
428 }
429
430 u32
431 nat64_get_tcp_est_timeout (void)
432 {
433   nat64_main_t *nm = &nat64_main;
434
435   return nm->tcp_est_timeout;
436 }
437
438 u32
439 nat64_get_tcp_incoming_syn_timeout (void)
440 {
441   nat64_main_t *nm = &nat64_main;
442
443   return nm->tcp_incoming_syn_timeout;
444 }
445
446 void
447 nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm)
448 {
449   nat64_main_t *nm = &nat64_main;
450   u32 now = (u32) vlib_time_now (vm);
451
452   switch (ste->proto)
453     {
454     case SNAT_PROTOCOL_ICMP:
455       ste->expire = now + nm->icmp_timeout;
456       return;
457     case SNAT_PROTOCOL_TCP:
458       {
459         switch (ste->tcp_state)
460           {
461           case NAT64_TCP_STATE_V4_INIT:
462           case NAT64_TCP_STATE_V6_INIT:
463           case NAT64_TCP_STATE_V4_FIN_RCV:
464           case NAT64_TCP_STATE_V6_FIN_RCV:
465           case NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV:
466           case NAT64_TCP_STATE_TRANS:
467             ste->expire = now + nm->tcp_trans_timeout;
468             return;
469           case NAT64_TCP_STATE_ESTABLISHED:
470             ste->expire = now + nm->tcp_est_timeout;
471             return;
472           default:
473             return;
474           }
475       }
476     case SNAT_PROTOCOL_UDP:
477       ste->expire = now + nm->udp_timeout;
478       return;
479     default:
480       return;
481     }
482 }
483
484 void
485 nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp,
486                              u8 is_ip6)
487 {
488   switch (ste->tcp_state)
489     {
490     case NAT64_TCP_STATE_CLOSED:
491       {
492         if (tcp->flags & TCP_FLAG_SYN)
493           {
494             if (is_ip6)
495               ste->tcp_state = NAT64_TCP_STATE_V6_INIT;
496             else
497               ste->tcp_state = NAT64_TCP_STATE_V4_INIT;
498           }
499         return;
500       }
501     case NAT64_TCP_STATE_V4_INIT:
502       {
503         if (is_ip6 && (tcp->flags & TCP_FLAG_SYN))
504           ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
505         return;
506       }
507     case NAT64_TCP_STATE_V6_INIT:
508       {
509         if (!is_ip6 && (tcp->flags & TCP_FLAG_SYN))
510           ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
511         return;
512       }
513     case NAT64_TCP_STATE_ESTABLISHED:
514       {
515         if (tcp->flags & TCP_FLAG_FIN)
516           {
517             if (is_ip6)
518               ste->tcp_state = NAT64_TCP_STATE_V6_FIN_RCV;
519             else
520               ste->tcp_state = NAT64_TCP_STATE_V4_FIN_RCV;
521           }
522         else if (tcp->flags & TCP_FLAG_RST)
523           {
524             ste->tcp_state = NAT64_TCP_STATE_TRANS;
525           }
526         return;
527       }
528     case NAT64_TCP_STATE_V4_FIN_RCV:
529       {
530         if (is_ip6 && (tcp->flags & TCP_FLAG_FIN))
531           ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV;
532         return;
533       }
534     case NAT64_TCP_STATE_V6_FIN_RCV:
535       {
536         if (!is_ip6 && (tcp->flags & TCP_FLAG_FIN))
537           ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV;
538         return;
539       }
540     case NAT64_TCP_STATE_TRANS:
541       {
542         if (!(tcp->flags & TCP_FLAG_RST))
543           ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
544         return;
545       }
546     default:
547       return;
548     }
549 }
550
551 /**
552  * @brief The 'nat64-expire-walk' process's main loop.
553  *
554  * Check expire time for NAT64 sessions.
555  */
556 static uword
557 nat64_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
558                       vlib_frame_t * f)
559 {
560   nat64_main_t *nm = &nat64_main;
561
562   while (1)
563     {
564       vlib_process_wait_for_event_or_clock (vm, 10.0);
565       vlib_process_get_events (vm, NULL);
566       u32 now = (u32) vlib_time_now (vm);
567
568       nad64_db_st_free_expired (&nm->db, now);
569     }
570
571   return 0;
572 }
573
574 static vlib_node_registration_t nat64_expire_walk_node;
575
576 /* *INDENT-OFF* */
577 VLIB_REGISTER_NODE (nat64_expire_walk_node, static) = {
578     .function = nat64_expire_walk_fn,
579     .type = VLIB_NODE_TYPE_PROCESS,
580     .name = "nat64-expire-walk",
581 };
582 /* *INDENT-ON* */
583
584 /*
585  * fd.io coding-style-patch-verification: ON
586  *
587  * Local Variables:
588  * eval: (c-set-style "gnu")
589  * End:
590  */