VPP-1507: Added binary api to dump configured ip_punt_redirect
[vpp.git] / src / vnet / classify / ip_classify.c
1 /*
2  * Copyright (c) 2015 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 #include <vnet/ip/ip.h>
16 #include <vnet/ethernet/ethernet.h>     /* for ethernet_header_t */
17 #include <vnet/classify/vnet_classify.h>
18 #include <vnet/dpo/classify_dpo.h>
19
20 typedef struct
21 {
22   u32 next_index;
23   u32 table_index;
24   u32 entry_index;
25 } ip_classify_trace_t;
26
27 /* packet trace format function */
28 static u8 *
29 format_ip_classify_trace (u8 * s, va_list * args)
30 {
31   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
32   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
33   ip_classify_trace_t *t = va_arg (*args, ip_classify_trace_t *);
34
35   s = format (s, "IP_CLASSIFY: next_index %d, table %d, entry %d",
36               t->next_index, t->table_index, t->entry_index);
37   return s;
38 }
39
40 vlib_node_registration_t ip4_classify_node;
41 vlib_node_registration_t ip6_classify_node;
42
43 #define foreach_ip_classify_error               \
44 _(MISS, "Classify misses")                      \
45 _(HIT, "Classify hits")                         \
46 _(CHAIN_HIT, "Classify hits after chain walk")
47
48 typedef enum
49 {
50 #define _(sym,str) IP_CLASSIFY_ERROR_##sym,
51   foreach_ip_classify_error
52 #undef _
53     IP_CLASSIFY_N_ERROR,
54 } ip_classify_error_t;
55
56 static char *ip_classify_error_strings[] = {
57 #define _(sym,string) string,
58   foreach_ip_classify_error
59 #undef _
60 };
61
62 static inline uword
63 ip_classify_inline (vlib_main_t * vm,
64                     vlib_node_runtime_t * node,
65                     vlib_frame_t * frame, int is_ip4)
66 {
67   u32 n_left_from, *from, *to_next;
68   ip_lookup_next_t next_index;
69   vnet_classify_main_t *vcm = &vnet_classify_main;
70   f64 now = vlib_time_now (vm);
71   u32 hits = 0;
72   u32 misses = 0;
73   u32 chain_hits = 0;
74   u32 n_next;
75
76   if (is_ip4)
77     {
78       n_next = IP4_LOOKUP_N_NEXT;
79     }
80   else
81     {
82       n_next = IP6_LOOKUP_N_NEXT;
83     }
84
85   from = vlib_frame_vector_args (frame);
86   n_left_from = frame->n_vectors;
87
88   /* First pass: compute hashes */
89
90   while (n_left_from > 2)
91     {
92       vlib_buffer_t *b0, *b1;
93       u32 bi0, bi1;
94       u8 *h0, *h1;
95       u32 cd_index0, cd_index1;
96       classify_dpo_t *cd0, *cd1;
97       u32 table_index0, table_index1;
98       vnet_classify_table_t *t0, *t1;
99
100       /* prefetch next iteration */
101       {
102         vlib_buffer_t *p1, *p2;
103
104         p1 = vlib_get_buffer (vm, from[1]);
105         p2 = vlib_get_buffer (vm, from[2]);
106
107         vlib_prefetch_buffer_header (p1, STORE);
108         CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
109         vlib_prefetch_buffer_header (p2, STORE);
110         CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
111       }
112
113       bi0 = from[0];
114       b0 = vlib_get_buffer (vm, bi0);
115       h0 = (void *) vlib_buffer_get_current (b0) -
116         ethernet_buffer_header_size (b0);
117
118       bi1 = from[1];
119       b1 = vlib_get_buffer (vm, bi1);
120       h1 = (void *) vlib_buffer_get_current (b1) -
121         ethernet_buffer_header_size (b1);
122
123       cd_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
124       cd0 = classify_dpo_get (cd_index0);
125       table_index0 = cd0->cd_table_index;
126
127       cd_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
128       cd1 = classify_dpo_get (cd_index1);
129       table_index1 = cd1->cd_table_index;
130
131       t0 = pool_elt_at_index (vcm->tables, table_index0);
132
133       t1 = pool_elt_at_index (vcm->tables, table_index1);
134
135       vnet_buffer (b0)->l2_classify.hash =
136         vnet_classify_hash_packet (t0, (u8 *) h0);
137
138       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
139
140       vnet_buffer (b1)->l2_classify.hash =
141         vnet_classify_hash_packet (t1, (u8 *) h1);
142
143       vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);
144
145       vnet_buffer (b0)->l2_classify.table_index = table_index0;
146
147       vnet_buffer (b1)->l2_classify.table_index = table_index1;
148
149       from += 2;
150       n_left_from -= 2;
151     }
152
153   while (n_left_from > 0)
154     {
155       vlib_buffer_t *b0;
156       u32 bi0;
157       u8 *h0;
158       u32 cd_index0;
159       classify_dpo_t *cd0;
160       u32 table_index0;
161       vnet_classify_table_t *t0;
162
163       bi0 = from[0];
164       b0 = vlib_get_buffer (vm, bi0);
165       h0 = (void *) vlib_buffer_get_current (b0) -
166         ethernet_buffer_header_size (b0);
167
168       cd_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
169       cd0 = classify_dpo_get (cd_index0);
170       table_index0 = cd0->cd_table_index;
171
172       t0 = pool_elt_at_index (vcm->tables, table_index0);
173       vnet_buffer (b0)->l2_classify.hash =
174         vnet_classify_hash_packet (t0, (u8 *) h0);
175
176       vnet_buffer (b0)->l2_classify.table_index = table_index0;
177       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
178
179       from++;
180       n_left_from--;
181     }
182
183   next_index = node->cached_next_index;
184   from = vlib_frame_vector_args (frame);
185   n_left_from = frame->n_vectors;
186
187   while (n_left_from > 0)
188     {
189       u32 n_left_to_next;
190
191       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
192
193       /* Not enough load/store slots to dual loop... */
194       while (n_left_from > 0 && n_left_to_next > 0)
195         {
196           u32 bi0;
197           vlib_buffer_t *b0;
198           u32 next0 = IP_LOOKUP_NEXT_DROP;
199           u32 table_index0;
200           vnet_classify_table_t *t0;
201           vnet_classify_entry_t *e0;
202           u64 hash0;
203           u8 *h0;
204
205           /* Stride 3 seems to work best */
206           if (PREDICT_TRUE (n_left_from > 3))
207             {
208               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
209               vnet_classify_table_t *tp1;
210               u32 table_index1;
211               u64 phash1;
212
213               table_index1 = vnet_buffer (p1)->l2_classify.table_index;
214
215               if (PREDICT_TRUE (table_index1 != ~0))
216                 {
217                   tp1 = pool_elt_at_index (vcm->tables, table_index1);
218                   phash1 = vnet_buffer (p1)->l2_classify.hash;
219                   vnet_classify_prefetch_entry (tp1, phash1);
220                 }
221             }
222
223           /* speculatively enqueue b0 to the current next frame */
224           bi0 = from[0];
225           to_next[0] = bi0;
226           from += 1;
227           to_next += 1;
228           n_left_from -= 1;
229           n_left_to_next -= 1;
230
231           b0 = vlib_get_buffer (vm, bi0);
232           h0 = b0->data;
233           table_index0 = vnet_buffer (b0)->l2_classify.table_index;
234           e0 = 0;
235           t0 = 0;
236           vnet_buffer (b0)->l2_classify.opaque_index = ~0;
237
238           if (PREDICT_TRUE (table_index0 != ~0))
239             {
240               hash0 = vnet_buffer (b0)->l2_classify.hash;
241               t0 = pool_elt_at_index (vcm->tables, table_index0);
242
243               e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
244               if (e0)
245                 {
246                   vnet_buffer (b0)->l2_classify.opaque_index
247                     = e0->opaque_index;
248                   vlib_buffer_advance (b0, e0->advance);
249                   next0 = (e0->next_index < node->n_next_nodes) ?
250                     e0->next_index : next0;
251                   hits++;
252                 }
253               else
254                 {
255                   while (1)
256                     {
257                       if (t0->next_table_index != ~0)
258                         t0 = pool_elt_at_index (vcm->tables,
259                                                 t0->next_table_index);
260                       else
261                         {
262                           next0 = (t0->miss_next_index < n_next) ?
263                             t0->miss_next_index : next0;
264                           misses++;
265                           break;
266                         }
267
268                       hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
269                       e0 = vnet_classify_find_entry
270                         (t0, (u8 *) h0, hash0, now);
271                       if (e0)
272                         {
273                           vnet_buffer (b0)->l2_classify.opaque_index
274                             = e0->opaque_index;
275                           vlib_buffer_advance (b0, e0->advance);
276                           next0 = (e0->next_index < node->n_next_nodes) ?
277                             e0->next_index : next0;
278                           hits++;
279                           chain_hits++;
280                           break;
281                         }
282                     }
283                 }
284             }
285
286           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
287                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
288             {
289               ip_classify_trace_t *t =
290                 vlib_add_trace (vm, node, b0, sizeof (*t));
291               t->next_index = next0;
292               t->table_index = t0 ? t0 - vcm->tables : ~0;
293               t->entry_index = e0 ? e0 - t0->entries : ~0;
294             }
295
296           /* verify speculative enqueue, maybe switch current next frame */
297           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
298                                            to_next, n_left_to_next,
299                                            bi0, next0);
300         }
301
302       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
303     }
304
305   vlib_node_increment_counter (vm, node->node_index,
306                                IP_CLASSIFY_ERROR_MISS, misses);
307   vlib_node_increment_counter (vm, node->node_index,
308                                IP_CLASSIFY_ERROR_HIT, hits);
309   vlib_node_increment_counter (vm, node->node_index,
310                                IP_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
311   return frame->n_vectors;
312 }
313
314 static uword
315 ip4_classify (vlib_main_t * vm,
316               vlib_node_runtime_t * node, vlib_frame_t * frame)
317 {
318   return ip_classify_inline (vm, node, frame, 1 /* is_ip4 */ );
319 }
320
321
322 /* *INDENT-OFF* */
323 VLIB_REGISTER_NODE (ip4_classify_node) = {
324   .function = ip4_classify,
325   .name = "ip4-classify",
326   .vector_size = sizeof (u32),
327   .sibling_of = "ip4-lookup",
328   .format_trace = format_ip_classify_trace,
329   .n_errors = ARRAY_LEN(ip_classify_error_strings),
330   .error_strings = ip_classify_error_strings,
331
332   .n_next_nodes = 0,
333 };
334 /* *INDENT-ON* */
335
336 VLIB_NODE_FUNCTION_MULTIARCH (ip4_classify_node, ip4_classify)
337      static uword
338        ip6_classify (vlib_main_t * vm,
339                      vlib_node_runtime_t * node, vlib_frame_t * frame)
340 {
341   return ip_classify_inline (vm, node, frame, 0 /* is_ip4 */ );
342 }
343
344
345 /* *INDENT-OFF* */
346 VLIB_REGISTER_NODE (ip6_classify_node) = {
347   .function = ip6_classify,
348   .name = "ip6-classify",
349   .vector_size = sizeof (u32),
350   .sibling_of = "ip6-lookup",
351   .format_trace = format_ip_classify_trace,
352   .n_errors = ARRAY_LEN(ip_classify_error_strings),
353   .error_strings = ip_classify_error_strings,
354
355   .n_next_nodes = 0,
356 };
357 /* *INDENT-ON* */
358
359 VLIB_NODE_FUNCTION_MULTIARCH (ip6_classify_node, ip6_classify)
360      static clib_error_t *ip_classify_init (vlib_main_t * vm)
361 {
362   return 0;
363 }
364
365 VLIB_INIT_FUNCTION (ip_classify_init);
366
367 /*
368  * fd.io coding-style-patch-verification: ON
369  *
370  * Local Variables:
371  * eval: (c-set-style "gnu")
372  * End:
373  */