nat: use SVR
[vpp.git] / src / plugins / nat / nat64_in2out.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 IPv6 to IPv4 translation (inside to outside network)
18  */
19
20 #include <nat/nat64.h>
21 #include <nat/nat_inlines.h>
22 #include <vnet/ip/ip6_to_ip4.h>
23 #include <vnet/fib/fib_table.h>
24
25 typedef struct
26 {
27   u32 sw_if_index;
28   u32 next_index;
29   u8 is_slow_path;
30 } nat64_in2out_trace_t;
31
32 static u8 *
33 format_nat64_in2out_trace (u8 * s, va_list * args)
34 {
35   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
36   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
37   nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
38   char *tag;
39
40   tag = t->is_slow_path ? "NAT64-in2out-slowpath" : "NAT64-in2out";
41
42   s =
43     format (s, "%s: sw_if_index %d, next index %d", tag, t->sw_if_index,
44             t->next_index);
45
46   return s;
47 }
48
49 #define foreach_nat64_in2out_error                       \
50 _(UNSUPPORTED_PROTOCOL, "unsupported protocol")          \
51 _(IN2OUT_PACKETS, "good in2out packets processed")       \
52 _(NO_TRANSLATION, "no translation")                      \
53 _(UNKNOWN, "unknown")                                    \
54 _(DROP_FRAGMENT, "drop fragment")                        \
55 _(TCP_PACKETS, "TCP packets")                            \
56 _(UDP_PACKETS, "UDP packets")                            \
57 _(ICMP_PACKETS, "ICMP packets")                          \
58 _(OTHER_PACKETS, "other protocol packets")               \
59 _(FRAGMENTS, "fragments")                                \
60 _(CACHED_FRAGMENTS, "cached fragments")                  \
61 _(PROCESSED_FRAGMENTS, "processed fragments")
62
63
64 typedef enum
65 {
66 #define _(sym,str) NAT64_IN2OUT_ERROR_##sym,
67   foreach_nat64_in2out_error
68 #undef _
69     NAT64_IN2OUT_N_ERROR,
70 } nat64_in2out_error_t;
71
72 static char *nat64_in2out_error_strings[] = {
73 #define _(sym,string) string,
74   foreach_nat64_in2out_error
75 #undef _
76 };
77
78 typedef enum
79 {
80   NAT64_IN2OUT_NEXT_IP4_LOOKUP,
81   NAT64_IN2OUT_NEXT_IP6_LOOKUP,
82   NAT64_IN2OUT_NEXT_DROP,
83   NAT64_IN2OUT_NEXT_SLOWPATH,
84   NAT64_IN2OUT_N_NEXT,
85 } nat64_in2out_next_t;
86
87 typedef struct nat64_in2out_set_ctx_t_
88 {
89   vlib_buffer_t *b;
90   vlib_main_t *vm;
91   u32 thread_index;
92 } nat64_in2out_set_ctx_t;
93
94 static inline u8
95 nat64_not_translate (u32 sw_if_index, ip6_address_t ip6_addr)
96 {
97   ip6_address_t *addr;
98   ip6_main_t *im6 = &ip6_main;
99   ip_lookup_main_t *lm6 = &im6->lookup_main;
100   ip_interface_address_t *ia = 0;
101
102   /* *INDENT-OFF* */
103   foreach_ip_interface_address (lm6, ia, sw_if_index, 0,
104   ({
105         addr = ip_interface_address_get_address (lm6, ia);
106         if (0 == ip6_address_compare (addr, &ip6_addr))
107                 return 1;
108   }));
109   /* *INDENT-ON* */
110
111   return 0;
112 }
113
114 /**
115  * @brief Check whether is a hairpinning.
116  *
117  * If the destination IP address of the packet is an IPv4 address assigned to
118  * the NAT64 itself, then the packet is a hairpin packet.
119  *
120  * param dst_addr Destination address of the packet.
121  *
122  * @returns 1 if hairpinning, otherwise 0.
123  */
124 static_always_inline int
125 is_hairpinning (ip6_address_t * dst_addr)
126 {
127   nat64_main_t *nm = &nat64_main;
128   int i;
129
130   for (i = 0; i < vec_len (nm->addr_pool); i++)
131     {
132       if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3])
133         return 1;
134     }
135
136   return 0;
137 }
138
139 static int
140 nat64_in2out_tcp_udp (vlib_main_t * vm, vlib_buffer_t * p, u16 l4_offset,
141                       u16 frag_hdr_offset, nat64_in2out_set_ctx_t * ctx)
142 {
143   ip6_header_t *ip6;
144   ip_csum_t csum = 0;
145   ip4_header_t *ip4;
146   u16 fragment_id;
147   u8 frag_more;
148   u16 frag_offset;
149   nat64_main_t *nm = &nat64_main;
150   nat64_db_bib_entry_t *bibe;
151   nat64_db_st_entry_t *ste;
152   ip46_address_t old_saddr, old_daddr;
153   ip4_address_t new_daddr;
154   u32 sw_if_index, fib_index;
155   u8 proto = vnet_buffer (p)->ip.reass.ip_proto;
156   u16 sport = vnet_buffer (p)->ip.reass.l4_src_port;
157   u16 dport = vnet_buffer (p)->ip.reass.l4_dst_port;
158   nat64_db_t *db = &nm->db[ctx->thread_index];
159
160   ip6 = vlib_buffer_get_current (p);
161
162   vlib_buffer_advance (p, l4_offset - sizeof (*ip4));
163   ip4 = vlib_buffer_get_current (p);
164
165   u32 ip_version_traffic_class_and_flow_label =
166     ip6->ip_version_traffic_class_and_flow_label;
167   u16 payload_length = ip6->payload_length;
168   u8 hop_limit = ip6->hop_limit;
169
170   old_saddr.as_u64[0] = ip6->src_address.as_u64[0];
171   old_saddr.as_u64[1] = ip6->src_address.as_u64[1];
172   old_daddr.as_u64[0] = ip6->dst_address.as_u64[0];
173   old_daddr.as_u64[1] = ip6->dst_address.as_u64[1];
174
175   if (PREDICT_FALSE (frag_hdr_offset))
176     {
177       //Only the first fragment
178       ip6_frag_hdr_t *hdr =
179         (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_hdr_offset);
180       fragment_id = frag_id_6to4 (hdr->identification);
181       frag_more = ip6_frag_hdr_more (hdr);
182       frag_offset = ip6_frag_hdr_offset (hdr);
183     }
184   else
185     {
186       fragment_id = 0;
187       frag_offset = 0;
188       frag_more = 0;
189     }
190
191   ip4->ip_version_and_header_length =
192     IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
193   ip4->tos = ip6_translate_tos (ip_version_traffic_class_and_flow_label);
194   ip4->length =
195     u16_net_add (payload_length, sizeof (*ip4) + sizeof (*ip6) - l4_offset);
196   ip4->fragment_id = fragment_id;
197   ip4->flags_and_fragment_offset =
198     clib_host_to_net_u16 (frag_offset |
199                           (frag_more ? IP4_HEADER_FLAG_MORE_FRAGMENTS : 0));
200   ip4->ttl = hop_limit;
201   ip4->protocol = (proto == IP_PROTOCOL_ICMP6) ? IP_PROTOCOL_ICMP : proto;
202
203   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
204   fib_index =
205     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
206
207   ste =
208     nat64_db_st_entry_find (db, &old_saddr, &old_daddr, sport, dport, proto,
209                             fib_index, 1);
210
211   if (ste)
212     {
213       bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
214       if (!bibe)
215         return -1;
216     }
217   else
218     {
219       bibe =
220         nat64_db_bib_entry_find (db, &old_saddr, sport, proto, fib_index, 1);
221
222       if (!bibe)
223         {
224           u16 out_port;
225           ip4_address_t out_addr;
226           if (nat64_alloc_out_addr_and_port
227               (fib_index, ip_proto_to_snat_proto (proto), &out_addr,
228                &out_port, ctx->thread_index))
229             return -1;
230
231           bibe =
232             nat64_db_bib_entry_create (ctx->thread_index, db,
233                                        &old_saddr.ip6, &out_addr, sport,
234                                        out_port, fib_index, proto, 0);
235           if (!bibe)
236             return -1;
237
238           vlib_set_simple_counter (&nm->total_bibs, ctx->thread_index, 0,
239                                    db->bib.bib_entries_num);
240         }
241
242       nat64_extract_ip4 (&old_daddr.ip6, &new_daddr, fib_index);
243       ste =
244         nat64_db_st_entry_create (ctx->thread_index, db, bibe,
245                                   &old_daddr.ip6, &new_daddr, dport);
246       if (!ste)
247         return -1;
248
249       vlib_set_simple_counter (&nm->total_sessions, ctx->thread_index, 0,
250                                db->st.st_entries_num);
251     }
252
253   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
254   ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
255
256   ip4->checksum = ip4_header_checksum (ip4);
257
258   if (!vnet_buffer (p)->ip.reass.is_non_first_fragment)
259     {
260       udp_header_t *udp = (udp_header_t *) (ip4 + 1);
261       udp->src_port = bibe->out_port;
262
263       //UDP checksum is optional over IPv4
264       if (proto == IP_PROTOCOL_UDP)
265         {
266           udp->checksum = 0;
267         }
268       else
269         {
270           tcp_header_t *tcp = (tcp_header_t *) (ip4 + 1);
271           csum = ip_csum_sub_even (tcp->checksum, old_saddr.as_u64[0]);
272           csum = ip_csum_sub_even (csum, old_saddr.as_u64[1]);
273           csum = ip_csum_sub_even (csum, old_daddr.as_u64[0]);
274           csum = ip_csum_sub_even (csum, old_daddr.as_u64[1]);
275           csum = ip_csum_add_even (csum, ip4->dst_address.as_u32);
276           csum = ip_csum_add_even (csum, ip4->src_address.as_u32);
277           csum = ip_csum_sub_even (csum, sport);
278           csum = ip_csum_add_even (csum, udp->src_port);
279           mss_clamping (nm->sm, tcp, &csum);
280           tcp->checksum = ip_csum_fold (csum);
281
282           nat64_tcp_session_set_state (ste, tcp, 1);
283         }
284     }
285
286   nat64_session_reset_timeout (ste, ctx->vm);
287
288   return 0;
289 }
290
291 static int
292 nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
293 {
294   nat64_main_t *nm = &nat64_main;
295   nat64_in2out_set_ctx_t *ctx = arg;
296   nat64_db_bib_entry_t *bibe;
297   nat64_db_st_entry_t *ste;
298   ip46_address_t saddr, daddr;
299   u32 sw_if_index, fib_index;
300   icmp46_header_t *icmp = ip6_next_header (ip6);
301   nat64_db_t *db = &nm->db[ctx->thread_index];
302
303   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
304   fib_index =
305     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
306
307   saddr.as_u64[0] = ip6->src_address.as_u64[0];
308   saddr.as_u64[1] = ip6->src_address.as_u64[1];
309   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
310   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
311
312   if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
313     {
314       u16 in_id = ((u16 *) (icmp))[2];
315       ste =
316         nat64_db_st_entry_find (db, &saddr, &daddr, in_id, 0,
317                                 IP_PROTOCOL_ICMP, fib_index, 1);
318
319       if (ste)
320         {
321           bibe =
322             nat64_db_bib_entry_by_index (db, IP_PROTOCOL_ICMP,
323                                          ste->bibe_index);
324           if (!bibe)
325             return -1;
326         }
327       else
328         {
329           bibe =
330             nat64_db_bib_entry_find (db, &saddr, in_id,
331                                      IP_PROTOCOL_ICMP, fib_index, 1);
332
333           if (!bibe)
334             {
335               u16 out_id;
336               ip4_address_t out_addr;
337               if (nat64_alloc_out_addr_and_port
338                   (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id,
339                    ctx->thread_index))
340                 return -1;
341
342               bibe =
343                 nat64_db_bib_entry_create (ctx->thread_index, db,
344                                            &ip6->src_address, &out_addr,
345                                            in_id, out_id, fib_index,
346                                            IP_PROTOCOL_ICMP, 0);
347               if (!bibe)
348                 return -1;
349
350               vlib_set_simple_counter (&nm->total_bibs, ctx->thread_index, 0,
351                                        db->bib.bib_entries_num);
352             }
353
354           nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
355           ste =
356             nat64_db_st_entry_create (ctx->thread_index, db, bibe,
357                                       &ip6->dst_address, &daddr.ip4, 0);
358           if (!ste)
359             return -1;
360
361           vlib_set_simple_counter (&nm->total_sessions, ctx->thread_index, 0,
362                                    db->st.st_entries_num);
363         }
364
365       nat64_session_reset_timeout (ste, ctx->vm);
366
367       ip4->src_address.as_u32 = bibe->out_addr.as_u32;
368       ((u16 *) (icmp))[2] = bibe->out_port;
369
370       ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
371     }
372   else
373     {
374       if (!vec_len (nm->addr_pool))
375         return -1;
376
377       ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
378       nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index);
379     }
380
381   return 0;
382 }
383
384 static int
385 nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
386                                 void *arg)
387 {
388   nat64_main_t *nm = &nat64_main;
389   nat64_in2out_set_ctx_t *ctx = arg;
390   nat64_db_st_entry_t *ste;
391   nat64_db_bib_entry_t *bibe;
392   ip46_address_t saddr, daddr;
393   u32 sw_if_index, fib_index;
394   u8 proto = ip6->protocol;
395   nat64_db_t *db = &nm->db[ctx->thread_index];
396
397   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
398   fib_index =
399     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
400
401   saddr.as_u64[0] = ip6->src_address.as_u64[0];
402   saddr.as_u64[1] = ip6->src_address.as_u64[1];
403   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
404   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
405
406   if (proto == IP_PROTOCOL_ICMP6)
407     {
408       icmp46_header_t *icmp = ip6_next_header (ip6);
409       u16 in_id = ((u16 *) (icmp))[2];
410       proto = IP_PROTOCOL_ICMP;
411
412       if (!
413           (icmp->type == ICMP4_echo_request
414            || icmp->type == ICMP4_echo_reply))
415         return -1;
416
417       ste =
418         nat64_db_st_entry_find (db, &daddr, &saddr, in_id, 0, proto,
419                                 fib_index, 1);
420       if (!ste)
421         return -1;
422
423       bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
424       if (!bibe)
425         return -1;
426
427       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
428       ((u16 *) (icmp))[2] = bibe->out_port;
429       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
430     }
431   else
432     {
433       udp_header_t *udp = ip6_next_header (ip6);
434       tcp_header_t *tcp = ip6_next_header (ip6);
435       u16 *checksum;
436       ip_csum_t csum;
437
438       u16 sport = udp->src_port;
439       u16 dport = udp->dst_port;
440
441       ste =
442         nat64_db_st_entry_find (db, &daddr, &saddr, dport, sport, proto,
443                                 fib_index, 1);
444       if (!ste)
445         return -1;
446
447       bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
448       if (!bibe)
449         return -1;
450
451       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
452       udp->dst_port = bibe->out_port;
453       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
454
455       if (proto == IP_PROTOCOL_TCP)
456         checksum = &tcp->checksum;
457       else
458         checksum = &udp->checksum;
459       csum = ip_csum_sub_even (*checksum, dport);
460       csum = ip_csum_add_even (csum, udp->dst_port);
461       *checksum = ip_csum_fold (csum);
462     }
463
464   return 0;
465 }
466
467 typedef struct unk_proto_st_walk_ctx_t_
468 {
469   ip6_address_t src_addr;
470   ip6_address_t dst_addr;
471   ip4_address_t out_addr;
472   u32 fib_index;
473   u32 thread_index;
474   u8 proto;
475 } unk_proto_st_walk_ctx_t;
476
477 static int
478 unk_proto_st_walk (nat64_db_st_entry_t * ste, void *arg)
479 {
480   nat64_main_t *nm = &nat64_main;
481   unk_proto_st_walk_ctx_t *ctx = arg;
482   nat64_db_bib_entry_t *bibe;
483   ip46_address_t saddr, daddr;
484   nat64_db_t *db = &nm->db[ctx->thread_index];
485
486   if (ip6_address_is_equal (&ste->in_r_addr, &ctx->dst_addr))
487     {
488       bibe = nat64_db_bib_entry_by_index (db, ste->proto, ste->bibe_index);
489       if (!bibe)
490         return -1;
491
492       if (ip6_address_is_equal (&bibe->in_addr, &ctx->src_addr)
493           && bibe->fib_index == ctx->fib_index)
494         {
495           clib_memset (&saddr, 0, sizeof (saddr));
496           saddr.ip4.as_u32 = bibe->out_addr.as_u32;
497           clib_memset (&daddr, 0, sizeof (daddr));
498           nat64_extract_ip4 (&ctx->dst_addr, &daddr.ip4, ctx->fib_index);
499
500           if (nat64_db_st_entry_find
501               (db, &daddr, &saddr, 0, 0, ctx->proto, ctx->fib_index, 0))
502             return -1;
503
504           ctx->out_addr.as_u32 = bibe->out_addr.as_u32;
505           return 1;
506         }
507     }
508
509   return 0;
510 }
511
512 static int
513 nat64_in2out_unk_proto (vlib_main_t * vm, vlib_buffer_t * p, u8 l4_protocol,
514                         u16 l4_offset, u16 frag_hdr_offset,
515                         nat64_in2out_set_ctx_t * s_ctx)
516 {
517   ip6_header_t *ip6;
518   ip4_header_t *ip4;
519   u16 fragment_id;
520   u16 frag_offset;
521   u8 frag_more;
522
523   ip6 = vlib_buffer_get_current (p);
524
525   ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4));
526
527   vlib_buffer_advance (p, l4_offset - sizeof (*ip4));
528
529   if (PREDICT_FALSE (frag_hdr_offset))
530     {
531       //Only the first fragment
532       ip6_frag_hdr_t *hdr =
533         (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_hdr_offset);
534       fragment_id = frag_id_6to4 (hdr->identification);
535       frag_offset = ip6_frag_hdr_offset (hdr);
536       frag_more = ip6_frag_hdr_more (hdr);
537     }
538   else
539     {
540       fragment_id = 0;
541       frag_offset = 0;
542       frag_more = 0;
543     }
544
545   nat64_main_t *nm = &nat64_main;
546   nat64_db_bib_entry_t *bibe;
547   nat64_db_st_entry_t *ste;
548   ip46_address_t saddr, daddr, addr;
549   u32 sw_if_index, fib_index;
550   int i;
551   nat64_db_t *db = &nm->db[s_ctx->thread_index];
552
553   sw_if_index = vnet_buffer (s_ctx->b)->sw_if_index[VLIB_RX];
554   fib_index =
555     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
556
557   saddr.as_u64[0] = ip6->src_address.as_u64[0];
558   saddr.as_u64[1] = ip6->src_address.as_u64[1];
559   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
560   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
561
562   ste =
563     nat64_db_st_entry_find (db, &saddr, &daddr, 0, 0, l4_protocol, fib_index,
564                             1);
565
566   if (ste)
567     {
568       bibe = nat64_db_bib_entry_by_index (db, l4_protocol, ste->bibe_index);
569       if (!bibe)
570         return -1;
571     }
572   else
573     {
574       bibe =
575         nat64_db_bib_entry_find (db, &saddr, 0, l4_protocol, fib_index, 1);
576
577       if (!bibe)
578         {
579           /* Choose same out address as for TCP/UDP session to same dst */
580           unk_proto_st_walk_ctx_t ctx = {
581             .src_addr.as_u64[0] = ip6->src_address.as_u64[0],
582             .src_addr.as_u64[1] = ip6->src_address.as_u64[1],
583             .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0],
584             .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1],
585             .out_addr.as_u32 = 0,
586             .fib_index = fib_index,
587             .proto = l4_protocol,
588             .thread_index = s_ctx->thread_index,
589           };
590
591           nat64_db_st_walk (db, IP_PROTOCOL_TCP, unk_proto_st_walk, &ctx);
592
593           if (!ctx.out_addr.as_u32)
594             nat64_db_st_walk (db, IP_PROTOCOL_UDP, unk_proto_st_walk, &ctx);
595
596           /* Verify if out address is not already in use for protocol */
597           clib_memset (&addr, 0, sizeof (addr));
598           addr.ip4.as_u32 = ctx.out_addr.as_u32;
599           if (nat64_db_bib_entry_find (db, &addr, 0, l4_protocol, 0, 0))
600             ctx.out_addr.as_u32 = 0;
601
602           if (!ctx.out_addr.as_u32)
603             {
604               for (i = 0; i < vec_len (nm->addr_pool); i++)
605                 {
606                   addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32;
607                   if (!nat64_db_bib_entry_find
608                       (db, &addr, 0, l4_protocol, 0, 0))
609                     break;
610                 }
611             }
612
613           if (!ctx.out_addr.as_u32)
614             return -1;
615
616           bibe =
617             nat64_db_bib_entry_create (s_ctx->thread_index, db,
618                                        &ip6->src_address, &ctx.out_addr,
619                                        0, 0, fib_index, l4_protocol, 0);
620           if (!bibe)
621             return -1;
622
623           vlib_set_simple_counter (&nm->total_bibs, s_ctx->thread_index, 0,
624                                    db->bib.bib_entries_num);
625         }
626
627       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
628       ste =
629         nat64_db_st_entry_create (s_ctx->thread_index, db, bibe,
630                                   &ip6->dst_address, &daddr.ip4, 0);
631       if (!ste)
632         return -1;
633
634       vlib_set_simple_counter (&nm->total_sessions, s_ctx->thread_index, 0,
635                                db->st.st_entries_num);
636     }
637
638   nat64_session_reset_timeout (ste, s_ctx->vm);
639
640   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
641   ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
642
643   ip4->ip_version_and_header_length =
644     IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
645   ip4->tos = ip6_translate_tos (ip6->ip_version_traffic_class_and_flow_label);
646   ip4->length = u16_net_add (ip6->payload_length,
647                              sizeof (*ip4) + sizeof (*ip6) - l4_offset);
648   ip4->fragment_id = fragment_id;
649   ip4->flags_and_fragment_offset =
650     clib_host_to_net_u16 (frag_offset |
651                           (frag_more ? IP4_HEADER_FLAG_MORE_FRAGMENTS : 0));
652   ip4->ttl = ip6->hop_limit;
653   ip4->protocol = l4_protocol;
654   ip4->checksum = ip4_header_checksum (ip4);
655
656   return 0;
657 }
658
659 static int
660 nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
661                                   ip6_header_t * ip6, u32 l4_offset,
662                                   u32 thread_index)
663 {
664   nat64_main_t *nm = &nat64_main;
665   nat64_db_bib_entry_t *bibe;
666   nat64_db_st_entry_t *ste;
667   ip46_address_t saddr, daddr;
668   u32 sw_if_index, fib_index;
669   udp_header_t *udp = (udp_header_t *) u8_ptr_add (ip6, l4_offset);
670   tcp_header_t *tcp = (tcp_header_t *) u8_ptr_add (ip6, l4_offset);
671   u8 proto = vnet_buffer (b)->ip.reass.ip_proto;
672   u16 sport = vnet_buffer (b)->ip.reass.l4_src_port;
673   u16 dport = vnet_buffer (b)->ip.reass.l4_dst_port;
674   u16 *checksum = NULL;
675   ip_csum_t csum = 0;
676   nat64_db_t *db = &nm->db[thread_index];
677
678   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
679   fib_index =
680     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
681
682   saddr.as_u64[0] = ip6->src_address.as_u64[0];
683   saddr.as_u64[1] = ip6->src_address.as_u64[1];
684   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
685   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
686
687   if (!vnet_buffer (b)->ip.reass.is_non_first_fragment)
688     {
689       if (proto == IP_PROTOCOL_UDP)
690         checksum = &udp->checksum;
691       else
692         checksum = &tcp->checksum;
693       csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]);
694       csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]);
695       csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]);
696       csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]);
697     }
698
699   ste =
700     nat64_db_st_entry_find (db, &saddr, &daddr, sport, dport, proto,
701                             fib_index, 1);
702
703   if (ste)
704     {
705       bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
706       if (!bibe)
707         return -1;
708     }
709   else
710     {
711       bibe = nat64_db_bib_entry_find (db, &saddr, sport, proto, fib_index, 1);
712
713       if (!bibe)
714         {
715           u16 out_port;
716           ip4_address_t out_addr;
717           if (nat64_alloc_out_addr_and_port
718               (fib_index, ip_proto_to_snat_proto (proto), &out_addr,
719                &out_port, thread_index))
720             return -1;
721
722           bibe =
723             nat64_db_bib_entry_create (thread_index, db, &ip6->src_address,
724                                        &out_addr, sport, out_port, fib_index,
725                                        proto, 0);
726           if (!bibe)
727             return -1;
728
729           vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
730                                    db->bib.bib_entries_num);
731         }
732
733       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
734       ste =
735         nat64_db_st_entry_create (thread_index, db, bibe, &ip6->dst_address,
736                                   &daddr.ip4, dport);
737       if (!ste)
738         return -1;
739
740       vlib_set_simple_counter (&nm->total_sessions, thread_index, 0,
741                                db->st.st_entries_num);
742     }
743
744   if (proto == IP_PROTOCOL_TCP)
745     nat64_tcp_session_set_state (ste, tcp, 1);
746
747   nat64_session_reset_timeout (ste, vm);
748
749   if (!vnet_buffer (b)->ip.reass.is_non_first_fragment)
750     {
751       udp->src_port = bibe->out_port;
752     }
753
754   nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
755
756   clib_memset (&daddr, 0, sizeof (daddr));
757   daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
758
759   bibe = 0;
760   /* *INDENT-OFF* */
761   vec_foreach (db, nm->db)
762     {
763       bibe = nat64_db_bib_entry_find (db, &daddr, dport, proto, 0, 0);
764
765       if (bibe)
766         break;
767     }
768   /* *INDENT-ON* */
769
770   if (!bibe)
771     return -1;
772
773   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
774   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
775
776   if (!vnet_buffer (b)->ip.reass.is_non_first_fragment)
777     {
778       csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
779       csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
780       csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
781       csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);
782       csum = ip_csum_sub_even (csum, sport);
783       csum = ip_csum_sub_even (csum, dport);
784       udp->dst_port = bibe->in_port;
785       csum = ip_csum_add_even (csum, udp->src_port);
786       csum = ip_csum_add_even (csum, udp->dst_port);
787       *checksum = ip_csum_fold (csum);
788     }
789
790   return 0;
791 }
792
793 static int
794 nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
795                                ip6_header_t * ip6, u32 thread_index)
796 {
797   nat64_main_t *nm = &nat64_main;
798   nat64_db_bib_entry_t *bibe;
799   nat64_db_st_entry_t *ste;
800   icmp46_header_t *icmp = ip6_next_header (ip6);
801   ip6_header_t *inner_ip6;
802   ip46_address_t saddr, daddr;
803   u32 sw_if_index, fib_index;
804   u8 proto;
805   udp_header_t *udp;
806   tcp_header_t *tcp;
807   u16 *checksum, sport, dport;
808   ip_csum_t csum;
809   nat64_db_t *db = &nm->db[thread_index];
810
811   if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
812     return -1;
813
814   inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
815
816   proto = inner_ip6->protocol;
817
818   if (proto == IP_PROTOCOL_ICMP6)
819     return -1;
820
821   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
822   fib_index =
823     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
824
825   saddr.as_u64[0] = inner_ip6->src_address.as_u64[0];
826   saddr.as_u64[1] = inner_ip6->src_address.as_u64[1];
827   daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0];
828   daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1];
829
830   udp = ip6_next_header (inner_ip6);
831   tcp = ip6_next_header (inner_ip6);
832
833   sport = udp->src_port;
834   dport = udp->dst_port;
835
836   if (proto == IP_PROTOCOL_UDP)
837     checksum = &udp->checksum;
838   else
839     checksum = &tcp->checksum;
840
841   csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]);
842   csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]);
843   csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]);
844   csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]);
845   csum = ip_csum_sub_even (csum, sport);
846   csum = ip_csum_sub_even (csum, dport);
847
848   ste =
849     nat64_db_st_entry_find (db, &daddr, &saddr, dport, sport, proto,
850                             fib_index, 1);
851   if (!ste)
852     return -1;
853
854   bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
855   if (!bibe)
856     return -1;
857
858   dport = udp->dst_port = bibe->out_port;
859   nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index);
860
861   clib_memset (&saddr, 0, sizeof (saddr));
862   clib_memset (&daddr, 0, sizeof (daddr));
863   saddr.ip4.as_u32 = ste->out_r_addr.as_u32;
864   daddr.ip4.as_u32 = bibe->out_addr.as_u32;
865
866   ste = 0;
867   /* *INDENT-OFF* */
868   vec_foreach (db, nm->db)
869     {
870       ste = nat64_db_st_entry_find (db, &saddr, &daddr, sport, dport, proto,
871                                     0, 0);
872
873       if (ste)
874         break;
875     }
876   /* *INDENT-ON* */
877
878   if (!ste)
879     return -1;
880
881   bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
882   if (!bibe)
883     return -1;
884
885   inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
886   inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
887   udp->src_port = bibe->in_port;
888
889   csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
890   csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
891   csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
892   csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
893   csum = ip_csum_add_even (csum, udp->src_port);
894   csum = ip_csum_add_even (csum, udp->dst_port);
895   *checksum = ip_csum_fold (csum);
896
897   if (!vec_len (nm->addr_pool))
898     return -1;
899
900   nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index);
901   ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
902   ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
903
904   icmp->checksum = 0;
905   csum = ip_csum_with_carry (0, ip6->payload_length);
906   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
907   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
908   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
909   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
910   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
911   csum =
912     ip_incremental_checksum (csum, icmp,
913                              clib_net_to_host_u16 (ip6->payload_length));
914   icmp->checksum = ~ip_csum_fold (csum);
915
916   return 0;
917 }
918
919 static int
920 nat64_in2out_unk_proto_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
921                                     ip6_header_t * ip6, u32 thread_index)
922 {
923   nat64_main_t *nm = &nat64_main;
924   nat64_db_bib_entry_t *bibe;
925   nat64_db_st_entry_t *ste;
926   ip46_address_t saddr, daddr, addr;
927   u32 sw_if_index, fib_index;
928   u8 proto = ip6->protocol;
929   int i;
930   nat64_db_t *db = &nm->db[thread_index];
931
932   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
933   fib_index =
934     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
935
936   saddr.as_u64[0] = ip6->src_address.as_u64[0];
937   saddr.as_u64[1] = ip6->src_address.as_u64[1];
938   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
939   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
940
941   ste =
942     nat64_db_st_entry_find (db, &saddr, &daddr, 0, 0, proto, fib_index, 1);
943
944   if (ste)
945     {
946       bibe = nat64_db_bib_entry_by_index (db, proto, ste->bibe_index);
947       if (!bibe)
948         return -1;
949     }
950   else
951     {
952       bibe = nat64_db_bib_entry_find (db, &saddr, 0, proto, fib_index, 1);
953
954       if (!bibe)
955         {
956           /* Choose same out address as for TCP/UDP session to same dst */
957           unk_proto_st_walk_ctx_t ctx = {
958             .src_addr.as_u64[0] = ip6->src_address.as_u64[0],
959             .src_addr.as_u64[1] = ip6->src_address.as_u64[1],
960             .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0],
961             .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1],
962             .out_addr.as_u32 = 0,
963             .fib_index = fib_index,
964             .proto = proto,
965             .thread_index = thread_index,
966           };
967
968           nat64_db_st_walk (db, IP_PROTOCOL_TCP, unk_proto_st_walk, &ctx);
969
970           if (!ctx.out_addr.as_u32)
971             nat64_db_st_walk (db, IP_PROTOCOL_UDP, unk_proto_st_walk, &ctx);
972
973           /* Verify if out address is not already in use for protocol */
974           clib_memset (&addr, 0, sizeof (addr));
975           addr.ip4.as_u32 = ctx.out_addr.as_u32;
976           if (nat64_db_bib_entry_find (db, &addr, 0, proto, 0, 0))
977             ctx.out_addr.as_u32 = 0;
978
979           if (!ctx.out_addr.as_u32)
980             {
981               for (i = 0; i < vec_len (nm->addr_pool); i++)
982                 {
983                   addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32;
984                   if (!nat64_db_bib_entry_find (db, &addr, 0, proto, 0, 0))
985                     break;
986                 }
987             }
988
989           if (!ctx.out_addr.as_u32)
990             return -1;
991
992           bibe =
993             nat64_db_bib_entry_create (thread_index, db, &ip6->src_address,
994                                        &ctx.out_addr, 0, 0, fib_index, proto,
995                                        0);
996           if (!bibe)
997             return -1;
998
999           vlib_set_simple_counter (&nm->total_bibs, thread_index, 0,
1000                                    db->bib.bib_entries_num);
1001         }
1002
1003       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
1004       ste =
1005         nat64_db_st_entry_create (thread_index, db, bibe, &ip6->dst_address,
1006                                   &daddr.ip4, 0);
1007       if (!ste)
1008         return -1;
1009
1010       vlib_set_simple_counter (&nm->total_sessions, thread_index, 0,
1011                                db->st.st_entries_num);
1012     }
1013
1014   nat64_session_reset_timeout (ste, vm);
1015
1016   nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
1017
1018   clib_memset (&daddr, 0, sizeof (daddr));
1019   daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
1020
1021   bibe = 0;
1022   /* *INDENT-OFF* */
1023   vec_foreach (db, nm->db)
1024     {
1025       bibe = nat64_db_bib_entry_find (db, &daddr, 0, proto, 0, 0);
1026
1027       if (bibe)
1028         break;
1029     }
1030   /* *INDENT-ON* */
1031
1032   if (!bibe)
1033     return -1;
1034
1035   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
1036   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
1037
1038   return 0;
1039 }
1040
1041 static inline uword
1042 nat64_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
1043                              vlib_frame_t * frame, u8 is_slow_path)
1044 {
1045   u32 n_left_from, *from, *to_next;
1046   nat64_in2out_next_t next_index;
1047   u32 pkts_processed = 0;
1048   u32 stats_node_index;
1049   u32 thread_index = vm->thread_index;
1050   nat64_main_t *nm = &nat64_main;
1051
1052   u32 tcp_packets = 0, udp_packets = 0, icmp_packets = 0, other_packets =
1053     0, fragments = 0;
1054
1055   stats_node_index =
1056     is_slow_path ? nm->in2out_slowpath_node_index : nm->in2out_node_index;
1057
1058   from = vlib_frame_vector_args (frame);
1059   n_left_from = frame->n_vectors;
1060   next_index = node->cached_next_index;
1061
1062   while (n_left_from > 0)
1063     {
1064       u32 n_left_to_next;
1065
1066       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1067
1068       while (n_left_from > 0 && n_left_to_next > 0)
1069         {
1070           u32 bi0;
1071           vlib_buffer_t *b0;
1072           u32 next0;
1073           ip6_header_t *ip60;
1074           u16 l4_offset0, frag_hdr_offset0;
1075           u8 l4_protocol0;
1076           u32 proto0;
1077           nat64_in2out_set_ctx_t ctx0;
1078           u32 sw_if_index0;
1079
1080           /* speculatively enqueue b0 to the current next frame */
1081           bi0 = from[0];
1082           to_next[0] = bi0;
1083           from += 1;
1084           to_next += 1;
1085           n_left_from -= 1;
1086           n_left_to_next -= 1;
1087
1088           b0 = vlib_get_buffer (vm, bi0);
1089           ip60 = vlib_buffer_get_current (b0);
1090
1091           ctx0.b = b0;
1092           ctx0.vm = vm;
1093           ctx0.thread_index = thread_index;
1094
1095           next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP;
1096
1097           if (PREDICT_FALSE
1098               (ip6_parse
1099                (vm, b0, ip60, b0->current_length, &l4_protocol0, &l4_offset0,
1100                 &frag_hdr_offset0)))
1101             {
1102               next0 = NAT64_IN2OUT_NEXT_DROP;
1103               b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
1104               goto trace0;
1105             }
1106
1107           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1108
1109           if (nat64_not_translate (sw_if_index0, ip60->dst_address))
1110             {
1111               next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
1112               goto trace0;
1113             }
1114
1115           proto0 = ip_proto_to_snat_proto (l4_protocol0);
1116
1117           if (is_slow_path)
1118             {
1119               if (PREDICT_TRUE (proto0 == ~0))
1120                 {
1121                   other_packets++;
1122                   if (is_hairpinning (&ip60->dst_address))
1123                     {
1124                       next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
1125                       if (nat64_in2out_unk_proto_hairpinning
1126                           (vm, b0, ip60, thread_index))
1127                         {
1128                           next0 = NAT64_IN2OUT_NEXT_DROP;
1129                           b0->error =
1130                             node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1131                         }
1132                       goto trace0;
1133                     }
1134
1135                   if (nat64_in2out_unk_proto
1136                       (vm, b0, l4_protocol0, l4_offset0, frag_hdr_offset0,
1137                        &ctx0))
1138                     {
1139                       next0 = NAT64_IN2OUT_NEXT_DROP;
1140                       b0->error =
1141                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1142                       goto trace0;
1143                     }
1144                 }
1145               goto trace0;
1146             }
1147           else
1148             {
1149               if (PREDICT_FALSE (proto0 == ~0))
1150                 {
1151                   next0 = NAT64_IN2OUT_NEXT_SLOWPATH;
1152                   goto trace0;
1153                 }
1154             }
1155
1156           if (proto0 == SNAT_PROTOCOL_ICMP)
1157             {
1158               icmp_packets++;
1159               if (is_hairpinning (&ip60->dst_address))
1160                 {
1161                   next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
1162                   if (nat64_in2out_icmp_hairpinning
1163                       (vm, b0, ip60, thread_index))
1164                     {
1165                       next0 = NAT64_IN2OUT_NEXT_DROP;
1166                       b0->error =
1167                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1168                     }
1169                   goto trace0;
1170                 }
1171
1172               if (icmp6_to_icmp
1173                   (vm, b0, nat64_in2out_icmp_set_cb, &ctx0,
1174                    nat64_in2out_inner_icmp_set_cb, &ctx0))
1175                 {
1176                   next0 = NAT64_IN2OUT_NEXT_DROP;
1177                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1178                   goto trace0;
1179                 }
1180             }
1181           else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP)
1182             {
1183               if (proto0 == SNAT_PROTOCOL_TCP)
1184                 tcp_packets++;
1185               else
1186                 udp_packets++;
1187
1188               if (is_hairpinning (&ip60->dst_address))
1189                 {
1190                   next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
1191                   if (nat64_in2out_tcp_udp_hairpinning
1192                       (vm, b0, ip60, l4_offset0, thread_index))
1193                     {
1194                       next0 = NAT64_IN2OUT_NEXT_DROP;
1195                       b0->error =
1196                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1197                     }
1198                   goto trace0;
1199                 }
1200
1201               if (nat64_in2out_tcp_udp
1202                   (vm, b0, l4_offset0, frag_hdr_offset0, &ctx0))
1203                 {
1204                   next0 = NAT64_IN2OUT_NEXT_DROP;
1205                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1206                   goto trace0;
1207                 }
1208             }
1209
1210         trace0:
1211           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1212                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1213             {
1214               nat64_in2out_trace_t *t =
1215                 vlib_add_trace (vm, node, b0, sizeof (*t));
1216               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1217               t->next_index = next0;
1218               t->is_slow_path = is_slow_path;
1219             }
1220
1221           pkts_processed += next0 == NAT64_IN2OUT_NEXT_IP4_LOOKUP;
1222
1223           /* verify speculative enqueue, maybe switch current next frame */
1224           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1225                                            n_left_to_next, bi0, next0);
1226         }
1227       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1228     }
1229   vlib_node_increment_counter (vm, stats_node_index,
1230                                NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
1231                                pkts_processed);
1232   vlib_node_increment_counter (vm, stats_node_index,
1233                                NAT64_IN2OUT_ERROR_TCP_PACKETS, tcp_packets);
1234   vlib_node_increment_counter (vm, stats_node_index,
1235                                NAT64_IN2OUT_ERROR_UDP_PACKETS, udp_packets);
1236   vlib_node_increment_counter (vm, stats_node_index,
1237                                NAT64_IN2OUT_ERROR_ICMP_PACKETS, icmp_packets);
1238   vlib_node_increment_counter (vm, stats_node_index,
1239                                NAT64_IN2OUT_ERROR_OTHER_PACKETS,
1240                                other_packets);
1241   vlib_node_increment_counter (vm, stats_node_index,
1242                                NAT64_IN2OUT_ERROR_FRAGMENTS, fragments);
1243
1244   return frame->n_vectors;
1245 }
1246
1247 VLIB_NODE_FN (nat64_in2out_node) (vlib_main_t * vm,
1248                                   vlib_node_runtime_t * node,
1249                                   vlib_frame_t * frame)
1250 {
1251   return nat64_in2out_node_fn_inline (vm, node, frame, 0);
1252 }
1253
1254 /* *INDENT-OFF* */
1255 VLIB_REGISTER_NODE (nat64_in2out_node) = {
1256   .name = "nat64-in2out",
1257   .vector_size = sizeof (u32),
1258   .format_trace = format_nat64_in2out_trace,
1259   .type = VLIB_NODE_TYPE_INTERNAL,
1260   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
1261   .error_strings = nat64_in2out_error_strings,
1262   .n_next_nodes = NAT64_IN2OUT_N_NEXT,
1263   /* edit / add dispositions here */
1264   .next_nodes = {
1265     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
1266     [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
1267     [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
1268     [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath",
1269   },
1270 };
1271 /* *INDENT-ON* */
1272
1273 VLIB_NODE_FN (nat64_in2out_slowpath_node) (vlib_main_t * vm,
1274                                            vlib_node_runtime_t * node,
1275                                            vlib_frame_t * frame)
1276 {
1277   return nat64_in2out_node_fn_inline (vm, node, frame, 1);
1278 }
1279
1280 /* *INDENT-OFF* */
1281 VLIB_REGISTER_NODE (nat64_in2out_slowpath_node) = {
1282   .name = "nat64-in2out-slowpath",
1283   .vector_size = sizeof (u32),
1284   .format_trace = format_nat64_in2out_trace,
1285   .type = VLIB_NODE_TYPE_INTERNAL,
1286   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
1287   .error_strings = nat64_in2out_error_strings,
1288   .n_next_nodes = NAT64_IN2OUT_N_NEXT,
1289   /* edit / add dispositions here */
1290   .next_nodes = {
1291     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
1292     [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
1293     [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
1294     [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath",
1295   },
1296 };
1297 /* *INDENT-ON* */
1298
1299 typedef struct nat64_in2out_frag_set_ctx_t_
1300 {
1301   vlib_main_t *vm;
1302   u32 sess_index;
1303   u32 thread_index;
1304   u16 l4_offset;
1305   u8 proto;
1306   u8 first_frag;
1307 } nat64_in2out_frag_set_ctx_t;
1308
1309
1310 #define foreach_nat64_in2out_handoff_error                       \
1311 _(CONGESTION_DROP, "congestion drop")                            \
1312 _(SAME_WORKER, "same worker")                                    \
1313 _(DO_HANDOFF, "do handoff")
1314
1315 typedef enum
1316 {
1317 #define _(sym,str) NAT64_IN2OUT_HANDOFF_ERROR_##sym,
1318   foreach_nat64_in2out_handoff_error
1319 #undef _
1320     NAT64_IN2OUT_HANDOFF_N_ERROR,
1321 } nat64_in2out_handoff_error_t;
1322
1323 static char *nat64_in2out_handoff_error_strings[] = {
1324 #define _(sym,string) string,
1325   foreach_nat64_in2out_handoff_error
1326 #undef _
1327 };
1328
1329 typedef struct
1330 {
1331   u32 next_worker_index;
1332 } nat64_in2out_handoff_trace_t;
1333
1334 static u8 *
1335 format_nat64_in2out_handoff_trace (u8 * s, va_list * args)
1336 {
1337   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1338   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1339   nat64_in2out_handoff_trace_t *t =
1340     va_arg (*args, nat64_in2out_handoff_trace_t *);
1341
1342   s =
1343     format (s, "NAT64-IN2OUT-HANDOFF: next-worker %d", t->next_worker_index);
1344
1345   return s;
1346 }
1347
1348 VLIB_NODE_FN (nat64_in2out_handoff_node) (vlib_main_t * vm,
1349                                           vlib_node_runtime_t * node,
1350                                           vlib_frame_t * frame)
1351 {
1352   nat64_main_t *nm = &nat64_main;
1353   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
1354   u32 n_enq, n_left_from, *from;
1355   u16 thread_indices[VLIB_FRAME_SIZE], *ti;
1356   u32 fq_index;
1357   u32 thread_index = vm->thread_index;
1358   u32 do_handoff = 0, same_worker = 0;
1359
1360   from = vlib_frame_vector_args (frame);
1361   n_left_from = frame->n_vectors;
1362   vlib_get_buffers (vm, from, bufs, n_left_from);
1363
1364   b = bufs;
1365   ti = thread_indices;
1366
1367   fq_index = nm->fq_in2out_index;
1368
1369   while (n_left_from > 0)
1370     {
1371       ip6_header_t *ip0;
1372
1373       ip0 = vlib_buffer_get_current (b[0]);
1374       ti[0] = nat64_get_worker_in2out (&ip0->src_address);
1375
1376       if (ti[0] != thread_index)
1377         do_handoff++;
1378       else
1379         same_worker++;
1380
1381       if (PREDICT_FALSE
1382           ((node->flags & VLIB_NODE_FLAG_TRACE)
1383            && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
1384         {
1385           nat64_in2out_handoff_trace_t *t =
1386             vlib_add_trace (vm, node, b[0], sizeof (*t));
1387           t->next_worker_index = ti[0];
1388         }
1389
1390       n_left_from -= 1;
1391       ti += 1;
1392       b += 1;
1393     }
1394
1395   n_enq =
1396     vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices,
1397                                    frame->n_vectors, 1);
1398
1399   if (n_enq < frame->n_vectors)
1400     vlib_node_increment_counter (vm, node->node_index,
1401                                  NAT64_IN2OUT_HANDOFF_ERROR_CONGESTION_DROP,
1402                                  frame->n_vectors - n_enq);
1403   vlib_node_increment_counter (vm, node->node_index,
1404                                NAT64_IN2OUT_HANDOFF_ERROR_SAME_WORKER,
1405                                same_worker);
1406   vlib_node_increment_counter (vm, node->node_index,
1407                                NAT64_IN2OUT_HANDOFF_ERROR_DO_HANDOFF,
1408                                do_handoff);
1409
1410   return frame->n_vectors;
1411 }
1412
1413 /* *INDENT-OFF* */
1414 VLIB_REGISTER_NODE (nat64_in2out_handoff_node) = {
1415   .name = "nat64-in2out-handoff",
1416   .vector_size = sizeof (u32),
1417   .format_trace = format_nat64_in2out_handoff_trace,
1418   .type = VLIB_NODE_TYPE_INTERNAL,
1419   .n_errors = ARRAY_LEN(nat64_in2out_handoff_error_strings),
1420   .error_strings = nat64_in2out_handoff_error_strings,
1421
1422   .n_next_nodes = 1,
1423
1424   .next_nodes = {
1425     [0] = "error-drop",
1426   },
1427 };
1428 /* *INDENT-ON* */
1429
1430 /*
1431  * fd.io coding-style-patch-verification: ON
1432  *
1433  * Local Variables:
1434  * eval: (c-set-style "gnu")
1435  * End:
1436  */