cnat: Destination based NAT
[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     {
86       /* Dont translate */
87       goto trace;
88     }
89
90   if (!rv)
91     {
92       /* session table hit */
93       cnat_timestamp_update (session->value.cs_ts_index, ctx->now);
94     }
95   else
96     {
97       ip46_address_t ip46_dst_address;
98       if (AF_IP4 == ctx->af)
99         ip46_address_set_ip4 (&ip46_dst_address, &ip4->dst_address);
100       else
101         ip46_address_set_ip6 (&ip46_dst_address, &ip6->dst_address);
102       rv = cnat_search_snat_prefix (&ip46_dst_address, ctx->af);
103       if (!rv)
104         {
105           /* Prefix table hit, we shouldn't source NAT */
106           goto trace;
107         }
108       /* New flow, create the sessions if necessary. session will be a snat
109          session, and rsession will be a dnat session
110          Note: packet going through this path are going to the outside,
111          so they will never hit the NAT again (they are not going towards
112          a VIP) */
113       if (AF_IP4 == ctx->af)
114         {
115           ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX],
116                                 &cm->snat_ip4);
117           ip46_address_set_ip4 (&session->value.cs_ip[VLIB_TX],
118                                 &ip4->dst_address);
119         }
120       else
121         {
122           ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX],
123                                 &cm->snat_ip6);
124           ip46_address_set_ip6 (&session->value.cs_ip[VLIB_TX],
125                                 &ip6->dst_address);
126         }
127
128       /* Port allocation, first try to use the original port, allocate one
129          if it is already used */
130       sport = udp0->src_port;
131       rv = cnat_allocate_port (cm, &sport);
132       if (rv)
133         {
134           vlib_node_increment_counter (vm, cnat_snat_ip4_node.index,
135                                        CNAT_ERROR_EXHAUSTED_PORTS, 1);
136           next0 = CNAT_SNAT_NEXT_DROP;
137           goto trace;
138         }
139
140       session->value.cs_port[VLIB_RX] = sport;
141       session->value.cs_port[VLIB_TX] = udp0->dst_port;
142       session->value.cs_lbi = INDEX_INVALID;
143       session->value.flags =
144         CNAT_SESSION_FLAG_NO_CLIENT | CNAT_SESSION_FLAG_ALLOC_PORT;
145
146       cnat_session_create (session, ctx, CNAT_SESSION_FLAG_HAS_SNAT);
147     }
148
149
150   if (AF_IP4 == ctx->af)
151     cnat_translation_ip4 (session, ip4, udp0);
152   else
153     cnat_translation_ip6 (session, ip6, udp0);
154
155 trace:
156   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
157     {
158       cnat_snat_trace_t *t;
159
160       t = vlib_add_trace (vm, node, b, sizeof (*t));
161
162       if (NULL != session)
163         clib_memcpy (&t->session, session, sizeof (t->session));
164     }
165   return next0;
166 }
167
168 VLIB_NODE_FN (cnat_snat_ip4_node) (vlib_main_t * vm,
169                                    vlib_node_runtime_t * node,
170                                    vlib_frame_t * frame)
171 {
172   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
173     return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP4,
174                              1 /* do_trace */ );
175   return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP4,
176                            0 /* do_trace */ );
177 }
178
179 VLIB_NODE_FN (cnat_snat_ip6_node) (vlib_main_t * vm,
180                                    vlib_node_runtime_t * node,
181                                    vlib_frame_t * frame)
182 {
183   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
184     return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP6,
185                              1 /* do_trace */ );
186   return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP6,
187                            0 /* do_trace */ );
188 }
189
190 /* *INDENT-OFF* */
191 VLIB_REGISTER_NODE (cnat_snat_ip4_node) =
192 {
193   .name = "ip4-cnat-snat",
194   .vector_size = sizeof (u32),
195   .format_trace = format_cnat_snat_trace,
196   .type = VLIB_NODE_TYPE_INTERNAL,
197   .n_errors = CNAT_N_ERROR,
198   .error_strings = cnat_error_strings,
199   .n_next_nodes = CNAT_SNAT_N_NEXT,
200   .next_nodes =
201   {
202     [CNAT_SNAT_NEXT_DROP] = "ip4-drop",
203   }
204 };
205
206 VLIB_REGISTER_NODE (cnat_snat_ip6_node) =
207 {
208   .name = "ip6-cnat-snat",
209   .vector_size = sizeof (u32),
210   .format_trace = format_cnat_snat_trace,
211   .type = VLIB_NODE_TYPE_INTERNAL,
212   .n_errors = CNAT_N_ERROR,
213   .error_strings = cnat_error_strings,
214   .n_next_nodes = CNAT_SNAT_N_NEXT,
215   .next_nodes =
216   {
217     [CNAT_SNAT_NEXT_DROP] = "ip6-drop",
218   }
219 };
220 /* *INDENT-ON* */
221
222
223 VNET_FEATURE_INIT (cnat_snat_ip4_node, static) =
224 {
225 .arc_name = "ip4-unicast",.node_name = "ip4-cnat-snat",};
226
227 VNET_FEATURE_INIT (cnat_snat_ip6_node, static) =
228 {
229 .arc_name = "ip6-unicast",.node_name = "ip6-cnat-snat",};
230
231 /*
232  * fd.io coding-style-patch-verification: ON
233  *
234  * Local Variables:
235  * eval: (c-set-style "gnu")
236  * End:
237  */