NAT: VPP-1537 IPFIX per worker processing
[vpp.git] / src / plugins / nat / 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 <nat/nat64.h>
21 #include <nat/nat64_db.h>
22 #include <nat/nat_reass.h>
23 #include <nat/nat_inlines.h>
24 #include <vnet/fib/ip4_fib.h>
25 #include <vppinfra/crc32.h>
26
27
28 nat64_main_t nat64_main;
29
30 /* *INDENT-OFF* */
31
32 /* Hook up input features */
33 VNET_FEATURE_INIT (nat64_in2out, static) = {
34   .arc_name = "ip6-unicast",
35   .node_name = "nat64-in2out",
36   .runs_before = VNET_FEATURES ("ip6-lookup"),
37 };
38 VNET_FEATURE_INIT (nat64_out2in, static) = {
39   .arc_name = "ip4-unicast",
40   .node_name = "nat64-out2in",
41   .runs_before = VNET_FEATURES ("ip4-lookup"),
42 };
43 VNET_FEATURE_INIT (nat64_in2out_handoff, static) = {
44   .arc_name = "ip6-unicast",
45   .node_name = "nat64-in2out-handoff",
46   .runs_before = VNET_FEATURES ("ip6-lookup"),
47 };
48 VNET_FEATURE_INIT (nat64_out2in_handoff, static) = {
49   .arc_name = "ip4-unicast",
50   .node_name = "nat64-out2in-handoff",
51   .runs_before = VNET_FEATURES ("ip4-lookup"),
52 };
53
54
55 static u8 well_known_prefix[] = {
56   0x00, 0x64, 0xff, 0x9b,
57   0x00, 0x00, 0x00, 0x00,
58   0x00, 0x00, 0x00, 0x00,
59   0x00, 0x00, 0x00, 0x00
60 };
61
62 /* *INDENT-ON* */
63
64 static void
65 nat64_ip4_add_del_interface_address_cb (ip4_main_t * im, uword opaque,
66                                         u32 sw_if_index,
67                                         ip4_address_t * address,
68                                         u32 address_length,
69                                         u32 if_address_index, u32 is_delete)
70 {
71   nat64_main_t *nm = &nat64_main;
72   int i, j;
73
74   for (i = 0; i < vec_len (nm->auto_add_sw_if_indices); i++)
75     {
76       if (sw_if_index == nm->auto_add_sw_if_indices[i])
77         {
78           if (!is_delete)
79             {
80               /* Don't trip over lease renewal, static config */
81               for (j = 0; j < vec_len (nm->addr_pool); j++)
82                 if (nm->addr_pool[j].addr.as_u32 == address->as_u32)
83                   return;
84
85               (void) nat64_add_del_pool_addr (vlib_get_thread_index (),
86                                               address, ~0, 1);
87               return;
88             }
89           else
90             {
91               (void) nat64_add_del_pool_addr (vlib_get_thread_index (),
92                                               address, ~0, 0);
93               return;
94             }
95         }
96     }
97 }
98
99 u32
100 nat64_get_worker_in2out (ip6_address_t * addr)
101 {
102   nat64_main_t *nm = &nat64_main;
103   snat_main_t *sm = nm->sm;
104   u32 next_worker_index = nm->sm->first_worker_index;
105   u32 hash;
106
107 #ifdef clib_crc32c_uses_intrinsics
108   hash = clib_crc32c ((u8 *) addr->as_u32, 16);
109 #else
110   u64 tmp = addr->as_u64[0] ^ addr->as_u64[1];
111   hash = clib_xxhash (tmp);
112 #endif
113
114   if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
115     next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
116   else
117     next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
118
119   return next_worker_index;
120 }
121
122 u32
123 nat64_get_worker_out2in (ip4_header_t * ip)
124 {
125   nat64_main_t *nm = &nat64_main;
126   snat_main_t *sm = nm->sm;
127   udp_header_t *udp;
128   u16 port;
129   u32 proto;
130
131   proto = ip_proto_to_snat_proto (ip->protocol);
132   udp = ip4_next_header (ip);
133   port = udp->dst_port;
134
135   /* fragments */
136   if (PREDICT_FALSE (ip4_is_fragment (ip)))
137     {
138       if (PREDICT_FALSE (nat_reass_is_drop_frag (0)))
139         return vlib_get_thread_index ();
140
141       if (PREDICT_TRUE (!ip4_is_first_fragment (ip)))
142         {
143           nat_reass_ip4_t *reass;
144
145           reass = nat_ip4_reass_find (ip->src_address, ip->dst_address,
146                                       ip->fragment_id, ip->protocol);
147
148           if (reass && (reass->thread_index != (u32) ~ 0))
149             return reass->thread_index;
150           else
151             return vlib_get_thread_index ();
152         }
153     }
154
155   /* unknown protocol */
156   if (PREDICT_FALSE (proto == ~0))
157     {
158       nat64_db_t *db;
159       ip46_address_t daddr;
160       nat64_db_bib_entry_t *bibe;
161
162       clib_memset (&daddr, 0, sizeof (daddr));
163       daddr.ip4.as_u32 = ip->dst_address.as_u32;
164
165       /* *INDENT-OFF* */
166       vec_foreach (db, nm->db)
167         {
168           bibe = nat64_db_bib_entry_find (db, &daddr, 0, ip->protocol, 0, 0);
169           if (bibe)
170             return (u32) (db - nm->db);
171         }
172       /* *INDENT-ON* */
173       return vlib_get_thread_index ();
174     }
175
176   /* ICMP */
177   if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP))
178     {
179       icmp46_header_t *icmp = (icmp46_header_t *) udp;
180       icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
181       if (!icmp_is_error_message (icmp))
182         port = echo->identifier;
183       else
184         {
185           ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
186           proto = ip_proto_to_snat_proto (inner_ip->protocol);
187           void *l4_header = ip4_next_header (inner_ip);
188           switch (proto)
189             {
190             case SNAT_PROTOCOL_ICMP:
191               icmp = (icmp46_header_t *) l4_header;
192               echo = (icmp_echo_header_t *) (icmp + 1);
193               port = echo->identifier;
194               break;
195             case SNAT_PROTOCOL_UDP:
196             case SNAT_PROTOCOL_TCP:
197               port = ((tcp_udp_header_t *) l4_header)->src_port;
198               break;
199             default:
200               return vlib_get_thread_index ();
201             }
202         }
203     }
204
205   /* worker by outside port  (TCP/UDP) */
206   port = clib_net_to_host_u16 (port);
207   if (port > 1024)
208     return nm->sm->first_worker_index + ((port - 1024) / sm->port_per_thread);
209
210   return vlib_get_thread_index ();
211 }
212
213 clib_error_t *
214 nat64_init (vlib_main_t * vm)
215 {
216   nat64_main_t *nm = &nat64_main;
217   vlib_thread_main_t *tm = vlib_get_thread_main ();
218   ip4_add_del_interface_address_callback_t cb4;
219   ip4_main_t *im = &ip4_main;
220   vlib_node_t *error_drop_node =
221     vlib_get_node_by_name (vm, (u8 *) "error-drop");
222
223   vec_validate (nm->db, tm->n_vlib_mains - 1);
224
225   nm->sm = &snat_main;
226
227   nm->fq_in2out_index = ~0;
228   nm->fq_out2in_index = ~0;
229   nm->error_node_index = error_drop_node->index;
230
231   /* set session timeouts to default values */
232   nm->udp_timeout = SNAT_UDP_TIMEOUT;
233   nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
234   nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
235   nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
236
237   nm->total_enabled_count = 0;
238
239   /* Set up the interface address add/del callback */
240   cb4.function = nat64_ip4_add_del_interface_address_cb;
241   cb4.function_opaque = 0;
242   vec_add1 (im->add_del_interface_address_callbacks, cb4);
243   nm->ip4_main = im;
244
245   /* Init counters */
246   nm->total_bibs.name = "total-bibs";
247   nm->total_bibs.stat_segment_name = "/nat64/total-bibs";
248   vlib_validate_simple_counter (&nm->total_bibs, 0);
249   vlib_zero_simple_counter (&nm->total_bibs, 0);
250   nm->total_sessions.name = "total-sessions";
251   nm->total_sessions.stat_segment_name = "/nat64/total-sessions";
252   vlib_validate_simple_counter (&nm->total_sessions, 0);
253   vlib_zero_simple_counter (&nm->total_sessions, 0);
254
255   return 0;
256 }
257
258 static void nat64_free_out_addr_and_port (struct nat64_db_s *db,
259                                           ip4_address_t * addr, u16 port,
260                                           u8 protocol);
261
262 void
263 nat64_set_hash (u32 bib_buckets, u32 bib_memory_size, u32 st_buckets,
264                 u32 st_memory_size)
265 {
266   nat64_main_t *nm = &nat64_main;
267   nat64_db_t *db;
268
269   nm->bib_buckets = bib_buckets;
270   nm->bib_memory_size = bib_memory_size;
271   nm->st_buckets = st_buckets;
272   nm->st_memory_size = st_memory_size;
273
274   /* *INDENT-OFF* */
275   vec_foreach (db, nm->db)
276     {
277       if (nat64_db_init (db, bib_buckets, bib_memory_size, st_buckets,
278                          st_memory_size, nat64_free_out_addr_and_port))
279         nat_log_err ("NAT64 DB init failed");
280     }
281   /* *INDENT-ON* */
282 }
283
284 int
285 nat64_add_del_pool_addr (u32 thread_index,
286                          ip4_address_t * addr, u32 vrf_id, u8 is_add)
287 {
288   nat64_main_t *nm = &nat64_main;
289   snat_address_t *a = 0;
290   snat_interface_t *interface;
291   int i;
292   nat64_db_t *db;
293   vlib_thread_main_t *tm = vlib_get_thread_main ();
294
295   /* Check if address already exists */
296   for (i = 0; i < vec_len (nm->addr_pool); i++)
297     {
298       if (nm->addr_pool[i].addr.as_u32 == addr->as_u32)
299         {
300           a = nm->addr_pool + i;
301           break;
302         }
303     }
304
305   if (is_add)
306     {
307       if (a)
308         return VNET_API_ERROR_VALUE_EXIST;
309
310       vec_add2 (nm->addr_pool, a, 1);
311       a->addr = *addr;
312       a->fib_index = ~0;
313       if (vrf_id != ~0)
314         a->fib_index =
315           fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id,
316                                              FIB_SOURCE_PLUGIN_HI);
317 #define _(N, id, n, s) \
318       clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); \
319       a->busy_##n##_ports = 0; \
320       vec_validate_init_empty (a->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
321       foreach_snat_protocol
322 #undef _
323     }
324   else
325     {
326       if (!a)
327         return VNET_API_ERROR_NO_SUCH_ENTRY;
328
329       if (a->fib_index != ~0)
330         fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP6,
331                           FIB_SOURCE_PLUGIN_HI);
332       /* Delete sessions using address */
333         /* *INDENT-OFF* */
334         vec_foreach (db, nm->db)
335           {
336             nat64_db_free_out_addr (thread_index, db, &a->addr);
337             vlib_set_simple_counter (&nm->total_bibs, db - nm->db, 0,
338                                      db->bib.bib_entries_num);
339             vlib_set_simple_counter (&nm->total_sessions, db - nm->db, 0,
340                                      db->st.st_entries_num);
341           }
342 #define _(N, id, n, s) \
343       clib_bitmap_free (a->busy_##n##_port_bitmap);
344       foreach_snat_protocol
345 #undef _
346         /* *INDENT-ON* */
347       vec_del1 (nm->addr_pool, i);
348     }
349
350   /* Add/del external address to FIB */
351   /* *INDENT-OFF* */
352   pool_foreach (interface, nm->interfaces,
353   ({
354     if (nat_interface_is_inside(interface))
355       continue;
356
357     snat_add_del_addr_to_fib (addr, 32, interface->sw_if_index, is_add);
358     break;
359   }));
360   /* *INDENT-ON* */
361
362   return 0;
363 }
364
365 void
366 nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx)
367 {
368   nat64_main_t *nm = &nat64_main;
369   snat_address_t *a = 0;
370
371   /* *INDENT-OFF* */
372   vec_foreach (a, nm->addr_pool)
373     {
374       if (fn (a, ctx))
375         break;
376     };
377   /* *INDENT-ON* */
378 }
379
380 int
381 nat64_add_interface_address (u32 sw_if_index, int is_add)
382 {
383   nat64_main_t *nm = &nat64_main;
384   ip4_main_t *ip4_main = nm->ip4_main;
385   ip4_address_t *first_int_addr;
386   int i;
387
388   first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0);
389
390   for (i = 0; i < vec_len (nm->auto_add_sw_if_indices); i++)
391     {
392       if (nm->auto_add_sw_if_indices[i] == sw_if_index)
393         {
394           if (is_add)
395             return VNET_API_ERROR_VALUE_EXIST;
396           else
397             {
398               /* if have address remove it */
399               if (first_int_addr)
400                 (void) nat64_add_del_pool_addr (vlib_get_thread_index (),
401                                                 first_int_addr, ~0, 0);
402               vec_del1 (nm->auto_add_sw_if_indices, i);
403               return 0;
404             }
405         }
406     }
407
408   if (!is_add)
409     return VNET_API_ERROR_NO_SUCH_ENTRY;
410
411   /* add to the auto-address list */
412   vec_add1 (nm->auto_add_sw_if_indices, sw_if_index);
413
414   /* If the address is already bound - or static - add it now */
415   if (first_int_addr)
416     (void) nat64_add_del_pool_addr (vlib_get_thread_index (),
417                                     first_int_addr, ~0, 1);
418
419   return 0;
420 }
421
422 int
423 nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add)
424 {
425   nat64_main_t *nm = &nat64_main;
426   snat_interface_t *interface = 0, *i;
427   snat_address_t *ap;
428   const char *feature_name, *arc_name;
429
430   /* Check if interface already exists */
431   /* *INDENT-OFF* */
432   pool_foreach (i, nm->interfaces,
433   ({
434     if (i->sw_if_index == sw_if_index)
435       {
436         interface = i;
437         break;
438       }
439   }));
440   /* *INDENT-ON* */
441
442   if (is_add)
443     {
444       if (interface)
445         goto set_flags;
446
447       pool_get (nm->interfaces, interface);
448       interface->sw_if_index = sw_if_index;
449       interface->flags = 0;
450     set_flags:
451       if (is_inside)
452         interface->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
453       else
454         interface->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
455
456       nm->total_enabled_count++;
457       vlib_process_signal_event (nm->sm->vlib_main,
458                                  nm->nat64_expire_walk_node_index,
459                                  NAT64_CLEANER_RESCHEDULE, 0);
460
461     }
462   else
463     {
464       if (!interface)
465         return VNET_API_ERROR_NO_SUCH_ENTRY;
466
467       if ((nat_interface_is_inside (interface)
468            && nat_interface_is_outside (interface)))
469         interface->flags &=
470           is_inside ? ~NAT_INTERFACE_FLAG_IS_INSIDE :
471           ~NAT_INTERFACE_FLAG_IS_OUTSIDE;
472       else
473         pool_put (nm->interfaces, interface);
474
475       nm->total_enabled_count--;
476     }
477
478   if (!is_inside)
479     {
480       /* *INDENT-OFF* */
481       vec_foreach (ap, nm->addr_pool)
482         snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, is_add);
483       /* *INDENT-ON* */
484     }
485
486   if (nm->sm->num_workers > 1)
487     {
488       feature_name =
489         is_inside ? "nat64-in2out-handoff" : "nat64-out2in-handoff";
490       if (nm->fq_in2out_index == ~0)
491         nm->fq_in2out_index =
492           vlib_frame_queue_main_init (nat64_in2out_node.index, 0);
493       if (nm->fq_out2in_index == ~0)
494         nm->fq_out2in_index =
495           vlib_frame_queue_main_init (nat64_out2in_node.index, 0);
496     }
497   else
498     feature_name = is_inside ? "nat64-in2out" : "nat64-out2in";
499
500   arc_name = is_inside ? "ip6-unicast" : "ip4-unicast";
501
502   return vnet_feature_enable_disable (arc_name, feature_name, sw_if_index,
503                                       is_add, 0, 0);
504 }
505
506 void
507 nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx)
508 {
509   nat64_main_t *nm = &nat64_main;
510   snat_interface_t *i = 0;
511
512   /* *INDENT-OFF* */
513   pool_foreach (i, nm->interfaces,
514   ({
515     if (fn (i, ctx))
516       break;
517   }));
518   /* *INDENT-ON* */
519 }
520
521 int
522 nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto,
523                                ip4_address_t * addr, u16 * port,
524                                u32 thread_index)
525 {
526   nat64_main_t *nm = &nat64_main;
527   snat_main_t *sm = nm->sm;
528   snat_session_key_t k;
529   u32 worker_index = 0;
530   int rv;
531
532   k.protocol = proto;
533
534   if (sm->num_workers > 1)
535     worker_index = thread_index - sm->first_worker_index;
536
537   rv =
538     sm->alloc_addr_and_port (nm->addr_pool, fib_index, thread_index, &k,
539                              sm->port_per_thread, worker_index);
540
541   if (!rv)
542     {
543       *port = k.port;
544       addr->as_u32 = k.addr.as_u32;
545     }
546
547   return rv;
548 }
549
550 static void
551 nat64_free_out_addr_and_port (struct nat64_db_s *db, ip4_address_t * addr,
552                               u16 port, u8 protocol)
553 {
554   nat64_main_t *nm = &nat64_main;
555   int i;
556   snat_address_t *a;
557   u32 thread_index = db - nm->db;
558   snat_protocol_t proto = ip_proto_to_snat_proto (protocol);
559   u16 port_host_byte_order = clib_net_to_host_u16 (port);
560
561   for (i = 0; i < vec_len (nm->addr_pool); i++)
562     {
563       a = nm->addr_pool + i;
564       if (addr->as_u32 != a->addr.as_u32)
565         continue;
566       switch (proto)
567         {
568 #define _(N, j, n, s) \
569         case SNAT_PROTOCOL_##N: \
570           ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
571                   port_host_byte_order) == 1); \
572           clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port, 0); \
573           a->busy_##n##_ports--; \
574           a->busy_##n##_ports_per_thread[thread_index]--; \
575           break;
576           foreach_snat_protocol
577 #undef _
578         default:
579           nat_log_notice ("unknown protocol");
580           return;
581         }
582       break;
583     }
584 }
585
586 /**
587  * @brief Add/delete static BIB entry in worker thread.
588  */
589 static uword
590 nat64_static_bib_worker_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
591                             vlib_frame_t * f)
592 {
593   nat64_main_t *nm = &nat64_main;
594   u32 thread_index = vm->thread_index;
595   nat64_db_t *db = &nm->db[thread_index];
596   nat64_static_bib_to_update_t *static_bib;
597   nat64_db_bib_entry_t *bibe;
598   ip46_address_t addr;
599
600   /* *INDENT-OFF* */
601   pool_foreach (static_bib, nm->static_bibs,
602   ({
603     if ((static_bib->thread_index != thread_index) || (static_bib->done))
604       continue;
605
606     if (static_bib->is_add)
607       {
608           (void) nat64_db_bib_entry_create (thread_index, db,
609                                             &static_bib->in_addr,
610                                             &static_bib->out_addr,
611                                             static_bib->in_port,
612                                             static_bib->out_port,
613                                             static_bib->fib_index,
614                                             static_bib->proto, 1);
615           vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
616                                    db->bib.bib_entries_num);
617       }
618     else
619       {
620         addr.as_u64[0] = static_bib->in_addr.as_u64[0];
621         addr.as_u64[1] = static_bib->in_addr.as_u64[1];
622         bibe = nat64_db_bib_entry_find (db, &addr, static_bib->in_port,
623                                         static_bib->proto,
624                                         static_bib->fib_index, 1);
625         if (bibe)
626           {
627             nat64_db_bib_entry_free (thread_index, db, bibe);
628             vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
629                                      db->bib.bib_entries_num);
630             vlib_set_simple_counter (&nm->total_sessions, thread_index, 0,
631                                      db->st.st_entries_num);
632           }
633       }
634
635       static_bib->done = 1;
636   }));
637   /* *INDENT-ON* */
638
639   return 0;
640 }
641
642 static vlib_node_registration_t nat64_static_bib_worker_node;
643
644 /* *INDENT-OFF* */
645 VLIB_REGISTER_NODE (nat64_static_bib_worker_node, static) = {
646     .function = nat64_static_bib_worker_fn,
647     .type = VLIB_NODE_TYPE_INPUT,
648     .state = VLIB_NODE_STATE_INTERRUPT,
649     .name = "nat64-static-bib-worker",
650 };
651 /* *INDENT-ON* */
652
653 int
654 nat64_add_del_static_bib_entry (ip6_address_t * in_addr,
655                                 ip4_address_t * out_addr, u16 in_port,
656                                 u16 out_port, u8 proto, u32 vrf_id, u8 is_add)
657 {
658   nat64_main_t *nm = &nat64_main;
659   nat64_db_bib_entry_t *bibe;
660   u32 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id,
661                                                      FIB_SOURCE_PLUGIN_HI);
662   snat_protocol_t p = ip_proto_to_snat_proto (proto);
663   ip46_address_t addr;
664   int i;
665   snat_address_t *a;
666   u32 thread_index = 0;
667   nat64_db_t *db;
668   nat64_static_bib_to_update_t *static_bib;
669   vlib_main_t *worker_vm;
670   u32 *to_be_free = 0, *index;
671
672   if (nm->sm->num_workers > 1)
673     {
674       thread_index = nat64_get_worker_in2out (in_addr);
675       db = &nm->db[thread_index];
676     }
677   else
678     db = &nm->db[nm->sm->num_workers];
679
680   addr.as_u64[0] = in_addr->as_u64[0];
681   addr.as_u64[1] = in_addr->as_u64[1];
682   bibe =
683     nat64_db_bib_entry_find (db, &addr, clib_host_to_net_u16 (in_port),
684                              proto, fib_index, 1);
685
686   if (is_add)
687     {
688       if (bibe)
689         return VNET_API_ERROR_VALUE_EXIST;
690
691       /* outside port must be assigned to same thread as internall address */
692       if ((out_port > 1024) && (nm->sm->num_workers > 1))
693         {
694           if (thread_index != ((out_port - 1024) / nm->sm->port_per_thread))
695             return VNET_API_ERROR_INVALID_VALUE_2;
696         }
697
698       for (i = 0; i < vec_len (nm->addr_pool); i++)
699         {
700           a = nm->addr_pool + i;
701           if (out_addr->as_u32 != a->addr.as_u32)
702             continue;
703           switch (p)
704             {
705 #define _(N, j, n, s) \
706             case SNAT_PROTOCOL_##N: \
707               if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
708                                             out_port)) \
709                 return VNET_API_ERROR_INVALID_VALUE; \
710               clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
711                                         out_port, 1); \
712               if (out_port > 1024) \
713                 { \
714                   a->busy_##n##_ports++; \
715                   a->busy_##n##_ports_per_thread[thread_index]++; \
716                 } \
717               break;
718               foreach_snat_protocol
719 #undef _
720             default:
721               clib_memset (&addr, 0, sizeof (addr));
722               addr.ip4.as_u32 = out_addr->as_u32;
723               if (nat64_db_bib_entry_find (db, &addr, 0, proto, fib_index, 0))
724                 return VNET_API_ERROR_INVALID_VALUE;
725             }
726           break;
727         }
728       if (!nm->sm->num_workers)
729         {
730           bibe =
731             nat64_db_bib_entry_create (thread_index, db, in_addr, out_addr,
732                                        clib_host_to_net_u16 (in_port),
733                                        clib_host_to_net_u16 (out_port),
734                                        fib_index, proto, 1);
735           if (!bibe)
736             return VNET_API_ERROR_UNSPECIFIED;
737
738           vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
739                                    db->bib.bib_entries_num);
740         }
741     }
742   else
743     {
744       if (!bibe)
745         return VNET_API_ERROR_NO_SUCH_ENTRY;
746
747       if (!nm->sm->num_workers)
748         {
749           nat64_db_bib_entry_free (thread_index, db, bibe);
750           vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
751                                    db->bib.bib_entries_num);
752         }
753     }
754
755   if (nm->sm->num_workers)
756     {
757       /* *INDENT-OFF* */
758       pool_foreach (static_bib, nm->static_bibs,
759       ({
760         if (static_bib->done)
761           vec_add1 (to_be_free, static_bib - nm->static_bibs);
762       }));
763       vec_foreach (index, to_be_free)
764         pool_put_index (nm->static_bibs, index[0]);
765       /* *INDENT-ON* */
766       vec_free (to_be_free);
767       pool_get (nm->static_bibs, static_bib);
768       static_bib->in_addr.as_u64[0] = in_addr->as_u64[0];
769       static_bib->in_addr.as_u64[1] = in_addr->as_u64[1];
770       static_bib->in_port = clib_host_to_net_u16 (in_port);
771       static_bib->out_addr.as_u32 = out_addr->as_u32;
772       static_bib->out_port = clib_host_to_net_u16 (out_port);
773       static_bib->fib_index = fib_index;
774       static_bib->proto = proto;
775       static_bib->is_add = is_add;
776       static_bib->thread_index = thread_index;
777       static_bib->done = 0;
778       worker_vm = vlib_mains[thread_index];
779       if (worker_vm)
780         vlib_node_set_interrupt_pending (worker_vm,
781                                          nat64_static_bib_worker_node.index);
782       else
783         return VNET_API_ERROR_UNSPECIFIED;
784     }
785
786   return 0;
787 }
788
789 int
790 nat64_set_udp_timeout (u32 timeout)
791 {
792   nat64_main_t *nm = &nat64_main;
793
794   if (timeout == 0)
795     nm->udp_timeout = SNAT_UDP_TIMEOUT;
796   else
797     nm->udp_timeout = timeout;
798
799   return 0;
800 }
801
802 u32
803 nat64_get_udp_timeout (void)
804 {
805   nat64_main_t *nm = &nat64_main;
806
807   return nm->udp_timeout;
808 }
809
810 int
811 nat64_set_icmp_timeout (u32 timeout)
812 {
813   nat64_main_t *nm = &nat64_main;
814
815   if (timeout == 0)
816     nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
817   else
818     nm->icmp_timeout = timeout;
819
820   return 0;
821 }
822
823 u32
824 nat64_get_icmp_timeout (void)
825 {
826   nat64_main_t *nm = &nat64_main;
827
828   return nm->icmp_timeout;
829 }
830
831 int
832 nat64_set_tcp_timeouts (u32 trans, u32 est)
833 {
834   nat64_main_t *nm = &nat64_main;
835
836   if (trans == 0)
837     nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
838   else
839     nm->tcp_trans_timeout = trans;
840
841   if (est == 0)
842     nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
843   else
844     nm->tcp_est_timeout = est;
845
846   return 0;
847 }
848
849 u32
850 nat64_get_tcp_trans_timeout (void)
851 {
852   nat64_main_t *nm = &nat64_main;
853
854   return nm->tcp_trans_timeout;
855 }
856
857 u32
858 nat64_get_tcp_est_timeout (void)
859 {
860   nat64_main_t *nm = &nat64_main;
861
862   return nm->tcp_est_timeout;
863 }
864
865 void
866 nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm)
867 {
868   nat64_main_t *nm = &nat64_main;
869   u32 now = (u32) vlib_time_now (vm);
870
871   switch (ip_proto_to_snat_proto (ste->proto))
872     {
873     case SNAT_PROTOCOL_ICMP:
874       ste->expire = now + nm->icmp_timeout;
875       return;
876     case SNAT_PROTOCOL_TCP:
877       {
878         switch (ste->tcp_state)
879           {
880           case NAT64_TCP_STATE_V4_INIT:
881           case NAT64_TCP_STATE_V6_INIT:
882           case NAT64_TCP_STATE_V4_FIN_RCV:
883           case NAT64_TCP_STATE_V6_FIN_RCV:
884           case NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV:
885           case NAT64_TCP_STATE_TRANS:
886             ste->expire = now + nm->tcp_trans_timeout;
887             return;
888           case NAT64_TCP_STATE_ESTABLISHED:
889             ste->expire = now + nm->tcp_est_timeout;
890             return;
891           default:
892             return;
893           }
894       }
895     case SNAT_PROTOCOL_UDP:
896       ste->expire = now + nm->udp_timeout;
897       return;
898     default:
899       ste->expire = now + nm->udp_timeout;
900       return;
901     }
902 }
903
904 void
905 nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp,
906                              u8 is_ip6)
907 {
908   switch (ste->tcp_state)
909     {
910     case NAT64_TCP_STATE_CLOSED:
911       {
912         if (tcp->flags & TCP_FLAG_SYN)
913           {
914             if (is_ip6)
915               ste->tcp_state = NAT64_TCP_STATE_V6_INIT;
916             else
917               ste->tcp_state = NAT64_TCP_STATE_V4_INIT;
918           }
919         return;
920       }
921     case NAT64_TCP_STATE_V4_INIT:
922       {
923         if (is_ip6 && (tcp->flags & TCP_FLAG_SYN))
924           ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
925         return;
926       }
927     case NAT64_TCP_STATE_V6_INIT:
928       {
929         if (!is_ip6 && (tcp->flags & TCP_FLAG_SYN))
930           ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
931         return;
932       }
933     case NAT64_TCP_STATE_ESTABLISHED:
934       {
935         if (tcp->flags & TCP_FLAG_FIN)
936           {
937             if (is_ip6)
938               ste->tcp_state = NAT64_TCP_STATE_V6_FIN_RCV;
939             else
940               ste->tcp_state = NAT64_TCP_STATE_V4_FIN_RCV;
941           }
942         else if (tcp->flags & TCP_FLAG_RST)
943           {
944             ste->tcp_state = NAT64_TCP_STATE_TRANS;
945           }
946         return;
947       }
948     case NAT64_TCP_STATE_V4_FIN_RCV:
949       {
950         if (is_ip6 && (tcp->flags & TCP_FLAG_FIN))
951           ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV;
952         return;
953       }
954     case NAT64_TCP_STATE_V6_FIN_RCV:
955       {
956         if (!is_ip6 && (tcp->flags & TCP_FLAG_FIN))
957           ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV;
958         return;
959       }
960     case NAT64_TCP_STATE_TRANS:
961       {
962         if (!(tcp->flags & TCP_FLAG_RST))
963           ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED;
964         return;
965       }
966     default:
967       return;
968     }
969 }
970
971 int
972 nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, u8 is_add)
973 {
974   nat64_main_t *nm = &nat64_main;
975   nat64_prefix_t *p = 0;
976   int i;
977
978   /* Verify prefix length */
979   if (plen != 32 && plen != 40 && plen != 48 && plen != 56 && plen != 64
980       && plen != 96)
981     return VNET_API_ERROR_INVALID_VALUE;
982
983   /* Check if tenant already have prefix */
984   for (i = 0; i < vec_len (nm->pref64); i++)
985     {
986       if (nm->pref64[i].vrf_id == vrf_id)
987         {
988           p = nm->pref64 + i;
989           break;
990         }
991     }
992
993   if (is_add)
994     {
995       if (!p)
996         {
997           vec_add2 (nm->pref64, p, 1);
998           p->fib_index =
999             fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id,
1000                                                FIB_SOURCE_PLUGIN_HI);
1001           p->vrf_id = vrf_id;
1002         }
1003
1004       p->prefix.as_u64[0] = prefix->as_u64[0];
1005       p->prefix.as_u64[1] = prefix->as_u64[1];
1006       p->plen = plen;
1007     }
1008   else
1009     {
1010       if (!p)
1011         return VNET_API_ERROR_NO_SUCH_ENTRY;
1012
1013       vec_del1 (nm->pref64, i);
1014     }
1015
1016   return 0;
1017 }
1018
1019 void
1020 nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx)
1021 {
1022   nat64_main_t *nm = &nat64_main;
1023   nat64_prefix_t *p = 0;
1024
1025   /* *INDENT-OFF* */
1026   vec_foreach (p, nm->pref64)
1027     {
1028       if (fn (p, ctx))
1029         break;
1030     };
1031   /* *INDENT-ON* */
1032 }
1033
1034 void
1035 nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
1036 {
1037   nat64_main_t *nm = &nat64_main;
1038   nat64_prefix_t *p, *gp = 0, *prefix = 0;
1039
1040   /* *INDENT-OFF* */
1041   vec_foreach (p, nm->pref64)
1042     {
1043       if (p->fib_index == fib_index)
1044         {
1045           prefix = p;
1046           break;
1047         }
1048
1049       if (p->fib_index == 0)
1050         gp = p;
1051     };
1052   /* *INDENT-ON* */
1053
1054   if (!prefix)
1055     prefix = gp;
1056
1057   if (prefix)
1058     {
1059       clib_memcpy_fast (ip6, &p->prefix, sizeof (ip6_address_t));
1060       switch (p->plen)
1061         {
1062         case 32:
1063           ip6->as_u32[1] = ip4->as_u32;
1064           break;
1065         case 40:
1066           ip6->as_u8[5] = ip4->as_u8[0];
1067           ip6->as_u8[6] = ip4->as_u8[1];
1068           ip6->as_u8[7] = ip4->as_u8[2];
1069           ip6->as_u8[9] = ip4->as_u8[3];
1070           break;
1071         case 48:
1072           ip6->as_u8[6] = ip4->as_u8[0];
1073           ip6->as_u8[7] = ip4->as_u8[1];
1074           ip6->as_u8[9] = ip4->as_u8[2];
1075           ip6->as_u8[10] = ip4->as_u8[3];
1076           break;
1077         case 56:
1078           ip6->as_u8[7] = ip4->as_u8[0];
1079           ip6->as_u8[9] = ip4->as_u8[1];
1080           ip6->as_u8[10] = ip4->as_u8[2];
1081           ip6->as_u8[11] = ip4->as_u8[3];
1082           break;
1083         case 64:
1084           ip6->as_u8[9] = ip4->as_u8[0];
1085           ip6->as_u8[10] = ip4->as_u8[1];
1086           ip6->as_u8[11] = ip4->as_u8[2];
1087           ip6->as_u8[12] = ip4->as_u8[3];
1088           break;
1089         case 96:
1090           ip6->as_u32[3] = ip4->as_u32;
1091           break;
1092         default:
1093           nat_log_notice ("invalid prefix length");
1094           break;
1095         }
1096     }
1097   else
1098     {
1099       clib_memcpy_fast (ip6, well_known_prefix, sizeof (ip6_address_t));
1100       ip6->as_u32[3] = ip4->as_u32;
1101     }
1102 }
1103
1104 void
1105 nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
1106 {
1107   nat64_main_t *nm = &nat64_main;
1108   nat64_prefix_t *p, *gp = 0;
1109   u8 plen = 0;
1110
1111   /* *INDENT-OFF* */
1112   vec_foreach (p, nm->pref64)
1113     {
1114       if (p->fib_index == fib_index)
1115         {
1116           plen = p->plen;
1117           break;
1118         }
1119
1120       if (p->vrf_id == 0)
1121         gp = p;
1122     };
1123   /* *INDENT-ON* */
1124
1125   if (!plen)
1126     {
1127       if (gp)
1128         plen = gp->plen;
1129       else
1130         plen = 96;
1131     }
1132
1133   switch (plen)
1134     {
1135     case 32:
1136       ip4->as_u32 = ip6->as_u32[1];
1137       break;
1138     case 40:
1139       ip4->as_u8[0] = ip6->as_u8[5];
1140       ip4->as_u8[1] = ip6->as_u8[6];
1141       ip4->as_u8[2] = ip6->as_u8[7];
1142       ip4->as_u8[3] = ip6->as_u8[9];
1143       break;
1144     case 48:
1145       ip4->as_u8[0] = ip6->as_u8[6];
1146       ip4->as_u8[1] = ip6->as_u8[7];
1147       ip4->as_u8[2] = ip6->as_u8[9];
1148       ip4->as_u8[3] = ip6->as_u8[10];
1149       break;
1150     case 56:
1151       ip4->as_u8[0] = ip6->as_u8[7];
1152       ip4->as_u8[1] = ip6->as_u8[9];
1153       ip4->as_u8[2] = ip6->as_u8[10];
1154       ip4->as_u8[3] = ip6->as_u8[11];
1155       break;
1156     case 64:
1157       ip4->as_u8[0] = ip6->as_u8[9];
1158       ip4->as_u8[1] = ip6->as_u8[10];
1159       ip4->as_u8[2] = ip6->as_u8[11];
1160       ip4->as_u8[3] = ip6->as_u8[12];
1161       break;
1162     case 96:
1163       ip4->as_u32 = ip6->as_u32[3];
1164       break;
1165     default:
1166       nat_log_notice ("invalid prefix length");
1167       break;
1168     }
1169 }
1170
1171 /**
1172  * @brief Per worker process checking expire time for NAT64 sessions.
1173  */
1174 static uword
1175 nat64_expire_worker_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
1176                              vlib_frame_t * f)
1177 {
1178   nat64_main_t *nm = &nat64_main;
1179   u32 thread_index = vm->thread_index;
1180   nat64_db_t *db = &nm->db[thread_index];
1181   u32 now = (u32) vlib_time_now (vm);
1182
1183   nad64_db_st_free_expired (thread_index, db, now);
1184   vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
1185                            db->bib.bib_entries_num);
1186   vlib_set_simple_counter (&nm->total_sessions, thread_index, 0,
1187                            db->st.st_entries_num);
1188
1189   return 0;
1190 }
1191
1192 static vlib_node_registration_t nat64_expire_worker_walk_node;
1193
1194 /* *INDENT-OFF* */
1195 VLIB_REGISTER_NODE (nat64_expire_worker_walk_node, static) = {
1196     .function = nat64_expire_worker_walk_fn,
1197     .type = VLIB_NODE_TYPE_INPUT,
1198     .state = VLIB_NODE_STATE_INTERRUPT,
1199     .name = "nat64-expire-worker-walk",
1200 };
1201 /* *INDENT-ON* */
1202
1203 static vlib_node_registration_t nat64_expire_walk_node;
1204
1205 /**
1206  * @brief Centralized process to drive per worker expire walk.
1207  */
1208 static uword
1209 nat64_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
1210                       vlib_frame_t * f)
1211 {
1212   nat64_main_t *nm = &nat64_main;
1213   vlib_main_t **worker_vms = 0, *worker_vm;
1214   int i;
1215   uword event_type, *event_data = 0;
1216
1217   nm->nat64_expire_walk_node_index = nat64_expire_walk_node.index;
1218
1219   if (vec_len (vlib_mains) == 0)
1220     vec_add1 (worker_vms, vm);
1221   else
1222     {
1223       for (i = 0; i < vec_len (vlib_mains); i++)
1224         {
1225           worker_vm = vlib_mains[i];
1226           if (worker_vm)
1227             vec_add1 (worker_vms, worker_vm);
1228         }
1229     }
1230
1231   while (1)
1232     {
1233       if (nm->total_enabled_count)
1234         {
1235           vlib_process_wait_for_event_or_clock (vm, 10.0);
1236           event_type = vlib_process_get_events (vm, &event_data);
1237         }
1238       else
1239         {
1240           vlib_process_wait_for_event (vm);
1241           event_type = vlib_process_get_events (vm, &event_data);
1242         }
1243
1244       switch (event_type)
1245         {
1246         case ~0:
1247           break;
1248         case NAT64_CLEANER_RESCHEDULE:
1249           break;
1250         default:
1251           nat_log_notice ("unknown event %u", event_type);
1252           break;
1253         }
1254
1255       for (i = 0; i < vec_len (worker_vms); i++)
1256         {
1257           worker_vm = worker_vms[i];
1258           vlib_node_set_interrupt_pending (worker_vm,
1259                                            nat64_expire_worker_walk_node.index);
1260         }
1261     }
1262
1263   return 0;
1264 }
1265
1266 /* *INDENT-OFF* */
1267 VLIB_REGISTER_NODE (nat64_expire_walk_node, static) = {
1268     .function = nat64_expire_walk_fn,
1269     .type = VLIB_NODE_TYPE_PROCESS,
1270     .name = "nat64-expire-walk",
1271 };
1272 /* *INDENT-ON* */
1273
1274 /*
1275  * fd.io coding-style-patch-verification: ON
1276  *
1277  * Local Variables:
1278  * eval: (c-set-style "gnu")
1279  * End:
1280  */