f78baff4e12da67eec3c00ed5ce817e55aed3084
[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 <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   u8 is_slow_path;
29 } nat64_in2out_trace_t;
30
31 static u8 *
32 format_nat64_in2out_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_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
37   char *tag;
38
39   tag = t->is_slow_path ? "NAT64-in2out-slowpath" : "NAT64-in2out";
40
41   s =
42     format (s, "%s: sw_if_index %d, next index %d", tag, t->sw_if_index,
43             t->next_index);
44
45   return s;
46 }
47
48 vlib_node_registration_t nat64_in2out_node;
49 vlib_node_registration_t nat64_in2out_slowpath_node;
50
51 #define foreach_nat64_in2out_error                 \
52 _(UNSUPPORTED_PROTOCOL, "unsupported protocol")    \
53 _(IN2OUT_PACKETS, "good in2out packets processed") \
54 _(NO_TRANSLATION, "no translation")                \
55 _(UNKNOWN, "unknown")
56
57 typedef enum
58 {
59 #define _(sym,str) NAT64_IN2OUT_ERROR_##sym,
60   foreach_nat64_in2out_error
61 #undef _
62     NAT64_IN2OUT_N_ERROR,
63 } nat64_in2out_error_t;
64
65 static char *nat64_in2out_error_strings[] = {
66 #define _(sym,string) string,
67   foreach_nat64_in2out_error
68 #undef _
69 };
70
71 typedef enum
72 {
73   NAT64_IN2OUT_NEXT_IP4_LOOKUP,
74   NAT64_IN2OUT_NEXT_IP6_LOOKUP,
75   NAT64_IN2OUT_NEXT_DROP,
76   NAT64_IN2OUT_NEXT_SLOWPATH,
77   NAT64_IN2OUT_N_NEXT,
78 } nat64_in2out_next_t;
79
80 typedef struct nat64_in2out_set_ctx_t_
81 {
82   vlib_buffer_t *b;
83   vlib_main_t *vm;
84 } nat64_in2out_set_ctx_t;
85
86 /**
87  * @brief Check whether is a hairpinning.
88  *
89  * If the destination IP address of the packet is an IPv4 address assigned to
90  * the NAT64 itself, then the packet is a hairpin packet.
91  *
92  * param dst_addr Destination address of the packet.
93  *
94  * @returns 1 if hairpinning, otherwise 0.
95  */
96 static_always_inline int
97 is_hairpinning (ip6_address_t * dst_addr)
98 {
99   nat64_main_t *nm = &nat64_main;
100   int i;
101
102   for (i = 0; i < vec_len (nm->addr_pool); i++)
103     {
104       if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3])
105         return 1;
106     }
107
108   return 0;
109 }
110
111 static int
112 nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
113                              void *arg)
114 {
115   nat64_main_t *nm = &nat64_main;
116   nat64_in2out_set_ctx_t *ctx = arg;
117   nat64_db_bib_entry_t *bibe;
118   nat64_db_st_entry_t *ste;
119   ip46_address_t saddr, daddr;
120   u32 sw_if_index, fib_index;
121   udp_header_t *udp = ip6_next_header (ip6);
122   u8 proto = ip6->protocol;
123   u16 sport = udp->src_port;
124   u16 dport = udp->dst_port;
125
126   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
127   fib_index =
128     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
129
130   saddr.as_u64[0] = ip6->src_address.as_u64[0];
131   saddr.as_u64[1] = ip6->src_address.as_u64[1];
132   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
133   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
134
135   ste =
136     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
137                             fib_index, 1);
138
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, &saddr, sport, proto, fib_index, 1);
149
150       if (!bibe)
151         {
152           u16 out_port;
153           ip4_address_t out_addr;
154           if (nat64_alloc_out_addr_and_port
155               (fib_index, ip_proto_to_snat_proto (proto), &out_addr,
156                &out_port))
157             return -1;
158
159           bibe =
160             nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
161                                        sport, clib_host_to_net_u16 (out_port),
162                                        fib_index, proto, 0);
163           if (!bibe)
164             return -1;
165         }
166
167       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
168       ste =
169         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
170                                   &daddr.ip4, dport);
171       if (!ste)
172         return -1;
173     }
174
175   nat64_session_reset_timeout (ste, ctx->vm);
176
177   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
178   udp->src_port = bibe->out_port;
179
180   ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
181
182   if (proto == IP_PROTOCOL_TCP)
183     {
184       u16 *checksum;
185       ip_csum_t csum;
186       tcp_header_t *tcp = ip6_next_header (ip6);
187
188       checksum = &tcp->checksum;
189       csum = ip_csum_sub_even (*checksum, sport);
190       csum = ip_csum_add_even (csum, udp->src_port);
191       *checksum = ip_csum_fold (csum);
192     }
193
194   return 0;
195 }
196
197 static int
198 nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
199 {
200   nat64_main_t *nm = &nat64_main;
201   nat64_in2out_set_ctx_t *ctx = arg;
202   nat64_db_bib_entry_t *bibe;
203   nat64_db_st_entry_t *ste;
204   ip46_address_t saddr, daddr;
205   u32 sw_if_index, fib_index;
206   icmp46_header_t *icmp = ip6_next_header (ip6);
207
208   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
209   fib_index =
210     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
211
212   saddr.as_u64[0] = ip6->src_address.as_u64[0];
213   saddr.as_u64[1] = ip6->src_address.as_u64[1];
214   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
215   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
216
217   if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
218     {
219       u16 in_id = ((u16 *) (icmp))[2];
220       ste =
221         nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0,
222                                 IP_PROTOCOL_ICMP, fib_index, 1);
223
224       if (ste)
225         {
226           bibe =
227             nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP,
228                                          ste->bibe_index);
229           if (!bibe)
230             return -1;
231         }
232       else
233         {
234           bibe =
235             nat64_db_bib_entry_find (&nm->db, &saddr, in_id,
236                                      IP_PROTOCOL_ICMP, fib_index, 1);
237
238           if (!bibe)
239             {
240               u16 out_id;
241               ip4_address_t out_addr;
242               if (nat64_alloc_out_addr_and_port
243                   (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id))
244                 return -1;
245
246               bibe =
247                 nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
248                                            &out_addr, in_id,
249                                            clib_host_to_net_u16 (out_id),
250                                            fib_index, IP_PROTOCOL_ICMP, 0);
251               if (!bibe)
252                 return -1;
253             }
254
255           nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
256           ste =
257             nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
258                                       &daddr.ip4, 0);
259           if (!ste)
260             return -1;
261         }
262
263       nat64_session_reset_timeout (ste, ctx->vm);
264
265       ip4->src_address.as_u32 = bibe->out_addr.as_u32;
266       ((u16 *) (icmp))[2] = bibe->out_port;
267
268       ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
269     }
270   else
271     {
272       if (!vec_len (nm->addr_pool))
273         return -1;
274
275       ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
276       nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index);
277     }
278
279   return 0;
280 }
281
282 static int
283 nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
284                                 void *arg)
285 {
286   nat64_main_t *nm = &nat64_main;
287   nat64_in2out_set_ctx_t *ctx = arg;
288   nat64_db_st_entry_t *ste;
289   nat64_db_bib_entry_t *bibe;
290   ip46_address_t saddr, daddr;
291   u32 sw_if_index, fib_index;
292   u8 proto = ip6->protocol;
293
294   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
295   fib_index =
296     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
297
298   saddr.as_u64[0] = ip6->src_address.as_u64[0];
299   saddr.as_u64[1] = ip6->src_address.as_u64[1];
300   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
301   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
302
303   if (proto == IP_PROTOCOL_ICMP6)
304     {
305       icmp46_header_t *icmp = ip6_next_header (ip6);
306       u16 in_id = ((u16 *) (icmp))[2];
307       proto = IP_PROTOCOL_ICMP;
308
309       if (!
310           (icmp->type == ICMP4_echo_request
311            || icmp->type == ICMP4_echo_reply))
312         return -1;
313
314       ste =
315         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto,
316                                 fib_index, 1);
317       if (!ste)
318         return -1;
319
320       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
321       if (!bibe)
322         return -1;
323
324       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
325       ((u16 *) (icmp))[2] = bibe->out_port;
326       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
327     }
328   else
329     {
330       udp_header_t *udp = ip6_next_header (ip6);
331       tcp_header_t *tcp = ip6_next_header (ip6);
332       u16 *checksum;
333       ip_csum_t csum;
334
335       u16 sport = udp->src_port;
336       u16 dport = udp->dst_port;
337
338       ste =
339         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
340                                 fib_index, 1);
341       if (!ste)
342         return -1;
343
344       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
345       if (!bibe)
346         return -1;
347
348       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
349       udp->dst_port = bibe->out_port;
350       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
351
352       if (proto == IP_PROTOCOL_TCP)
353         checksum = &tcp->checksum;
354       else
355         checksum = &udp->checksum;
356       csum = ip_csum_sub_even (*checksum, dport);
357       csum = ip_csum_add_even (csum, udp->dst_port);
358       *checksum = ip_csum_fold (csum);
359     }
360
361   return 0;
362 }
363
364 typedef struct unk_proto_st_walk_ctx_t_
365 {
366   ip6_address_t src_addr;
367   ip6_address_t dst_addr;
368   ip4_address_t out_addr;
369   u32 fib_index;
370   u8 proto;
371 } unk_proto_st_walk_ctx_t;
372
373 static int
374 unk_proto_st_walk (nat64_db_st_entry_t * ste, void *arg)
375 {
376   nat64_main_t *nm = &nat64_main;
377   unk_proto_st_walk_ctx_t *ctx = arg;
378   nat64_db_bib_entry_t *bibe;
379   ip46_address_t saddr, daddr;
380
381   if (ip46_address_is_equal (&ste->in_r_addr, &ctx->dst_addr))
382     {
383       bibe =
384         nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index);
385       if (!bibe)
386         return -1;
387
388       if (ip46_address_is_equal (&bibe->in_addr, &ctx->src_addr)
389           && bibe->fib_index == ctx->fib_index)
390         {
391           memset (&saddr, 0, sizeof (saddr));
392           saddr.ip4.as_u32 = bibe->out_addr.as_u32;
393           memset (&daddr, 0, sizeof (daddr));
394           nat64_extract_ip4 (&ctx->dst_addr, &daddr.ip4, ctx->fib_index);
395
396           if (nat64_db_st_entry_find
397               (&nm->db, &daddr, &saddr, 0, 0, ctx->proto, ctx->fib_index, 0))
398             return -1;
399
400           ctx->out_addr.as_u32 = bibe->out_addr.as_u32;
401           return 1;
402         }
403     }
404
405   return 0;
406 }
407
408 static int
409 nat64_in2out_unk_proto_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
410                                void *arg)
411 {
412   nat64_main_t *nm = &nat64_main;
413   nat64_in2out_set_ctx_t *ctx = arg;
414   nat64_db_bib_entry_t *bibe;
415   nat64_db_st_entry_t *ste;
416   ip46_address_t saddr, daddr, addr;
417   u32 sw_if_index, fib_index;
418   u8 proto = ip6->protocol;
419   int i;
420
421   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
422   fib_index =
423     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
424
425   saddr.as_u64[0] = ip6->src_address.as_u64[0];
426   saddr.as_u64[1] = ip6->src_address.as_u64[1];
427   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
428   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
429
430   ste =
431     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index,
432                             1);
433
434   if (ste)
435     {
436       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
437       if (!bibe)
438         return -1;
439     }
440   else
441     {
442       bibe =
443         nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1);
444
445       if (!bibe)
446         {
447           /* Choose same out address as for TCP/UDP session to same dst */
448           unk_proto_st_walk_ctx_t ctx = {
449             .src_addr.as_u64[0] = ip6->src_address.as_u64[0],
450             .src_addr.as_u64[1] = ip6->src_address.as_u64[1],
451             .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0],
452             .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1],
453             .out_addr.as_u32 = 0,
454             .fib_index = fib_index,
455             .proto = proto,
456           };
457
458           nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk,
459                             &ctx);
460
461           if (!ctx.out_addr.as_u32)
462             nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk,
463                               &ctx);
464
465           /* Verify if out address is not already in use for protocol */
466           memset (&addr, 0, sizeof (addr));
467           addr.ip4.as_u32 = ctx.out_addr.as_u32;
468           if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0))
469             ctx.out_addr.as_u32 = 0;
470
471           if (!ctx.out_addr.as_u32)
472             {
473               for (i = 0; i < vec_len (nm->addr_pool); i++)
474                 {
475                   addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32;
476                   if (!nat64_db_bib_entry_find
477                       (&nm->db, &addr, 0, proto, 0, 0))
478                     break;
479                 }
480             }
481
482           if (!ctx.out_addr.as_u32)
483             return -1;
484
485           bibe =
486             nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
487                                        &ctx.out_addr, 0, 0, fib_index, proto,
488                                        0);
489           if (!bibe)
490             return -1;
491         }
492
493       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
494       ste =
495         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
496                                   &daddr.ip4, 0);
497       if (!ste)
498         return -1;
499     }
500
501   nat64_session_reset_timeout (ste, ctx->vm);
502
503   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
504   ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
505
506   return 0;
507 }
508
509
510
511 static int
512 nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
513                                   ip6_header_t * ip6)
514 {
515   nat64_main_t *nm = &nat64_main;
516   nat64_db_bib_entry_t *bibe;
517   nat64_db_st_entry_t *ste;
518   ip46_address_t saddr, daddr;
519   u32 sw_if_index, fib_index;
520   udp_header_t *udp = ip6_next_header (ip6);
521   tcp_header_t *tcp = ip6_next_header (ip6);
522   u8 proto = ip6->protocol;
523   u16 sport = udp->src_port;
524   u16 dport = udp->dst_port;
525   u16 *checksum;
526   ip_csum_t csum;
527
528   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
529   fib_index =
530     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
531
532   saddr.as_u64[0] = ip6->src_address.as_u64[0];
533   saddr.as_u64[1] = ip6->src_address.as_u64[1];
534   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
535   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
536
537   if (proto == IP_PROTOCOL_UDP)
538     checksum = &udp->checksum;
539   else
540     checksum = &tcp->checksum;
541
542   csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]);
543   csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]);
544   csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]);
545   csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]);
546   csum = ip_csum_sub_even (csum, sport);
547   csum = ip_csum_sub_even (csum, dport);
548
549   ste =
550     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
551                             fib_index, 1);
552
553   if (ste)
554     {
555       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
556       if (!bibe)
557         return -1;
558     }
559   else
560     {
561       bibe =
562         nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
563
564       if (!bibe)
565         {
566           u16 out_port;
567           ip4_address_t out_addr;
568           if (nat64_alloc_out_addr_and_port
569               (fib_index, ip_proto_to_snat_proto (proto), &out_addr,
570                &out_port))
571             return -1;
572
573           bibe =
574             nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
575                                        sport, clib_host_to_net_u16 (out_port),
576                                        fib_index, proto, 0);
577           if (!bibe)
578             return -1;
579         }
580
581       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
582       ste =
583         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
584                                   &daddr.ip4, dport);
585       if (!ste)
586         return -1;
587     }
588
589   nat64_session_reset_timeout (ste, vm);
590
591   sport = udp->src_port = bibe->out_port;
592   nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
593
594   memset (&saddr, 0, sizeof (saddr));
595   memset (&daddr, 0, sizeof (daddr));
596   saddr.ip4.as_u32 = bibe->out_addr.as_u32;
597   daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
598
599   ste =
600     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0,
601                             0);
602
603   if (ste)
604     {
605       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
606       if (!bibe)
607         return -1;
608     }
609   else
610     {
611       bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0);
612
613       if (!bibe)
614         return -1;
615
616       ste =
617         nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address,
618                                   &saddr.ip4, sport);
619     }
620
621   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
622   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
623   udp->dst_port = bibe->in_port;
624
625   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
626   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
627   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
628   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);
629   csum = ip_csum_add_even (csum, udp->src_port);
630   csum = ip_csum_add_even (csum, udp->dst_port);
631   *checksum = ip_csum_fold (csum);
632
633   return 0;
634 }
635
636 static int
637 nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
638                                ip6_header_t * ip6)
639 {
640   nat64_main_t *nm = &nat64_main;
641   nat64_db_bib_entry_t *bibe;
642   nat64_db_st_entry_t *ste;
643   icmp46_header_t *icmp = ip6_next_header (ip6);
644   ip6_header_t *inner_ip6;
645   ip46_address_t saddr, daddr;
646   u32 sw_if_index, fib_index;
647   u8 proto;
648   udp_header_t *udp;
649   tcp_header_t *tcp;
650   u16 *checksum, sport, dport;
651   ip_csum_t csum;
652
653   if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
654     return -1;
655
656   inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
657
658   proto = inner_ip6->protocol;
659
660   if (proto == IP_PROTOCOL_ICMP6)
661     return -1;
662
663   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
664   fib_index =
665     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
666
667   saddr.as_u64[0] = inner_ip6->src_address.as_u64[0];
668   saddr.as_u64[1] = inner_ip6->src_address.as_u64[1];
669   daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0];
670   daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1];
671
672   udp = ip6_next_header (inner_ip6);
673   tcp = ip6_next_header (inner_ip6);
674
675   sport = udp->src_port;
676   dport = udp->dst_port;
677
678   if (proto == IP_PROTOCOL_UDP)
679     checksum = &udp->checksum;
680   else
681     checksum = &tcp->checksum;
682
683   csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]);
684   csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]);
685   csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]);
686   csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]);
687   csum = ip_csum_sub_even (csum, sport);
688   csum = ip_csum_sub_even (csum, dport);
689
690   ste =
691     nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
692                             fib_index, 1);
693   if (!ste)
694     return -1;
695
696   bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
697   if (!bibe)
698     return -1;
699
700   dport = udp->dst_port = bibe->out_port;
701   nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index);
702
703   memset (&saddr, 0, sizeof (saddr));
704   memset (&daddr, 0, sizeof (daddr));
705   saddr.ip4.as_u32 = ste->out_r_addr.as_u32;
706   daddr.ip4.as_u32 = bibe->out_addr.as_u32;
707
708   ste =
709     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0,
710                             0);
711   if (!ste)
712     return -1;
713
714   bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
715   if (!bibe)
716     return -1;
717
718   inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
719   inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
720   udp->src_port = bibe->in_port;
721
722   csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
723   csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
724   csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
725   csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
726   csum = ip_csum_add_even (csum, udp->src_port);
727   csum = ip_csum_add_even (csum, udp->dst_port);
728   *checksum = ip_csum_fold (csum);
729
730   if (!vec_len (nm->addr_pool))
731     return -1;
732
733   nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index);
734   ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
735   ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
736
737   icmp->checksum = 0;
738   csum = ip_csum_with_carry (0, ip6->payload_length);
739   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
740   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
741   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
742   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
743   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
744   csum =
745     ip_incremental_checksum (csum, icmp,
746                              clib_net_to_host_u16 (ip6->payload_length));
747   icmp->checksum = ~ip_csum_fold (csum);
748
749   return 0;
750 }
751
752 static int
753 nat64_in2out_unk_proto_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
754                                     ip6_header_t * ip6)
755 {
756   nat64_main_t *nm = &nat64_main;
757   nat64_db_bib_entry_t *bibe;
758   nat64_db_st_entry_t *ste;
759   ip46_address_t saddr, daddr, addr;
760   u32 sw_if_index, fib_index;
761   u8 proto = ip6->protocol;
762   int i;
763
764   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
765   fib_index =
766     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
767
768   saddr.as_u64[0] = ip6->src_address.as_u64[0];
769   saddr.as_u64[1] = ip6->src_address.as_u64[1];
770   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
771   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
772
773   ste =
774     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index,
775                             1);
776
777   if (ste)
778     {
779       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
780       if (!bibe)
781         return -1;
782     }
783   else
784     {
785       bibe =
786         nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1);
787
788       if (!bibe)
789         {
790           /* Choose same out address as for TCP/UDP session to same dst */
791           unk_proto_st_walk_ctx_t ctx = {
792             .src_addr.as_u64[0] = ip6->src_address.as_u64[0],
793             .src_addr.as_u64[1] = ip6->src_address.as_u64[1],
794             .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0],
795             .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1],
796             .out_addr.as_u32 = 0,
797             .fib_index = fib_index,
798             .proto = proto,
799           };
800
801           nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk,
802                             &ctx);
803
804           if (!ctx.out_addr.as_u32)
805             nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk,
806                               &ctx);
807
808           /* Verify if out address is not already in use for protocol */
809           memset (&addr, 0, sizeof (addr));
810           addr.ip4.as_u32 = ctx.out_addr.as_u32;
811           if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0))
812             ctx.out_addr.as_u32 = 0;
813
814           if (!ctx.out_addr.as_u32)
815             {
816               for (i = 0; i < vec_len (nm->addr_pool); i++)
817                 {
818                   addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32;
819                   if (!nat64_db_bib_entry_find
820                       (&nm->db, &addr, 0, proto, 0, 0))
821                     break;
822                 }
823             }
824
825           if (!ctx.out_addr.as_u32)
826             return -1;
827
828           bibe =
829             nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
830                                        &ctx.out_addr, 0, 0, fib_index, proto,
831                                        0);
832           if (!bibe)
833             return -1;
834         }
835
836       nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
837       ste =
838         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
839                                   &daddr.ip4, 0);
840       if (!ste)
841         return -1;
842     }
843
844   nat64_session_reset_timeout (ste, vm);
845
846   nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
847
848   memset (&saddr, 0, sizeof (saddr));
849   memset (&daddr, 0, sizeof (daddr));
850   saddr.ip4.as_u32 = bibe->out_addr.as_u32;
851   daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
852
853   ste = nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, 0, 0);
854
855   if (ste)
856     {
857       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
858       if (!bibe)
859         return -1;
860     }
861   else
862     {
863       bibe = nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, 0, 0);
864
865       if (!bibe)
866         return -1;
867
868       ste =
869         nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address,
870                                   &saddr.ip4, 0);
871     }
872
873   ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
874   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
875
876   return 0;
877 }
878
879 static inline uword
880 nat64_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
881                              vlib_frame_t * frame, u8 is_slow_path)
882 {
883   u32 n_left_from, *from, *to_next;
884   nat64_in2out_next_t next_index;
885   u32 pkts_processed = 0;
886   u32 stats_node_index;
887
888   stats_node_index =
889     is_slow_path ? nat64_in2out_slowpath_node.index : nat64_in2out_node.index;
890
891   from = vlib_frame_vector_args (frame);
892   n_left_from = frame->n_vectors;
893   next_index = node->cached_next_index;
894
895   while (n_left_from > 0)
896     {
897       u32 n_left_to_next;
898
899       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
900
901       while (n_left_from > 0 && n_left_to_next > 0)
902         {
903           u32 bi0;
904           vlib_buffer_t *b0;
905           u32 next0;
906           ip6_header_t *ip60;
907           u16 l4_offset0, frag_offset0;
908           u8 l4_protocol0;
909           u32 proto0;
910           nat64_in2out_set_ctx_t ctx0;
911
912           /* speculatively enqueue b0 to the current next frame */
913           bi0 = from[0];
914           to_next[0] = bi0;
915           from += 1;
916           to_next += 1;
917           n_left_from -= 1;
918           n_left_to_next -= 1;
919
920           b0 = vlib_get_buffer (vm, bi0);
921           ip60 = vlib_buffer_get_current (b0);
922
923           ctx0.b = b0;
924           ctx0.vm = vm;
925
926           next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP;
927
928           if (PREDICT_FALSE
929               (ip6_parse
930                (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
931                 &frag_offset0)))
932             {
933               next0 = NAT64_IN2OUT_NEXT_DROP;
934               b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
935               goto trace0;
936             }
937
938           proto0 = ip_proto_to_snat_proto (l4_protocol0);
939           if (frag_offset0 != 0)
940             {
941               next0 = NAT64_IN2OUT_NEXT_DROP;
942               b0->error =
943                 node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
944               goto trace0;
945             }
946
947           if (is_slow_path)
948             {
949               if (PREDICT_TRUE (proto0 == ~0))
950                 {
951                   if (is_hairpinning (&ip60->dst_address))
952                     {
953                       next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
954                       if (nat64_in2out_unk_proto_hairpinning (vm, b0, ip60))
955                         {
956                           next0 = NAT64_IN2OUT_NEXT_DROP;
957                           b0->error =
958                             node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
959                         }
960                       goto trace0;
961                     }
962
963                   if (ip6_to_ip4 (b0, nat64_in2out_unk_proto_set_cb, &ctx0))
964                     {
965                       next0 = NAT64_IN2OUT_NEXT_DROP;
966                       b0->error =
967                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
968                       goto trace0;
969                     }
970                 }
971               goto trace0;
972             }
973           else
974             {
975               if (PREDICT_FALSE (proto0 == ~0))
976                 {
977                   next0 = NAT64_IN2OUT_NEXT_SLOWPATH;
978                   goto trace0;
979                 }
980             }
981
982           if (proto0 == SNAT_PROTOCOL_ICMP)
983             {
984               if (is_hairpinning (&ip60->dst_address))
985                 {
986                   next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
987                   if (nat64_in2out_icmp_hairpinning (vm, b0, ip60))
988                     {
989                       next0 = NAT64_IN2OUT_NEXT_DROP;
990                       b0->error =
991                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
992                     }
993                   goto trace0;
994                 }
995
996               if (icmp6_to_icmp
997                   (b0, nat64_in2out_icmp_set_cb, &ctx0,
998                    nat64_in2out_inner_icmp_set_cb, &ctx0))
999                 {
1000                   next0 = NAT64_IN2OUT_NEXT_DROP;
1001                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1002                   goto trace0;
1003                 }
1004             }
1005           else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP)
1006             {
1007               if (is_hairpinning (&ip60->dst_address))
1008                 {
1009                   next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
1010                   if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60))
1011                     {
1012                       next0 = NAT64_IN2OUT_NEXT_DROP;
1013                       b0->error =
1014                         node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1015                     }
1016                   goto trace0;
1017                 }
1018
1019               if (ip6_to_ip4_tcp_udp
1020                   (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0))
1021                 {
1022                   next0 = NAT64_IN2OUT_NEXT_DROP;
1023                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
1024                   goto trace0;
1025                 }
1026             }
1027
1028         trace0:
1029           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1030                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1031             {
1032               nat64_in2out_trace_t *t =
1033                 vlib_add_trace (vm, node, b0, sizeof (*t));
1034               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1035               t->next_index = next0;
1036               t->is_slow_path = is_slow_path;
1037             }
1038
1039           pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
1040
1041           /* verify speculative enqueue, maybe switch current next frame */
1042           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1043                                            n_left_to_next, bi0, next0);
1044         }
1045       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1046     }
1047   vlib_node_increment_counter (vm, stats_node_index,
1048                                NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
1049                                pkts_processed);
1050   return frame->n_vectors;
1051 }
1052
1053 static uword
1054 nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1055                       vlib_frame_t * frame)
1056 {
1057   return nat64_in2out_node_fn_inline (vm, node, frame, 0);
1058 }
1059
1060 /* *INDENT-OFF* */
1061 VLIB_REGISTER_NODE (nat64_in2out_node) = {
1062   .function = nat64_in2out_node_fn,
1063   .name = "nat64-in2out",
1064   .vector_size = sizeof (u32),
1065   .format_trace = format_nat64_in2out_trace,
1066   .type = VLIB_NODE_TYPE_INTERNAL,
1067   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
1068   .error_strings = nat64_in2out_error_strings,
1069   .n_next_nodes = NAT64_IN2OUT_N_NEXT,
1070   /* edit / add dispositions here */
1071   .next_nodes = {
1072     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
1073     [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
1074     [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
1075     [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath",
1076   },
1077 };
1078 /* *INDENT-ON* */
1079
1080 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
1081
1082 static uword
1083 nat64_in2out_slowpath_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1084                                vlib_frame_t * frame)
1085 {
1086   return nat64_in2out_node_fn_inline (vm, node, frame, 1);
1087 }
1088
1089 /* *INDENT-OFF* */
1090 VLIB_REGISTER_NODE (nat64_in2out_slowpath_node) = {
1091   .function = nat64_in2out_slowpath_node_fn,
1092   .name = "nat64-in2out-slowpath",
1093   .vector_size = sizeof (u32),
1094   .format_trace = format_nat64_in2out_trace,
1095   .type = VLIB_NODE_TYPE_INTERNAL,
1096   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
1097   .error_strings = nat64_in2out_error_strings,
1098   .n_next_nodes = NAT64_IN2OUT_N_NEXT,
1099   /* edit / add dispositions here */
1100   .next_nodes = {
1101     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
1102     [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
1103     [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
1104     [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath",
1105   },
1106 };
1107 /* *INDENT-ON* */
1108
1109 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_slowpath_node,
1110                               nat64_in2out_slowpath_node_fn);
1111
1112 /*
1113  * fd.io coding-style-patch-verification: ON
1114  *
1115  * Local Variables:
1116  * eval: (c-set-style "gnu")
1117  * End:
1118  */