ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / src / plugins / ct6 / ct6_in2out.c
1 /*
2  * ct6_in2out.c - ip6 connection tracker, inside-to-outside path
3  *
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:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vppinfra/error.h>
20 #include <ct6/ct6.h>
21
22 typedef struct
23 {
24   u32 sw_if_index;
25   u32 next_index;
26   u32 session_index;
27 } ct6_in2out_trace_t;
28
29 #ifndef CLIB_MARCH_VARIANT
30
31 /* packet trace format function */
32 static u8 *
33 format_ct6_in2out_trace (u8 * s, va_list * args)
34 {
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 *);
38
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);
41   return s;
42 }
43
44 vlib_node_registration_t ct6_in2out_node;
45
46 #endif /* CLIB_MARCH_VARIANT */
47
48 #define foreach_ct6_in2out_error                \
49 _(PROCESSED, "ct6 packets processed")           \
50 _(CREATED, "ct6 sessions created")              \
51 _(RECYCLED, "ct6 sessions recycled")
52
53 typedef enum
54 {
55 #define _(sym,str) CT6_IN2OUT_ERROR_##sym,
56   foreach_ct6_in2out_error
57 #undef _
58     CT6_IN2OUT_N_ERROR,
59 } ct6_in2out_error_t;
60
61 #ifndef CLIB_MARCH_VARIANT
62 static char *ct6_in2out_error_strings[] = {
63 #define _(sym,string) string,
64   foreach_ct6_in2out_error
65 #undef _
66 };
67 #endif /* CLIB_MARCH_VARIANT */
68
69 typedef enum
70 {
71   CT6_IN2OUT_NEXT_DROP,
72   CT6_IN2OUT_N_NEXT,
73 } ct6_next_t;
74
75 #ifndef CLIB_MARCH_VARIANT
76 ct6_session_t *
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,
80                                u32 * createp)
81 {
82   ct6_session_t *s0;
83
84   /* Empty arena? */
85   if (PREDICT_FALSE (cmp->last_index[my_thread_index] == ~0))
86     goto alloc0;
87
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]);
91
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);
95
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);
102
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))
106     {
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]);
113
114       ct6_lru_remove (cmp, s0);
115       *recyclep += 1;
116     }
117   else
118     {
119     alloc0:
120       /* Allocate a fresh session */
121       pool_get (cmp->sessions[my_thread_index], s0);
122       *createp += 1;
123     }
124
125   /* Session setup */
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);
133   return s0;
134 }
135 #endif /* CLIB_MARCH_VARIANT */
136
137 always_inline uword
138 ct6_in2out_inline (vlib_main_t * vm,
139                    vlib_node_runtime_t * node, vlib_frame_t * frame,
140                    int is_trace)
141 {
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);
148   u32 created = 0;
149   u32 recycled = 0;
150
151   from = vlib_frame_vector_args (frame);
152   n_left_from = frame->n_vectors;
153
154   vlib_get_buffers (vm, from, bufs, n_left_from);
155   b = bufs;
156   next = nexts;
157
158 #if 0
159   while (n_left_from >= 4)
160     {
161       /* Prefetch next iteration. */
162       if (PREDICT_TRUE (n_left_from >= 8))
163         {
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);
172         }
173
174       /* $$$$ process 4x pkts right here */
175       next[0] = 0;
176       next[1] = 0;
177       next[2] = 0;
178       next[3] = 0;
179
180       if (is_trace)
181         {
182           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
183             {
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];
187             }
188           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
189             {
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];
193             }
194           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
195             {
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];
199             }
200           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
201             {
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];
205             }
206         }
207
208       b += 4;
209       next += 4;
210       n_left_from -= 4;
211     }
212 #endif
213
214   while (n_left_from > 0)
215     {
216       clib_bihash_kv_48_8_t kvp0;
217       ct6_session_key_t *key0;
218       ct6_session_t *s0;
219       u32 session_index0 = ~0;
220       u32 next0, delta0;
221       ethernet_header_t *e0;
222
223       ip6_header_t *ip0;
224       udp_header_t *udp0;
225
226       /* $$$ Set to 0 for pg testing */
227       if (1)
228         {
229           vnet_feature_next (&next0, b[0]);
230           next[0] = next0;
231         }
232       else
233         next[0] = CT6_IN2OUT_NEXT_DROP;
234
235       /*
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.
239        *
240        * Unfortunately, we have to re-parse the L2 header.
241        */
242
243       e0 = vlib_buffer_get_current (b[0]);
244       delta0 = sizeof (*e0);
245       delta0 += (e0->type == clib_net_to_host_u16 (ETHERNET_TYPE_VLAN))
246         ? 4 : 0;
247       delta0 += (e0->type == clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AD))
248         ? 8 : 0;
249
250       if (PREDICT_TRUE (delta0 == sizeof (*e0)))
251         {
252           if (e0->type != clib_host_to_net_u16 (ETHERNET_TYPE_IP6))
253             goto trace0;
254         }
255       else
256         {
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))
259             goto trace0;
260         }
261
262       ip0 = (ip6_header_t *) (vlib_buffer_get_current (b[0]) + delta0);
263
264       /*
265        * Pass non-global unicast traffic
266        */
267       if (PREDICT_FALSE (!ip6_address_is_global_unicast (&ip0->src_address)
268                          ||
269                          !ip6_address_is_global_unicast (&ip0->dst_address)))
270         goto trace0;
271       /* Pass non-udp, non-tcp traffic */
272       if (PREDICT_FALSE (ip0->protocol != IP_PROTOCOL_TCP &&
273                          ip0->protocol != IP_PROTOCOL_UDP))
274         goto trace0;
275
276       udp0 = ip6_next_header (ip0);
277
278       /*
279        * See if we know about this flow.
280        * Key set up for the out2in path, the performant case
281        */
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));
287       key0->as_u64[4] = 0;
288       key0->as_u64[5] = 0;
289       key0->sport = udp0->dst_port;
290       key0->dport = udp0->src_port;
291       key0->proto = ip0->protocol;
292
293       /* Need to create a new session? */
294       if (clib_bihash_search_48_8 (&cmp->session_hash, &kvp0, &kvp0) < 0)
295         {
296           s0 =
297             ct6_create_or_recycle_session (cmp, &kvp0, now, my_thread_index,
298                                            &recycled, &created);
299           session_index0 = kvp0.value;
300         }
301       else
302         {
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);
306         }
307
308     trace0:
309       if (is_trace)
310         {
311           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
312             {
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;
318             }
319         }
320
321       b += 1;
322       next += 1;
323       n_left_from -= 1;
324     }
325
326   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
327
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);
334
335   return frame->n_vectors;
336 }
337
338 VLIB_NODE_FN (ct6_in2out_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
339                                 vlib_frame_t * frame)
340 {
341   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
342     return ct6_in2out_inline (vm, node, frame, 1 /* is_trace */ );
343   else
344     return ct6_in2out_inline (vm, node, frame, 0 /* is_trace */ );
345 }
346
347 #ifndef CLIB_MARCH_VARIANT
348 VLIB_REGISTER_NODE (ct6_in2out_node) =
349 {
350   .name = "ct6-in2out",
351   .vector_size = sizeof (u32),
352   .format_trace = format_ct6_in2out_trace,
353   .type = VLIB_NODE_TYPE_INTERNAL,
354
355   .n_errors = ARRAY_LEN(ct6_in2out_error_strings),
356   .error_strings = ct6_in2out_error_strings,
357
358   .n_next_nodes = CT6_IN2OUT_N_NEXT,
359
360   /* edit / add dispositions here */
361   .next_nodes = {
362         [CT6_IN2OUT_NEXT_DROP] = "error-drop",
363   },
364   .unformat_buffer = unformat_ethernet_header,
365 };
366 #endif /* CLIB_MARCH_VARIANT */
367
368 /*
369  * fd.io coding-style-patch-verification: ON
370  *
371  * Local Variables:
372  * eval: (c-set-style "gnu")
373  * End:
374  */