Line data Source code
1 : /*
2 : * Copyright (c) 2018 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 Classify for one armed NAT44 (in+out interface)
18 : */
19 :
20 : #include <vlib/vlib.h>
21 : #include <vnet/vnet.h>
22 : #include <vnet/fib/ip4_fib.h>
23 :
24 : #include <nat/nat44-ed/nat44_ed.h>
25 : #include <nat/nat44-ed/nat44_ed_inlines.h>
26 :
27 : #define foreach_nat44_classify_error \
28 : _(NEXT_IN2OUT, "next in2out") \
29 : _(NEXT_OUT2IN, "next out2in") \
30 : _(FRAG_CACHED, "fragment cached")
31 :
32 : typedef enum
33 : {
34 : #define _(sym,str) NAT44_CLASSIFY_ERROR_##sym,
35 : foreach_nat44_classify_error
36 : #undef _
37 : NAT44_CLASSIFY_N_ERROR,
38 : } nat44_classify_error_t;
39 :
40 : typedef enum
41 : {
42 : NAT44_CLASSIFY_NEXT_IN2OUT,
43 : NAT44_CLASSIFY_NEXT_OUT2IN,
44 : NAT44_CLASSIFY_NEXT_DROP,
45 : NAT44_CLASSIFY_N_NEXT,
46 : } nat44_classify_next_t;
47 :
48 : typedef struct
49 : {
50 : u8 next_in2out;
51 : u8 cached;
52 : } nat44_classify_trace_t;
53 :
54 : static u8 *
55 29 : format_nat44_classify_trace (u8 * s, va_list * args)
56 : {
57 29 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
58 29 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
59 29 : nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *);
60 : char *next;
61 :
62 29 : if (t->cached)
63 0 : s = format (s, "nat44-classify: fragment cached");
64 : else
65 : {
66 29 : next = t->next_in2out ? "nat44-ed-in2out" : "nat44-ed-out2in";
67 29 : s = format (s, "nat44-classify: next %s", next);
68 : }
69 :
70 29 : return s;
71 : }
72 :
73 : static inline uword
74 49 : nat44_handoff_classify_node_fn_inline (vlib_main_t * vm,
75 : vlib_node_runtime_t * node,
76 : vlib_frame_t * frame)
77 : {
78 : u32 n_left_from, *from, *to_next;
79 : nat44_classify_next_t next_index;
80 49 : snat_main_t *sm = &snat_main;
81 : snat_static_mapping_t *m;
82 49 : u32 next_in2out = 0, next_out2in = 0;
83 :
84 49 : from = vlib_frame_vector_args (frame);
85 49 : n_left_from = frame->n_vectors;
86 49 : next_index = node->cached_next_index;
87 :
88 98 : while (n_left_from > 0)
89 : {
90 : u32 n_left_to_next;
91 :
92 49 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
93 :
94 152 : while (n_left_from > 0 && n_left_to_next > 0)
95 : {
96 : u32 bi0;
97 : vlib_buffer_t *b0;
98 103 : u32 next0 = NAT_NEXT_IN2OUT_CLASSIFY;
99 : ip4_header_t *ip0;
100 : snat_address_t *ap;
101 :
102 : /* speculatively enqueue b0 to the current next frame */
103 103 : bi0 = from[0];
104 103 : to_next[0] = bi0;
105 103 : from += 1;
106 103 : to_next += 1;
107 103 : n_left_from -= 1;
108 103 : n_left_to_next -= 1;
109 :
110 103 : b0 = vlib_get_buffer (vm, bi0);
111 103 : ip0 = vlib_buffer_get_current (b0);
112 :
113 156 : vec_foreach (ap, sm->addresses)
114 : {
115 92 : if (ip0->dst_address.as_u32 == ap->addr.as_u32)
116 : {
117 39 : next0 = NAT_NEXT_OUT2IN_CLASSIFY;
118 39 : goto enqueue0;
119 : }
120 : }
121 :
122 64 : if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
123 : {
124 : /* try to classify the fragment based on IP header alone */
125 64 : m = nat44_ed_sm_o2i_lookup (sm, ip0->dst_address, 0, 0, 0);
126 64 : if (m)
127 : {
128 9 : if (m->local_addr.as_u32 != m->external_addr.as_u32)
129 0 : next0 = NAT_NEXT_OUT2IN_CLASSIFY;
130 9 : goto enqueue0;
131 : }
132 55 : m = nat44_ed_sm_o2i_lookup (
133 55 : sm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
134 55 : 0, ip0->protocol);
135 55 : if (m)
136 : {
137 3 : if (m->local_addr.as_u32 != m->external_addr.as_u32)
138 3 : next0 = NAT_NEXT_OUT2IN_CLASSIFY;
139 : }
140 : }
141 :
142 52 : enqueue0:
143 103 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
144 : && (b0->flags & VLIB_BUFFER_IS_TRACED)))
145 : {
146 : nat44_classify_trace_t *t =
147 103 : vlib_add_trace (vm, node, b0, sizeof (*t));
148 103 : t->cached = 0;
149 103 : t->next_in2out = next0 == NAT_NEXT_IN2OUT_CLASSIFY ? 1 : 0;
150 : }
151 :
152 103 : next_in2out += next0 == NAT_NEXT_IN2OUT_CLASSIFY;
153 103 : next_out2in += next0 == NAT_NEXT_OUT2IN_CLASSIFY;
154 :
155 : /* verify speculative enqueue, maybe switch current next frame */
156 103 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
157 : to_next, n_left_to_next,
158 : bi0, next0);
159 : }
160 :
161 49 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
162 : }
163 :
164 49 : vlib_node_increment_counter (vm, node->node_index,
165 : NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out);
166 49 : vlib_node_increment_counter (vm, node->node_index,
167 : NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in);
168 49 : return frame->n_vectors;
169 : }
170 :
171 : static inline uword
172 21 : nat44_ed_classify_node_fn_inline (vlib_main_t * vm,
173 : vlib_node_runtime_t * node,
174 : vlib_frame_t * frame)
175 : {
176 : u32 n_left_from, *from, *to_next;
177 : nat44_classify_next_t next_index;
178 21 : snat_main_t *sm = &snat_main;
179 : snat_static_mapping_t *m;
180 21 : u32 next_in2out = 0, next_out2in = 0;
181 :
182 21 : from = vlib_frame_vector_args (frame);
183 21 : n_left_from = frame->n_vectors;
184 21 : next_index = node->cached_next_index;
185 :
186 42 : while (n_left_from > 0)
187 : {
188 : u32 n_left_to_next;
189 :
190 21 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
191 :
192 42 : while (n_left_from > 0 && n_left_to_next > 0)
193 : {
194 : u32 bi0;
195 : vlib_buffer_t *b0;
196 21 : u32 next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
197 : u32 sw_if_index0, rx_fib_index0;
198 : ip4_header_t *ip0;
199 : snat_address_t *ap;
200 : clib_bihash_kv_16_8_t ed_kv0, ed_value0;
201 :
202 : /* speculatively enqueue b0 to the current next frame */
203 21 : bi0 = from[0];
204 21 : to_next[0] = bi0;
205 21 : from += 1;
206 21 : to_next += 1;
207 21 : n_left_from -= 1;
208 21 : n_left_to_next -= 1;
209 :
210 21 : b0 = vlib_get_buffer (vm, bi0);
211 21 : ip0 = vlib_buffer_get_current (b0);
212 :
213 : u32 arc_next;
214 21 : vnet_feature_next (&arc_next, b0);
215 21 : vnet_buffer2 (b0)->nat.arc_next = arc_next;
216 :
217 21 : if (ip0->protocol != IP_PROTOCOL_ICMP)
218 : {
219 : /* process leading fragment/whole packet (with L4 header) */
220 21 : sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
221 : rx_fib_index0 =
222 21 : fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
223 : sw_if_index0);
224 21 : init_ed_k (&ed_kv0, ip0->src_address.as_u32,
225 21 : vnet_buffer (b0)->ip.reass.l4_src_port,
226 : ip0->dst_address.as_u32,
227 21 : vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0,
228 21 : ip0->protocol);
229 : /* process whole packet */
230 21 : if (!clib_bihash_search_16_8 (&sm->flow_hash, &ed_kv0,
231 : &ed_value0))
232 : {
233 11 : ASSERT (vm->thread_index ==
234 : ed_value_get_thread_index (&ed_value0));
235 11 : snat_main_per_thread_data_t *tsm =
236 11 : &sm->per_thread_data[vm->thread_index];
237 11 : snat_session_t *s = pool_elt_at_index (
238 : tsm->sessions, ed_value_get_session_index (&ed_value0));
239 : clib_bihash_kv_16_8_t i2o_kv;
240 11 : nat_6t_flow_to_ed_k (&i2o_kv, &s->i2o);
241 22 : vnet_buffer2 (b0)->nat.cached_session_index =
242 11 : ed_value_get_session_index (&ed_value0);
243 11 : if (i2o_kv.key[0] == ed_kv0.key[0] &&
244 11 : i2o_kv.key[1] == ed_kv0.key[1])
245 : {
246 11 : next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
247 : }
248 : else
249 : {
250 0 : next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
251 : }
252 :
253 11 : goto enqueue0;
254 : }
255 : /* session doesn't exist so continue in code */
256 : }
257 :
258 13 : vec_foreach (ap, sm->addresses)
259 : {
260 5 : if (ip0->dst_address.as_u32 == ap->addr.as_u32)
261 : {
262 2 : next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
263 2 : goto enqueue0;
264 : }
265 : }
266 :
267 8 : if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
268 : {
269 : /* try to classify the fragment based on IP header alone */
270 7 : m = nat44_ed_sm_o2i_lookup (sm, ip0->dst_address, 0, 0, 0);
271 7 : if (m)
272 : {
273 0 : if (m->local_addr.as_u32 != m->external_addr.as_u32)
274 0 : next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
275 0 : goto enqueue0;
276 : }
277 7 : m = nat44_ed_sm_o2i_lookup (
278 7 : sm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
279 7 : 0, ip0->protocol);
280 7 : if (m)
281 : {
282 1 : if (m->local_addr.as_u32 != m->external_addr.as_u32)
283 1 : next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
284 : }
285 : }
286 :
287 7 : enqueue0:
288 21 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
289 : && (b0->flags & VLIB_BUFFER_IS_TRACED)))
290 : {
291 : nat44_classify_trace_t *t =
292 21 : vlib_add_trace (vm, node, b0, sizeof (*t));
293 21 : t->cached = 0;
294 21 : t->next_in2out = next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH ? 1 : 0;
295 : }
296 :
297 21 : next_in2out += next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH;
298 21 : next_out2in += next0 == NAT_NEXT_OUT2IN_ED_FAST_PATH;
299 :
300 : /* verify speculative enqueue, maybe switch current next frame */
301 21 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
302 : to_next, n_left_to_next,
303 : bi0, next0);
304 : }
305 :
306 21 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
307 : }
308 :
309 21 : vlib_node_increment_counter (vm, node->node_index,
310 : NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out);
311 21 : vlib_node_increment_counter (vm, node->node_index,
312 : NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in);
313 21 : return frame->n_vectors;
314 : }
315 :
316 2257 : VLIB_NODE_FN (nat44_ed_classify_node) (vlib_main_t * vm,
317 : vlib_node_runtime_t * node,
318 : vlib_frame_t * frame)
319 : {
320 21 : return nat44_ed_classify_node_fn_inline (vm, node, frame);
321 : }
322 :
323 49304 : VLIB_REGISTER_NODE (nat44_ed_classify_node) = {
324 : .name = "nat44-ed-classify",
325 : .vector_size = sizeof (u32),
326 : .sibling_of = "nat-default",
327 : .format_trace = format_nat44_classify_trace,
328 : .type = VLIB_NODE_TYPE_INTERNAL,
329 : };
330 :
331 2285 : VLIB_NODE_FN (nat44_handoff_classify_node) (vlib_main_t * vm,
332 : vlib_node_runtime_t * node,
333 : vlib_frame_t * frame)
334 : {
335 49 : return nat44_handoff_classify_node_fn_inline (vm, node, frame);
336 : }
337 :
338 49304 : VLIB_REGISTER_NODE (nat44_handoff_classify_node) = {
339 : .name = "nat44-handoff-classify",
340 : .vector_size = sizeof (u32),
341 : .sibling_of = "nat-default",
342 : .format_trace = format_nat44_classify_trace,
343 : .type = VLIB_NODE_TYPE_INTERNAL,
344 : };
345 :
346 : /*
347 : * fd.io coding-style-patch-verification: ON
348 : *
349 : * Local Variables:
350 : * eval: (c-set-style "gnu")
351 : * End:
352 : */
|