eba6932681f58ad909eb0e796f557c167742678a
[vpp.git] / src / plugins / snat / nat64_in2out.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief NAT64 IPv6 to IPv4 translation (inside to outside network)
18  */
19
20 #include <snat/nat64.h>
21 #include <vnet/ip/ip6_to_ip4.h>
22 #include <vnet/fib/fib_table.h>
23
24 typedef struct
25 {
26   u32 sw_if_index;
27   u32 next_index;
28 } nat64_in2out_trace_t;
29
30 static u8 *
31 format_nat64_in2out_trace (u8 * s, va_list * args)
32 {
33   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
34   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
35   nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
36
37   s =
38     format (s, "NAT64-in2out: sw_if_index %d, next index %d", t->sw_if_index,
39             t->next_index);
40
41   return s;
42 }
43
44 vlib_node_registration_t nat64_in2out_node;
45
46 #define foreach_nat64_in2out_error                 \
47 _(UNSUPPORTED_PROTOCOL, "unsupported protocol")    \
48 _(IN2OUT_PACKETS, "good in2out packets processed") \
49 _(NO_TRANSLATION, "no translation")                \
50 _(UNKNOWN, "unknown")
51
52 typedef enum
53 {
54 #define _(sym,str) NAT64_IN2OUT_ERROR_##sym,
55   foreach_nat64_in2out_error
56 #undef _
57     NAT64_IN2OUT_N_ERROR,
58 } nat64_in2out_error_t;
59
60 static char *nat64_in2out_error_strings[] = {
61 #define _(sym,string) string,
62   foreach_nat64_in2out_error
63 #undef _
64 };
65
66 typedef enum
67 {
68   NAT64_IN2OUT_NEXT_LOOKUP,
69   NAT64_IN2OUT_NEXT_DROP,
70   NAT64_IN2OUT_N_NEXT,
71 } nat64_in2out_next_t;
72
73 typedef struct nat64_in2out_set_ctx_t_
74 {
75   vlib_buffer_t *b;
76   vlib_main_t *vm;
77 } nat64_in2out_set_ctx_t;
78
79 static int
80 nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
81                              void *arg)
82 {
83   nat64_main_t *nm = &nat64_main;
84   nat64_in2out_set_ctx_t *ctx = arg;
85   nat64_db_bib_entry_t *bibe;
86   nat64_db_st_entry_t *ste;
87   ip46_address_t saddr, daddr;
88   u32 sw_if_index, fib_index;
89   udp_header_t *udp = ip6_next_header (ip6);
90   snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
91   u16 sport = udp->src_port;
92   u16 dport = udp->dst_port;
93
94   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
95   fib_index =
96     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
97
98   saddr.as_u64[0] = ip6->src_address.as_u64[0];
99   saddr.as_u64[1] = ip6->src_address.as_u64[1];
100   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
101   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
102
103   ste =
104     nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
105                             fib_index, 1);
106
107   if (ste)
108     {
109       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
110       if (!bibe)
111         return -1;
112     }
113   else
114     {
115       bibe =
116         nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
117
118       if (!bibe)
119         {
120           u16 out_port;
121           ip4_address_t out_addr;
122           if (nat64_alloc_out_addr_and_port
123               (fib_index, proto, &out_addr, &out_port))
124             return -1;
125
126           bibe =
127             nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
128                                        sport, clib_host_to_net_u16 (out_port),
129                                        fib_index, proto, 0);
130           if (!bibe)
131             return -1;
132         }
133
134       ste =
135         nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
136                                   &daddr.ip4, dport);
137       if (!ste)
138         return -1;
139     }
140
141   nat64_session_reset_timeout (ste, ctx->vm);
142
143   ip4->src_address.as_u32 = bibe->out_addr.as_u32;
144   udp->src_port = bibe->out_port;
145
146   ip4->dst_address.as_u32 = daddr.ip4.as_u32;
147
148   return 0;
149 }
150
151 static int
152 nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
153 {
154   nat64_main_t *nm = &nat64_main;
155   nat64_in2out_set_ctx_t *ctx = arg;
156   nat64_db_bib_entry_t *bibe;
157   nat64_db_st_entry_t *ste;
158   ip46_address_t saddr, daddr;
159   u32 sw_if_index, fib_index;
160   icmp46_header_t *icmp = ip6_next_header (ip6);
161
162   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
163   fib_index =
164     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
165
166   saddr.as_u64[0] = ip6->src_address.as_u64[0];
167   saddr.as_u64[1] = ip6->src_address.as_u64[1];
168   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
169   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
170
171   if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
172     {
173       u16 in_id = ((u16 *) (icmp))[2];
174       ste =
175         nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0,
176                                 SNAT_PROTOCOL_ICMP, fib_index, 1);
177
178       if (ste)
179         {
180           bibe =
181             nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
182                                          ste->bibe_index);
183           if (!bibe)
184             return -1;
185         }
186       else
187         {
188           bibe =
189             nat64_db_bib_entry_find (&nm->db, &saddr, in_id,
190                                      SNAT_PROTOCOL_ICMP, fib_index, 1);
191
192           if (!bibe)
193             {
194               u16 out_id;
195               ip4_address_t out_addr;
196               if (nat64_alloc_out_addr_and_port
197                   (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id))
198                 return -1;
199
200               bibe =
201                 nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
202                                            &out_addr, in_id,
203                                            clib_host_to_net_u16 (out_id),
204                                            fib_index, SNAT_PROTOCOL_ICMP, 0);
205               if (!bibe)
206                 return -1;
207             }
208           ste =
209             nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
210                                       &daddr.ip4, 0);
211           if (!ste)
212             return -1;
213         }
214
215       nat64_session_reset_timeout (ste, ctx->vm);
216
217       ip4->src_address.as_u32 = bibe->out_addr.as_u32;
218       ((u16 *) (icmp))[2] = bibe->out_port;
219
220       ip4->dst_address.as_u32 = daddr.ip4.as_u32;
221     }
222   else
223     {
224       if (!vec_len (nm->addr_pool))
225         return -1;
226
227       ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
228       ip4->dst_address.as_u32 = daddr.ip4.as_u32;
229     }
230
231   return 0;
232 }
233
234 static int
235 nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
236                                 void *arg)
237 {
238   nat64_main_t *nm = &nat64_main;
239   nat64_in2out_set_ctx_t *ctx = arg;
240   nat64_db_st_entry_t *ste;
241   nat64_db_bib_entry_t *bibe;
242   ip46_address_t saddr, daddr;
243   u32 sw_if_index, fib_index;
244   snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
245
246   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
247   fib_index =
248     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
249
250   saddr.as_u64[0] = ip6->src_address.as_u64[0];
251   saddr.as_u64[1] = ip6->src_address.as_u64[1];
252   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
253   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
254
255   if (proto == SNAT_PROTOCOL_ICMP)
256     {
257       icmp46_header_t *icmp = ip6_next_header (ip6);
258       u16 in_id = ((u16 *) (icmp))[2];
259
260       if (!
261           (icmp->type == ICMP4_echo_request
262            || icmp->type == ICMP4_echo_reply))
263         return -1;
264
265       ste =
266         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto,
267                                 fib_index, 1);
268       if (!ste)
269         return -1;
270
271       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
272       if (!bibe)
273         return -1;
274
275       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
276       ((u16 *) (icmp))[2] = bibe->out_port;
277       ip4->src_address.as_u32 = saddr.ip4.as_u32;
278     }
279   else
280     {
281       udp_header_t *udp = ip6_next_header (ip6);
282       u16 sport = udp->src_port;
283       u16 dport = udp->dst_port;
284
285       ste =
286         nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
287                                 fib_index, 1);
288       if (!ste)
289         return -1;
290
291       bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
292       if (!bibe)
293         return -1;
294
295       ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
296       udp->dst_port = bibe->out_port;
297       ip4->src_address.as_u32 = saddr.ip4.as_u32;
298     }
299
300   return 0;
301 }
302
303 static uword
304 nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
305                       vlib_frame_t * frame)
306 {
307   u32 n_left_from, *from, *to_next;
308   nat64_in2out_next_t next_index;
309   u32 pkts_processed = 0;
310
311   from = vlib_frame_vector_args (frame);
312   n_left_from = frame->n_vectors;
313   next_index = node->cached_next_index;
314
315   while (n_left_from > 0)
316     {
317       u32 n_left_to_next;
318
319       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
320
321       while (n_left_from > 0 && n_left_to_next > 0)
322         {
323           u32 bi0;
324           vlib_buffer_t *b0;
325           u32 next0;
326           ip6_header_t *ip60;
327           u16 l4_offset0, frag_offset0;
328           u8 l4_protocol0;
329           u32 proto0;
330           nat64_in2out_set_ctx_t ctx0;
331
332           /* speculatively enqueue b0 to the current next frame */
333           bi0 = from[0];
334           to_next[0] = bi0;
335           from += 1;
336           to_next += 1;
337           n_left_from -= 1;
338           n_left_to_next -= 1;
339
340           b0 = vlib_get_buffer (vm, bi0);
341           ip60 = vlib_buffer_get_current (b0);
342
343           ctx0.b = b0;
344           ctx0.vm = vm;
345
346           next0 = NAT64_IN2OUT_NEXT_LOOKUP;
347
348           if (PREDICT_FALSE
349               (ip6_parse
350                (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
351                 &frag_offset0)))
352             {
353               next0 = NAT64_IN2OUT_NEXT_DROP;
354               b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
355               goto trace0;
356             }
357
358           proto0 = ip_proto_to_snat_proto (l4_protocol0);
359           if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0)))
360             {
361               next0 = NAT64_IN2OUT_NEXT_DROP;
362               b0->error =
363                 node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
364               goto trace0;
365             }
366
367           if (proto0 == SNAT_PROTOCOL_ICMP)
368             {
369               if (icmp6_to_icmp
370                   (b0, nat64_in2out_icmp_set_cb, &ctx0,
371                    nat64_in2out_inner_icmp_set_cb, &ctx0))
372                 {
373                   next0 = NAT64_IN2OUT_NEXT_DROP;
374                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
375                   goto trace0;
376                 }
377             }
378           else
379             {
380               if (ip6_to_ip4_tcp_udp
381                   (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0))
382                 {
383                   next0 = NAT64_IN2OUT_NEXT_DROP;
384                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
385                   goto trace0;
386                 }
387             }
388
389         trace0:
390           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
391                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
392             {
393               nat64_in2out_trace_t *t =
394                 vlib_add_trace (vm, node, b0, sizeof (*t));
395               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
396               t->next_index = next0;
397             }
398
399           pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
400
401           /* verify speculative enqueue, maybe switch current next frame */
402           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
403                                            n_left_to_next, bi0, next0);
404         }
405       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
406     }
407   vlib_node_increment_counter (vm, nat64_in2out_node.index,
408                                NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
409                                pkts_processed);
410   return frame->n_vectors;
411 }
412
413 /* *INDENT-OFF* */
414 VLIB_REGISTER_NODE (nat64_in2out_node) = {
415   .function = nat64_in2out_node_fn,.name = "nat64-in2out",
416   .vector_size = sizeof (u32),
417   .format_trace = format_nat64_in2out_trace,
418   .type = VLIB_NODE_TYPE_INTERNAL,
419   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
420   .error_strings = nat64_in2out_error_strings,
421   .n_next_nodes = 2,
422   /* edit / add dispositions here */
423   .next_nodes = {
424     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
425     [NAT64_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
426   },
427 };
428 /* *INDENT-ON* */
429
430 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
431
432 /*
433  * fd.io coding-style-patch-verification: ON
434  *
435  * Local Variables:
436  * eval: (c-set-style "gnu")
437  * End:
438  */