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