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