2 * ct6_in2out.c - ip6 connection tracker, inside-to-outside path
4 * Copyright (c) 2019 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vppinfra/error.h>
29 #ifndef CLIB_MARCH_VARIANT
31 /* packet trace format function */
33 format_ct6_in2out_trace (u8 * s, va_list * args)
35 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
36 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
37 ct6_in2out_trace_t *t = va_arg (*args, ct6_in2out_trace_t *);
39 s = format (s, "CT6_IN2OUT: sw_if_index %d, next index %d session %d\n",
40 t->sw_if_index, t->next_index, t->session_index);
44 vlib_node_registration_t ct6_in2out_node;
46 #endif /* CLIB_MARCH_VARIANT */
48 #define foreach_ct6_in2out_error \
49 _(PROCESSED, "ct6 packets processed") \
50 _(CREATED, "ct6 sessions created") \
51 _(RECYCLED, "ct6 sessions recycled")
55 #define _(sym,str) CT6_IN2OUT_ERROR_##sym,
56 foreach_ct6_in2out_error
61 #ifndef CLIB_MARCH_VARIANT
62 static char *ct6_in2out_error_strings[] = {
63 #define _(sym,string) string,
64 foreach_ct6_in2out_error
67 #endif /* CLIB_MARCH_VARIANT */
75 #ifndef CLIB_MARCH_VARIANT
77 ct6_create_or_recycle_session (ct6_main_t * cmp,
78 clib_bihash_kv_48_8_t * kvpp, f64 now,
79 u32 my_thread_index, u32 * recyclep,
85 if (PREDICT_FALSE (cmp->last_index[my_thread_index] == ~0))
88 /* Look at the least-recently-used session */
89 s0 = pool_elt_at_index (cmp->sessions[my_thread_index],
90 cmp->last_index[my_thread_index]);
92 if (CLIB_DEBUG > 0 && s0->expires < now)
93 clib_warning ("session %d expired %.2f time now %.2f",
94 s0 - cmp->sessions[my_thread_index], s0->expires, now);
96 if (CLIB_DEBUG > 0 && pool_elts (cmp->sessions[my_thread_index]) >=
97 cmp->max_sessions_per_worker)
98 clib_warning ("recycle session %d have %d max %d",
99 s0 - cmp->sessions[my_thread_index],
100 pool_elts (cmp->sessions[my_thread_index]),
101 cmp->max_sessions_per_worker);
103 /* Session expired, or we have as many sessions as is allowed by law? */
104 if ((s0->expires < now) || (pool_elts (cmp->sessions[my_thread_index])
105 >= cmp->max_sessions_per_worker))
107 /* recycle the session */
108 if (clib_bihash_add_del_48_8 (&cmp->session_hash,
109 (clib_bihash_kv_48_8_t *) s0,
110 0 /* is_add */ ) < 0)
111 clib_warning ("session %d not found in hash?",
112 s0 - cmp->sessions[my_thread_index]);
114 ct6_lru_remove (cmp, s0);
120 /* Allocate a fresh session */
121 pool_get (cmp->sessions[my_thread_index], s0);
126 memset (s0, 0, sizeof (*s0));
127 clib_memcpy_fast (s0, kvpp, sizeof (ct6_session_key_t));
128 s0->thread_index = my_thread_index;
129 s0->expires = now + cmp->session_timeout_interval;
130 kvpp->value = s0 - cmp->sessions[my_thread_index];
131 clib_bihash_add_del_48_8 (&cmp->session_hash, kvpp, 1 /* is_add */ );
132 ct6_lru_add (cmp, s0, now);
135 #endif /* CLIB_MARCH_VARIANT */
138 ct6_in2out_inline (vlib_main_t * vm,
139 vlib_node_runtime_t * node, vlib_frame_t * frame,
142 u32 n_left_from, *from;
143 vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
144 u16 nexts[VLIB_FRAME_SIZE], *next;
145 ct6_main_t *cmp = &ct6_main;
146 u32 my_thread_index = vm->thread_index;
147 f64 now = vlib_time_now (vm);
151 from = vlib_frame_vector_args (frame);
152 n_left_from = frame->n_vectors;
154 vlib_get_buffers (vm, from, bufs, n_left_from);
159 while (n_left_from >= 4)
161 /* Prefetch next iteration. */
162 if (PREDICT_TRUE (n_left_from >= 8))
164 vlib_prefetch_buffer_header (b[4], STORE);
165 vlib_prefetch_buffer_header (b[5], STORE);
166 vlib_prefetch_buffer_header (b[6], STORE);
167 vlib_prefetch_buffer_header (b[7], STORE);
168 clib_prefetch_store (b[4]->data);
169 clib_prefetch_store (b[5]->data);
170 clib_prefetch_store (b[6]->data);
171 clib_prefetch_store (b[7]->data);
174 /* $$$$ process 4x pkts right here */
182 if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
184 ct6_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
185 t->next_index = next[0];
186 t->sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
188 if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
190 ct6_trace_t *t = vlib_add_trace (vm, node, b[1], sizeof (*t));
191 t->next_index = next[1];
192 t->sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
194 if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
196 ct6_trace_t *t = vlib_add_trace (vm, node, b[2], sizeof (*t));
197 t->next_index = next[2];
198 t->sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
200 if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
202 ct6_trace_t *t = vlib_add_trace (vm, node, b[3], sizeof (*t));
203 t->next_index = next[3];
204 t->sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
214 while (n_left_from > 0)
216 clib_bihash_kv_48_8_t kvp0;
217 ct6_session_key_t *key0;
219 u32 session_index0 = ~0;
221 ethernet_header_t *e0;
226 /* $$$ Set to 0 for pg testing */
229 vnet_feature_next (&next0, b[0]);
233 next[0] = CT6_IN2OUT_NEXT_DROP;
236 * This is an output feature which runs at the last possible
237 * moment. Assume an ethernet header. Make sure the packet is
238 * actually ipv6 before we do anything else.
240 * Unfortunately, we have to re-parse the L2 header.
243 e0 = vlib_buffer_get_current (b[0]);
244 delta0 = sizeof (*e0);
245 delta0 += (e0->type == clib_net_to_host_u16 (ETHERNET_TYPE_VLAN))
247 delta0 += (e0->type == clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AD))
250 if (PREDICT_TRUE (delta0 == sizeof (*e0)))
252 if (e0->type != clib_host_to_net_u16 (ETHERNET_TYPE_IP6))
257 u16 *tagged_etype_ptr = vlib_buffer_get_current (b[0]) + delta0 - 2;
258 if (*tagged_etype_ptr != clib_host_to_net_u16 (ETHERNET_TYPE_IP6))
262 ip0 = (ip6_header_t *) (vlib_buffer_get_current (b[0]) + delta0);
265 * Pass non-global unicast traffic
267 if (PREDICT_FALSE (!ip6_address_is_global_unicast (&ip0->src_address)
269 !ip6_address_is_global_unicast (&ip0->dst_address)))
271 /* Pass non-udp, non-tcp traffic */
272 if (PREDICT_FALSE (ip0->protocol != IP_PROTOCOL_TCP &&
273 ip0->protocol != IP_PROTOCOL_UDP))
276 udp0 = ip6_next_header (ip0);
279 * See if we know about this flow.
280 * Key set up for the out2in path, the performant case
282 key0 = (ct6_session_key_t *) & kvp0;
283 clib_memcpy_fast (&key0->src, &ip0->dst_address,
284 sizeof (ip6_address_t));
285 clib_memcpy_fast (&key0->dst, &ip0->src_address,
286 sizeof (ip6_address_t));
289 key0->sport = udp0->dst_port;
290 key0->dport = udp0->src_port;
291 key0->proto = ip0->protocol;
293 /* Need to create a new session? */
294 if (clib_bihash_search_48_8 (&cmp->session_hash, &kvp0, &kvp0) < 0)
297 ct6_create_or_recycle_session (cmp, &kvp0, now, my_thread_index,
298 &recycled, &created);
299 session_index0 = kvp0.value;
303 s0 = pool_elt_at_index (cmp->sessions[my_thread_index], kvp0.value);
304 session_index0 = kvp0.value;
305 ct6_update_session_hit (cmp, s0, now);
311 if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
313 ct6_in2out_trace_t *t =
314 vlib_add_trace (vm, node, b[0], sizeof (*t));
315 t->next_index = next[0];
316 t->sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
317 t->session_index = session_index0;
326 vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
328 vlib_node_increment_counter (vm, node->node_index,
329 CT6_IN2OUT_ERROR_PROCESSED, frame->n_vectors);
330 vlib_node_increment_counter (vm, node->node_index,
331 CT6_IN2OUT_ERROR_CREATED, created);
332 vlib_node_increment_counter (vm, node->node_index,
333 CT6_IN2OUT_ERROR_RECYCLED, recycled);
335 return frame->n_vectors;
338 VLIB_NODE_FN (ct6_in2out_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
339 vlib_frame_t * frame)
341 if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
342 return ct6_in2out_inline (vm, node, frame, 1 /* is_trace */ );
344 return ct6_in2out_inline (vm, node, frame, 0 /* is_trace */ );
347 #ifndef CLIB_MARCH_VARIANT
348 VLIB_REGISTER_NODE (ct6_in2out_node) =
350 .name = "ct6-in2out",
351 .vector_size = sizeof (u32),
352 .format_trace = format_ct6_in2out_trace,
353 .type = VLIB_NODE_TYPE_INTERNAL,
355 .n_errors = ARRAY_LEN(ct6_in2out_error_strings),
356 .error_strings = ct6_in2out_error_strings,
358 .n_next_nodes = CT6_IN2OUT_N_NEXT,
360 /* edit / add dispositions here */
362 [CT6_IN2OUT_NEXT_DROP] = "error-drop",
364 .unformat_buffer = unformat_ethernet_header,
366 #endif /* CLIB_MARCH_VARIANT */
369 * fd.io coding-style-patch-verification: ON
372 * eval: (c-set-style "gnu")