acl-plugin: remove clib_warnings on plugin init
[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       //TODO: ICMP error
225       clib_warning ("not ICMP echo request/reply, %u", icmp->type);
226       return -1;
227     }
228
229   return 0;
230 }
231
232 static int
233 nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
234                                 void *ctx)
235 {
236   //TODO:
237   return -1;
238 }
239
240 static uword
241 nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
242                       vlib_frame_t * frame)
243 {
244   u32 n_left_from, *from, *to_next;
245   nat64_in2out_next_t next_index;
246   u32 pkts_processed = 0;
247
248   from = vlib_frame_vector_args (frame);
249   n_left_from = frame->n_vectors;
250   next_index = node->cached_next_index;
251
252   while (n_left_from > 0)
253     {
254       u32 n_left_to_next;
255
256       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
257
258       while (n_left_from > 0 && n_left_to_next > 0)
259         {
260           u32 bi0;
261           vlib_buffer_t *b0;
262           u32 next0;
263           ip6_header_t *ip60;
264           u16 l4_offset0, frag_offset0;
265           u8 l4_protocol0;
266           u32 proto0;
267           nat64_in2out_set_ctx_t ctx0;
268
269           /* speculatively enqueue b0 to the current next frame */
270           bi0 = from[0];
271           to_next[0] = bi0;
272           from += 1;
273           to_next += 1;
274           n_left_from -= 1;
275           n_left_to_next -= 1;
276
277           b0 = vlib_get_buffer (vm, bi0);
278           ip60 = vlib_buffer_get_current (b0);
279
280           ctx0.b = b0;
281           ctx0.vm = vm;
282
283           next0 = NAT64_IN2OUT_NEXT_LOOKUP;
284
285           if (PREDICT_FALSE
286               (ip6_parse
287                (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
288                 &frag_offset0)))
289             {
290               next0 = NAT64_IN2OUT_NEXT_DROP;
291               b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
292               goto trace0;
293             }
294
295           proto0 = ip_proto_to_snat_proto (l4_protocol0);
296           if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0)))
297             {
298               next0 = NAT64_IN2OUT_NEXT_DROP;
299               b0->error =
300                 node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
301               goto trace0;
302             }
303
304           if (proto0 == SNAT_PROTOCOL_ICMP)
305             {
306               if (icmp6_to_icmp
307                   (b0, nat64_in2out_icmp_set_cb, &ctx0,
308                    nat64_in2out_inner_icmp_set_cb, &ctx0))
309                 {
310                   next0 = NAT64_IN2OUT_NEXT_DROP;
311                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
312                   goto trace0;
313                 }
314             }
315           else
316             {
317               if (ip6_to_ip4_tcp_udp
318                   (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0))
319                 {
320                   next0 = NAT64_IN2OUT_NEXT_DROP;
321                   b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
322                   goto trace0;
323                 }
324             }
325
326         trace0:
327           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
328                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
329             {
330               nat64_in2out_trace_t *t =
331                 vlib_add_trace (vm, node, b0, sizeof (*t));
332               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
333               t->next_index = next0;
334             }
335
336           pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
337
338           /* verify speculative enqueue, maybe switch current next frame */
339           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
340                                            n_left_to_next, bi0, next0);
341         }
342       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
343     }
344   vlib_node_increment_counter (vm, nat64_in2out_node.index,
345                                NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
346                                pkts_processed);
347   return frame->n_vectors;
348 }
349
350 /* *INDENT-OFF* */
351 VLIB_REGISTER_NODE (nat64_in2out_node) = {
352   .function = nat64_in2out_node_fn,.name = "nat64-in2out",
353   .vector_size = sizeof (u32),
354   .format_trace = format_nat64_in2out_trace,
355   .type = VLIB_NODE_TYPE_INTERNAL,
356   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
357   .error_strings = nat64_in2out_error_strings,
358   .n_next_nodes = 2,
359   /* edit / add dispositions here */
360   .next_nodes = {
361     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
362     [NAT64_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
363   },
364 };
365 /* *INDENT-ON* */
366
367 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
368
369 /*
370  * fd.io coding-style-patch-verification: ON
371  *
372  * Local Variables:
373  * eval: (c-set-style "gnu")
374  * End:
375  */