acl-plugin: CLI to clear all sessions
[vpp.git] / src / plugins / snat / 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 <snat/nat64.h>
21 #include <vnet/ip/ip6_to_ip4.h>
22 #include <vnet/fib/fib_table.h>
23
24 typedef struct
25 {
26   u32 sw_if_index;
27   u32 next_index;
28 } nat64_in2out_trace_t;
29
30 static u8 *
31 format_nat64_in2out_trace (u8 * s, va_list * args)
32 {
33   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
34   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
35   nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
36
37   s =
38     format (s, "NAT64-in2out: sw_if_index %d, next index %d", t->sw_if_index,
39             t->next_index);
40
41   return s;
42 }
43
44 vlib_node_registration_t nat64_in2out_node;
45
46 #define foreach_nat64_in2out_error                 \
47 _(UNSUPPORTED_PROTOCOL, "unsupported protocol")    \
48 _(IN2OUT_PACKETS, "good in2out packets processed") \
49 _(NO_TRANSLATION, "no translation")                \
50 _(UNKNOWN, "unknown")
51
52 typedef enum
53 {
54 #define _(sym,str) NAT64_IN2OUT_ERROR_##sym,
55   foreach_nat64_in2out_error
56 #undef _
57     NAT64_IN2OUT_N_ERROR,
58 } nat64_in2out_error_t;
59
60 static char *nat64_in2out_error_strings[] = {
61 #define _(sym,string) string,
62   foreach_nat64_in2out_error
63 #undef _
64 };
65
66 typedef enum
67 {
68   NAT64_IN2OUT_NEXT_IP4_LOOKUP,
69   NAT64_IN2OUT_NEXT_IP6_LOOKUP,
70   NAT64_IN2OUT_NEXT_DROP,
71   NAT64_IN2OUT_N_NEXT,
72 } nat64_in2out_next_t;
73
74 typedef struct nat64_in2out_set_ctx_t_
75 {
76   vlib_buffer_t *b;
77   vlib_main_t *vm;
78 } nat64_in2out_set_ctx_t;
79
80 /**
81  * @brief Check whether is a hairpinning.
82  *
83  * If the destination IP address of the packet is an IPv4 address assigned to
84  * the NAT64 itself, then the packet is a hairpin packet.
85  *
86  * param dst_addr Destination address of the packet.
87  *
88  * @returns 1 if hairpinning, otherwise 0.
89  */
90 static_always_inline int
91 is_hairpinning (ip6_address_t * dst_addr)
92 {
93   nat64_main_t *nm = &nat64_main;
94   int i;
95
96   for (i = 0; i < vec_len (nm->addr_pool); i++)
97     {
98       if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3])
99         return 1;
100     }
101
102   return 0;
103 }
104
105 static int
106 nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
107                              void *arg)
108 {
109   nat64_main_t *nm = &nat64_main;
110   nat64_in2out_set_ctx_t *ctx = arg;
111   nat64_db_bib_entry_t *bibe;
112   nat64_db_st_entry_t *ste;
113   ip46_address_t saddr, daddr;
114   u32 sw_if_index, fib_index;
115   udp_header_t *udp = ip6_next_header (ip6);
116   snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
117   u16 sport = udp->src_port;
118   u16 dport = udp->dst_port;
119
120   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
121   fib_index =
122     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
123
124   saddr.as_u64[0] = ip6->src_address.as_u64[0];
125   saddr.as_u64[1] = ip6->src_address.as_u64[1];
126   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
127   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
128
129   ste =
130     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
131                             fib_index, 1);
132
133   if (ste)
134     {
135       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
136       if (!bibe)
137         return -1;
138     }
139   else
140     {
141       bibe =
142         nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
143
144       if (!bibe)
145         {
146           u16 out_port;
147           ip4_address_t out_addr;
148           if (nat64_alloc_out_addr_and_port
149               (fib_index, proto, &out_addr, &out_port))
150             return -1;
151
152           bibe =
153             nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
154                                        sport, clib_host_to_net_u16 (out_port),
155                                        fib_index, proto, 0);
156           if (!bibe)
157             return -1;
158         }
159
160       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
161       ste =
162         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
163                                   &daddr.ip4, dport);
164       if (!ste)
165         return -1;
166     }
167
168   nat64_session_reset_timeout (ste, ctx->vm);
169
170   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
171   udp->src_port = bibe->out_port;
172
173   ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
174
175   if (proto == SNAT_PROTOCOL_TCP)
176     {
177       u16 *checksum;
178       ip_csum_t csum;
179       tcp_header_t *tcp = ip6_next_header (ip6);
180
181       checksum = &tcp->checksum;
182       csum = ip_csum_sub_even (*checksum, sport);
183       csum = ip_csum_add_even (csum, udp->src_port);
184       *checksum = ip_csum_fold (csum);
185     }
186
187   return 0;
188 }
189
190 static int
191 nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
192 {
193   nat64_main_t *nm = &nat64_main;
194   nat64_in2out_set_ctx_t *ctx = arg;
195   nat64_db_bib_entry_t *bibe;
196   nat64_db_st_entry_t *ste;
197   ip46_address_t saddr, daddr;
198   u32 sw_if_index, fib_index;
199   icmp46_header_t *icmp = ip6_next_header (ip6);
200
201   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
202   fib_index =
203     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
204
205   saddr.as_u64[0] = ip6->src_address.as_u64[0];
206   saddr.as_u64[1] = ip6->src_address.as_u64[1];
207   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
208   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
209
210   if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
211     {
212       u16 in_id = ((u16 *) (icmp))[2];
213       ste =
214         nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0,
215                                 SNAT_PROTOCOL_ICMP, fib_index, 1);
216
217       if (ste)
218         {
219           bibe =
220             nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
221                                          ste->bibe_index);
222           if (!bibe)
223             return -1;
224         }
225       else
226         {
227           bibe =
228             nat64_db_bib_entry_find (&nm->db, &saddr, in_id,
229                                      SNAT_PROTOCOL_ICMP, fib_index, 1);
230
231           if (!bibe)
232             {
233               u16 out_id;
234               ip4_address_t out_addr;
235               if (nat64_alloc_out_addr_and_port
236                   (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id))
237                 return -1;
238
239               bibe =
240                 nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
241                                            &out_addr, in_id,
242                                            clib_host_to_net_u16 (out_id),
243                                            fib_index, SNAT_PROTOCOL_ICMP, 0);
244               if (!bibe)
245                 return -1;
246             }
247
248           nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
249           ste =
250             nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
251                                       &daddr.ip4, 0);
252           if (!ste)
253             return -1;
254         }
255
256       nat64_session_reset_timeout (ste, ctx->vm);
257
258       ip4->src_address.as_u32 = bibe->out_addr.as_u32;
259       ((u16 *) (icmp))[2] = bibe->out_port;
260
261       ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
262     }
263   else
264     {
265       if (!vec_len (nm->addr_pool))
266         return -1;
267
268       ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
269       nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index);
270     }
271
272   return 0;
273 }
274
275 static int
276 nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
277                                 void *arg)
278 {
279   nat64_main_t *nm = &nat64_main;
280   nat64_in2out_set_ctx_t *ctx = arg;
281   nat64_db_st_entry_t *ste;
282   nat64_db_bib_entry_t *bibe;
283   ip46_address_t saddr, daddr;
284   u32 sw_if_index, fib_index;
285   snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
286
287   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
288   fib_index =
289     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
290
291   saddr.as_u64[0] = ip6->src_address.as_u64[0];
292   saddr.as_u64[1] = ip6->src_address.as_u64[1];
293   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
294   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
295
296   if (proto == SNAT_PROTOCOL_ICMP)
297     {
298       icmp46_header_t *icmp = ip6_next_header (ip6);
299       u16 in_id = ((u16 *) (icmp))[2];
300
301       if (!
302           (icmp->type == ICMP4_echo_request
303            || icmp->type == ICMP4_echo_reply))
304         return -1;
305
306       ste =
307         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto,
308                                 fib_index, 1);
309       if (!ste)
310         return -1;
311
312       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
313       if (!bibe)
314         return -1;
315
316       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
317       ((u16 *) (icmp))[2] = bibe->out_port;
318       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
319     }
320   else
321     {
322       udp_header_t *udp = ip6_next_header (ip6);
323       tcp_header_t *tcp = ip6_next_header (ip6);
324       u16 *checksum;
325       ip_csum_t csum;
326
327       u16 sport = udp->src_port;
328       u16 dport = udp->dst_port;
329
330       ste =
331         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
332                                 fib_index, 1);
333       if (!ste)
334         return -1;
335
336       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
337       if (!bibe)
338         return -1;
339
340       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
341       udp->dst_port = bibe->out_port;
342       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
343
344       if (proto == SNAT_PROTOCOL_TCP)
345         checksum = &tcp->checksum;
346       else
347         checksum = &udp->checksum;
348       csum = ip_csum_sub_even (*checksum, dport);
349       csum = ip_csum_add_even (csum, udp->dst_port);
350       *checksum = ip_csum_fold (csum);
351     }
352
353   return 0;
354 }
355
356 static int
357 nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
358                                   ip6_header_t * ip6)
359 {
360   nat64_main_t *nm = &nat64_main;
361   nat64_db_bib_entry_t *bibe;
362   nat64_db_st_entry_t *ste;
363   ip46_address_t saddr, daddr;
364   u32 sw_if_index, fib_index;
365   udp_header_t *udp = ip6_next_header (ip6);
366   tcp_header_t *tcp = ip6_next_header (ip6);
367   snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
368   u16 sport = udp->src_port;
369   u16 dport = udp->dst_port;
370   u16 *checksum;
371   ip_csum_t csum;
372
373   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
374   fib_index =
375     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
376
377   saddr.as_u64[0] = ip6->src_address.as_u64[0];
378   saddr.as_u64[1] = ip6->src_address.as_u64[1];
379   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
380   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
381
382   if (proto == SNAT_PROTOCOL_UDP)
383     checksum = &udp->checksum;
384   else
385     checksum = &tcp->checksum;
386
387   csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]);
388   csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]);
389   csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]);
390   csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]);
391   csum = ip_csum_sub_even (csum, sport);
392   csum = ip_csum_sub_even (csum, dport);
393
394   ste =
395     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
396                             fib_index, 1);
397
398   if (ste)
399     {
400       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
401       if (!bibe)
402         return -1;
403     }
404   else
405     {
406       bibe =
407         nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
408
409       if (!bibe)
410         {
411           u16 out_port;
412           ip4_address_t out_addr;
413           if (nat64_alloc_out_addr_and_port
414               (fib_index, proto, &out_addr, &out_port))
415             return -1;
416
417           bibe =
418             nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
419                                        sport, clib_host_to_net_u16 (out_port),
420                                        fib_index, proto, 0);
421           if (!bibe)
422             return -1;
423         }
424
425       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
426       ste =
427         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
428                                   &daddr.ip4, dport);
429       if (!ste)
430         return -1;
431     }
432
433   nat64_session_reset_timeout (ste, vm);
434
435   sport = udp->src_port = bibe->out_port;
436   nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
437
438   memset (&saddr, 0, sizeof (saddr));
439   memset (&daddr, 0, sizeof (daddr));
440   saddr.ip4.as_u32 = bibe->out_addr.as_u32;
441   daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
442
443   ste =
444     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0,
445                             0);
446
447   if (ste)
448     {
449       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
450       if (!bibe)
451         return -1;
452     }
453   else
454     {
455       bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0);
456
457       if (!bibe)
458         return -1;
459
460       ste =
461         nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address,
462                                   &saddr.ip4, sport);
463     }
464
465   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
466   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
467   udp->dst_port = bibe->in_port;
468
469   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
470   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
471   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
472   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);
473   csum = ip_csum_add_even (csum, udp->src_port);
474   csum = ip_csum_add_even (csum, udp->dst_port);
475   *checksum = ip_csum_fold (csum);
476
477   return 0;
478 }
479
480 static int
481 nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
482                                ip6_header_t * ip6)
483 {
484   nat64_main_t *nm = &nat64_main;
485   nat64_db_bib_entry_t *bibe;
486   nat64_db_st_entry_t *ste;
487   icmp46_header_t *icmp = ip6_next_header (ip6);
488   ip6_header_t *inner_ip6;
489   ip46_address_t saddr, daddr;
490   u32 sw_if_index, fib_index;
491   snat_protocol_t proto;
492   udp_header_t *udp;
493   tcp_header_t *tcp;
494   u16 *checksum, sport, dport;
495   ip_csum_t csum;
496
497   if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
498     return -1;
499
500   inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
501
502   proto = ip_proto_to_snat_proto (inner_ip6->protocol);
503
504   if (proto == SNAT_PROTOCOL_ICMP)
505     return -1;
506
507   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
508   fib_index =
509     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
510
511   saddr.as_u64[0] = inner_ip6->src_address.as_u64[0];
512   saddr.as_u64[1] = inner_ip6->src_address.as_u64[1];
513   daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0];
514   daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1];
515
516   udp = ip6_next_header (inner_ip6);
517   tcp = ip6_next_header (inner_ip6);
518
519   sport = udp->src_port;
520   dport = udp->dst_port;
521
522   if (proto == SNAT_PROTOCOL_UDP)
523     checksum = &udp->checksum;
524   else
525     checksum = &tcp->checksum;
526
527   csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]);
528   csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]);
529   csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]);
530   csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]);
531   csum = ip_csum_sub_even (csum, sport);
532   csum = ip_csum_sub_even (csum, dport);
533
534   ste =
535     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
536                             fib_index, 1);
537   if (!ste)
538     return -1;
539
540   bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
541   if (!bibe)
542     return -1;
543
544   dport = udp->dst_port = bibe->out_port;
545   nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index);
546
547   memset (&saddr, 0, sizeof (saddr));
548   memset (&daddr, 0, sizeof (daddr));
549   saddr.ip4.as_u32 = ste->out_r_addr.as_u32;
550   daddr.ip4.as_u32 = bibe->out_addr.as_u32;
551
552   ste =
553     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0,
554                             0);
555   if (!ste)
556     return -1;
557
558   bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
559   if (!bibe)
560     return -1;
561
562   inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
563   inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
564   udp->src_port = bibe->in_port;
565
566   csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
567   csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
568   csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
569   csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
570   csum = ip_csum_add_even (csum, udp->src_port);
571   csum = ip_csum_add_even (csum, udp->dst_port);
572   *checksum = ip_csum_fold (csum);
573
574   if (!vec_len (nm->addr_pool))
575     return -1;
576
577   nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index);
578   ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
579   ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
580
581   icmp->checksum = 0;
582   csum = ip_csum_with_carry (0, ip6->payload_length);
583   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
584   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
585   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
586   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
587   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
588   csum =
589     ip_incremental_checksum (csum, icmp,
590                              clib_net_to_host_u16 (ip6->payload_length));
591   icmp->checksum = ~ip_csum_fold (csum);
592
593   return 0;
594 }
595
596 static uword
597 nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
598                       vlib_frame_t * frame)
599 {
600   u32 n_left_from, *from, *to_next;
601   nat64_in2out_next_t next_index;
602   u32 pkts_processed = 0;
603
604   from = vlib_frame_vector_args (frame);
605   n_left_from = frame->n_vectors;
606   next_index = node->cached_next_index;
607
608   while (n_left_from > 0)
609     {
610       u32 n_left_to_next;
611
612       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
613
614       while (n_left_from > 0 && n_left_to_next > 0)
615         {
616           u32 bi0;
617           vlib_buffer_t *b0;
618           u32 next0;
619           ip6_header_t *ip60;
620           u16 l4_offset0, frag_offset0;
621           u8 l4_protocol0;
622           u32 proto0;
623           nat64_in2out_set_ctx_t ctx0;
624
625           /* speculatively enqueue b0 to the current next frame */
626           bi0 = from[0];
627           to_next[0] = bi0;
628           from += 1;
629           to_next += 1;
630           n_left_from -= 1;
631           n_left_to_next -= 1;
632
633           b0 = vlib_get_buffer (vm, bi0);
634           ip60 = vlib_buffer_get_current (b0);
635
636           ctx0.b = b0;
637           ctx0.vm = vm;
638
639           next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP;
640
641           if (PREDICT_FALSE
642               (ip6_parse
643                (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
644                 &frag_offset0)))
645             {
646               next0 = NAT64_IN2OUT_NEXT_DROP;
647               b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
648               goto trace0;
649             }
650
651           proto0 = ip_proto_to_snat_proto (l4_protocol0);
652           if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0)))
653             {
654               next0 = NAT64_IN2OUT_NEXT_DROP;
655               b0->error =
656                 node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
657               goto trace0;
658             }
659
660           if (proto0 == SNAT_PROTOCOL_ICMP)
661             {
662               if (is_hairpinning (&ip60->dst_address))
663                 {
664                   next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
665                   if (nat64_in2out_icmp_hairpinning (vm, b0, ip60))
666                     {
667                       next0 = NAT64_IN2OUT_NEXT_DROP;
668                       b0->error =
669                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
670                     }
671                   goto trace0;
672                 }
673
674               if (icmp6_to_icmp
675                   (b0, nat64_in2out_icmp_set_cb, &ctx0,
676                    nat64_in2out_inner_icmp_set_cb, &ctx0))
677                 {
678                   next0 = NAT64_IN2OUT_NEXT_DROP;
679                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
680                   goto trace0;
681                 }
682             }
683           else
684             {
685               if (is_hairpinning (&ip60->dst_address))
686                 {
687                   next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
688                   if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60))
689                     {
690                       next0 = NAT64_IN2OUT_NEXT_DROP;
691                       b0->error =
692                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
693                     }
694                   goto trace0;
695                 }
696
697               if (ip6_to_ip4_tcp_udp
698                   (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0))
699                 {
700                   next0 = NAT64_IN2OUT_NEXT_DROP;
701                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
702                   goto trace0;
703                 }
704             }
705
706         trace0:
707           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
708                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
709             {
710               nat64_in2out_trace_t *t =
711                 vlib_add_trace (vm, node, b0, sizeof (*t));
712               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
713               t->next_index = next0;
714             }
715
716           pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
717
718           /* verify speculative enqueue, maybe switch current next frame */
719           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
720                                            n_left_to_next, bi0, next0);
721         }
722       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
723     }
724   vlib_node_increment_counter (vm, nat64_in2out_node.index,
725                                NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
726                                pkts_processed);
727   return frame->n_vectors;
728 }
729
730 /* *INDENT-OFF* */
731 VLIB_REGISTER_NODE (nat64_in2out_node) = {
732   .function = nat64_in2out_node_fn,.name = "nat64-in2out",
733   .vector_size = sizeof (u32),
734   .format_trace = format_nat64_in2out_trace,
735   .type = VLIB_NODE_TYPE_INTERNAL,
736   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
737   .error_strings = nat64_in2out_error_strings,
738   .n_next_nodes = 2,
739   /* edit / add dispositions here */
740   .next_nodes = {
741     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
742     [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
743     [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
744   },
745 };
746 /* *INDENT-ON* */
747
748 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
749
750 /*
751  * fd.io coding-style-patch-verification: ON
752  *
753  * Local Variables:
754  * eval: (c-set-style "gnu")
755  * End:
756  */