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