Line data Source code
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_translation.h>
19 : #include <cnat/cnat_inline.h>
20 : #include <cnat/cnat_src_policy.h>
21 : #include <cnat/cnat_snat_policy.h>
22 :
23 : #include <vnet/dpo/load_balance.h>
24 : #include <vnet/dpo/load_balance_map.h>
25 :
26 : #include <vnet/ip/ip4_inlines.h>
27 : #include <vnet/ip/ip6_inlines.h>
28 :
29 : typedef enum cnat_feature_next_
30 : {
31 : CNAT_FEATURE_NEXT_DROP,
32 : CNAT_FEATURE_N_NEXT,
33 : } cnat_feature_next_t;
34 :
35 : vlib_node_registration_t cnat_input_feature_ip4_node;
36 : vlib_node_registration_t cnat_input_feature_ip6_node;
37 : vlib_node_registration_t cnat_output_feature_ip4_node;
38 : vlib_node_registration_t cnat_output_feature_ip6_node;
39 :
40 : always_inline uword
41 0 : cnat_input_feature_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
42 : vlib_buffer_t *b, cnat_node_ctx_t *ctx,
43 : int session_not_found, cnat_session_t *session)
44 : {
45 0 : vlib_combined_counter_main_t *cntm = &cnat_translation_counters;
46 0 : const cnat_translation_t *ct = NULL;
47 0 : ip4_header_t *ip4 = NULL;
48 : ip_protocol_t iproto;
49 0 : ip6_header_t *ip6 = NULL;
50 : udp_header_t *udp0;
51 : cnat_client_t *cc;
52 : u32 next0;
53 : index_t cti;
54 0 : u8 trace_flags = 0;
55 :
56 : /* By default follow arc default next */
57 0 : vnet_feature_next (&next0, b);
58 :
59 0 : if (AF_IP4 == ctx->af)
60 : {
61 0 : ip4 = vlib_buffer_get_current (b);
62 0 : iproto = ip4->protocol;
63 0 : udp0 = (udp_header_t *) (ip4 + 1);
64 0 : cc = cnat_client_ip4_find (
65 0 : &ip4->dst_address); /* TODO do this only if no session? */
66 : }
67 : else
68 : {
69 0 : ip6 = vlib_buffer_get_current (b);
70 0 : iproto = ip6->protocol;
71 0 : udp0 = (udp_header_t *) (ip6 + 1);
72 0 : cc = cnat_client_ip6_find (&ip6->dst_address); /* TODO: same as above */
73 : }
74 :
75 : /* Wrong session key */
76 0 : if (session->key.cs_proto == 0)
77 0 : goto trace;
78 :
79 0 : if (!session_not_found)
80 : /* session table hit */
81 0 : cnat_timestamp_update (session->value.cs_ts_index, ctx->now);
82 0 : else if (!cc)
83 0 : goto trace; /* dst address is not a vip */
84 : else
85 : {
86 0 : ct = cnat_find_translation (
87 0 : cc->parent_cci, clib_host_to_net_u16 (udp0->dst_port), iproto);
88 0 : if (NULL == ct)
89 : /* Dont translate */
90 : /* TODO: create identity session to avoid slowpath ? */
91 0 : goto trace;
92 :
93 : /* New flow, create the sessions */
94 : const load_balance_t *lb0;
95 : cnat_ep_trk_t *trk0;
96 0 : u32 rsession_flags = CNAT_SESSION_FLAG_NO_CLIENT;
97 0 : u32 dpoi_index = -1;
98 :
99 0 : lb0 = load_balance_get (ct->ct_lb.dpoi_index);
100 0 : if (!lb0->lb_n_buckets)
101 : /* Can't translate TODO: should drop / reject? */
102 0 : goto trace;
103 :
104 : /* session table miss */
105 0 : trk0 = cnat_load_balance (ct, ctx->af, ip4, ip6, &dpoi_index);
106 0 : if (PREDICT_FALSE (NULL == trk0))
107 : {
108 : /* Dont translate & Follow the fib programming */
109 0 : vnet_buffer (b)->ip.adj_index[VLIB_TX] = cc->cc_parent.dpoi_index;
110 0 : next0 = cc->cc_parent.dpoi_next_node;
111 0 : goto trace;
112 : }
113 :
114 0 : ip46_address_copy (&session->value.cs_ip[VLIB_TX],
115 0 : &trk0->ct_ep[VLIB_TX].ce_ip.ip);
116 :
117 : /* never source nat in this node */
118 0 : if (AF_IP4 == ctx->af)
119 0 : ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX],
120 0 : &ip4->src_address);
121 : else
122 0 : ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX],
123 0 : &ip6->src_address);
124 :
125 0 : session->value.cs_port[VLIB_TX] =
126 0 : clib_host_to_net_u16 (trk0->ct_ep[VLIB_TX].ce_port);
127 0 : session->value.cs_port[VLIB_RX] = udp0->src_port;
128 0 : session->value.flags = 0;
129 :
130 0 : if (trk0->ct_flags & CNAT_TRK_FLAG_NO_NAT)
131 : {
132 : const dpo_id_t *dpo0;
133 : const load_balance_t *lb1;
134 :
135 0 : lb1 = load_balance_get (trk0->ct_dpo.dpoi_index);
136 : /* Assume backend has exactly one item in LB */
137 0 : dpo0 = load_balance_get_bucket_i (lb1, 0);
138 :
139 0 : session->value.dpoi_next_node = dpo0->dpoi_next_node;
140 0 : session->value.cs_lbi = dpo0->dpoi_index;
141 0 : session->value.flags = CNAT_SESSION_FLAG_NO_NAT;
142 : }
143 :
144 : /* refcnt session in current client */
145 0 : cnat_client_cnt_session (cc);
146 0 : cnat_session_create (session, ctx, CNAT_LOCATION_OUTPUT, rsession_flags);
147 0 : trace_flags |= CNAT_TRACE_SESSION_CREATED;
148 : }
149 :
150 0 : if (session->value.flags & CNAT_SESSION_FLAG_NO_NAT)
151 : {
152 : /* If we don't translate, directly do the lookup & bypass arc */
153 0 : next0 = session->value.dpoi_next_node;
154 0 : vnet_buffer (b)->ip.adj_index[VLIB_TX] = session->value.cs_lbi;
155 0 : goto trace;
156 : }
157 :
158 0 : if (AF_IP4 == ctx->af)
159 0 : cnat_translation_ip4 (session, ip4, udp0);
160 : else
161 0 : cnat_translation_ip6 (session, ip6, udp0);
162 :
163 0 : if (NULL != ct)
164 : {
165 0 : cti = ct - cnat_translation_pool;
166 0 : vlib_increment_combined_counter (cntm, ctx->thread_index, cti, 1,
167 : vlib_buffer_length_in_chain (vm, b));
168 : }
169 :
170 0 : trace:
171 0 : if (PREDICT_FALSE (ctx->do_trace))
172 : {
173 0 : trace_flags |= session_not_found ? 0 : CNAT_TRACE_SESSION_FOUND;
174 0 : cnat_add_trace (vm, node, b, session, ct, trace_flags);
175 : }
176 0 : return next0;
177 : }
178 :
179 559 : VLIB_NODE_FN (cnat_input_feature_ip4_node)
180 : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
181 : {
182 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
183 0 : return cnat_node_inline (vm, node, frame, cnat_input_feature_fn, AF_IP4,
184 : CNAT_LOCATION_INPUT, 1 /* do_trace */);
185 0 : return cnat_node_inline (vm, node, frame, cnat_input_feature_fn, AF_IP4,
186 : CNAT_LOCATION_INPUT, 0 /* do_trace */);
187 : }
188 :
189 159080 : VLIB_REGISTER_NODE (cnat_input_feature_ip4_node) = {
190 : .name = "cnat-input-ip4",
191 : .vector_size = sizeof (u32),
192 : .format_trace = format_cnat_trace,
193 : .type = VLIB_NODE_TYPE_INTERNAL,
194 : .n_errors = CNAT_N_ERROR,
195 : .error_strings = cnat_error_strings,
196 : .sibling_of = "ip4-lookup",
197 : };
198 :
199 58823 : VNET_FEATURE_INIT (cnat_in_ip4_feature, static) = {
200 : .arc_name = "ip4-unicast",
201 : .node_name = "cnat-input-ip4",
202 : .runs_before = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
203 : };
204 :
205 559 : VLIB_NODE_FN (cnat_input_feature_ip6_node)
206 : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
207 : {
208 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
209 0 : return cnat_node_inline (vm, node, frame, cnat_input_feature_fn, AF_IP6,
210 : CNAT_LOCATION_INPUT, 1 /* do_trace */);
211 0 : return cnat_node_inline (vm, node, frame, cnat_input_feature_fn, AF_IP6,
212 : CNAT_LOCATION_INPUT, 0 /* do_trace */);
213 : }
214 :
215 159080 : VLIB_REGISTER_NODE (cnat_input_feature_ip6_node) = {
216 : .name = "cnat-input-ip6",
217 : .vector_size = sizeof (u32),
218 : .format_trace = format_cnat_trace,
219 : .type = VLIB_NODE_TYPE_INTERNAL,
220 : .n_errors = CNAT_N_ERROR,
221 : .error_strings = cnat_error_strings,
222 : .sibling_of = "ip6-lookup",
223 : };
224 :
225 58823 : VNET_FEATURE_INIT (cnat_in_ip6_feature, static) = {
226 : .arc_name = "ip6-unicast",
227 : .node_name = "cnat-input-ip6",
228 : .runs_before = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
229 : };
230 :
231 : /* output feature node, creates snat sessions when required and
232 : * translates back for existing sessions */
233 : always_inline uword
234 0 : cnat_output_feature_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
235 : vlib_buffer_t *b, cnat_node_ctx_t *ctx,
236 : int session_not_found, cnat_session_t *session)
237 : {
238 0 : cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
239 0 : ip4_header_t *ip4 = NULL;
240 : ip_protocol_t iproto;
241 0 : ip6_header_t *ip6 = NULL;
242 : udp_header_t *udp0;
243 0 : u32 iph_offset = 0;
244 : u32 next0;
245 : u16 sport;
246 0 : u8 do_snat = 0;
247 0 : u8 trace_flags = 0;
248 : int rv;
249 :
250 : /* By default follow arc default next */
251 0 : vnet_feature_next (&next0, b);
252 0 : iph_offset = vnet_buffer (b)->ip.save_rewrite_length;
253 :
254 0 : if (AF_IP4 == ctx->af)
255 : {
256 0 : ip4 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b) + iph_offset);
257 0 : iproto = ip4->protocol;
258 0 : udp0 = (udp_header_t *) (ip4 + 1);
259 : }
260 : else
261 : {
262 0 : ip6 = (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b) + iph_offset);
263 0 : iproto = ip6->protocol;
264 0 : udp0 = (udp_header_t *) (ip6 + 1);
265 : }
266 :
267 : /* Wrong session key */
268 0 : if (session->key.cs_proto == 0)
269 0 : goto trace;
270 :
271 0 : if (!session_not_found)
272 : {
273 : /* session table hit */
274 0 : cnat_timestamp_update (session->value.cs_ts_index, ctx->now);
275 : }
276 0 : else if (!cpm->snat_policy)
277 0 : goto trace;
278 : else
279 : {
280 0 : do_snat = cpm->snat_policy (b, session);
281 0 : if (do_snat != 1)
282 0 : goto trace;
283 :
284 0 : if (AF_IP4 == ctx->af)
285 : {
286 0 : if (ip_address_is_zero (&cpm->snat_ip4.ce_ip))
287 0 : goto trace;
288 :
289 0 : ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX],
290 0 : &ip_addr_v4 (&cpm->snat_ip4.ce_ip));
291 0 : ip46_address_set_ip4 (&session->value.cs_ip[VLIB_TX],
292 0 : &ip4->dst_address);
293 : }
294 : else
295 : {
296 0 : if (ip_address_is_zero (&cpm->snat_ip6.ce_ip))
297 0 : goto trace;
298 :
299 0 : ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX],
300 0 : &ip_addr_v6 (&cpm->snat_ip6.ce_ip));
301 0 : ip46_address_set_ip6 (&session->value.cs_ip[VLIB_TX],
302 0 : &ip6->dst_address);
303 : }
304 0 : sport = 0;
305 0 : rv = cnat_allocate_port (&sport, iproto);
306 0 : if (rv)
307 : {
308 0 : vlib_node_increment_counter (vm, cnat_output_feature_ip6_node.index,
309 : CNAT_ERROR_EXHAUSTED_PORTS, 1);
310 0 : next0 = CNAT_FEATURE_NEXT_DROP;
311 0 : goto trace;
312 : }
313 0 : session->value.cs_port[VLIB_RX] = sport;
314 0 : session->value.cs_port[VLIB_TX] = sport;
315 0 : if (iproto == IP_PROTOCOL_TCP || iproto == IP_PROTOCOL_UDP)
316 0 : session->value.cs_port[VLIB_TX] = udp0->dst_port;
317 :
318 0 : session->value.cs_lbi = INDEX_INVALID;
319 0 : session->value.flags =
320 : CNAT_SESSION_FLAG_NO_CLIENT | CNAT_SESSION_FLAG_ALLOC_PORT;
321 :
322 0 : trace_flags |= CNAT_TRACE_SESSION_CREATED;
323 0 : cnat_session_create (session, ctx, CNAT_LOCATION_INPUT,
324 : CNAT_SESSION_FLAG_NO_CLIENT |
325 : CNAT_SESSION_RETRY_SNAT);
326 : }
327 :
328 0 : if (AF_IP4 == ctx->af)
329 0 : cnat_translation_ip4 (session, ip4, udp0);
330 : else
331 0 : cnat_translation_ip6 (session, ip6, udp0);
332 :
333 0 : trace:
334 0 : if (PREDICT_FALSE (ctx->do_trace))
335 : {
336 0 : trace_flags |= do_snat ? 0 : CNAT_TRACE_NO_NAT;
337 0 : trace_flags |= session_not_found ? 0 : CNAT_TRACE_SESSION_FOUND;
338 0 : cnat_add_trace (vm, node, b, session, NULL, trace_flags);
339 : }
340 0 : return next0;
341 : }
342 :
343 559 : VLIB_NODE_FN (cnat_output_feature_ip4_node)
344 : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
345 : {
346 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
347 0 : return cnat_node_inline (vm, node, frame, cnat_output_feature_fn, AF_IP4,
348 : CNAT_LOCATION_OUTPUT, 1 /* do_trace */);
349 0 : return cnat_node_inline (vm, node, frame, cnat_output_feature_fn, AF_IP4,
350 : CNAT_LOCATION_OUTPUT, 0 /* do_trace */);
351 : }
352 :
353 159080 : VLIB_REGISTER_NODE (cnat_output_feature_ip4_node) = {
354 : .name = "cnat-output-ip4",
355 : .vector_size = sizeof (u32),
356 : .format_trace = format_cnat_trace,
357 : .type = VLIB_NODE_TYPE_INTERNAL,
358 : .n_errors = CNAT_N_ERROR,
359 : .error_strings = cnat_error_strings,
360 : .n_next_nodes = CNAT_FEATURE_N_NEXT,
361 : .next_nodes = {
362 : [CNAT_FEATURE_NEXT_DROP] = "error-drop",
363 : },
364 : };
365 :
366 58823 : VNET_FEATURE_INIT (cnat_out_ip4_feature, static) = {
367 : .arc_name = "ip4-output",
368 : .node_name = "cnat-output-ip4",
369 : .runs_before = VNET_FEATURES ("gso-ip4"),
370 : .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
371 : };
372 :
373 559 : VLIB_NODE_FN (cnat_output_feature_ip6_node)
374 : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
375 : {
376 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
377 0 : return cnat_node_inline (vm, node, frame, cnat_output_feature_fn, AF_IP6,
378 : CNAT_LOCATION_OUTPUT, 1 /* do_trace */);
379 0 : return cnat_node_inline (vm, node, frame, cnat_output_feature_fn, AF_IP6,
380 : CNAT_LOCATION_OUTPUT, 0 /* do_trace */);
381 : }
382 :
383 159080 : VLIB_REGISTER_NODE (cnat_output_feature_ip6_node) = {
384 : .name = "cnat-output-ip6",
385 : .vector_size = sizeof (u32),
386 : .format_trace = format_cnat_trace,
387 : .type = VLIB_NODE_TYPE_INTERNAL,
388 : .n_errors = CNAT_N_ERROR,
389 : .error_strings = cnat_error_strings,
390 : .n_next_nodes = CNAT_FEATURE_N_NEXT,
391 : .next_nodes = {
392 : [CNAT_FEATURE_NEXT_DROP] = "error-drop",
393 : },
394 : };
395 :
396 58823 : VNET_FEATURE_INIT (cnat_out_ip6_feature, static) = {
397 : .arc_name = "ip6-output",
398 : .node_name = "cnat-output-ip6",
399 : .runs_before = VNET_FEATURES ("gso-ip6"),
400 : .runs_after = VNET_FEATURES ("acl-plugin-out-ip6-fa"),
401 : };
402 :
403 : /*
404 : * fd.io coding-style-patch-verification: ON
405 : *
406 : * Local Variables:
407 : * eval: (c-set-style "gnu")
408 : * End:
409 : */
|