cnat: Add support for SNat ICMP
[vpp.git] / src / plugins / cnat / cnat_node_snat.c
1 /*
2  * Copyright (c) 2020 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 #include <vlibmemory/api.h>
17 #include <cnat/cnat_node.h>
18 #include <cnat/cnat_snat.h>
19
20 typedef enum cnat_snat_next_
21 {
22   CNAT_SNAT_NEXT_DROP,
23   CNAT_SNAT_N_NEXT,
24 } cnat_snat_next_t;
25
26 typedef struct cnat_snat_trace_
27 {
28   cnat_session_t session;
29   u32 found_session;
30   u32 created_session;
31 } cnat_snat_trace_t;
32
33 vlib_node_registration_t cnat_snat_ip4_node;
34 vlib_node_registration_t cnat_snat_ip6_node;
35
36 static u8 *
37 format_cnat_snat_trace (u8 * s, va_list * args)
38 {
39   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
40   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
41   cnat_snat_trace_t *t = va_arg (*args, cnat_snat_trace_t *);
42
43   if (t->found_session)
44     s = format (s, "found: %U", format_cnat_session, &t->session, 1);
45   else if (t->created_session)
46     s = format (s, "created: %U\n  tr: %U",
47                 format_cnat_session, &t->session, 1);
48   else
49     s = format (s, "not found");
50   return s;
51 }
52
53 /* CNat sub for source NAT as a feature arc on ip[46]-unicast
54    This node's sub shouldn't apply to the same flows as
55    cnat_vip_inline */
56 always_inline uword
57 cnat_snat_inline (vlib_main_t * vm,
58                   vlib_node_runtime_t * node,
59                   vlib_buffer_t * b,
60                   cnat_node_ctx_t * ctx, int rv, cnat_session_t * session)
61 {
62   cnat_main_t *cm = &cnat_main;
63   int created_session = 0;
64   ip4_header_t *ip4;
65   ip_protocol_t iproto;
66   ip6_header_t *ip6;
67   udp_header_t *udp0;
68   u32 arc_next0;
69   u16 next0;
70   u16 sport;
71
72   if (AF_IP4 == ctx->af)
73     {
74       ip4 = vlib_buffer_get_current (b);
75       iproto = ip4->protocol;
76       udp0 = (udp_header_t *) (ip4 + 1);
77     }
78   else
79     {
80       ip6 = vlib_buffer_get_current (b);
81       iproto = ip6->protocol;
82       udp0 = (udp_header_t *) (ip6 + 1);
83     }
84
85   /* By default don't follow previous next0 */
86   vnet_feature_next (&arc_next0, b);
87   next0 = arc_next0;
88
89   if (iproto != IP_PROTOCOL_UDP && iproto != IP_PROTOCOL_TCP
90       && iproto != IP_PROTOCOL_ICMP && iproto != IP_PROTOCOL_ICMP6)
91     {
92       /* Dont translate */
93       goto trace;
94     }
95
96   if (!rv)
97     {
98       /* session table hit */
99       cnat_timestamp_update (session->value.cs_ts_index, ctx->now);
100     }
101   else
102     {
103       ip46_address_t ip46_dst_address;
104       if (AF_IP4 == ctx->af)
105         ip46_address_set_ip4 (&ip46_dst_address, &ip4->dst_address);
106       else
107         ip46_address_set_ip6 (&ip46_dst_address, &ip6->dst_address);
108       rv = cnat_search_snat_prefix (&ip46_dst_address, ctx->af);
109       if (!rv)
110         {
111           /* Prefix table hit, we shouldn't source NAT */
112           goto trace;
113         }
114       /* New flow, create the sessions if necessary. session will be a snat
115          session, and rsession will be a dnat session
116          Note: packet going through this path are going to the outside,
117          so they will never hit the NAT again (they are not going towards
118          a VIP) */
119       if (AF_IP4 == ctx->af)
120         {
121           ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX],
122                                 &cm->snat_ip4);
123           ip46_address_set_ip4 (&session->value.cs_ip[VLIB_TX],
124                                 &ip4->dst_address);
125         }
126       else
127         {
128           ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX],
129                                 &cm->snat_ip6);
130           ip46_address_set_ip6 (&session->value.cs_ip[VLIB_TX],
131                                 &ip6->dst_address);
132         }
133
134
135       sport = 0;
136       rv = cnat_allocate_port (&sport, iproto);
137       if (rv)
138         {
139           vlib_node_increment_counter (vm, cnat_snat_ip4_node.index,
140                                        CNAT_ERROR_EXHAUSTED_PORTS, 1);
141           next0 = CNAT_SNAT_NEXT_DROP;
142           goto trace;
143         }
144       session->value.cs_port[VLIB_RX] = sport;
145       session->value.cs_port[VLIB_TX] = sport;
146       if (iproto == IP_PROTOCOL_TCP || iproto == IP_PROTOCOL_UDP)
147         session->value.cs_port[VLIB_TX] = udp0->dst_port;
148
149       session->value.cs_lbi = INDEX_INVALID;
150       session->value.flags =
151         CNAT_SESSION_FLAG_NO_CLIENT | CNAT_SESSION_FLAG_ALLOC_PORT;
152
153       created_session = 1;
154       cnat_session_create (session, ctx, CNAT_SESSION_FLAG_HAS_SNAT);
155     }
156
157
158   if (AF_IP4 == ctx->af)
159     cnat_translation_ip4 (session, ip4, udp0);
160   else
161     cnat_translation_ip6 (session, ip6, udp0);
162
163 trace:
164   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
165     {
166       cnat_snat_trace_t *t;
167
168       t = vlib_add_trace (vm, node, b, sizeof (*t));
169
170       t->found_session = !rv;
171       t->created_session = created_session;
172       if (t->found_session || t->created_session)
173         clib_memcpy (&t->session, session, sizeof (t->session));
174     }
175   return next0;
176 }
177
178 VLIB_NODE_FN (cnat_snat_ip4_node) (vlib_main_t * vm,
179                                    vlib_node_runtime_t * node,
180                                    vlib_frame_t * frame)
181 {
182   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
183     return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP4,
184                              1 /* do_trace */ );
185   return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP4,
186                            0 /* do_trace */ );
187 }
188
189 VLIB_NODE_FN (cnat_snat_ip6_node) (vlib_main_t * vm,
190                                    vlib_node_runtime_t * node,
191                                    vlib_frame_t * frame)
192 {
193   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
194     return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP6,
195                              1 /* do_trace */ );
196   return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP6,
197                            0 /* do_trace */ );
198 }
199
200 /* *INDENT-OFF* */
201 VLIB_REGISTER_NODE (cnat_snat_ip4_node) =
202 {
203   .name = "ip4-cnat-snat",
204   .vector_size = sizeof (u32),
205   .format_trace = format_cnat_snat_trace,
206   .type = VLIB_NODE_TYPE_INTERNAL,
207   .n_errors = CNAT_N_ERROR,
208   .error_strings = cnat_error_strings,
209   .n_next_nodes = CNAT_SNAT_N_NEXT,
210   .next_nodes =
211   {
212     [CNAT_SNAT_NEXT_DROP] = "ip4-drop",
213   }
214 };
215
216 VLIB_REGISTER_NODE (cnat_snat_ip6_node) =
217 {
218   .name = "ip6-cnat-snat",
219   .vector_size = sizeof (u32),
220   .format_trace = format_cnat_snat_trace,
221   .type = VLIB_NODE_TYPE_INTERNAL,
222   .n_errors = CNAT_N_ERROR,
223   .error_strings = cnat_error_strings,
224   .n_next_nodes = CNAT_SNAT_N_NEXT,
225   .next_nodes =
226   {
227     [CNAT_SNAT_NEXT_DROP] = "ip6-drop",
228   }
229 };
230 /* *INDENT-ON* */
231
232
233 VNET_FEATURE_INIT (cnat_snat_ip4_node, static) =
234 {
235 .arc_name = "ip4-unicast",.node_name = "ip4-cnat-snat",};
236
237 VNET_FEATURE_INIT (cnat_snat_ip6_node, static) =
238 {
239 .arc_name = "ip6-unicast",.node_name = "ip6-cnat-snat",};
240
241 /*
242  * fd.io coding-style-patch-verification: ON
243  *
244  * Local Variables:
245  * eval: (c-set-style "gnu")
246  * End:
247  */