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