SNAT: IP fragmentation (VPP-890)
[vpp.git] / src / plugins / nat / nat64_out2in.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 IPv4 to IPv6 translation (otside to inside network)
18  */
19
20 #include <nat/nat64.h>
21 #include <nat/nat_reass.h>
22 #include <vnet/ip/ip4_to_ip6.h>
23 #include <vnet/fib/ip4_fib.h>
24
25 typedef struct
26 {
27   u32 sw_if_index;
28   u32 next_index;
29 } nat64_out2in_trace_t;
30
31 static u8 *
32 format_nat64_out2in_trace (u8 * s, va_list * args)
33 {
34   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
35   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
36   nat64_out2in_trace_t *t = va_arg (*args, nat64_out2in_trace_t *);
37
38   s =
39     format (s, "NAT64-out2in: sw_if_index %d, next index %d", t->sw_if_index,
40             t->next_index);
41
42   return s;
43 }
44
45 typedef struct
46 {
47   u32 sw_if_index;
48   u32 next_index;
49   u8 cached;
50 } nat64_out2in_reass_trace_t;
51
52 static u8 *
53 format_nat64_out2in_reass_trace (u8 * s, va_list * args)
54 {
55   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
56   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
57   nat64_out2in_reass_trace_t *t =
58     va_arg (*args, nat64_out2in_reass_trace_t *);
59
60   s =
61     format (s, "NAT64-out2in-reass: sw_if_index %d, next index %d, status %s",
62             t->sw_if_index, t->next_index,
63             t->cached ? "cached" : "translated");
64
65   return s;
66 }
67
68 vlib_node_registration_t nat64_out2in_node;
69 vlib_node_registration_t nat64_out2in_reass_node;
70
71 #define foreach_nat64_out2in_error                       \
72 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol")          \
73 _(OUT2IN_PACKETS, "Good out2in packets processed")       \
74 _(NO_TRANSLATION, "No translation")                      \
75 _(UNKNOWN, "unknown")                                    \
76 _(DROP_FRAGMENT, "Drop fragment")                        \
77 _(MAX_REASS, "Maximum reassemblies exceeded")            \
78 _(MAX_FRAG, "Maximum fragments per reassembly exceeded")
79
80
81 typedef enum
82 {
83 #define _(sym,str) NAT64_OUT2IN_ERROR_##sym,
84   foreach_nat64_out2in_error
85 #undef _
86     NAT64_OUT2IN_N_ERROR,
87 } nat64_out2in_error_t;
88
89 static char *nat64_out2in_error_strings[] = {
90 #define _(sym,string) string,
91   foreach_nat64_out2in_error
92 #undef _
93 };
94
95 typedef enum
96 {
97   NAT64_OUT2IN_NEXT_LOOKUP,
98   NAT64_OUT2IN_NEXT_DROP,
99   NAT64_OUT2IN_NEXT_REASS,
100   NAT64_OUT2IN_N_NEXT,
101 } nat64_out2in_next_t;
102
103 typedef struct nat64_out2in_set_ctx_t_
104 {
105   vlib_buffer_t *b;
106   vlib_main_t *vm;
107 } nat64_out2in_set_ctx_t;
108
109 static int
110 nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
111                              void *arg)
112 {
113   nat64_main_t *nm = &nat64_main;
114   nat64_out2in_set_ctx_t *ctx = arg;
115   nat64_db_bib_entry_t *bibe;
116   nat64_db_st_entry_t *ste;
117   ip46_address_t saddr, daddr;
118   ip6_address_t ip6_saddr;
119   udp_header_t *udp = ip4_next_header (ip4);
120   tcp_header_t *tcp = ip4_next_header (ip4);
121   u8 proto = ip4->protocol;
122   u16 dport = udp->dst_port;
123   u16 sport = udp->src_port;
124   u32 sw_if_index, fib_index;
125   u16 *checksum;
126   ip_csum_t csum;
127
128   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
129   fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
130
131   memset (&saddr, 0, sizeof (saddr));
132   saddr.ip4.as_u32 = ip4->src_address.as_u32;
133   memset (&daddr, 0, sizeof (daddr));
134   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
135
136   ste =
137     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
138                             fib_index, 0);
139   if (ste)
140     {
141       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
142       if (!bibe)
143         return -1;
144     }
145   else
146     {
147       bibe =
148         nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, fib_index, 0);
149
150       if (!bibe)
151         return -1;
152
153       nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index);
154       ste =
155         nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4,
156                                   sport);
157     }
158
159   nat64_session_reset_timeout (ste, ctx->vm);
160
161   ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
162   ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
163
164   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
165   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
166   udp->dst_port = bibe->in_port;
167
168   if (proto == IP_PROTOCOL_UDP)
169     checksum = &udp->checksum;
170   else
171     checksum = &tcp->checksum;
172   csum = ip_csum_sub_even (*checksum, dport);
173   csum = ip_csum_add_even (csum, udp->dst_port);
174   *checksum = ip_csum_fold (csum);
175
176   vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
177
178   return 0;
179 }
180
181 static int
182 nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
183 {
184   nat64_main_t *nm = &nat64_main;
185   nat64_out2in_set_ctx_t *ctx = arg;
186   nat64_db_bib_entry_t *bibe;
187   nat64_db_st_entry_t *ste;
188   ip46_address_t saddr, daddr;
189   ip6_address_t ip6_saddr;
190   u32 sw_if_index, fib_index;
191   icmp46_header_t *icmp = ip4_next_header (ip4);
192
193   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
194   fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
195
196   memset (&saddr, 0, sizeof (saddr));
197   saddr.ip4.as_u32 = ip4->src_address.as_u32;
198   memset (&daddr, 0, sizeof (daddr));
199   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
200
201   if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
202     {
203       u16 out_id = ((u16 *) (icmp))[2];
204       ste =
205         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, out_id, 0,
206                                 IP_PROTOCOL_ICMP, fib_index, 0);
207
208       if (ste)
209         {
210           bibe =
211             nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP,
212                                          ste->bibe_index);
213           if (!bibe)
214             return -1;
215         }
216       else
217         {
218           bibe =
219             nat64_db_bib_entry_find (&nm->db, &daddr, out_id,
220                                      IP_PROTOCOL_ICMP, fib_index, 0);
221           if (!bibe)
222             return -1;
223
224           nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index);
225           ste =
226             nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4,
227                                       0);
228         }
229
230       nat64_session_reset_timeout (ste, ctx->vm);
231
232       ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
233       ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
234
235       ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
236       ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
237       ((u16 *) (icmp))[2] = bibe->in_port;
238
239       vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
240     }
241   else
242     {
243       ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
244
245       nat64_compose_ip6 (&ip6->src_address, &ip4->src_address,
246                          vnet_buffer (ctx->b)->sw_if_index[VLIB_TX]);
247       ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
248       ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
249     }
250
251   return 0;
252 }
253
254 static int
255 nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
256                                 void *arg)
257 {
258   nat64_main_t *nm = &nat64_main;
259   nat64_out2in_set_ctx_t *ctx = arg;
260   nat64_db_bib_entry_t *bibe;
261   nat64_db_st_entry_t *ste;
262   ip46_address_t saddr, daddr;
263   u32 sw_if_index, fib_index;
264   u8 proto = ip4->protocol;
265
266   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
267   fib_index =
268     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
269
270   memset (&saddr, 0, sizeof (saddr));
271   saddr.ip4.as_u32 = ip4->src_address.as_u32;
272   memset (&daddr, 0, sizeof (daddr));
273   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
274
275   if (proto == IP_PROTOCOL_ICMP6)
276     {
277       icmp46_header_t *icmp = ip4_next_header (ip4);
278       u16 out_id = ((u16 *) (icmp))[2];
279       proto = IP_PROTOCOL_ICMP;
280
281       if (!
282           (icmp->type == ICMP6_echo_request
283            || icmp->type == ICMP6_echo_reply))
284         return -1;
285
286       ste =
287         nat64_db_st_entry_find (&nm->db, &saddr, &daddr, out_id, 0, proto,
288                                 fib_index, 0);
289       if (!ste)
290         return -1;
291
292       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
293       if (!bibe)
294         return -1;
295
296       ip6->dst_address.as_u64[0] = ste->in_r_addr.as_u64[0];
297       ip6->dst_address.as_u64[1] = ste->in_r_addr.as_u64[1];
298       ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
299       ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
300       ((u16 *) (icmp))[2] = bibe->in_port;
301
302       vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
303     }
304   else
305     {
306       udp_header_t *udp = ip4_next_header (ip4);
307       tcp_header_t *tcp = ip4_next_header (ip4);
308       u16 dport = udp->dst_port;
309       u16 sport = udp->src_port;
310       u16 *checksum;
311       ip_csum_t csum;
312
313       ste =
314         nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
315                                 fib_index, 0);
316       if (!ste)
317         return -1;
318
319       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
320       if (!bibe)
321         return -1;
322
323       nat64_compose_ip6 (&ip6->dst_address, &daddr.ip4, bibe->fib_index);
324       ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
325       ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
326       udp->src_port = bibe->in_port;
327
328       if (proto == IP_PROTOCOL_UDP)
329         checksum = &udp->checksum;
330       else
331         checksum = &tcp->checksum;
332       if (*checksum)
333         {
334           csum = ip_csum_sub_even (*checksum, sport);
335           csum = ip_csum_add_even (csum, udp->src_port);
336           *checksum = ip_csum_fold (csum);
337         }
338
339       vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
340     }
341
342   return 0;
343 }
344
345 static int
346 nat64_out2in_unk_proto_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
347                                void *arg)
348 {
349   nat64_main_t *nm = &nat64_main;
350   nat64_out2in_set_ctx_t *ctx = arg;
351   nat64_db_bib_entry_t *bibe;
352   nat64_db_st_entry_t *ste;
353   ip46_address_t saddr, daddr;
354   ip6_address_t ip6_saddr;
355   u32 sw_if_index, fib_index;
356   u8 proto = ip4->protocol;
357
358   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
359   fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
360
361   memset (&saddr, 0, sizeof (saddr));
362   saddr.ip4.as_u32 = ip4->src_address.as_u32;
363   memset (&daddr, 0, sizeof (daddr));
364   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
365
366   ste =
367     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, fib_index,
368                             0);
369   if (ste)
370     {
371       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
372       if (!bibe)
373         return -1;
374     }
375   else
376     {
377       bibe =
378         nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, fib_index, 0);
379
380       if (!bibe)
381         return -1;
382
383       nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index);
384       ste =
385         nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, 0);
386     }
387
388   nat64_session_reset_timeout (ste, ctx->vm);
389
390   ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
391   ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
392
393   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
394   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
395
396   vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
397
398   return 0;
399 }
400
401 static uword
402 nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
403                       vlib_frame_t * frame)
404 {
405   u32 n_left_from, *from, *to_next;
406   nat64_out2in_next_t next_index;
407   u32 pkts_processed = 0;
408
409   from = vlib_frame_vector_args (frame);
410   n_left_from = frame->n_vectors;
411   next_index = node->cached_next_index;
412   while (n_left_from > 0)
413     {
414       u32 n_left_to_next;
415
416       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
417
418       while (n_left_from > 0 && n_left_to_next > 0)
419         {
420           u32 bi0;
421           vlib_buffer_t *b0;
422           u32 next0;
423           ip4_header_t *ip40;
424           u32 proto0;
425           nat64_out2in_set_ctx_t ctx0;
426
427           /* speculatively enqueue b0 to the current next frame */
428           bi0 = from[0];
429           to_next[0] = bi0;
430           from += 1;
431           to_next += 1;
432           n_left_from -= 1;
433           n_left_to_next -= 1;
434
435           b0 = vlib_get_buffer (vm, bi0);
436           ip40 = vlib_buffer_get_current (b0);
437
438           ctx0.b = b0;
439           ctx0.vm = vm;
440
441           next0 = NAT64_OUT2IN_NEXT_LOOKUP;
442
443           proto0 = ip_proto_to_snat_proto (ip40->protocol);
444
445           if (PREDICT_FALSE (proto0 == ~0))
446             {
447               if (ip4_to_ip6 (b0, nat64_out2in_unk_proto_set_cb, &ctx0))
448                 {
449                   next0 = NAT64_OUT2IN_NEXT_DROP;
450                   b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
451                 }
452               goto trace0;
453             }
454
455           if (PREDICT_FALSE (ip4_is_fragment (ip40)))
456             {
457               next0 = NAT64_OUT2IN_NEXT_REASS;
458               goto trace0;
459             }
460
461           if (proto0 == SNAT_PROTOCOL_ICMP)
462             {
463               if (icmp_to_icmp6
464                   (b0, nat64_out2in_icmp_set_cb, &ctx0,
465                    nat64_out2in_inner_icmp_set_cb, &ctx0))
466                 {
467                   next0 = NAT64_OUT2IN_NEXT_DROP;
468                   b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
469                   goto trace0;
470                 }
471             }
472           else
473             {
474               if (ip4_to_ip6_tcp_udp (b0, nat64_out2in_tcp_udp_set_cb, &ctx0))
475                 {
476                   next0 = NAT64_OUT2IN_NEXT_DROP;
477                   b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
478                   goto trace0;
479                 }
480             }
481
482         trace0:
483           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
484                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
485             {
486               nat64_out2in_trace_t *t =
487                 vlib_add_trace (vm, node, b0, sizeof (*t));
488               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
489               t->next_index = next0;
490             }
491
492           pkts_processed += next0 != NAT64_OUT2IN_NEXT_DROP;
493
494           /* verify speculative enqueue, maybe switch current next frame */
495           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
496                                            n_left_to_next, bi0, next0);
497         }
498       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
499     }
500   vlib_node_increment_counter (vm, nat64_out2in_node.index,
501                                NAT64_OUT2IN_ERROR_OUT2IN_PACKETS,
502                                pkts_processed);
503   return frame->n_vectors;
504 }
505
506 /* *INDENT-OFF* */
507 VLIB_REGISTER_NODE (nat64_out2in_node) = {
508   .function = nat64_out2in_node_fn,
509   .name = "nat64-out2in",
510   .vector_size = sizeof (u32),
511   .format_trace = format_nat64_out2in_trace,
512   .type = VLIB_NODE_TYPE_INTERNAL,
513   .n_errors = ARRAY_LEN (nat64_out2in_error_strings),
514   .error_strings = nat64_out2in_error_strings,
515   .n_next_nodes = NAT64_OUT2IN_N_NEXT,
516   /* edit / add dispositions here */
517   .next_nodes = {
518     [NAT64_OUT2IN_NEXT_DROP] = "error-drop",
519     [NAT64_OUT2IN_NEXT_LOOKUP] = "ip6-lookup",
520     [NAT64_OUT2IN_NEXT_REASS] = "nat64-out2in-reass",
521   },
522 };
523 /* *INDENT-ON* */
524
525 VLIB_NODE_FUNCTION_MULTIARCH (nat64_out2in_node, nat64_out2in_node_fn);
526
527 typedef struct nat64_out2in_frag_set_ctx_t_
528 {
529   vlib_main_t *vm;
530   vlib_buffer_t *b;
531   u32 sess_index;
532   u8 proto;
533   u8 first_frag;
534 } nat64_out2in_frag_set_ctx_t;
535
536 static int
537 nat64_out2in_frag_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
538 {
539   nat64_main_t *nm = &nat64_main;
540   nat64_out2in_frag_set_ctx_t *ctx = arg;
541   nat64_db_st_entry_t *ste;
542   nat64_db_bib_entry_t *bibe;
543   udp_header_t *udp = ip4_next_header (ip4);
544   ip_csum_t csum;
545   u16 *checksum;
546
547   ste = nat64_db_st_entry_by_index (&nm->db, ctx->proto, ctx->sess_index);
548   if (!ste)
549     return -1;
550
551   bibe = nat64_db_bib_entry_by_index (&nm->db, ctx->proto, ste->bibe_index);
552   if (!bibe)
553     return -1;
554
555   nat64_session_reset_timeout (ste, ctx->vm);
556
557   if (ctx->first_frag)
558     {
559       udp->dst_port = bibe->in_port;
560
561       if (ip4->protocol == IP_PROTOCOL_UDP)
562         {
563           checksum = &udp->checksum;
564
565           if (!checksum)
566             {
567               u16 udp_len =
568                 clib_host_to_net_u16 (ip4->length) - sizeof (*ip4);
569               csum = ip_incremental_checksum (0, udp, udp_len);
570               csum =
571                 ip_csum_with_carry (csum, clib_host_to_net_u16 (udp_len));
572               csum =
573                 ip_csum_with_carry (csum,
574                                     clib_host_to_net_u16 (IP_PROTOCOL_UDP));
575               csum = ip_csum_with_carry (csum, ste->in_r_addr.as_u64[0]);
576               csum = ip_csum_with_carry (csum, ste->in_r_addr.as_u64[1]);
577               csum = ip_csum_with_carry (csum, bibe->in_addr.as_u64[0]);
578               csum = ip_csum_with_carry (csum, bibe->in_addr.as_u64[1]);
579               *checksum = ~ip_csum_fold (csum);
580             }
581           else
582             {
583               csum = ip_csum_sub_even (*checksum, bibe->out_addr.as_u32);
584               csum = ip_csum_sub_even (csum, ste->out_r_addr.as_u32);
585               csum = ip_csum_sub_even (csum, bibe->out_port);
586               csum = ip_csum_add_even (csum, ste->in_r_addr.as_u64[0]);
587               csum = ip_csum_add_even (csum, ste->in_r_addr.as_u64[1]);
588               csum = ip_csum_add_even (csum, bibe->in_addr.as_u64[0]);
589               csum = ip_csum_add_even (csum, bibe->in_addr.as_u64[1]);
590               csum = ip_csum_add_even (csum, bibe->in_port);
591               *checksum = ip_csum_fold (csum);
592             }
593         }
594       else
595         {
596           tcp_header_t *tcp = ip4_next_header (ip4);
597           checksum = &tcp->checksum;
598           csum = ip_csum_sub_even (*checksum, bibe->out_addr.as_u32);
599           csum = ip_csum_sub_even (csum, ste->out_r_addr.as_u32);
600           csum = ip_csum_sub_even (csum, bibe->out_port);
601           csum = ip_csum_add_even (csum, ste->in_r_addr.as_u64[0]);
602           csum = ip_csum_add_even (csum, ste->in_r_addr.as_u64[1]);
603           csum = ip_csum_add_even (csum, bibe->in_addr.as_u64[0]);
604           csum = ip_csum_add_even (csum, bibe->in_addr.as_u64[1]);
605           csum = ip_csum_add_even (csum, bibe->in_port);
606           *checksum = ip_csum_fold (csum);
607         }
608
609     }
610
611   ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
612   ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
613
614   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
615   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
616
617   vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
618
619   return 0;
620 }
621
622 static uword
623 nat64_out2in_reass_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
624                             vlib_frame_t * frame)
625 {
626   u32 n_left_from, *from, *to_next;
627   nat64_out2in_next_t next_index;
628   u32 pkts_processed = 0;
629   u32 *fragments_to_drop = 0;
630   u32 *fragments_to_loopback = 0;
631   nat64_main_t *nm = &nat64_main;
632
633   from = vlib_frame_vector_args (frame);
634   n_left_from = frame->n_vectors;
635   next_index = node->cached_next_index;
636
637   while (n_left_from > 0)
638     {
639       u32 n_left_to_next;
640
641       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
642
643       while (n_left_from > 0 && n_left_to_next > 0)
644         {
645           u32 bi0;
646           vlib_buffer_t *b0;
647           u32 next0;
648           ip4_header_t *ip40;
649           u8 cached0 = 0;
650           u32 sw_if_index0, fib_index0;
651           udp_header_t *udp0;
652           nat_reass_ip4_t *reass0;
653           ip46_address_t saddr0, daddr0;
654           nat64_db_st_entry_t *ste0;
655           nat64_db_bib_entry_t *bibe0;
656           ip6_address_t ip6_saddr0;
657           nat64_out2in_frag_set_ctx_t ctx0;
658
659           /* speculatively enqueue b0 to the current next frame */
660           bi0 = from[0];
661           to_next[0] = bi0;
662           from += 1;
663           to_next += 1;
664           n_left_from -= 1;
665           n_left_to_next -= 1;
666
667           b0 = vlib_get_buffer (vm, bi0);
668           next0 = NAT64_OUT2IN_NEXT_LOOKUP;
669
670           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
671           fib_index0 =
672             fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
673                                                  sw_if_index0);
674
675           if (PREDICT_FALSE (nat_reass_is_drop_frag (1)))
676             {
677               next0 = NAT64_OUT2IN_NEXT_DROP;
678               b0->error = node->errors[NAT64_OUT2IN_ERROR_DROP_FRAGMENT];
679               goto trace0;
680             }
681
682           ip40 = vlib_buffer_get_current (b0);
683
684           if (PREDICT_FALSE (!(ip40->protocol == IP_PROTOCOL_TCP
685                                || ip40->protocol == IP_PROTOCOL_UDP)))
686             {
687               next0 = NAT64_OUT2IN_NEXT_DROP;
688               b0->error = node->errors[NAT64_OUT2IN_ERROR_DROP_FRAGMENT];
689               goto trace0;
690             }
691
692           udp0 = ip4_next_header (ip40);
693
694           reass0 = nat_ip4_reass_find_or_create (ip40->src_address,
695                                                  ip40->dst_address,
696                                                  ip40->fragment_id,
697                                                  ip40->protocol,
698                                                  1, &fragments_to_drop);
699
700           if (PREDICT_FALSE (!reass0))
701             {
702               next0 = NAT64_OUT2IN_NEXT_DROP;
703               b0->error = node->errors[NAT64_OUT2IN_ERROR_MAX_REASS];
704               goto trace0;
705             }
706
707           if (PREDICT_FALSE (ip4_is_first_fragment (ip40)))
708             {
709               ctx0.first_frag = 1;
710
711               memset (&saddr0, 0, sizeof (saddr0));
712               saddr0.ip4.as_u32 = ip40->src_address.as_u32;
713               memset (&daddr0, 0, sizeof (daddr0));
714               daddr0.ip4.as_u32 = ip40->dst_address.as_u32;
715
716               ste0 =
717                 nat64_db_st_entry_find (&nm->db, &daddr0, &saddr0,
718                                         udp0->dst_port, udp0->src_port,
719                                         ip40->protocol, fib_index0, 0);
720               if (!ste0)
721                 {
722                   bibe0 =
723                     nat64_db_bib_entry_find (&nm->db, &daddr0, udp0->dst_port,
724                                              ip40->protocol, fib_index0, 0);
725                   if (!bibe0)
726                     {
727                       next0 = NAT64_OUT2IN_NEXT_DROP;
728                       b0->error =
729                         node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
730                       goto trace0;
731                     }
732
733                   nat64_compose_ip6 (&ip6_saddr0, &ip40->src_address,
734                                      bibe0->fib_index);
735                   ste0 =
736                     nat64_db_st_entry_create (&nm->db, bibe0, &ip6_saddr0,
737                                               &saddr0.ip4, udp0->src_port);
738
739                   if (!ste0)
740                     {
741                       next0 = NAT64_OUT2IN_NEXT_DROP;
742                       b0->error =
743                         node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
744                       goto trace0;
745                     }
746                 }
747               reass0->sess_index =
748                 nat64_db_st_entry_get_index (&nm->db, ste0);
749
750               nat_ip4_reass_get_frags (reass0, &fragments_to_loopback);
751             }
752           else
753             {
754               ctx0.first_frag = 0;
755
756               if (PREDICT_FALSE (reass0->sess_index == (u32) ~ 0))
757                 {
758                   if (nat_ip4_reass_add_fragment (reass0, bi0))
759                     {
760                       b0->error = node->errors[NAT64_OUT2IN_ERROR_MAX_FRAG];
761                       next0 = NAT64_OUT2IN_NEXT_DROP;
762                       goto trace0;
763                     }
764                   cached0 = 1;
765                   goto trace0;
766                 }
767             }
768
769           ctx0.sess_index = reass0->sess_index;
770           ctx0.proto = ip40->protocol;
771           ctx0.vm = vm;
772           ctx0.b = b0;
773
774           if (ip4_to_ip6_fragmented (b0, nat64_out2in_frag_set_cb, &ctx0))
775             {
776               next0 = NAT64_OUT2IN_NEXT_DROP;
777               b0->error = node->errors[NAT64_OUT2IN_ERROR_UNKNOWN];
778               goto trace0;
779             }
780
781         trace0:
782           if (PREDICT_FALSE
783               ((node->flags & VLIB_NODE_FLAG_TRACE)
784                && (b0->flags & VLIB_BUFFER_IS_TRACED)))
785             {
786               nat64_out2in_reass_trace_t *t =
787                 vlib_add_trace (vm, node, b0, sizeof (*t));
788               t->cached = cached0;
789               t->sw_if_index = sw_if_index0;
790               t->next_index = next0;
791             }
792
793           if (cached0)
794             {
795               n_left_to_next++;
796               to_next--;
797             }
798           else
799             {
800               pkts_processed += next0 != NAT64_OUT2IN_NEXT_DROP;
801
802               /* verify speculative enqueue, maybe switch current next frame */
803               vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
804                                                to_next, n_left_to_next,
805                                                bi0, next0);
806             }
807
808           if (n_left_from == 0 && vec_len (fragments_to_loopback))
809             {
810               from = vlib_frame_vector_args (frame);
811               u32 len = vec_len (fragments_to_loopback);
812               if (len <= VLIB_FRAME_SIZE)
813                 {
814                   clib_memcpy (from, fragments_to_loopback,
815                                sizeof (u32) * len);
816                   n_left_from = len;
817                   vec_reset_length (fragments_to_loopback);
818                 }
819               else
820                 {
821                   clib_memcpy (from,
822                                fragments_to_loopback + (len -
823                                                         VLIB_FRAME_SIZE),
824                                sizeof (u32) * VLIB_FRAME_SIZE);
825                   n_left_from = VLIB_FRAME_SIZE;
826                   _vec_len (fragments_to_loopback) = len - VLIB_FRAME_SIZE;
827                 }
828             }
829         }
830
831       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
832     }
833
834   vlib_node_increment_counter (vm, nat64_out2in_reass_node.index,
835                                NAT64_OUT2IN_ERROR_OUT2IN_PACKETS,
836                                pkts_processed);
837
838   nat_send_all_to_node (vm, fragments_to_drop, node,
839                         &node->errors[NAT64_OUT2IN_ERROR_DROP_FRAGMENT],
840                         NAT64_OUT2IN_NEXT_DROP);
841
842   vec_free (fragments_to_drop);
843   vec_free (fragments_to_loopback);
844   return frame->n_vectors;
845 }
846
847 /* *INDENT-OFF* */
848 VLIB_REGISTER_NODE (nat64_out2in_reass_node) = {
849   .function = nat64_out2in_reass_node_fn,
850   .name = "nat64-out2in-reass",
851   .vector_size = sizeof (u32),
852   .format_trace = format_nat64_out2in_reass_trace,
853   .type = VLIB_NODE_TYPE_INTERNAL,
854   .n_errors = ARRAY_LEN (nat64_out2in_error_strings),
855   .error_strings = nat64_out2in_error_strings,
856   .n_next_nodes = NAT64_OUT2IN_N_NEXT,
857   /* edit / add dispositions here */
858   .next_nodes = {
859     [NAT64_OUT2IN_NEXT_DROP] = "error-drop",
860     [NAT64_OUT2IN_NEXT_LOOKUP] = "ip6-lookup",
861     [NAT64_OUT2IN_NEXT_REASS] = "nat64-out2in-reass",
862   },
863 };
864 /* *INDENT-ON* */
865
866 VLIB_NODE_FUNCTION_MULTIARCH (nat64_out2in_reass_node,
867                               nat64_out2in_reass_node_fn);
868
869 /*
870  * fd.io coding-style-patch-verification: ON
871  *
872  * Local Variables:
873  * eval: (c-set-style "gnu")
874  * End:
875  */